NetBSD/dist/wpa_supplicant/wpa_supplicant.c

2483 lines
65 KiB
C

/*
* WPA Supplicant
* Copyright (c) 2003-2005, Jouni Malinen <jkmaline@cc.hut.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Alternatively, this software may be distributed under the terms of BSD
* license.
*
* See README and COPYING for more details.
*/
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <unistd.h>
#include <string.h>
#include <sys/time.h>
#include <time.h>
#include <signal.h>
#include <sys/types.h>
#ifndef CONFIG_NATIVE_WINDOWS
#include <sys/socket.h>
#include <sys/un.h>
#endif /* CONFIG_NATIVE_WINDOWS */
#include <unistd.h>
#include <ctype.h>
#ifndef CONFIG_NATIVE_WINDOWS
#include <netinet/in.h>
#endif /* CONFIG_NATIVE_WINDOWS */
#include <fcntl.h>
#define OPENSSL_DISABLE_OLD_DES_SUPPORT
#include "common.h"
#include "eapol_sm.h"
#include "eap.h"
#include "wpa.h"
#include "driver.h"
#include "eloop.h"
#include "wpa_supplicant.h"
#include "config.h"
#include "l2_packet.h"
#include "wpa_supplicant_i.h"
#include "ctrl_iface.h"
#include "pcsc_funcs.h"
#include "version.h"
static const char *wpa_supplicant_version =
"wpa_supplicant v" VERSION_STR "\n"
"Copyright (c) 2003-2005, Jouni Malinen <jkmaline@cc.hut.fi> and contributors";
static const char *wpa_supplicant_license =
"This program is free software. You can distribute it and/or modify it\n"
"under the terms of the GNU General Public License version 2.\n"
"\n"
"Alternatively, this software may be distributed under the terms of the\n"
"BSD license. See README and COPYING for more details.\n"
#ifdef EAP_TLS_FUNCS
"\nThis product includes software developed by the OpenSSL Project\n"
"for use in the OpenSSL Toolkit (http://www.openssl.org/)\n"
#endif /* EAP_TLS_FUNCS */
;
static const char *wpa_supplicant_full_license =
"This program is free software; you can redistribute it and/or modify\n"
"it under the terms of the GNU General Public License version 2 as\n"
"published by the Free Software Foundation.\n"
"\n"
"This program is distributed in the hope that it will be useful,\n"
"but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
"GNU General Public License for more details.\n"
"\n"
"You should have received a copy of the GNU General Public License\n"
"along with this program; if not, write to the Free Software\n"
"Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\n"
"\n"
"Alternatively, this software may be distributed under the terms of the\n"
"BSD license.\n"
"\n"
"Redistribution and use in source and binary forms, with or without\n"
"modification, are permitted provided that the following conditions are\n"
"met:\n"
"\n"
"1. Redistributions of source code must retain the above copyright\n"
" notice, this list of conditions and the following disclaimer.\n"
"\n"
"2. Redistributions in binary form must reproduce the above copyright\n"
" notice, this list of conditions and the following disclaimer in the\n"
" documentation and/or other materials provided with the distribution.\n"
"\n"
"3. Neither the name(s) of the above-listed copyright holder(s) nor the\n"
" names of its contributors may be used to endorse or promote products\n"
" derived from this software without specific prior written permission.\n"
"\n"
"THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n"
"\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n"
"LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n"
"A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n"
"OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n"
"SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n"
"LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n"
"DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n"
"THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n"
"(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n"
"OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
"\n";
extern struct wpa_driver_ops *wpa_supplicant_drivers[];
static void wpa_supplicant_scan_results(struct wpa_supplicant *wpa_s);
static int wpa_supplicant_driver_init(struct wpa_supplicant *wpa_s,
int wait_for_interface);
static void wpa_supplicant_associate(struct wpa_supplicant *wpa_s,
struct wpa_scan_result *bss,
struct wpa_ssid *ssid);
static int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s,
struct wpa_scan_result *bss,
struct wpa_ssid *ssid,
u8 *wpa_ie, int *wpa_ie_len);
extern int wpa_debug_level;
extern int wpa_debug_show_keys;
extern int wpa_debug_timestamp;
void wpa_msg(struct wpa_supplicant *wpa_s, int level, char *fmt, ...)
{
va_list ap;
char *buf;
const int buflen = 2048;
int len;
buf = malloc(buflen);
if (buf == NULL) {
printf("Failed to allocate message buffer for:\n");
va_start(ap, fmt);
vprintf(fmt, ap);
printf("\n");
va_end(ap);
return;
}
va_start(ap, fmt);
len = vsnprintf(buf, buflen, fmt, ap);
va_end(ap);
wpa_printf(level, "%s", buf);
wpa_supplicant_ctrl_iface_send(wpa_s, level, buf, len);
free(buf);
}
int wpa_eapol_send(void *ctx, int type, u8 *buf, size_t len)
{
struct wpa_supplicant *wpa_s = ctx;
u8 *msg, *dst, bssid[ETH_ALEN];
size_t msglen;
struct l2_ethhdr *ethhdr;
struct ieee802_1x_hdr *hdr;
int res;
/* TODO: could add l2_packet_sendmsg that allows fragments to avoid
* extra copy here */
if (wpa_s->key_mgmt == WPA_KEY_MGMT_PSK ||
wpa_s->key_mgmt == WPA_KEY_MGMT_NONE) {
/* Current SSID is not using IEEE 802.1X/EAP, so drop possible
* EAPOL frames (mainly, EAPOL-Start) from EAPOL state
* machines. */
wpa_printf(MSG_DEBUG, "WPA: drop TX EAPOL in non-IEEE 802.1X "
"mode (type=%d len=%lu)", type,
(unsigned long) len);
return -1;
}
if (wpa_s->cur_pmksa && type == IEEE802_1X_TYPE_EAPOL_START) {
/* Trying to use PMKSA caching - do not send EAPOL-Start frames
* since they will trigger full EAPOL authentication. */
wpa_printf(MSG_DEBUG, "RSN: PMKSA caching - do not send "
"EAPOL-Start");
return -1;
}
if (memcmp(wpa_s->bssid, "\x00\x00\x00\x00\x00\x00", ETH_ALEN) == 0) {
wpa_printf(MSG_DEBUG, "BSSID not set when trying to send an "
"EAPOL frame");
if (wpa_drv_get_bssid(wpa_s, bssid) == 0 &&
memcmp(bssid, "\x00\x00\x00\x00\x00\x00", ETH_ALEN) != 0) {
dst = bssid;
wpa_printf(MSG_DEBUG, "Using current BSSID " MACSTR
" from the driver as the EAPOL destination",
MAC2STR(dst));
} else {
dst = wpa_s->last_eapol_src;
wpa_printf(MSG_DEBUG, "Using the source address of the"
" last received EAPOL frame " MACSTR " as "
"the EAPOL destination",
MAC2STR(dst));
}
} else {
/* BSSID was already set (from (Re)Assoc event, so use it as
* the EAPOL destination. */
dst = wpa_s->bssid;
}
msglen = sizeof(*ethhdr) + sizeof(*hdr) + len;
msg = malloc(msglen);
if (msg == NULL)
return -1;
ethhdr = (struct l2_ethhdr *) msg;
memcpy(ethhdr->h_dest, dst, ETH_ALEN);
memcpy(ethhdr->h_source, wpa_s->own_addr, ETH_ALEN);
ethhdr->h_proto = htons(ETH_P_EAPOL);
hdr = (struct ieee802_1x_hdr *) (ethhdr + 1);
hdr->version = wpa_s->conf->eapol_version;
hdr->type = type;
hdr->length = htons(len);
memcpy((u8 *) (hdr + 1), buf, len);
wpa_hexdump(MSG_MSGDUMP, "TX EAPOL", msg, msglen);
res = l2_packet_send(wpa_s->l2, msg, msglen);
free(msg);
return res;
}
int wpa_eapol_send_preauth(void *ctx, int type, u8 *buf, size_t len)
{
struct wpa_supplicant *wpa_s = ctx;
u8 *msg;
size_t msglen;
struct l2_ethhdr *ethhdr;
struct ieee802_1x_hdr *hdr;
int res;
/* TODO: could add l2_packet_sendmsg that allows fragments to avoid
* extra copy here */
if (wpa_s->l2_preauth == NULL)
return -1;
msglen = sizeof(*ethhdr) + sizeof(*hdr) + len;
msg = malloc(msglen);
if (msg == NULL)
return -1;
ethhdr = (struct l2_ethhdr *) msg;
memcpy(ethhdr->h_dest, wpa_s->preauth_bssid, ETH_ALEN);
memcpy(ethhdr->h_source, wpa_s->own_addr, ETH_ALEN);
ethhdr->h_proto = htons(ETH_P_RSN_PREAUTH);
hdr = (struct ieee802_1x_hdr *) (ethhdr + 1);
hdr->version = wpa_s->conf->eapol_version;
hdr->type = type;
hdr->length = htons(len);
memcpy((u8 *) (hdr + 1), buf, len);
wpa_hexdump(MSG_MSGDUMP, "TX EAPOL (preauth)", msg, msglen);
res = l2_packet_send(wpa_s->l2_preauth, msg, msglen);
free(msg);
return res;
}
/**
* wpa_eapol_set_wep_key - set WEP key for the driver
* @ctx: pointer to wpa_supplicant data
* @unicast: 1 = individual unicast key, 0 = broadcast key
* @keyidx: WEP key index (0..3)
* @key: pointer to key data
* @keylen: key length in bytes
*
* Returns 0 on success or < 0 on error.
*/
static int wpa_eapol_set_wep_key(void *ctx, int unicast, int keyidx,
u8 *key, size_t keylen)
{
struct wpa_supplicant *wpa_s = ctx;
wpa_s->keys_cleared = 0;
return wpa_drv_set_key(wpa_s, WPA_ALG_WEP,
unicast ? wpa_s->bssid :
(u8 *) "\xff\xff\xff\xff\xff\xff",
keyidx, unicast, (u8 *) "", 0, key, keylen);
}
/* Configure default/group WEP key for static WEP */
static int wpa_set_wep_key(void *ctx, int set_tx, int keyidx, const u8 *key,
size_t keylen)
{
struct wpa_supplicant *wpa_s = ctx;
wpa_s->keys_cleared = 0;
return wpa_drv_set_key(wpa_s, WPA_ALG_WEP,
(u8 *) "\xff\xff\xff\xff\xff\xff",
keyidx, set_tx, (u8 *) "", 0, key, keylen);
}
static int wpa_supplicant_set_wpa_none_key(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid)
{
u8 key[32];
size_t keylen;
wpa_alg alg;
u8 seq[6] = { 0 };
/* IBSS/WPA-None uses only one key (Group) for both receiving and
* sending unicast and multicast packets. */
if (ssid->mode != IEEE80211_MODE_IBSS) {
wpa_printf(MSG_INFO, "WPA: Invalid mode %d (not IBSS/ad-hoc) "
"for WPA-None", ssid->mode);
return -1;
}
if (!ssid->psk_set) {
wpa_printf(MSG_INFO, "WPA: No PSK configured for WPA-None");
return -1;
}
switch (wpa_s->group_cipher) {
case WPA_CIPHER_CCMP:
memcpy(key, ssid->psk, 16);
keylen = 16;
alg = WPA_ALG_CCMP;
break;
case WPA_CIPHER_TKIP:
/* WPA-None uses the same Michael MIC key for both TX and RX */
memcpy(key, ssid->psk, 16 + 8);
memcpy(key + 16 + 8, ssid->psk + 16, 8);
keylen = 32;
alg = WPA_ALG_TKIP;
break;
default:
wpa_printf(MSG_INFO, "WPA: Invalid group cipher %d for "
"WPA-None", wpa_s->group_cipher);
return -1;
}
/* TODO: should actually remember the previously used seq#, both for TX
* and RX from each STA.. */
return wpa_drv_set_key(wpa_s, alg, (u8 *) "\xff\xff\xff\xff\xff\xff",
0, 1, seq, 6, key, keylen);
}
void wpa_supplicant_notify_eapol_done(void *ctx)
{
struct wpa_supplicant *wpa_s = ctx;
wpa_msg(wpa_s, MSG_DEBUG, "WPA: EAPOL processing complete");
eloop_cancel_timeout(wpa_supplicant_scan, wpa_s, NULL);
wpa_supplicant_cancel_auth_timeout(wpa_s);
}
static struct wpa_blacklist *
wpa_blacklist_get(struct wpa_supplicant *wpa_s, const u8 *bssid)
{
struct wpa_blacklist *e;
e = wpa_s->blacklist;
while (e) {
if (memcmp(e->bssid, bssid, ETH_ALEN) == 0)
return e;
e = e->next;
}
return NULL;
}
static int wpa_blacklist_add(struct wpa_supplicant *wpa_s, const u8 *bssid)
{
struct wpa_blacklist *e;
e = wpa_blacklist_get(wpa_s, bssid);
if (e) {
e->count++;
wpa_printf(MSG_DEBUG, "BSSID " MACSTR " blacklist count "
"incremented to %d",
MAC2STR(bssid), e->count);
return 0;
}
e = malloc(sizeof(*e));
if (e == NULL)
return -1;
memset(e, 0, sizeof(*e));
memcpy(e->bssid, bssid, ETH_ALEN);
e->count = 1;
e->next = wpa_s->blacklist;
wpa_s->blacklist = e;
wpa_printf(MSG_DEBUG, "Added BSSID " MACSTR " into blacklist",
MAC2STR(bssid));
return 0;
}
static int wpa_blacklist_del(struct wpa_supplicant *wpa_s, const u8 *bssid)
{
struct wpa_blacklist *e, *prev = NULL;
e = wpa_s->blacklist;
while (e) {
if (memcmp(e->bssid, bssid, ETH_ALEN) == 0) {
if (prev == NULL) {
wpa_s->blacklist = e->next;
} else {
prev->next = e->next;
}
wpa_printf(MSG_DEBUG, "Removed BSSID " MACSTR " from "
"blacklist", MAC2STR(bssid));
free(e);
return 0;
}
prev = e;
e = e->next;
}
return -1;
}
static void wpa_blacklist_clear(struct wpa_supplicant *wpa_s)
{
struct wpa_blacklist *e, *prev;
e = wpa_s->blacklist;
wpa_s->blacklist = NULL;
while (e) {
prev = e;
e = e->next;
wpa_printf(MSG_DEBUG, "Removed BSSID " MACSTR " from "
"blacklist (clear)", MAC2STR(prev->bssid));
free(prev);
}
}
const char * wpa_ssid_txt(u8 *ssid, size_t ssid_len)
{
static char ssid_txt[MAX_SSID_LEN + 1];
char *pos;
if (ssid_len > MAX_SSID_LEN)
ssid_len = MAX_SSID_LEN;
memcpy(ssid_txt, ssid, ssid_len);
ssid_txt[ssid_len] = '\0';
for (pos = ssid_txt; *pos != '\0'; pos++) {
if ((u8) *pos < 32 || (u8) *pos >= 127)
*pos = '_';
}
return ssid_txt;
}
void wpa_supplicant_req_scan(struct wpa_supplicant *wpa_s, int sec, int usec)
{
wpa_msg(wpa_s, MSG_DEBUG, "Setting scan request: %d sec %d usec",
sec, usec);
eloop_cancel_timeout(wpa_supplicant_scan, wpa_s, NULL);
eloop_register_timeout(sec, usec, wpa_supplicant_scan, wpa_s, NULL);
}
void wpa_supplicant_cancel_scan(struct wpa_supplicant *wpa_s)
{
wpa_msg(wpa_s, MSG_DEBUG, "Cancelling scan request");
eloop_cancel_timeout(wpa_supplicant_scan, wpa_s, NULL);
}
static void wpa_supplicant_timeout(void *eloop_ctx, void *timeout_ctx)
{
struct wpa_supplicant *wpa_s = eloop_ctx;
wpa_msg(wpa_s, MSG_INFO, "Authentication with " MACSTR " timed out.",
MAC2STR(wpa_s->bssid));
wpa_blacklist_add(wpa_s, wpa_s->bssid);
wpa_supplicant_disassociate(wpa_s, REASON_DEAUTH_LEAVING);
wpa_s->reassociate = 1;
wpa_supplicant_req_scan(wpa_s, 0, 0);
}
void wpa_supplicant_req_auth_timeout(struct wpa_supplicant *wpa_s,
int sec, int usec)
{
wpa_msg(wpa_s, MSG_DEBUG, "Setting authentication timeout: %d sec "
"%d usec", sec, usec);
eloop_cancel_timeout(wpa_supplicant_timeout, wpa_s, NULL);
eloop_register_timeout(sec, usec, wpa_supplicant_timeout, wpa_s, NULL);
}
void wpa_supplicant_cancel_auth_timeout(struct wpa_supplicant *wpa_s)
{
wpa_msg(wpa_s, MSG_DEBUG, "Cancelling authentication timeout");
eloop_cancel_timeout(wpa_supplicant_timeout, wpa_s, NULL);
wpa_blacklist_del(wpa_s, wpa_s->bssid);
}
static void wpa_supplicant_initiate_eapol(struct wpa_supplicant *wpa_s)
{
struct eapol_config eapol_conf;
struct wpa_ssid *ssid = wpa_s->current_ssid;
if (wpa_s->key_mgmt == WPA_KEY_MGMT_PSK) {
eapol_sm_notify_eap_success(wpa_s->eapol, FALSE);
eapol_sm_notify_eap_fail(wpa_s->eapol, FALSE);
}
if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE ||
wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE)
eapol_sm_notify_portControl(wpa_s->eapol, ForceAuthorized);
else
eapol_sm_notify_portControl(wpa_s->eapol, Auto);
memset(&eapol_conf, 0, sizeof(eapol_conf));
if (wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) {
eapol_conf.accept_802_1x_keys = 1;
eapol_conf.required_keys = 0;
if (ssid->eapol_flags & EAPOL_FLAG_REQUIRE_KEY_UNICAST) {
eapol_conf.required_keys |= EAPOL_REQUIRE_KEY_UNICAST;
}
if (ssid->eapol_flags & EAPOL_FLAG_REQUIRE_KEY_BROADCAST) {
eapol_conf.required_keys |=
EAPOL_REQUIRE_KEY_BROADCAST;
}
}
eapol_conf.fast_reauth = wpa_s->conf->fast_reauth;
eapol_conf.workaround = ssid->eap_workaround;
eapol_sm_notify_config(wpa_s->eapol, ssid, &eapol_conf);
}
static void wpa_supplicant_set_non_wpa_policy(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid)
{
int i;
if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA)
wpa_s->key_mgmt = WPA_KEY_MGMT_IEEE8021X_NO_WPA;
else
wpa_s->key_mgmt = WPA_KEY_MGMT_NONE;
free(wpa_s->ap_wpa_ie);
wpa_s->ap_wpa_ie = NULL;
wpa_s->ap_wpa_ie_len = 0;
free(wpa_s->ap_rsn_ie);
wpa_s->ap_rsn_ie = NULL;
wpa_s->ap_rsn_ie_len = 0;
free(wpa_s->assoc_wpa_ie);
wpa_s->assoc_wpa_ie = NULL;
wpa_s->assoc_wpa_ie_len = 0;
wpa_s->pairwise_cipher = WPA_CIPHER_NONE;
wpa_s->group_cipher = WPA_CIPHER_NONE;
for (i = 0; i < NUM_WEP_KEYS; i++) {
if (ssid->wep_key_len[i] > 5) {
wpa_s->pairwise_cipher = WPA_CIPHER_WEP104;
wpa_s->group_cipher = WPA_CIPHER_WEP104;
break;
} else if (ssid->wep_key_len[i] > 0) {
wpa_s->pairwise_cipher = WPA_CIPHER_WEP40;
wpa_s->group_cipher = WPA_CIPHER_WEP40;
break;
}
}
wpa_s->cur_pmksa = NULL;
}
static int wpa_supplicant_select_config(struct wpa_supplicant *wpa_s)
{
struct wpa_ssid *ssid;
if (wpa_s->conf->ap_scan == 1)
return 0;
ssid = wpa_supplicant_get_ssid(wpa_s);
if (ssid == NULL) {
wpa_printf(MSG_INFO, "No network configuration found for the "
"current AP");
return -1;
}
wpa_printf(MSG_DEBUG, "Network configuration found for the current "
"AP");
if (ssid->key_mgmt & (WPA_KEY_MGMT_PSK | WPA_KEY_MGMT_IEEE8021X |
WPA_KEY_MGMT_WPA_NONE)) {
u8 wpa_ie[80];
int wpa_ie_len;
wpa_supplicant_set_suites(wpa_s, NULL, ssid,
wpa_ie, &wpa_ie_len);
} else {
wpa_supplicant_set_non_wpa_policy(wpa_s, ssid);
}
wpa_s->current_ssid = ssid;
wpa_supplicant_initiate_eapol(wpa_s);
return 0;
}
static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s)
{
scard_deinit(wpa_s->scard);
wpa_s->scard = NULL;
eapol_sm_register_scard_ctx(wpa_s->eapol, NULL);
l2_packet_deinit(wpa_s->l2);
wpa_s->l2 = NULL;
#ifdef CONFIG_XSUPPLICANT_IFACE
if (wpa_s->dot1x_s > -1) {
close(wpa_s->dot1x_s);
wpa_s->dot1x_s = -1;
}
#endif /* CONFIG_XSUPPLICANT_IFACE */
wpa_supplicant_ctrl_iface_deinit(wpa_s);
if (wpa_s->conf != NULL) {
wpa_config_free(wpa_s->conf);
wpa_s->conf = NULL;
}
free(wpa_s->assoc_wpa_ie);
wpa_s->assoc_wpa_ie = NULL;
free(wpa_s->ap_wpa_ie);
wpa_s->ap_wpa_ie = NULL;
free(wpa_s->ap_rsn_ie);
wpa_s->ap_rsn_ie = NULL;
free(wpa_s->confname);
wpa_s->confname = NULL;
eapol_sm_deinit(wpa_s->eapol);
wpa_s->eapol = NULL;
rsn_preauth_deinit(wpa_s);
pmksa_candidate_free(wpa_s);
pmksa_cache_free(wpa_s);
wpa_blacklist_clear(wpa_s);
free(wpa_s->scan_results);
wpa_s->scan_results = NULL;
wpa_s->num_scan_results = 0;
}
static void wpa_clear_keys(struct wpa_supplicant *wpa_s, u8 *addr)
{
u8 *bcast = (u8 *) "\xff\xff\xff\xff\xff\xff";
if (wpa_s->keys_cleared) {
/* Some drivers (e.g., ndiswrapper & NDIS drivers) seem to have
* timing issues with keys being cleared just before new keys
* are set or just after association or something similar. This
* shows up in group key handshake failing often because of the
* client not receiving the first encrypted packets correctly.
* Skipping some of the extra key clearing steps seems to help
* in completing group key handshake more reliably. */
wpa_printf(MSG_DEBUG, "No keys have been configured - "
"skip key clearing");
return;
}
wpa_drv_set_key(wpa_s, WPA_ALG_NONE, bcast, 0, 0, NULL, 0, NULL, 0);
wpa_drv_set_key(wpa_s, WPA_ALG_NONE, bcast, 1, 0, NULL, 0, NULL, 0);
wpa_drv_set_key(wpa_s, WPA_ALG_NONE, bcast, 2, 0, NULL, 0, NULL, 0);
wpa_drv_set_key(wpa_s, WPA_ALG_NONE, bcast, 3, 0, NULL, 0, NULL, 0);
if (addr) {
wpa_drv_set_key(wpa_s, WPA_ALG_NONE, addr, 0, 0, NULL, 0, NULL,
0);
}
wpa_s->keys_cleared = 1;
}
static void wpa_supplicant_stop_countermeasures(void *eloop_ctx,
void *sock_ctx)
{
struct wpa_supplicant *wpa_s = eloop_ctx;
if (wpa_s->countermeasures) {
wpa_s->countermeasures = 0;
wpa_drv_set_countermeasures(wpa_s, 0);
wpa_msg(wpa_s, MSG_INFO, "WPA: TKIP countermeasures stopped");
wpa_supplicant_req_scan(wpa_s, 0, 0);
}
}
static void wpa_supplicant_mark_disassoc(struct wpa_supplicant *wpa_s)
{
wpa_s->wpa_state = WPA_DISCONNECTED;
memset(wpa_s->bssid, 0, ETH_ALEN);
eapol_sm_notify_portEnabled(wpa_s->eapol, FALSE);
eapol_sm_notify_portValid(wpa_s->eapol, FALSE);
if (wpa_s->key_mgmt == WPA_KEY_MGMT_PSK)
eapol_sm_notify_eap_success(wpa_s->eapol, FALSE);
}
static void wpa_find_assoc_pmkid(struct wpa_supplicant *wpa_s)
{
struct wpa_ie_data ie;
int i;
if (wpa_parse_wpa_ie(wpa_s, wpa_s->assoc_wpa_ie,
wpa_s->assoc_wpa_ie_len, &ie) < 0 ||
ie.pmkid == NULL)
return;
for (i = 0; i < ie.num_pmkid; i++) {
wpa_s->cur_pmksa = pmksa_cache_get(wpa_s, NULL,
ie.pmkid + i * PMKID_LEN);
if (wpa_s->cur_pmksa) {
eapol_sm_notify_pmkid_attempt(wpa_s->eapol, 1);
break;
}
}
wpa_printf(MSG_DEBUG, "RSN: PMKID from assoc IE %sfound from PMKSA "
"cache", wpa_s->cur_pmksa ? "" : "not ");
}
static void wpa_supplicant_add_pmkid_candidate(struct wpa_supplicant *wpa_s,
union wpa_event_data *data)
{
if (data == NULL) {
wpa_printf(MSG_DEBUG, "RSN: No data in PMKID candidate event");
return;
}
wpa_printf(MSG_DEBUG, "RSN: PMKID candidate event - bssid=" MACSTR
" index=%d preauth=%d",
MAC2STR(data->pmkid_candidate.bssid),
data->pmkid_candidate.index,
data->pmkid_candidate.preauth);
if (!data->pmkid_candidate.preauth) {
wpa_printf(MSG_DEBUG, "RSN: Ignored PMKID candidate without "
"preauth flag");
return;
}
pmksa_candidate_add(wpa_s, data->pmkid_candidate.bssid,
data->pmkid_candidate.index);
}
static int wpa_supplicant_dynamic_keys(struct wpa_supplicant *wpa_s)
{
if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE ||
wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE)
return 0;
if (wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA &&
wpa_s->current_ssid &&
!(wpa_s->current_ssid->eapol_flags &
(EAPOL_FLAG_REQUIRE_KEY_UNICAST |
EAPOL_FLAG_REQUIRE_KEY_BROADCAST))) {
/* IEEE 802.1X, but not using dynamic WEP keys (i.e., either
* plaintext or static WEP keys). */
return 0;
}
return 1;
}
static void wpa_supplicant_associnfo(struct wpa_supplicant *wpa_s,
union wpa_event_data *data)
{
int l, len;
u8 *p;
wpa_printf(MSG_DEBUG, "Association info event");
wpa_hexdump(MSG_DEBUG, "req_ies", data->assoc_info.req_ies,
data->assoc_info.req_ies_len);
wpa_hexdump(MSG_DEBUG, "resp_ies", data->assoc_info.resp_ies,
data->assoc_info.resp_ies_len);
if (wpa_s->assoc_wpa_ie) {
free(wpa_s->assoc_wpa_ie);
wpa_s->assoc_wpa_ie = NULL;
wpa_s->assoc_wpa_ie_len = 0;
}
p = data->assoc_info.req_ies;
l = data->assoc_info.req_ies_len;
/* Go through the IEs and make a copy of the WPA/RSN IE, if present. */
while (l >= 2) {
len = p[1] + 2;
if (len > l) {
wpa_hexdump(MSG_DEBUG, "Truncated IE in assoc_info",
p, l);
break;
}
if ((p[0] == GENERIC_INFO_ELEM && p[1] >= 6 &&
(memcmp(&p[2], "\x00\x50\xF2\x01\x01\x00", 6) == 0)) ||
(p[0] == RSN_INFO_ELEM && p[1] >= 2)) {
wpa_s->assoc_wpa_ie = malloc(len);
if (wpa_s->assoc_wpa_ie == NULL)
break;
wpa_s->assoc_wpa_ie_len = len;
memcpy(wpa_s->assoc_wpa_ie, p, len);
wpa_hexdump(MSG_DEBUG, "assoc_wpa_ie",
wpa_s->assoc_wpa_ie,
wpa_s->assoc_wpa_ie_len);
wpa_find_assoc_pmkid(wpa_s);
break;
}
l -= len;
p += len;
}
/* WPA/RSN IE from Beacon/ProbeResp */
free(wpa_s->ap_wpa_ie);
wpa_s->ap_wpa_ie = NULL;
wpa_s->ap_wpa_ie_len = 0;
free(wpa_s->ap_rsn_ie);
wpa_s->ap_rsn_ie = NULL;
wpa_s->ap_rsn_ie_len = 0;
p = data->assoc_info.beacon_ies;
l = data->assoc_info.beacon_ies_len;
/* Go through the IEs and make a copy of the WPA/RSN IEs, if present.
*/
while (l >= 2) {
len = p[1] + 2;
if (len > l) {
wpa_hexdump(MSG_DEBUG, "Truncated IE in beacon_ies",
p, l);
break;
}
if (wpa_s->ap_wpa_ie == NULL &&
p[0] == GENERIC_INFO_ELEM && p[1] >= 6 &&
memcmp(&p[2], "\x00\x50\xF2\x01\x01\x00", 6) == 0) {
wpa_s->ap_wpa_ie = malloc(len);
if (wpa_s->ap_wpa_ie) {
memcpy(wpa_s->ap_wpa_ie, p, len);
wpa_s->ap_wpa_ie_len = len;
}
}
if (wpa_s->ap_rsn_ie == NULL &&
p[0] == RSN_INFO_ELEM && p[1] >= 2) {
wpa_s->ap_rsn_ie = malloc(len);
if (wpa_s->ap_rsn_ie) {
memcpy(wpa_s->ap_rsn_ie, p, len);
wpa_s->ap_rsn_ie_len = len;
}
}
l -= len;
p += len;
}
}
void wpa_supplicant_event(struct wpa_supplicant *wpa_s, wpa_event_type event,
union wpa_event_data *data)
{
int pairwise;
time_t now;
u8 bssid[ETH_ALEN];
switch (event) {
case EVENT_ASSOC:
wpa_s->wpa_state = WPA_ASSOCIATED;
wpa_printf(MSG_DEBUG, "Association event - clear replay "
"counter");
memset(wpa_s->rx_replay_counter, 0, WPA_REPLAY_COUNTER_LEN);
wpa_s->rx_replay_counter_set = 0;
wpa_s->renew_snonce = 1;
if (wpa_drv_get_bssid(wpa_s, bssid) >= 0 &&
memcmp(bssid, wpa_s->bssid, ETH_ALEN) != 0) {
wpa_msg(wpa_s, MSG_DEBUG, "Associated to a new BSS: "
"BSSID=" MACSTR, MAC2STR(bssid));
memcpy(wpa_s->bssid, bssid, ETH_ALEN);
if (wpa_supplicant_dynamic_keys(wpa_s)) {
wpa_clear_keys(wpa_s, bssid);
}
wpa_supplicant_select_config(wpa_s);
}
wpa_msg(wpa_s, MSG_INFO, "Associated with " MACSTR,
MAC2STR(bssid));
/* Set portEnabled first to FALSE in order to get EAP state
* machine out of the SUCCESS state and eapSuccess cleared.
* Without this, EAPOL PAE state machine may transit to
* AUTHENTICATING state based on obsolete eapSuccess and then
* trigger BE_AUTH to SUCCESS and PAE to AUTHENTICATED without
* ever giving chance to EAP state machine to reset the state.
*/
eapol_sm_notify_portEnabled(wpa_s->eapol, FALSE);
eapol_sm_notify_portValid(wpa_s->eapol, FALSE);
if (wpa_s->key_mgmt == WPA_KEY_MGMT_PSK)
eapol_sm_notify_eap_success(wpa_s->eapol, FALSE);
/* 802.1X::portControl = Auto */
eapol_sm_notify_portEnabled(wpa_s->eapol, TRUE);
wpa_s->eapol_received = 0;
if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE ||
wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE) {
wpa_supplicant_cancel_auth_timeout(wpa_s);
} else {
/* Timeout for receiving the first EAPOL packet */
wpa_supplicant_req_auth_timeout(wpa_s, 10, 0);
}
break;
case EVENT_DISASSOC:
if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE) {
/* At least Host AP driver and a Prism3 card seemed to
* be generating streams of disconnected events when
* configuring IBSS for WPA-None. Ignore them for now.
*/
wpa_printf(MSG_DEBUG, "Disconnect event - ignore in "
"IBSS/WPA-None mode");
break;
}
if (wpa_s->wpa_state >= WPA_ASSOCIATED)
wpa_supplicant_req_scan(wpa_s, 0, 100000);
wpa_blacklist_add(wpa_s, wpa_s->bssid);
wpa_supplicant_mark_disassoc(wpa_s);
wpa_msg(wpa_s, MSG_INFO, "Disconnect event - remove keys");
if (wpa_supplicant_dynamic_keys(wpa_s)) {
wpa_s->keys_cleared = 0;
wpa_clear_keys(wpa_s, wpa_s->bssid);
}
break;
case EVENT_MICHAEL_MIC_FAILURE:
wpa_msg(wpa_s, MSG_WARNING, "Michael MIC failure detected");
pairwise = (data && data->michael_mic_failure.unicast);
wpa_supplicant_key_request(wpa_s, 1, pairwise);
time(&now);
if (wpa_s->last_michael_mic_error &&
now - wpa_s->last_michael_mic_error <= 60) {
/* initialize countermeasures */
wpa_s->countermeasures = 1;
wpa_msg(wpa_s, MSG_WARNING, "TKIP countermeasures "
"started");
/* Need to wait for completion of request frame. We do
* not get any callback for the message completion, so
* just wait a short while and hope for the best. */
usleep(10000);
wpa_drv_set_countermeasures(wpa_s, 1);
wpa_supplicant_deauthenticate(
wpa_s, REASON_MICHAEL_MIC_FAILURE);
eloop_cancel_timeout(
wpa_supplicant_stop_countermeasures, wpa_s,
NULL);
eloop_register_timeout(
60, 0, wpa_supplicant_stop_countermeasures,
wpa_s, NULL);
/* TODO: mark the AP rejected for 60 second. STA is
* allowed to associate with another AP.. */
}
wpa_s->last_michael_mic_error = now;
break;
case EVENT_SCAN_RESULTS:
wpa_supplicant_scan_results(wpa_s);
break;
case EVENT_ASSOCINFO:
wpa_supplicant_associnfo(wpa_s, data);
break;
case EVENT_INTERFACE_STATUS:
if (strcmp(wpa_s->ifname, data->interface_status.ifname) != 0)
break;
switch (data->interface_status.ievent) {
case EVENT_INTERFACE_ADDED:
if (!wpa_s->interface_removed)
break;
wpa_s->interface_removed = 0;
wpa_printf(MSG_DEBUG, "Configured interface was "
"added.");
if (wpa_supplicant_driver_init(wpa_s, 1) < 0) {
wpa_printf(MSG_INFO, "Failed to initialize "
"the driver after interface was "
"added.");
}
break;
case EVENT_INTERFACE_REMOVED:
wpa_printf(MSG_DEBUG, "Configured interface was "
"removed.");
wpa_s->interface_removed = 1;
wpa_supplicant_mark_disassoc(wpa_s);
l2_packet_deinit(wpa_s->l2);
break;
}
break;
case EVENT_PMKID_CANDIDATE:
wpa_supplicant_add_pmkid_candidate(wpa_s, data);
break;
default:
wpa_printf(MSG_INFO, "Unknown event %d", event);
break;
}
}
static void wpa_supplicant_terminate(int sig, void *eloop_ctx,
void *signal_ctx)
{
struct wpa_supplicant *wpa_s = eloop_ctx;
for (wpa_s = wpa_s->head; wpa_s; wpa_s = wpa_s->next) {
wpa_msg(wpa_s, MSG_INFO, "Signal %d received - terminating",
sig);
}
eloop_terminate();
}
int wpa_supplicant_reload_configuration(struct wpa_supplicant *wpa_s)
{
struct wpa_config *conf;
int reconf_ctrl;
if (wpa_s->confname == NULL)
return -1;
conf = wpa_config_read(wpa_s->confname);
if (conf == NULL) {
wpa_msg(wpa_s, MSG_ERROR, "Failed to parse the configuration "
"file '%s' - exiting", wpa_s->confname);
return -1;
}
reconf_ctrl = !!conf->ctrl_interface != !!wpa_s->conf->ctrl_interface
|| (conf->ctrl_interface && wpa_s->conf->ctrl_interface &&
strcmp(conf->ctrl_interface, wpa_s->conf->ctrl_interface)
!= 0);
if (reconf_ctrl)
wpa_supplicant_ctrl_iface_deinit(wpa_s);
wpa_s->current_ssid = NULL;
eapol_sm_notify_config(wpa_s->eapol, NULL, NULL);
rsn_preauth_deinit(wpa_s);
wpa_config_free(wpa_s->conf);
wpa_s->conf = conf;
if (reconf_ctrl)
wpa_supplicant_ctrl_iface_init(wpa_s);
wpa_s->reassociate = 1;
wpa_supplicant_req_scan(wpa_s, 0, 0);
wpa_msg(wpa_s, MSG_DEBUG, "Reconfiguration completed");
return 0;
}
#ifndef CONFIG_NATIVE_WINDOWS
static void wpa_supplicant_reconfig(int sig, void *eloop_ctx,
void *signal_ctx)
{
struct wpa_supplicant *wpa_s = eloop_ctx;
wpa_printf(MSG_DEBUG, "Signal %d received - reconfiguring", sig);
for (wpa_s = wpa_s->head; wpa_s; wpa_s = wpa_s->next) {
if (wpa_supplicant_reload_configuration(wpa_s) < 0) {
eloop_terminate();
}
}
}
#endif /* CONFIG_NATIVE_WINDOWS */
static void wpa_supplicant_gen_assoc_event(struct wpa_supplicant *wpa_s)
{
struct wpa_ssid *ssid;
union wpa_event_data data;
ssid = wpa_supplicant_get_ssid(wpa_s);
if (ssid == NULL)
return;
wpa_printf(MSG_DEBUG, "Already associated with a configured network - "
"generating associated event");
memset(&data, 0, sizeof(data));
wpa_supplicant_event(wpa_s, EVENT_ASSOC, &data);
}
void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx)
{
struct wpa_supplicant *wpa_s = eloop_ctx;
struct wpa_ssid *ssid;
if (wpa_s->conf->ap_scan == 0) {
wpa_supplicant_gen_assoc_event(wpa_s);
return;
}
if (wpa_s->conf->ap_scan == 2) {
ssid = wpa_s->conf->ssid;
if (ssid == NULL)
return;
wpa_supplicant_associate(wpa_s, NULL, ssid);
return;
}
if (wpa_s->wpa_state == WPA_DISCONNECTED)
wpa_s->wpa_state = WPA_SCANNING;
ssid = wpa_s->conf->ssid;
if (wpa_s->prev_scan_ssid != BROADCAST_SSID_SCAN) {
while (ssid) {
if (ssid == wpa_s->prev_scan_ssid) {
ssid = ssid->next;
break;
}
ssid = ssid->next;
}
}
while (ssid) {
if (ssid->scan_ssid)
break;
ssid = ssid->next;
}
wpa_printf(MSG_DEBUG, "Starting AP scan (%s SSID)",
ssid ? "specific": "broadcast");
if (ssid) {
wpa_hexdump_ascii(MSG_DEBUG, "Scan SSID",
ssid->ssid, ssid->ssid_len);
wpa_s->prev_scan_ssid = ssid;
} else
wpa_s->prev_scan_ssid = BROADCAST_SSID_SCAN;
if (wpa_drv_scan(wpa_s, ssid ? ssid->ssid : NULL,
ssid ? ssid->ssid_len : 0)) {
wpa_printf(MSG_WARNING, "Failed to initiate AP scan.");
wpa_supplicant_req_scan(wpa_s, 10, 0);
}
}
static wpa_cipher cipher_suite2driver(int cipher)
{
switch (cipher) {
case WPA_CIPHER_NONE:
return CIPHER_NONE;
case WPA_CIPHER_WEP40:
return CIPHER_WEP40;
case WPA_CIPHER_WEP104:
return CIPHER_WEP104;
case WPA_CIPHER_CCMP:
return CIPHER_CCMP;
case WPA_CIPHER_TKIP:
default:
return CIPHER_TKIP;
}
}
static wpa_key_mgmt key_mgmt2driver(int key_mgmt)
{
switch (key_mgmt) {
case WPA_KEY_MGMT_NONE:
return KEY_MGMT_NONE;
case WPA_KEY_MGMT_IEEE8021X_NO_WPA:
return KEY_MGMT_802_1X_NO_WPA;
case WPA_KEY_MGMT_IEEE8021X:
return KEY_MGMT_802_1X;
case WPA_KEY_MGMT_WPA_NONE:
return KEY_MGMT_WPA_NONE;
case WPA_KEY_MGMT_PSK:
default:
return KEY_MGMT_PSK;
}
}
static int wpa_supplicant_suites_from_ai(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid,
struct wpa_ie_data *ie) {
if (wpa_s->assoc_wpa_ie == NULL)
return -1;
if (wpa_parse_wpa_ie(wpa_s, wpa_s->assoc_wpa_ie,
wpa_s->assoc_wpa_ie_len, ie)) {
wpa_msg(wpa_s, MSG_INFO, "WPA: Failed to parse WPA IE from "
"association info");
return -1;
}
wpa_printf(MSG_DEBUG, "WPA: Using WPA IE from AssocReq to set cipher "
"suites");
if (!(ie->group_cipher & ssid->group_cipher)) {
wpa_msg(wpa_s, MSG_INFO, "WPA: Driver used disabled group "
"cipher 0x%x (mask 0x%x) - reject",
ie->group_cipher, ssid->group_cipher);
return -1;
}
if (!(ie->pairwise_cipher & ssid->pairwise_cipher)) {
wpa_msg(wpa_s, MSG_INFO, "WPA: Driver used disabled pairwise "
"cipher 0x%x (mask 0x%x) - reject",
ie->pairwise_cipher, ssid->pairwise_cipher);
return -1;
}
if (!(ie->key_mgmt & ssid->key_mgmt)) {
wpa_msg(wpa_s, MSG_INFO, "WPA: Driver used disabled key "
"management 0x%x (mask 0x%x) - reject",
ie->key_mgmt, ssid->key_mgmt);
return -1;
}
return 0;
}
static int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s,
struct wpa_scan_result *bss,
struct wpa_ssid *ssid,
u8 *wpa_ie, int *wpa_ie_len)
{
struct wpa_ie_data ie;
int sel, proto;
u8 *ap_ie;
size_t ap_ie_len;
if (bss && bss->rsn_ie_len && (ssid->proto & WPA_PROTO_RSN)) {
wpa_msg(wpa_s, MSG_DEBUG, "RSN: using IEEE 802.11i/D9.0");
proto = WPA_PROTO_RSN;
ap_ie = bss->rsn_ie;
ap_ie_len = bss->rsn_ie_len;
} else if (bss) {
wpa_msg(wpa_s, MSG_DEBUG, "WPA: using IEEE 802.11i/D3.0");
proto = WPA_PROTO_WPA;
ap_ie = bss->wpa_ie;
ap_ie_len = bss->wpa_ie_len;
} else {
if (ssid->proto & WPA_PROTO_RSN)
proto = WPA_PROTO_RSN;
else
proto = WPA_PROTO_WPA;
ap_ie = NULL;
ap_ie_len = 0;
if (wpa_supplicant_suites_from_ai(wpa_s, ssid, &ie) < 0) {
memset(&ie, 0, sizeof(ie));
ie.group_cipher = ssid->group_cipher;
ie.pairwise_cipher = ssid->pairwise_cipher;
ie.key_mgmt = ssid->key_mgmt;
wpa_printf(MSG_DEBUG, "WPA: Set cipher suites based "
"on configuration");
}
}
if (ap_ie && wpa_parse_wpa_ie(wpa_s, ap_ie, ap_ie_len, &ie)) {
wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to parse WPA IE for "
"the selected BSS.");
return -1;
}
wpa_printf(MSG_DEBUG, "WPA: Selected cipher suites: group %d "
"pairwise %d key_mgmt %d",
ie.group_cipher, ie.pairwise_cipher, ie.key_mgmt);
wpa_s->proto = proto;
free(wpa_s->ap_wpa_ie);
wpa_s->ap_wpa_ie = NULL;
wpa_s->ap_wpa_ie_len = 0;
if (bss && bss->wpa_ie_len) {
wpa_s->ap_wpa_ie = malloc(bss->wpa_ie_len);
if (wpa_s->ap_wpa_ie == NULL) {
wpa_printf(MSG_INFO, "WPA: malloc failed");
return -1;
}
memcpy(wpa_s->ap_wpa_ie, bss->wpa_ie, bss->wpa_ie_len);
wpa_s->ap_wpa_ie_len = bss->wpa_ie_len;
}
free(wpa_s->ap_rsn_ie);
wpa_s->ap_rsn_ie = NULL;
wpa_s->ap_rsn_ie_len = 0;
if (bss && bss->rsn_ie_len) {
wpa_s->ap_rsn_ie = malloc(bss->rsn_ie_len);
if (wpa_s->ap_rsn_ie == NULL) {
wpa_printf(MSG_INFO, "WPA: malloc failed");
return -1;
}
memcpy(wpa_s->ap_rsn_ie, bss->rsn_ie, bss->rsn_ie_len);
wpa_s->ap_rsn_ie_len = bss->rsn_ie_len;
}
sel = ie.group_cipher & ssid->group_cipher;
if (sel & WPA_CIPHER_CCMP) {
wpa_s->group_cipher = WPA_CIPHER_CCMP;
wpa_msg(wpa_s, MSG_DEBUG, "WPA: using GTK CCMP");
} else if (sel & WPA_CIPHER_TKIP) {
wpa_s->group_cipher = WPA_CIPHER_TKIP;
wpa_msg(wpa_s, MSG_DEBUG, "WPA: using GTK TKIP");
} else if (sel & WPA_CIPHER_WEP104) {
wpa_s->group_cipher = WPA_CIPHER_WEP104;
wpa_msg(wpa_s, MSG_DEBUG, "WPA: using GTK WEP104");
} else if (sel & WPA_CIPHER_WEP40) {
wpa_s->group_cipher = WPA_CIPHER_WEP40;
wpa_msg(wpa_s, MSG_DEBUG, "WPA: using GTK WEP40");
} else {
wpa_printf(MSG_WARNING, "WPA: Failed to select group cipher.");
return -1;
}
sel = ie.pairwise_cipher & ssid->pairwise_cipher;
if (sel & WPA_CIPHER_CCMP) {
wpa_s->pairwise_cipher = WPA_CIPHER_CCMP;
wpa_msg(wpa_s, MSG_DEBUG, "WPA: using PTK CCMP");
} else if (sel & WPA_CIPHER_TKIP) {
wpa_s->pairwise_cipher = WPA_CIPHER_TKIP;
wpa_msg(wpa_s, MSG_DEBUG, "WPA: using PTK TKIP");
} else if (sel & WPA_CIPHER_NONE) {
wpa_s->pairwise_cipher = WPA_CIPHER_NONE;
wpa_msg(wpa_s, MSG_DEBUG, "WPA: using PTK NONE");
} else {
wpa_printf(MSG_WARNING, "WPA: Failed to select pairwise "
"cipher.");
return -1;
}
sel = ie.key_mgmt & ssid->key_mgmt;
if (sel & WPA_KEY_MGMT_IEEE8021X) {
wpa_s->key_mgmt = WPA_KEY_MGMT_IEEE8021X;
wpa_msg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT 802.1X");
} else if (sel & WPA_KEY_MGMT_PSK) {
wpa_s->key_mgmt = WPA_KEY_MGMT_PSK;
wpa_msg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT WPA-PSK");
} else if (sel & WPA_KEY_MGMT_WPA_NONE) {
wpa_s->key_mgmt = WPA_KEY_MGMT_WPA_NONE;
wpa_msg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT WPA-NONE");
} else {
wpa_printf(MSG_WARNING, "WPA: Failed to select authenticated "
"key management type.");
return -1;
}
*wpa_ie_len = wpa_gen_wpa_ie(wpa_s, wpa_ie);
if (*wpa_ie_len < 0) {
wpa_printf(MSG_WARNING, "WPA: Failed to generate WPA IE.");
return -1;
}
wpa_hexdump(MSG_DEBUG, "WPA: Own WPA IE", wpa_ie, *wpa_ie_len);
if (wpa_s->assoc_wpa_ie == NULL) {
/*
* Make a copy of the WPA/RSN IE so that 4-Way Handshake gets
* the correct version of the IE even if PMKSA caching is
* aborted (which would remove PMKID from IE generation).
*/
wpa_s->assoc_wpa_ie = malloc(*wpa_ie_len);
if (wpa_s->assoc_wpa_ie) {
memcpy(wpa_s->assoc_wpa_ie, wpa_ie, *wpa_ie_len);
wpa_s->assoc_wpa_ie_len = *wpa_ie_len;
}
}
if (ssid->key_mgmt & WPA_KEY_MGMT_PSK) {
wpa_s->pmk_len = PMK_LEN;
memcpy(wpa_s->pmk, ssid->psk, PMK_LEN);
} else if (wpa_s->cur_pmksa) {
wpa_s->pmk_len = wpa_s->cur_pmksa->pmk_len;
memcpy(wpa_s->pmk, wpa_s->cur_pmksa->pmk, wpa_s->pmk_len);
} else {
wpa_s->pmk_len = PMK_LEN;
memset(wpa_s->pmk, 0, PMK_LEN);
#ifdef CONFIG_XSUPPLICANT_IFACE
wpa_s->ext_pmk_received = 0;
#endif /* CONFIG_XSUPPLICANT_IFACE */
}
return 0;
}
static void wpa_supplicant_associate(struct wpa_supplicant *wpa_s,
struct wpa_scan_result *bss,
struct wpa_ssid *ssid)
{
u8 wpa_ie[80];
int wpa_ie_len;
int use_crypt;
int algs = AUTH_ALG_OPEN_SYSTEM;
int cipher_pairwise, cipher_group;
struct wpa_driver_associate_params params;
int wep_keys_set = 0;
struct wpa_driver_capa capa;
wpa_s->reassociate = 0;
if (bss) {
wpa_msg(wpa_s, MSG_INFO, "Trying to associate with " MACSTR
" (SSID='%s' freq=%d MHz)", MAC2STR(bss->bssid),
wpa_ssid_txt(ssid->ssid, ssid->ssid_len), bss->freq);
memset(wpa_s->bssid, 0, ETH_ALEN);
} else {
wpa_msg(wpa_s, MSG_INFO, "Trying to associate with SSID '%s'",
wpa_ssid_txt(ssid->ssid, ssid->ssid_len));
}
wpa_supplicant_cancel_scan(wpa_s);
/* Starting new association, so clear the possibly used WPA IE from the
* previous association. */
free(wpa_s->assoc_wpa_ie);
wpa_s->assoc_wpa_ie = NULL;
wpa_s->assoc_wpa_ie_len = 0;
if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) {
if (ssid->leap) {
if (ssid->non_leap == 0)
algs = AUTH_ALG_LEAP;
else
algs |= AUTH_ALG_LEAP;
}
}
wpa_printf(MSG_DEBUG, "Automatic auth_alg selection: 0x%x", algs);
if (ssid->auth_alg) {
algs = 0;
if (ssid->auth_alg & WPA_AUTH_ALG_OPEN)
algs |= AUTH_ALG_OPEN_SYSTEM;
if (ssid->auth_alg & WPA_AUTH_ALG_SHARED)
algs |= AUTH_ALG_SHARED_KEY;
if (ssid->auth_alg & WPA_AUTH_ALG_LEAP)
algs |= AUTH_ALG_LEAP;
wpa_printf(MSG_DEBUG, "Overriding auth_alg selection: 0x%x",
algs);
}
wpa_drv_set_auth_alg(wpa_s, algs);
if (bss && (bss->wpa_ie_len || bss->rsn_ie_len) &&
(ssid->key_mgmt & (WPA_KEY_MGMT_IEEE8021X | WPA_KEY_MGMT_PSK))) {
wpa_s->cur_pmksa = pmksa_cache_get(wpa_s, bss->bssid, NULL);
if (wpa_s->cur_pmksa) {
wpa_hexdump(MSG_DEBUG, "RSN: PMKID",
wpa_s->cur_pmksa->pmkid, PMKID_LEN);
eapol_sm_notify_pmkid_attempt(wpa_s->eapol, 1);
}
if (wpa_supplicant_set_suites(wpa_s, bss, ssid,
wpa_ie, &wpa_ie_len)) {
wpa_printf(MSG_WARNING, "WPA: Failed to set WPA key "
"management and encryption suites");
return;
}
} else if (ssid->key_mgmt &
(WPA_KEY_MGMT_PSK | WPA_KEY_MGMT_IEEE8021X |
WPA_KEY_MGMT_WPA_NONE)) {
if (wpa_supplicant_set_suites(wpa_s, NULL, ssid,
wpa_ie, &wpa_ie_len)) {
wpa_printf(MSG_WARNING, "WPA: Failed to set WPA key "
"management and encryption suites (no scan "
"results)");
return;
}
} else {
wpa_supplicant_set_non_wpa_policy(wpa_s, ssid);
wpa_ie_len = 0;
}
wpa_clear_keys(wpa_s, bss ? bss->bssid : NULL);
use_crypt = 1;
cipher_pairwise = cipher_suite2driver(wpa_s->pairwise_cipher);
cipher_group = cipher_suite2driver(wpa_s->group_cipher);
if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE ||
wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) {
int i;
if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE)
use_crypt = 0;
for (i = 0; i < NUM_WEP_KEYS; i++) {
if (ssid->wep_key_len[i]) {
use_crypt = 1;
wep_keys_set = 1;
wpa_set_wep_key(wpa_s,
i == ssid->wep_tx_keyidx,
i, ssid->wep_key[i],
ssid->wep_key_len[i]);
}
}
}
if (wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) {
if ((ssid->eapol_flags &
(EAPOL_FLAG_REQUIRE_KEY_UNICAST |
EAPOL_FLAG_REQUIRE_KEY_BROADCAST)) == 0 &&
!wep_keys_set) {
use_crypt = 0;
} else {
/* Assume that dynamic WEP-104 keys will be used and
* set cipher suites in order for drivers to expect
* encryption. */
cipher_pairwise = cipher_group = CIPHER_WEP104;
}
}
if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE) {
/* Set the key before (and later after) association */
wpa_supplicant_set_wpa_none_key(wpa_s, ssid);
}
wpa_drv_set_drop_unencrypted(wpa_s, use_crypt);
wpa_s->wpa_state = WPA_ASSOCIATING;
memset(&params, 0, sizeof(params));
if (bss) {
params.bssid = bss->bssid;
params.ssid = bss->ssid;
params.ssid_len = bss->ssid_len;
params.freq = bss->freq;
} else {
params.ssid = ssid->ssid;
params.ssid_len = ssid->ssid_len;
}
params.wpa_ie = wpa_ie;
params.wpa_ie_len = wpa_ie_len;
params.pairwise_suite = cipher_pairwise;
params.group_suite = cipher_group;
params.key_mgmt_suite = key_mgmt2driver(wpa_s->key_mgmt);
params.auth_alg = algs;
params.mode = ssid->mode;
if (wpa_drv_associate(wpa_s, &params) < 0) {
wpa_msg(wpa_s, MSG_INFO, "Association request to the driver "
"failed");
/* try to continue anyway; new association will be tried again
* after timeout */
}
if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE) {
/* Set the key after the association just in case association
* cleared the previously configured key. */
wpa_supplicant_set_wpa_none_key(wpa_s, ssid);
/* No need to timeout authentication since there is no key
* management. */
wpa_supplicant_cancel_auth_timeout(wpa_s);
} else {
/* Timeout for IEEE 802.11 authentication and association */
wpa_supplicant_req_auth_timeout(wpa_s, 5, 0);
}
if (wep_keys_set && wpa_drv_get_capa(wpa_s, &capa) == 0 &&
capa.flags & WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC) {
/* Set static WEP keys again */
int i;
for (i = 0; i < NUM_WEP_KEYS; i++) {
if (ssid->wep_key_len[i]) {
wpa_set_wep_key(wpa_s,
i == ssid->wep_tx_keyidx,
i, ssid->wep_key[i],
ssid->wep_key_len[i]);
}
}
}
wpa_s->current_ssid = ssid;
wpa_supplicant_initiate_eapol(wpa_s);
}
void wpa_supplicant_disassociate(struct wpa_supplicant *wpa_s,
int reason_code)
{
u8 *addr = NULL;
wpa_s->wpa_state = WPA_DISCONNECTED;
if (memcmp(wpa_s->bssid, "\x00\x00\x00\x00\x00\x00", ETH_ALEN) != 0) {
wpa_drv_disassociate(wpa_s, wpa_s->bssid, reason_code);
addr = wpa_s->bssid;
}
wpa_clear_keys(wpa_s, addr);
wpa_s->current_ssid = NULL;
eapol_sm_notify_config(wpa_s->eapol, NULL, NULL);
eapol_sm_notify_portEnabled(wpa_s->eapol, FALSE);
eapol_sm_notify_portValid(wpa_s->eapol, FALSE);
}
void wpa_supplicant_deauthenticate(struct wpa_supplicant *wpa_s,
int reason_code)
{
u8 *addr = NULL;
wpa_s->wpa_state = WPA_DISCONNECTED;
if (memcmp(wpa_s->bssid, "\x00\x00\x00\x00\x00\x00", ETH_ALEN) != 0) {
wpa_drv_deauthenticate(wpa_s, wpa_s->bssid, reason_code);
addr = wpa_s->bssid;
}
wpa_clear_keys(wpa_s, addr);
wpa_s->current_ssid = NULL;
eapol_sm_notify_config(wpa_s->eapol, NULL, NULL);
eapol_sm_notify_portEnabled(wpa_s->eapol, FALSE);
eapol_sm_notify_portValid(wpa_s->eapol, FALSE);
}
static void wpa_supplicant_imsi_identity(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid)
{
int aka = 0;
u8 *pos = ssid->eap_methods;
while (pos && *pos != EAP_TYPE_NONE) {
if (*pos == EAP_TYPE_AKA) {
aka = 1;
break;
}
pos++;
}
if (ssid->identity == NULL && wpa_s->imsi) {
ssid->identity = malloc(1 + wpa_s->imsi_len);
if (ssid->identity) {
ssid->identity[0] = aka ? '0' : '1';
memcpy(ssid->identity + 1, wpa_s->imsi,
wpa_s->imsi_len);
ssid->identity_len = 1 + wpa_s->imsi_len;
wpa_hexdump_ascii(MSG_DEBUG, "permanent identity from "
"IMSI", ssid->identity,
ssid->identity_len);
}
}
}
static void wpa_supplicant_scard_init(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid)
{
char buf[100];
size_t len;
if (ssid->pcsc == NULL)
return;
if (wpa_s->scard != NULL) {
wpa_supplicant_imsi_identity(wpa_s, ssid);
return;
}
wpa_printf(MSG_DEBUG, "Selected network is configured to use SIM - "
"initialize PCSC");
wpa_s->scard = scard_init(SCARD_TRY_BOTH, ssid->pin);
if (wpa_s->scard == NULL) {
wpa_printf(MSG_WARNING, "Failed to initialize SIM "
"(pcsc-lite)");
/* TODO: what to do here? */
return;
}
eapol_sm_register_scard_ctx(wpa_s->eapol, wpa_s->scard);
len = sizeof(buf);
if (scard_get_imsi(wpa_s->scard, buf, &len)) {
wpa_printf(MSG_WARNING, "Failed to get IMSI from SIM");
/* TODO: what to do here? */
return;
}
wpa_hexdump_ascii(MSG_DEBUG, "IMSI", (u8 *) buf, len);
free(wpa_s->imsi);
wpa_s->imsi = malloc(len);
if (wpa_s->imsi) {
memcpy(wpa_s->imsi, buf, len);
wpa_s->imsi_len = len;
wpa_supplicant_imsi_identity(wpa_s, ssid);
}
}
static struct wpa_scan_result *
wpa_supplicant_select_bss(struct wpa_supplicant *wpa_s, struct wpa_ssid *group,
struct wpa_scan_result *results, int num,
struct wpa_ssid **selected_ssid)
{
struct wpa_ssid *ssid;
struct wpa_scan_result *bss, *selected = NULL;
int i;
struct wpa_blacklist *e;
wpa_printf(MSG_DEBUG, "Selecting BSS from priority group %d",
group->priority);
bss = NULL;
ssid = NULL;
/* First, try to find WPA-enabled AP */
for (i = 0; i < num && !selected; i++) {
bss = &results[i];
wpa_printf(MSG_DEBUG, "%d: " MACSTR " ssid='%s' "
"wpa_ie_len=%lu rsn_ie_len=%lu",
i, MAC2STR(bss->bssid),
wpa_ssid_txt(bss->ssid, bss->ssid_len),
(unsigned long) bss->wpa_ie_len,
(unsigned long) bss->rsn_ie_len);
if ((e = wpa_blacklist_get(wpa_s, bss->bssid)) &&
e->count > 1) {
wpa_printf(MSG_DEBUG, " skip - blacklisted");
continue;
}
if (bss->wpa_ie_len == 0 && bss->rsn_ie_len == 0) {
wpa_printf(MSG_DEBUG, " skip - no WPA/RSN IE");
continue;
}
for (ssid = group; ssid; ssid = ssid->pnext) {
struct wpa_ie_data ie;
if (bss->ssid_len != ssid->ssid_len ||
memcmp(bss->ssid, ssid->ssid,
bss->ssid_len) != 0) {
wpa_printf(MSG_DEBUG, " skip - "
"SSID mismatch");
continue;
}
if (ssid->bssid_set &&
memcmp(bss->bssid, ssid->bssid, ETH_ALEN) != 0) {
wpa_printf(MSG_DEBUG, " skip - "
"BSSID mismatch");
continue;
}
if (!(((ssid->proto & WPA_PROTO_RSN) &&
wpa_parse_wpa_ie(wpa_s, bss->rsn_ie,
bss->rsn_ie_len, &ie) == 0) ||
((ssid->proto & WPA_PROTO_WPA) &&
wpa_parse_wpa_ie(wpa_s, bss->wpa_ie,
bss->wpa_ie_len, &ie) == 0))) {
wpa_printf(MSG_DEBUG, " skip - "
"could not parse WPA/RSN IE");
continue;
}
if (!(ie.proto & ssid->proto)) {
wpa_printf(MSG_DEBUG, " skip - "
"proto mismatch");
continue;
}
if (!(ie.pairwise_cipher & ssid->pairwise_cipher)) {
wpa_printf(MSG_DEBUG, " skip - "
"PTK cipher mismatch");
continue;
}
if (!(ie.group_cipher & ssid->group_cipher)) {
wpa_printf(MSG_DEBUG, " skip - "
"GTK cipher mismatch");
continue;
}
if (!(ie.key_mgmt & ssid->key_mgmt)) {
wpa_printf(MSG_DEBUG, " skip - "
"key mgmt mismatch");
continue;
}
selected = bss;
*selected_ssid = ssid;
wpa_printf(MSG_DEBUG, " selected");
break;
}
}
/* If no WPA-enabled AP found, try to find non-WPA AP, if configuration
* allows this. */
for (i = 0; i < num && !selected; i++) {
bss = &results[i];
if ((e = wpa_blacklist_get(wpa_s, bss->bssid)) &&
e->count > 1) {
continue;
}
for (ssid = group; ssid; ssid = ssid->pnext) {
if (bss->ssid_len == ssid->ssid_len &&
memcmp(bss->ssid, ssid->ssid, bss->ssid_len) == 0
&&
(!ssid->bssid_set ||
memcmp(bss->bssid, ssid->bssid, ETH_ALEN) == 0) &&
((ssid->key_mgmt & WPA_KEY_MGMT_NONE) ||
(ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA)))
{
selected = bss;
*selected_ssid = ssid;
wpa_printf(MSG_DEBUG, " selected non-WPA AP "
MACSTR " ssid='%s'",
MAC2STR(bss->bssid),
wpa_ssid_txt(bss->ssid,
bss->ssid_len));
break;
}
}
}
return selected;
}
static int wpa_supplicant_get_scan_results(struct wpa_supplicant *wpa_s)
{
#define SCAN_AP_LIMIT 128
struct wpa_scan_result *results, *tmp;
int num;
results = malloc(SCAN_AP_LIMIT * sizeof(struct wpa_scan_result));
if (results == NULL) {
wpa_printf(MSG_WARNING, "Failed to allocate memory for scan "
"results");
return -1;
}
num = wpa_drv_get_scan_results(wpa_s, results, SCAN_AP_LIMIT);
wpa_printf(MSG_DEBUG, "Scan results: %d", num);
if (num < 0) {
wpa_printf(MSG_DEBUG, "Failed to get scan results");
free(results);
return -1;
}
if (num > SCAN_AP_LIMIT) {
wpa_printf(MSG_INFO, "Not enough room for all APs (%d < %d)",
num, SCAN_AP_LIMIT);
num = SCAN_AP_LIMIT;
}
/* Free unneeded memory for unused scan result entries */
tmp = realloc(results, num * sizeof(struct wpa_scan_result));
if (tmp || num == 0) {
results = tmp;
}
free(wpa_s->scan_results);
wpa_s->scan_results = results;
wpa_s->num_scan_results = num;
return 0;
}
static void wpa_supplicant_scan_results(struct wpa_supplicant *wpa_s)
{
int num, prio;
struct wpa_scan_result *selected = NULL;
struct wpa_ssid *ssid;
struct wpa_scan_result *results;
if (wpa_supplicant_get_scan_results(wpa_s) < 0) {
wpa_printf(MSG_DEBUG, "Failed to get scan results - try "
"scanning again");
wpa_supplicant_req_scan(wpa_s, 1, 0);
return;
}
results = wpa_s->scan_results;
num = wpa_s->num_scan_results;
while (selected == NULL) {
for (prio = 0; prio < wpa_s->conf->num_prio; prio++) {
selected = wpa_supplicant_select_bss(
wpa_s, wpa_s->conf->pssid[prio], results, num,
&ssid);
if (selected)
break;
}
if (selected == NULL && wpa_s->blacklist) {
wpa_printf(MSG_DEBUG, "No APs found - clear blacklist "
"and try again");
wpa_blacklist_clear(wpa_s);
} else if (selected == NULL) {
break;
}
}
if (selected) {
if (wpa_s->reassociate ||
memcmp(selected->bssid, wpa_s->bssid, ETH_ALEN) != 0) {
wpa_supplicant_scard_init(wpa_s, ssid);
wpa_supplicant_associate(wpa_s, selected, ssid);
} else {
wpa_printf(MSG_DEBUG, "Already associated with the "
"selected AP.");
}
rsn_preauth_scan_results(wpa_s, results, num);
} else {
wpa_printf(MSG_DEBUG, "No suitable AP found.");
wpa_supplicant_req_scan(wpa_s, 5, 0);
}
}
static int wpa_get_beacon_ie(struct wpa_supplicant *wpa_s)
{
int i, ret = 0;
struct wpa_scan_result *results, *curr = NULL;
results = wpa_s->scan_results;
if (results == NULL) {
return -1;
}
for (i = 0; i < wpa_s->num_scan_results; i++) {
if (memcmp(results[i].bssid, wpa_s->bssid, ETH_ALEN) == 0) {
curr = &results[i];
break;
}
}
if (curr) {
free(wpa_s->ap_wpa_ie);
wpa_s->ap_wpa_ie_len = curr->wpa_ie_len;
if (curr->wpa_ie_len) {
wpa_s->ap_wpa_ie = malloc(wpa_s->ap_wpa_ie_len);
if (wpa_s->ap_wpa_ie) {
memcpy(wpa_s->ap_wpa_ie, curr->wpa_ie,
curr->wpa_ie_len);
} else {
ret = -1;
}
} else {
wpa_s->ap_wpa_ie = NULL;
}
free(wpa_s->ap_rsn_ie);
wpa_s->ap_rsn_ie_len = curr->rsn_ie_len;
if (curr->rsn_ie_len) {
wpa_s->ap_rsn_ie = malloc(wpa_s->ap_rsn_ie_len);
if (wpa_s->ap_rsn_ie) {
memcpy(wpa_s->ap_rsn_ie, curr->rsn_ie,
curr->rsn_ie_len);
} else {
ret = -1;
}
} else {
wpa_s->ap_rsn_ie = NULL;
}
} else {
ret = -1;
}
return ret;
}
int wpa_supplicant_get_beacon_ie(struct wpa_supplicant *wpa_s)
{
if (wpa_get_beacon_ie(wpa_s) == 0) {
return 0;
}
/* No WPA/RSN IE found in the cached scan results. Try to get updated
* scan results from the driver. */
if (wpa_supplicant_get_scan_results(wpa_s) < 0) {
return -1;
}
return wpa_get_beacon_ie(wpa_s);
}
#ifdef CONFIG_XSUPPLICANT_IFACE
static void wpa_supplicant_dot1x_receive(int sock, void *eloop_ctx,
void *sock_ctx)
{
struct wpa_supplicant *wpa_s = eloop_ctx;
u8 buf[128];
int res;
res = recv(sock, buf, sizeof(buf), 0);
wpa_printf(MSG_DEBUG, "WPA: Receive from dot1x (Xsupplicant) socket "
"==> %d", res);
if (res < 0) {
perror("recv");
return;
}
if (res != PMK_LEN) {
wpa_printf(MSG_WARNING, "WPA: Invalid master key length (%d) "
"from dot1x", res);
return;
}
wpa_hexdump(MSG_DEBUG, "WPA: Master key (dot1x)", buf, PMK_LEN);
if (wpa_s->key_mgmt & WPA_KEY_MGMT_IEEE8021X) {
memcpy(wpa_s->pmk, buf, PMK_LEN);
wpa_s->ext_pmk_received = 1;
} else {
wpa_printf(MSG_INFO, "WPA: Not in IEEE 802.1X mode - dropping "
"dot1x PMK update (%d)", wpa_s->key_mgmt);
}
}
static int wpa_supplicant_802_1x_init(struct wpa_supplicant *wpa_s)
{
int s;
struct sockaddr_un addr;
s = socket(AF_LOCAL, SOCK_DGRAM, 0);
if (s < 0) {
perror("socket");
return -1;
}
memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_LOCAL;
addr.sun_path[0] = '\0';
snprintf(addr.sun_path + 1, sizeof(addr.sun_path) - 1,
"wpa_supplicant");
if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
perror("bind");
close(s);
return -1;
}
wpa_s->dot1x_s = s;
eloop_register_read_sock(s, wpa_supplicant_dot1x_receive, wpa_s,
NULL);
return 0;
}
#endif /* CONFIG_XSUPPLICANT_IFACE */
static int wpa_supplicant_set_driver(struct wpa_supplicant *wpa_s,
const char *name)
{
int i;
if (wpa_s == NULL)
return -1;
if (wpa_supplicant_drivers[0] == NULL) {
wpa_printf(MSG_ERROR, "No driver interfaces build into "
"wpa_supplicant.");
return -1;
}
if (name == NULL) {
/* default to first driver in the list */
wpa_s->driver = wpa_supplicant_drivers[0];
return 0;
}
for (i = 0; wpa_supplicant_drivers[i]; i++) {
if (strcmp(name, wpa_supplicant_drivers[i]->name) == 0) {
wpa_s->driver = wpa_supplicant_drivers[i];
return 0;
}
}
printf("Unsupported driver '%s'.\n", name);
return -1;
}
static void wpa_supplicant_fd_workaround(void)
{
int s, i;
/* When started from pcmcia-cs scripts, wpa_supplicant might start with
* fd 0, 1, and 2 closed. This will cause some issues because many
* places in wpa_supplicant are still printing out to stdout. As a
* workaround, make sure that fd's 0, 1, and 2 are not used for other
* sockets. */
for (i = 0; i < 3; i++) {
s = open("/dev/null", O_RDWR);
if (s > 2) {
close(s);
break;
}
}
}
static int wpa_supplicant_driver_init(struct wpa_supplicant *wpa_s,
int wait_for_interface)
{
static int interface_count = 0;
for (;;) {
wpa_s->l2 = l2_packet_init(wpa_s->ifname,
wpa_drv_get_mac_addr(wpa_s),
ETH_P_EAPOL,
wpa_supplicant_rx_eapol, wpa_s);
if (wpa_s->l2)
break;
else if (!wait_for_interface)
return -1;
printf("Waiting for interface..\n");
sleep(5);
}
if (l2_packet_get_own_addr(wpa_s->l2, wpa_s->own_addr)) {
fprintf(stderr, "Failed to get own L2 address\n");
return -1;
}
wpa_printf(MSG_DEBUG, "Own MAC address: " MACSTR,
MAC2STR(wpa_s->own_addr));
if (wpa_drv_set_wpa(wpa_s, 1) < 0) {
fprintf(stderr, "Failed to enable WPA in the driver.\n");
return -1;
}
wpa_clear_keys(wpa_s, NULL);
/* Make sure that TKIP countermeasures are not left enabled (could
* happen if wpa_supplicant is killed during countermeasures. */
wpa_drv_set_countermeasures(wpa_s, 0);
wpa_drv_set_drop_unencrypted(wpa_s, 1);
wpa_s->prev_scan_ssid = BROADCAST_SSID_SCAN;
wpa_supplicant_req_scan(wpa_s, interface_count, 100000);
interface_count++;
return 0;
}
static void usage(void)
{
int i;
printf("%s\n\n%s\n"
"usage:\n"
" wpa_supplicant [-BddehLqqvw] -i<ifname> -c<config file> "
"[-D<driver>] \\\n"
" [-P<pid file>] "
"[-N -i<ifname> -c<conf> [-D<driver>] ...]\n"
"\n"
"drivers:\n",
wpa_supplicant_version, wpa_supplicant_license);
for (i = 0; wpa_supplicant_drivers[i]; i++) {
printf(" %s = %s\n",
wpa_supplicant_drivers[i]->name,
wpa_supplicant_drivers[i]->desc);
}
printf("options:\n"
" -B = run daemon in the background\n"
" -d = increase debugging verbosity (-dd even more)\n"
" -K = include keys (passwords, etc.) in debug output\n"
" -t = include timestamp in debug messages\n"
#ifdef CONFIG_XSUPPLICANT_IFACE
#ifdef IEEE8021X_EAPOL
" -e = use external IEEE 802.1X Supplicant (e.g., "
"xsupplicant)\n"
" (this disables the internal Supplicant)\n"
#endif /* IEEE8021X_EAPOL */
#endif /* CONFIG_XSUPPLICANT_IFACE */
" -h = show this help text\n"
" -L = show license (GPL and BSD)\n"
" -q = decrease debugging verbosity (-qq even less)\n"
" -v = show version\n"
" -w = wait for interface to be added, if needed\n"
" -N = start describing new interface\n");
}
static void license(void)
{
printf("%s\n\n%s\n",
wpa_supplicant_version, wpa_supplicant_full_license);
}
static struct wpa_supplicant * wpa_supplicant_alloc(void)
{
struct wpa_supplicant *wpa_s;
wpa_s = malloc(sizeof(*wpa_s));
if (wpa_s == NULL)
return NULL;
memset(wpa_s, 0, sizeof(*wpa_s));
wpa_s->ctrl_sock = -1;
#ifdef CONFIG_XSUPPLICANT_IFACE
wpa_s->dot1x_s = -1;
#endif /* CONFIG_XSUPPLICANT_IFACE */
return wpa_s;
}
static int wpa_supplicant_init(struct wpa_supplicant *wpa_s,
const char *confname, const char *driver,
const char *ifname)
{
wpa_printf(MSG_DEBUG, "Initializing interface '%s' conf '%s' driver "
"'%s'", ifname, confname, driver ? driver : "default");
if (wpa_supplicant_set_driver(wpa_s, driver) < 0) {
return -1;
}
if (confname) {
wpa_s->confname = rel2abs_path(confname);
if (wpa_s->confname == NULL) {
wpa_printf(MSG_ERROR, "Failed to get absolute path "
"for configuration file '%s'.", confname);
return -1;
}
wpa_printf(MSG_DEBUG, "Configuration file '%s' -> '%s'",
confname, wpa_s->confname);
wpa_s->conf = wpa_config_read(wpa_s->confname);
if (wpa_s->conf == NULL) {
printf("Failed to read configuration file '%s'.\n",
wpa_s->confname);
return -1;
}
}
if (wpa_s->conf == NULL || wpa_s->conf->ssid == NULL) {
usage();
printf("\nNo networks (SSID) configured.\n");
return -1;
}
if (ifname == NULL) {
usage();
printf("\nInterface name is required.\n");
return -1;
}
if (strlen(ifname) >= sizeof(wpa_s->ifname)) {
printf("Too long interface name '%s'.\n", ifname);
return -1;
}
strncpy(wpa_s->ifname, ifname, sizeof(wpa_s->ifname));
return 0;
}
static int wpa_supplicant_init2(struct wpa_supplicant *wpa_s,
int disable_eapol, int wait_for_interface)
{
const char *ifname;
wpa_printf(MSG_DEBUG, "Initializing interface (2) '%s'",
wpa_s->ifname);
if (!disable_eapol) {
struct eapol_ctx *ctx;
ctx = malloc(sizeof(*ctx));
if (ctx == NULL) {
printf("Failed to allocate EAPOL context.\n");
return -1;
}
memset(ctx, 0, sizeof(*ctx));
ctx->ctx = wpa_s;
ctx->msg_ctx = wpa_s;
ctx->preauth = 0;
ctx->eapol_done_cb = wpa_supplicant_notify_eapol_done;
ctx->eapol_send = wpa_eapol_send;
ctx->set_wep_key = wpa_eapol_set_wep_key;
wpa_s->eapol = eapol_sm_init(ctx);
if (wpa_s->eapol == NULL) {
free(ctx);
printf("Failed to initialize EAPOL state machines.\n");
return -1;
}
}
/* RSNA Supplicant Key Management - INITIALIZE */
eapol_sm_notify_portEnabled(wpa_s->eapol, FALSE);
eapol_sm_notify_portValid(wpa_s->eapol, FALSE);
/* Initialize driver interface and register driver event handler before
* L2 receive handler so that association events are processed before
* EAPOL-Key packets if both become available for the same select()
* call. */
wpa_s->drv_priv = wpa_drv_init(wpa_s, wpa_s->ifname);
if (wpa_s->drv_priv == NULL) {
fprintf(stderr, "Failed to initialize driver interface\n");
return -1;
}
ifname = wpa_drv_get_ifname(wpa_s);
if (ifname && strcmp(ifname, wpa_s->ifname) != 0) {
wpa_printf(MSG_DEBUG, "Driver interface replaced interface "
"name with '%s'", ifname);
strncpy(wpa_s->ifname, ifname, sizeof(wpa_s->ifname));
}
wpa_s->renew_snonce = 1;
if (wpa_supplicant_driver_init(wpa_s, wait_for_interface) < 0) {
return -1;
}
if (wpa_supplicant_ctrl_iface_init(wpa_s)) {
printf("Failed to initialize control interface '%s'.\n"
"You may have another wpa_supplicant process already "
"running or the file was\n"
"left by an unclean termination of wpa_supplicant in "
"which case you will need\n"
"to manually remove this file before starting "
"wpa_supplicant again.\n",
wpa_s->conf->ctrl_interface);
return -1;
}
#ifdef CONFIG_XSUPPLICANT_IFACE
if (disable_eapol)
wpa_supplicant_802_1x_init(wpa_s);
#endif /* CONFIG_XSUPPLICANT_IFACE */
return 0;
}
static void wpa_supplicant_deinit(struct wpa_supplicant *wpa_s)
{
if (wpa_s->drv_priv) {
if (wpa_drv_set_wpa(wpa_s, 0) < 0) {
fprintf(stderr, "Failed to disable WPA in the "
"driver.\n");
}
wpa_drv_set_drop_unencrypted(wpa_s, 0);
wpa_drv_set_countermeasures(wpa_s, 0);
wpa_clear_keys(wpa_s, NULL);
wpa_drv_deinit(wpa_s);
}
wpa_supplicant_cleanup(wpa_s);
}
int main(int argc, char *argv[])
{
struct wpa_supplicant *head, *wpa_s;
int c;
const char *confname, *driver, *ifname;
char *pid_file = NULL;
int daemonize = 0, wait_for_interface = 0, disable_eapol = 0, exitcode;
#ifdef CONFIG_NATIVE_WINDOWS
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2, 0), &wsaData)) {
printf("Could not find a usable WinSock.dll\n");
return -1;
}
#endif /* CONFIG_NATIVE_WINDOWS */
head = wpa_s = wpa_supplicant_alloc();
if (wpa_s == NULL)
return -1;
wpa_s->head = head;
wpa_supplicant_fd_workaround();
eloop_init(head);
ifname = confname = driver = NULL;
for (;;) {
c = getopt(argc, argv, "Bc:D:dehi:KLNP:qtvw");
if (c < 0)
break;
switch (c) {
case 'B':
daemonize++;
break;
case 'c':
confname = optarg;
break;
case 'D':
driver = optarg;
break;
case 'd':
wpa_debug_level--;
break;
#ifdef CONFIG_XSUPPLICANT_IFACE
#ifdef IEEE8021X_EAPOL
case 'e':
disable_eapol++;
break;
#endif /* IEEE8021X_EAPOL */
#endif /* CONFIG_XSUPPLICANT_IFACE */
case 'h':
usage();
return -1;
case 'i':
ifname = optarg;
break;
case 'K':
wpa_debug_show_keys++;
break;
case 'L':
license();
return -1;
case 'P':
pid_file = rel2abs_path(optarg);
break;
case 'q':
wpa_debug_level++;
break;
case 't':
wpa_debug_timestamp++;
break;
case 'v':
printf("%s\n", wpa_supplicant_version);
return -1;
case 'w':
wait_for_interface++;
break;
case 'N':
if (wpa_supplicant_init(wpa_s, confname, driver,
ifname))
return -1;
wpa_s->next = wpa_supplicant_alloc();
wpa_s = wpa_s->next;
if (wpa_s == NULL)
return -1;
wpa_s->head = head;
ifname = confname = driver = NULL;
break;
default:
usage();
return -1;
}
}
if (wpa_supplicant_init(wpa_s, confname, driver, ifname))
return -1;
exitcode = 0;
if (wait_for_interface && daemonize) {
wpa_printf(MSG_DEBUG, "Daemonize..");
if (daemon(0, 0)) {
perror("daemon");
exitcode = -1;
goto cleanup;
}
}
for (wpa_s = head; wpa_s; wpa_s = wpa_s->next) {
if (wpa_supplicant_init2(wpa_s, disable_eapol,
wait_for_interface)) {
exitcode = -1;
goto cleanup;
}
}
if (!wait_for_interface && daemonize) {
wpa_printf(MSG_DEBUG, "Daemonize..");
if (daemon(0, 0)) {
perror("daemon");
exitcode = -1;
goto cleanup;
}
}
if (pid_file) {
FILE *f = fopen(pid_file, "w");
if (f) {
fprintf(f, "%u\n", getpid());
fclose(f);
}
}
eloop_register_signal(SIGINT, wpa_supplicant_terminate, NULL);
eloop_register_signal(SIGTERM, wpa_supplicant_terminate, NULL);
#ifndef CONFIG_NATIVE_WINDOWS
eloop_register_signal(SIGHUP, wpa_supplicant_reconfig, NULL);
#endif /* CONFIG_NATIVE_WINDOWS */
eloop_run();
for (wpa_s = head; wpa_s; wpa_s = wpa_s->next) {
wpa_supplicant_deauthenticate(wpa_s, REASON_DEAUTH_LEAVING);
}
cleanup:
wpa_s = head;
while (wpa_s) {
struct wpa_supplicant *prev;
wpa_supplicant_deinit(wpa_s);
prev = wpa_s;
wpa_s = wpa_s->next;
free(prev);
}
eloop_destroy();
if (pid_file) {
unlink(pid_file);
free(pid_file);
}
#ifdef CONFIG_NATIVE_WINDOWS
WSACleanup();
#endif /* CONFIG_NATIVE_WINDOWS */
return exitcode;
}