diff --git a/contrib/wpa_supplicant/driver_ndis.c b/contrib/wpa_supplicant/driver_ndis.c new file mode 100644 index 000000000000..678d228a1614 --- /dev/null +++ b/contrib/wpa_supplicant/driver_ndis.c @@ -0,0 +1,1589 @@ +/* + * WPA Supplicant - Windows/NDIS driver interface + * Copyright (c) 2004-2005, Jouni Malinen + * + * 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 +#include +#include +#include +#include +#include + +#include "common.h" +#include "driver.h" +#include "wpa_supplicant.h" +#include "l2_packet.h" +#include "eloop.h" +#include "wpa.h" +#include "driver_ndis.h" + +int wpa_driver_register_event_cb(struct wpa_driver_ndis_data *drv); + +static void wpa_driver_ndis_poll(void *drv); + + +/* FIX: to be removed once this can be compiled with the complete NDIS + * header files */ +#ifndef OID_802_11_BSSID +#define OID_802_11_BSSID 0x0d010101 +#define OID_802_11_SSID 0x0d010102 +#define OID_802_11_INFRASTRUCTURE_MODE 0x0d010108 +#define OID_802_11_ADD_WEP 0x0D010113 +#define OID_802_11_REMOVE_WEP 0x0D010114 +#define OID_802_11_DISASSOCIATE 0x0D010115 +#define OID_802_11_BSSID_LIST 0x0d010217 +#define OID_802_11_AUTHENTICATION_MODE 0x0d010118 +#define OID_802_11_PRIVACY_FILTER 0x0d010119 +#define OID_802_11_BSSID_LIST_SCAN 0x0d01011A +#define OID_802_11_WEP_STATUS 0x0d01011B +#define OID_802_11_ENCRYPTION_STATUS OID_802_11_WEP_STATUS +#define OID_802_11_ADD_KEY 0x0d01011D +#define OID_802_11_REMOVE_KEY 0x0d01011E +#define OID_802_11_ASSOCIATION_INFORMATION 0x0d01011F +#define OID_802_11_TEST 0x0d010120 +#define OID_802_11_CAPABILITY 0x0d010122 +#define OID_802_11_PMKID 0x0d010123 + +#define NDIS_802_11_LENGTH_SSID 32 +#define NDIS_802_11_LENGTH_RATES 8 +#define NDIS_802_11_LENGTH_RATES_EX 16 + +typedef UCHAR NDIS_802_11_MAC_ADDRESS[6]; + +typedef struct NDIS_802_11_SSID { + ULONG SsidLength; + UCHAR Ssid[NDIS_802_11_LENGTH_SSID]; +} NDIS_802_11_SSID; + +typedef LONG NDIS_802_11_RSSI; + +typedef enum NDIS_802_11_NETWORK_TYPE { + Ndis802_11FH, + Ndis802_11DS, + Ndis802_11OFDM5, + Ndis802_11OFDM24, + Ndis802_11NetworkTypeMax +} NDIS_802_11_NETWORK_TYPE; + +typedef struct NDIS_802_11_CONFIGURATION_FH { + ULONG Length; + ULONG HopPattern; + ULONG HopSet; + ULONG DwellTime; +} NDIS_802_11_CONFIGURATION_FH; + +typedef struct NDIS_802_11_CONFIGURATION { + ULONG Length; + ULONG BeaconPeriod; + ULONG ATIMWindow; + ULONG DSConfig; + NDIS_802_11_CONFIGURATION_FH FHConfig; +} NDIS_802_11_CONFIGURATION; + +typedef enum NDIS_802_11_NETWORK_INFRASTRUCTURE { + Ndis802_11IBSS, + Ndis802_11Infrastructure, + Ndis802_11AutoUnknown, + Ndis802_11InfrastructureMax +} NDIS_802_11_NETWORK_INFRASTRUCTURE; + +typedef enum NDIS_802_11_AUTHENTICATION_MODE { + Ndis802_11AuthModeOpen, + Ndis802_11AuthModeShared, + Ndis802_11AuthModeAutoSwitch, + Ndis802_11AuthModeWPA, + Ndis802_11AuthModeWPAPSK, + Ndis802_11AuthModeWPANone, + Ndis802_11AuthModeWPA2, + Ndis802_11AuthModeWPA2PSK, + Ndis802_11AuthModeMax +} NDIS_802_11_AUTHENTICATION_MODE; + +typedef enum NDIS_802_11_WEP_STATUS { + Ndis802_11WEPEnabled, + Ndis802_11Encryption1Enabled = Ndis802_11WEPEnabled, + Ndis802_11WEPDisabled, + Ndis802_11EncryptionDisabled = Ndis802_11WEPDisabled, + Ndis802_11WEPKeyAbsent, + Ndis802_11Encryption1KeyAbsent = Ndis802_11WEPKeyAbsent, + Ndis802_11WEPNotSupported, + Ndis802_11EncryptionNotSupported = Ndis802_11WEPNotSupported, + Ndis802_11Encryption2Enabled, + Ndis802_11Encryption2KeyAbsent, + Ndis802_11Encryption3Enabled, + Ndis802_11Encryption3KeyAbsent +} NDIS_802_11_WEP_STATUS, NDIS_802_11_ENCRYPTION_STATUS; + +typedef enum NDIS_802_11_PRIVACY_FILTER { + Ndis802_11PrivFilterAcceptAll, + Ndis802_11PrivFilter8021xWEP +} NDIS_802_11_PRIVACY_FILTER; + +typedef UCHAR NDIS_802_11_RATES[NDIS_802_11_LENGTH_RATES]; +typedef UCHAR NDIS_802_11_RATES_EX[NDIS_802_11_LENGTH_RATES_EX]; + +typedef struct NDIS_WLAN_BSSID_EX { + ULONG Length; + NDIS_802_11_MAC_ADDRESS MacAddress; /* BSSID */ + UCHAR Reserved[2]; + NDIS_802_11_SSID Ssid; + ULONG Privacy; + NDIS_802_11_RSSI Rssi; + NDIS_802_11_NETWORK_TYPE NetworkTypeInUse; + NDIS_802_11_CONFIGURATION Configuration; + NDIS_802_11_NETWORK_INFRASTRUCTURE InfrastructureMode; + NDIS_802_11_RATES_EX SupportedRates; + ULONG IELength; + UCHAR IEs[1]; +} NDIS_WLAN_BSSID_EX; + +typedef struct NDIS_802_11_BSSID_LIST_EX { + ULONG NumberOfItems; + NDIS_WLAN_BSSID_EX Bssid[1]; +} NDIS_802_11_BSSID_LIST_EX; + +typedef struct NDIS_802_11_FIXED_IEs { + UCHAR Timestamp[8]; + USHORT BeaconInterval; + USHORT Capabilities; +} NDIS_802_11_FIXED_IEs; + +typedef struct NDIS_802_11_WEP { + ULONG Length; + ULONG KeyIndex; + ULONG KeyLength; + UCHAR KeyMaterial[1]; +} NDIS_802_11_WEP; + +typedef ULONG NDIS_802_11_KEY_INDEX; +typedef ULONGLONG NDIS_802_11_KEY_RSC; + +typedef struct NDIS_802_11_KEY { + ULONG Length; + ULONG KeyIndex; + ULONG KeyLength; + NDIS_802_11_MAC_ADDRESS BSSID; + NDIS_802_11_KEY_RSC KeyRSC; + UCHAR KeyMaterial[1]; +} NDIS_802_11_KEY; + +typedef struct NDIS_802_11_REMOVE_KEY { + ULONG Length; + ULONG KeyIndex; + NDIS_802_11_MAC_ADDRESS BSSID; +} NDIS_802_11_REMOVE_KEY; + +typedef struct NDIS_802_11_AI_REQFI { + USHORT Capabilities; + USHORT ListenInterval; + NDIS_802_11_MAC_ADDRESS CurrentAPAddress; +} NDIS_802_11_AI_REQFI; + +typedef struct NDIS_802_11_AI_RESFI { + USHORT Capabilities; + USHORT StatusCode; + USHORT AssociationId; +} NDIS_802_11_AI_RESFI; + +typedef struct NDIS_802_11_ASSOCIATION_INFORMATION { + ULONG Length; + USHORT AvailableRequestFixedIEs; + NDIS_802_11_AI_REQFI RequestFixedIEs; + ULONG RequestIELength; + ULONG OffsetRequestIEs; + USHORT AvailableResponseFixedIEs; + NDIS_802_11_AI_RESFI ResponseFixedIEs; + ULONG ResponseIELength; + ULONG OffsetResponseIEs; +} NDIS_802_11_ASSOCIATION_INFORMATION; + +typedef struct NDIS_802_11_AUTHENTICATION_ENCRYPTION { + NDIS_802_11_AUTHENTICATION_MODE AuthModeSupported; + NDIS_802_11_ENCRYPTION_STATUS EncryptStatusSupported; +} NDIS_802_11_AUTHENTICATION_ENCRYPTION; + +typedef struct NDIS_802_11_CAPABILITY { + ULONG Length; + ULONG Version; + ULONG NoOfPMKIDs; + ULONG NoOfAuthEncryptPairSupported; + NDIS_802_11_AUTHENTICATION_ENCRYPTION + AuthenticationEncryptionSupported[1]; +} NDIS_802_11_CAPABILITY; + +typedef UCHAR NDIS_802_11_PMKID_VALUE[16]; + +typedef struct BSSID_INFO { + NDIS_802_11_MAC_ADDRESS BSSID; + NDIS_802_11_PMKID_VALUE PMKID; +} BSSID_INFO; + +typedef struct NDIS_802_11_PMKID { + ULONG Length; + ULONG BSSIDInfoCount; + BSSID_INFO BSSIDInfo[1]; +} NDIS_802_11_PMKID; + +typedef enum NDIS_802_11_STATUS_TYPE { + Ndis802_11StatusType_Authentication, + Ndis802_11StatusType_PMKID_CandidateList = 2, + Ndis802_11StatusTypeMax +} NDIS_802_11_STATUS_TYPE; + +typedef struct NDIS_802_11_STATUS_INDICATION { + NDIS_802_11_STATUS_TYPE StatusType; +} NDIS_802_11_STATUS_INDICATION; + +typedef struct PMKID_CANDIDATE { + NDIS_802_11_MAC_ADDRESS BSSID; + ULONG Flags; +} PMKID_CANDIDATE; + +#define NDIS_802_11_PMKID_CANDIDATE_PREAUTH_ENABLED 0x01 + +typedef struct NDIS_802_11_PMKID_CANDIDATE_LIST { + ULONG Version; + ULONG NumCandidates; + PMKID_CANDIDATE CandidateList[1]; +} NDIS_802_11_PMKID_CANDIDATE_LIST; + +typedef struct NDIS_802_11_AUTHENTICATION_REQUEST { + ULONG Length; + NDIS_802_11_MAC_ADDRESS Bssid; + ULONG Flags; +} NDIS_802_11_AUTHENTICATION_REQUEST; + +#define NDIS_802_11_AUTH_REQUEST_REAUTH 0x01 +#define NDIS_802_11_AUTH_REQUEST_KEYUPDATE 0x02 +#define NDIS_802_11_AUTH_REQUEST_PAIRWISE_ERROR 0x06 +#define NDIS_802_11_AUTH_REQUEST_GROUP_ERROR 0x0E + +#endif + + +static int ndis_get_oid(struct wpa_driver_ndis_data *drv, unsigned int oid, + char *data, int len) +{ + char *buf; + PACKET_OID_DATA *o; + int ret; + + buf = malloc(sizeof(*o) + len); + if (buf == NULL) + return -1; + memset(buf, 0, sizeof(*o) + len); + o = (PACKET_OID_DATA *) buf; + o->Oid = oid; + o->Length = len; + + if (!PacketRequest(drv->adapter, FALSE, o)) { + wpa_printf(MSG_DEBUG, "%s: oid=0x%x len (%d) failed", + __func__, oid, len); + free(buf); + return -1; + } + if (o->Length > len) { + wpa_printf(MSG_DEBUG, "%s: oid=0x%x Length (%d) > len (%d)", + __func__, oid, (unsigned int) o->Length, len); + free(buf); + return -1; + } + memcpy(data, o->Data, o->Length); + ret = o->Length; + free(buf); + return ret; +} + + +static int ndis_set_oid(struct wpa_driver_ndis_data *drv, unsigned int oid, + char *data, int len) +{ + char *buf; + PACKET_OID_DATA *o; + + buf = malloc(sizeof(*o) + len); + if (buf == NULL) + return -1; + memset(buf, 0, sizeof(*o) + len); + o = (PACKET_OID_DATA *) buf; + o->Oid = oid; + o->Length = len; + if (data) + memcpy(o->Data, data, len); + + if (!PacketRequest(drv->adapter, TRUE, o)) { + wpa_printf(MSG_DEBUG, "%s: oid=0x%x len (%d) failed", + __func__, oid, len); + free(buf); + return -1; + } + free(buf); + return 0; +} + + +static int ndis_set_auth_mode(struct wpa_driver_ndis_data *drv, int mode) +{ + u32 auth_mode = mode; + if (ndis_set_oid(drv, OID_802_11_AUTHENTICATION_MODE, + (char *) &auth_mode, sizeof(auth_mode)) < 0) { + wpa_printf(MSG_DEBUG, "NDIS: Failed to set " + "OID_802_11_AUTHENTICATION_MODE (%d)", + (int) auth_mode); + return -1; + } + return 0; +} + + +static int ndis_get_auth_mode(struct wpa_driver_ndis_data *drv) +{ + u32 auth_mode; + int res; + res = ndis_get_oid(drv, OID_802_11_AUTHENTICATION_MODE, + (char *) &auth_mode, sizeof(auth_mode)); + if (res != sizeof(auth_mode)) { + wpa_printf(MSG_DEBUG, "NDIS: Failed to get " + "OID_802_11_AUTHENTICATION_MODE"); + return -1; + } + return auth_mode; +} + + +static int ndis_set_encr_status(struct wpa_driver_ndis_data *drv, int encr) +{ + u32 encr_status = encr; + if (ndis_set_oid(drv, OID_802_11_ENCRYPTION_STATUS, + (char *) &encr_status, sizeof(encr_status)) < 0) { + wpa_printf(MSG_DEBUG, "NDIS: Failed to set " + "OID_802_11_ENCRYPTION_STATUS (%d)", encr); + return -1; + } + return 0; +} + + +static int ndis_get_encr_status(struct wpa_driver_ndis_data *drv) +{ + u32 encr; + int res; + res = ndis_get_oid(drv, OID_802_11_ENCRYPTION_STATUS, + (char *) &encr, sizeof(encr)); + if (res != sizeof(encr)) { + wpa_printf(MSG_DEBUG, "NDIS: Failed to get " + "OID_802_11_ENCRYPTION_STATUS"); + return -1; + } + return encr; +} + + +static int wpa_driver_ndis_get_bssid(void *priv, u8 *bssid) +{ + struct wpa_driver_ndis_data *drv = priv; + + return ndis_get_oid(drv, OID_802_11_BSSID, bssid, ETH_ALEN) < 0 ? + -1 : 0; +} + + + +static int wpa_driver_ndis_get_ssid(void *priv, u8 *ssid) +{ + struct wpa_driver_ndis_data *drv = priv; + NDIS_802_11_SSID buf; + int res; + + res = ndis_get_oid(drv, OID_802_11_SSID, (char *) &buf, sizeof(buf)); + if (!res) { + wpa_printf(MSG_DEBUG, "NDIS: Failed to get SSID"); + return -1; + } + memcpy(ssid, buf.Ssid, buf.SsidLength); + return buf.SsidLength; +} + + +static int wpa_driver_ndis_set_ssid(struct wpa_driver_ndis_data *drv, + const u8 *ssid, size_t ssid_len) +{ + NDIS_802_11_SSID buf; + + memset(&buf, 0, sizeof(buf)); + buf.SsidLength = ssid_len; + memcpy(buf.Ssid, ssid, ssid_len); + return ndis_set_oid(drv, OID_802_11_SSID, (char *) &buf, sizeof(buf)); +} + + +/* Disconnect using OID_802_11_DISASSOCIATE. This will also turn the radio off. + */ +static int wpa_driver_ndis_radio_off(struct wpa_driver_ndis_data *drv) +{ + drv->radio_enabled = 0; + return ndis_set_oid(drv, OID_802_11_DISASSOCIATE, " ", 4); +} + + +/* Disconnect by setting SSID to random (i.e., likely not used). */ +static int wpa_driver_ndis_disconnect(struct wpa_driver_ndis_data *drv) +{ + char ssid[32]; + int i; + for (i = 0; i < 32; i++) + ssid[i] = rand() & 0xff; + return wpa_driver_ndis_set_ssid(drv, ssid, 32); +} + + +static int wpa_driver_ndis_deauthenticate(void *priv, const u8 *addr, + int reason_code) +{ + struct wpa_driver_ndis_data *drv = priv; + return wpa_driver_ndis_disconnect(drv); +} + + +static int wpa_driver_ndis_disassociate(void *priv, const u8 *addr, + int reason_code) +{ + struct wpa_driver_ndis_data *drv = priv; + return wpa_driver_ndis_disconnect(drv); +} + + +static int wpa_driver_ndis_set_wpa(void *priv, int enabled) +{ + wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled); + return 0; +} + + +static void wpa_driver_ndis_scan_timeout(void *eloop_ctx, void *timeout_ctx) +{ + wpa_printf(MSG_DEBUG, "Scan timeout - try to get results"); + wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL); +} + + +static int wpa_driver_ndis_scan(void *priv, const u8 *ssid, size_t ssid_len) +{ + struct wpa_driver_ndis_data *drv = priv; + int res; + + if (!drv->radio_enabled) { + wpa_printf(MSG_DEBUG, "NDIS: turning radio on before the first" + " scan"); + if (wpa_driver_ndis_disconnect(drv) < 0) { + wpa_printf(MSG_DEBUG, "NDIS: failed to enable radio"); + } + drv->radio_enabled = 1; + } + + res = ndis_set_oid(drv, OID_802_11_BSSID_LIST_SCAN, " ", 4); + eloop_register_timeout(3, 0, wpa_driver_ndis_scan_timeout, drv, + drv->ctx); + return res; +} + + +static void wpa_driver_ndis_get_ies(struct wpa_scan_result *res, u8 *ie, + size_t ie_len) +{ + u8 *pos = ie; + u8 *end = ie + ie_len; + + if (ie_len < sizeof(NDIS_802_11_FIXED_IEs)) + return; + + pos += sizeof(NDIS_802_11_FIXED_IEs); + /* wpa_hexdump(MSG_MSGDUMP, "IEs", pos, end - pos); */ + while (pos + 1 < end && pos + 2 + pos[1] <= end) { + u8 ielen = 2 + pos[1]; + if (ielen > SSID_MAX_WPA_IE_LEN) { + pos += ielen; + continue; + } + if (pos[0] == GENERIC_INFO_ELEM && pos[1] >= 4 && + memcmp(pos + 2, "\x00\x50\xf2\x01", 4) == 0) { + memcpy(res->wpa_ie, pos, ielen); + res->wpa_ie_len = ielen; + } else if (pos[0] == RSN_INFO_ELEM) { + memcpy(res->rsn_ie, pos, ielen); + res->rsn_ie_len = ielen; + } + pos += ielen; + } +} + + +static int wpa_driver_ndis_get_scan_results(void *priv, + struct wpa_scan_result *results, + size_t max_size) +{ + struct wpa_driver_ndis_data *drv = priv; + NDIS_802_11_BSSID_LIST_EX *b; + size_t blen; + int len, count, i, j; + char *pos; + + blen = 65535; + b = malloc(blen); + if (b == NULL) + return -1; + memset(b, 0, blen); + len = ndis_get_oid(drv, OID_802_11_BSSID_LIST, (char *) b, blen); + if (len < 0) { + wpa_printf(MSG_DEBUG, "NDIS: failed to get scan results"); + free(b); + return -1; + } + count = b->NumberOfItems; + + if (count > max_size) + count = max_size; + + memset(results, 0, max_size * sizeof(struct wpa_scan_result)); + pos = (char *) &b->Bssid[0]; + for (i = 0; i < count; i++) { + NDIS_WLAN_BSSID_EX *bss = (NDIS_WLAN_BSSID_EX *) pos; + memcpy(results[i].bssid, bss->MacAddress, ETH_ALEN); + memcpy(results[i].ssid, bss->Ssid.Ssid, bss->Ssid.SsidLength); + results[i].ssid_len = bss->Ssid.SsidLength; + if (bss->Privacy) + results[i].caps = 1; /* FIX? */ + results[i].level = (int) bss->Rssi; + results[i].freq = bss->Configuration.DSConfig / 1000; + for (j = 0; j < sizeof(bss->SupportedRates); j++) { + if ((bss->SupportedRates[j] & 0x7f) > + results[i].maxrate) { + results[i].maxrate = + bss->SupportedRates[j] & 0x7f; + } + } + wpa_driver_ndis_get_ies(&results[i], bss->IEs, bss->IELength); + pos += bss->Length; + } + + free(b); + return count; +} + + +static int wpa_driver_ndis_remove_key(struct wpa_driver_ndis_data *drv, + int key_idx, const u8 *addr, + const u8 *bssid, int pairwise) +{ + NDIS_802_11_REMOVE_KEY rkey; + NDIS_802_11_KEY_INDEX index; + int res, res2; + + memset(&rkey, 0, sizeof(rkey)); + + rkey.Length = sizeof(rkey); + rkey.KeyIndex = key_idx; + if (pairwise) + rkey.KeyIndex |= 1 << 30; + memcpy(rkey.BSSID, bssid, ETH_ALEN); + + res = ndis_set_oid(drv, OID_802_11_REMOVE_KEY, (char *) &rkey, + sizeof(rkey)); + if (!pairwise) { + res2 = ndis_set_oid(drv, OID_802_11_REMOVE_WEP, + (char *) &index, sizeof(index)); + } else + res2 = 0; + + if (res < 0 && res2 < 0) + return res; + return 0; +} + + +static int wpa_driver_ndis_add_wep(struct wpa_driver_ndis_data *drv, + int pairwise, int key_idx, int set_tx, + const u8 *key, size_t key_len) +{ + NDIS_802_11_WEP *wep; + size_t len; + int res; + + len = 12 + key_len; + wep = malloc(len); + if (wep == NULL) + return -1; + memset(wep, 0, len); + wep->Length = len; + wep->KeyIndex = key_idx; + if (set_tx) + wep->KeyIndex |= 1 << 31; +#if 0 /* Setting bit30 does not seem to work with some NDIS drivers */ + if (pairwise) + wep->KeyIndex |= 1 << 30; +#endif + wep->KeyLength = key_len; + memcpy(wep->KeyMaterial, key, key_len); + + wpa_hexdump_key(MSG_MSGDUMP, "NDIS: OIS_802_11_ADD_WEP", + (char *) wep, len); + res = ndis_set_oid(drv, OID_802_11_ADD_WEP, (char *) wep, len); + + free(wep); + + return res; +} + +static int wpa_driver_ndis_set_key(void *priv, wpa_alg alg, const u8 *addr, + int key_idx, int set_tx, + const u8 *seq, size_t seq_len, + const u8 *key, size_t key_len) +{ + struct wpa_driver_ndis_data *drv = priv; + size_t len; + NDIS_802_11_KEY *nkey; + int i, res, pairwise; + u8 bssid[ETH_ALEN]; + + if (addr == NULL || memcmp(addr, "\xff\xff\xff\xff\xff\xff", + ETH_ALEN) == 0) { + /* Group Key */ + pairwise = 0; + wpa_driver_ndis_get_bssid(drv, bssid); + } else { + /* Pairwise Key */ + pairwise = 1; + memcpy(bssid, addr, ETH_ALEN); + } + + if (alg == WPA_ALG_NONE || key_len == 0) { + return wpa_driver_ndis_remove_key(drv, key_idx, addr, bssid, + pairwise); + } + + if (alg == WPA_ALG_WEP) { + return wpa_driver_ndis_add_wep(drv, pairwise, key_idx, set_tx, + key, key_len); + } + + len = 12 + 6 + 6 + 8 + key_len; + + nkey = malloc(len); + if (nkey == NULL) + return -1; + memset(nkey, 0, len); + + nkey->Length = len; + nkey->KeyIndex = key_idx; + if (set_tx) + nkey->KeyIndex |= 1 << 31; + if (pairwise) + nkey->KeyIndex |= 1 << 30; + if (seq && seq_len) + nkey->KeyIndex |= 1 << 29; + nkey->KeyLength = key_len; + memcpy(nkey->BSSID, bssid, ETH_ALEN); + if (seq && seq_len) { + for (i = 0; i < seq_len; i++) + nkey->KeyRSC |= seq[i] << (i * 8); + } + if (alg == WPA_ALG_TKIP && key_len == 32) { + memcpy(nkey->KeyMaterial, key, 16); + memcpy(nkey->KeyMaterial + 16, key + 24, 8); + memcpy(nkey->KeyMaterial + 24, key + 16, 8); + } else { + memcpy(nkey->KeyMaterial, key, key_len); + } + + wpa_hexdump_key(MSG_MSGDUMP, "NDIS: OIS_802_11_ADD_KEY", + (char *) nkey, len); + res = ndis_set_oid(drv, OID_802_11_ADD_KEY, (char *) nkey, len); + free(nkey); + + return res; +} + + +static int +wpa_driver_ndis_associate(void *priv, + struct wpa_driver_associate_params *params) +{ + struct wpa_driver_ndis_data *drv = priv; + u32 auth_mode, encr, priv_mode, mode; + + /* Note: Setting OID_802_11_INFRASTRUCTURE_MODE clears current keys, + * so static WEP keys needs to be set again after this. */ + if (params->mode == IEEE80211_MODE_IBSS) + mode = Ndis802_11IBSS; + else + mode = Ndis802_11Infrastructure; + if (ndis_set_oid(drv, OID_802_11_INFRASTRUCTURE_MODE, + (char *) &mode, sizeof(mode)) < 0) { + wpa_printf(MSG_DEBUG, "NDIS: Failed to set " + "OID_802_11_INFRASTRUCTURE_MODE (%d)", + (int) mode); + /* Try to continue anyway */ + } + + if (params->wpa_ie == NULL || params->wpa_ie_len == 0) { + if (params->auth_alg & AUTH_ALG_SHARED_KEY) { + if (params->auth_alg & AUTH_ALG_OPEN_SYSTEM) + auth_mode = Ndis802_11AuthModeAutoSwitch; + else + auth_mode = Ndis802_11AuthModeShared; + } else + auth_mode = Ndis802_11AuthModeOpen; + priv_mode = Ndis802_11PrivFilterAcceptAll; + } else if (params->wpa_ie[0] == RSN_INFO_ELEM) { + priv_mode = Ndis802_11PrivFilter8021xWEP; + if (params->key_mgmt_suite == KEY_MGMT_PSK) + auth_mode = Ndis802_11AuthModeWPA2PSK; + else + auth_mode = Ndis802_11AuthModeWPA2; + } else { + priv_mode = Ndis802_11PrivFilter8021xWEP; + if (params->key_mgmt_suite == KEY_MGMT_WPA_NONE) + auth_mode = Ndis802_11AuthModeWPANone; + else if (params->key_mgmt_suite == KEY_MGMT_PSK) + auth_mode = Ndis802_11AuthModeWPAPSK; + else + auth_mode = Ndis802_11AuthModeWPA; + } + + switch (params->pairwise_suite) { + case CIPHER_CCMP: + encr = Ndis802_11Encryption3Enabled; + break; + case CIPHER_TKIP: + encr = Ndis802_11Encryption2Enabled; + break; + case CIPHER_WEP40: + case CIPHER_WEP104: + encr = Ndis802_11Encryption1Enabled; + break; + case CIPHER_NONE: + if (params->group_suite == CIPHER_CCMP) + encr = Ndis802_11Encryption3Enabled; + else + encr = Ndis802_11Encryption2Enabled; + break; + default: + encr = Ndis802_11EncryptionDisabled; + }; + + if (ndis_set_oid(drv, OID_802_11_PRIVACY_FILTER, + (char *) &priv_mode, sizeof(priv_mode)) < 0) { + wpa_printf(MSG_DEBUG, "NDIS: Failed to set " + "OID_802_11_PRIVACY_FILTER (%d)", + (int) priv_mode); + /* Try to continue anyway */ + } + + ndis_set_auth_mode(drv, auth_mode); + ndis_set_encr_status(drv, encr); + + return wpa_driver_ndis_set_ssid(drv, params->ssid, params->ssid_len); +} + + +static int wpa_driver_ndis_set_pmkid(struct wpa_driver_ndis_data *drv) +{ + int len, count, i, ret; + struct ndis_pmkid_entry *entry; + NDIS_802_11_PMKID *p; + + count = 0; + entry = drv->pmkid; + while (entry) { + count++; + if (count >= drv->no_of_pmkid) + break; + entry = entry->next; + } + len = 8 + count * sizeof(BSSID_INFO); + p = malloc(len); + if (p == NULL) + return -1; + memset(p, 0, len); + p->Length = len; + p->BSSIDInfoCount = count; + entry = drv->pmkid; + for (i = 0; i < count; i++) { + memcpy(&p->BSSIDInfo[i].BSSID, entry->bssid, ETH_ALEN); + memcpy(&p->BSSIDInfo[i].PMKID, entry->pmkid, 16); + entry = entry->next; + } + wpa_hexdump(MSG_MSGDUMP, "NDIS: OID_802_11_PMKID", (char *) p, len); + ret = ndis_set_oid(drv, OID_802_11_PMKID, (char *) p, len); + free(p); + return ret; +} + + +static int wpa_driver_ndis_add_pmkid(void *priv, const u8 *bssid, + const u8 *pmkid) +{ + struct wpa_driver_ndis_data *drv = priv; + struct ndis_pmkid_entry *entry, *prev; + + if (drv->no_of_pmkid == 0) + return 0; + + prev = NULL; + entry = drv->pmkid; + while (entry) { + if (memcmp(entry->bssid, bssid, ETH_ALEN) == 0) + break; + prev = entry; + entry = entry->next; + } + + if (entry) { + /* Replace existing entry for this BSSID and move it into the + * beginning of the list. */ + memcpy(entry->pmkid, pmkid, 16); + if (prev) { + prev->next = entry->next; + entry->next = drv->pmkid; + drv->pmkid = entry; + } + } else { + entry = malloc(sizeof(*entry)); + if (entry) { + memcpy(entry->bssid, bssid, ETH_ALEN); + memcpy(entry->pmkid, pmkid, 16); + entry->next = drv->pmkid; + drv->pmkid = entry; + } + } + + return wpa_driver_ndis_set_pmkid(drv); +} + + +static int wpa_driver_ndis_remove_pmkid(void *priv, const u8 *bssid, + const u8 *pmkid) +{ + struct wpa_driver_ndis_data *drv = priv; + struct ndis_pmkid_entry *entry, *prev; + + if (drv->no_of_pmkid == 0) + return 0; + + entry = drv->pmkid; + prev = NULL; + drv->pmkid = NULL; + while (entry) { + if (memcmp(entry->bssid, bssid, ETH_ALEN) == 0 && + memcmp(entry->pmkid, pmkid, 16) == 0) { + if (prev) + prev->next = entry->next; + else + drv->pmkid = entry->next; + free(entry); + break; + } + prev = entry; + entry = entry->next; + } + return wpa_driver_ndis_set_pmkid(drv); +} + + +static int wpa_driver_ndis_flush_pmkid(void *priv) +{ + struct wpa_driver_ndis_data *drv = priv; + NDIS_802_11_PMKID p; + struct ndis_pmkid_entry *pmkid, *prev; + + if (drv->no_of_pmkid == 0) + return 0; + + pmkid = drv->pmkid; + drv->pmkid = NULL; + while (pmkid) { + prev = pmkid; + pmkid = pmkid->next; + free(prev); + } + + memset(&p, 0, sizeof(p)); + p.Length = 8; + p.BSSIDInfoCount = 0; + wpa_hexdump(MSG_MSGDUMP, "NDIS: OID_802_11_PMKID (flush)", + (char *) &p, 8); + return ndis_set_oid(drv, OID_802_11_PMKID, (char *) &p, 8); +} + + +static int wpa_driver_ndis_get_associnfo(struct wpa_driver_ndis_data *drv) +{ + char buf[512]; + NDIS_802_11_ASSOCIATION_INFORMATION *ai; + int len; + union wpa_event_data data; + + len = ndis_get_oid(drv, OID_802_11_ASSOCIATION_INFORMATION, buf, + sizeof(buf)); + if (len < 0) { + wpa_printf(MSG_DEBUG, "NDIS: failed to get association " + "information"); + return -1; + } + if (len > sizeof(buf)) { + /* Some drivers seem to be producing incorrect length for this + * data. Limit the length to the current buffer size to avoid + * crashing in hexdump. The data seems to be otherwise valid, + * so better try to use it. */ + wpa_printf(MSG_DEBUG, "NDIS: ignored bogus association " + "information length %d", len); + len = ndis_get_oid(drv, OID_802_11_ASSOCIATION_INFORMATION, + buf, sizeof(buf)); + if (len < -1) { + wpa_printf(MSG_DEBUG, "NDIS: re-reading association " + "information failed"); + return -1; + } + if (len > sizeof(buf)) { + wpa_printf(MSG_DEBUG, "NDIS: ignored bogus association" + " information length %d (re-read)", len); + len = sizeof(buf); + } + } + wpa_hexdump(MSG_MSGDUMP, "NDIS: association information", buf, len); + if (len < sizeof(*ai)) { + wpa_printf(MSG_DEBUG, "NDIS: too short association " + "information"); + return -1; + } + ai = (NDIS_802_11_ASSOCIATION_INFORMATION *) buf; + wpa_printf(MSG_DEBUG, "NDIS: ReqFixed=0x%x RespFixed=0x%x off_req=%d " + "off_resp=%d len_req=%d len_resp=%d", + ai->AvailableRequestFixedIEs, ai->AvailableResponseFixedIEs, + (int) ai->OffsetRequestIEs, (int) ai->OffsetResponseIEs, + (int) ai->RequestIELength, (int) ai->ResponseIELength); + + if (ai->OffsetRequestIEs + ai->RequestIELength > len || + ai->OffsetResponseIEs + ai->ResponseIELength > len) { + wpa_printf(MSG_DEBUG, "NDIS: association information - " + "IE overflow"); + return -1; + } + + wpa_hexdump(MSG_MSGDUMP, "NDIS: Request IEs", + buf + ai->OffsetRequestIEs, ai->RequestIELength); + wpa_hexdump(MSG_MSGDUMP, "NDIS: Response IEs", + buf + ai->OffsetResponseIEs, ai->ResponseIELength); + + memset(&data, 0, sizeof(data)); + data.assoc_info.req_ies = buf + ai->OffsetRequestIEs; + data.assoc_info.req_ies_len = ai->RequestIELength; + data.assoc_info.resp_ies = buf + ai->OffsetResponseIEs; + data.assoc_info.resp_ies_len = ai->ResponseIELength; + wpa_supplicant_event(drv->ctx, EVENT_ASSOCINFO, &data); + + return 0; +} + + +static void wpa_driver_ndis_poll_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_driver_ndis_data *drv = eloop_ctx; + u8 bssid[ETH_ALEN]; + + if (wpa_driver_ndis_get_bssid(drv, bssid)) { + /* Disconnected */ + if (memcmp(drv->bssid, "\x00\x00\x00\x00\x00\x00", ETH_ALEN) + != 0) { + memset(drv->bssid, 0, ETH_ALEN); + wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, NULL); + } + } else { + /* Connected */ + if (memcmp(drv->bssid, bssid, ETH_ALEN) != 0) { + memcpy(drv->bssid, bssid, ETH_ALEN); + wpa_driver_ndis_get_associnfo(drv); + wpa_supplicant_event(drv->ctx, EVENT_ASSOC, NULL); + } + } + eloop_register_timeout(1, 0, wpa_driver_ndis_poll_timeout, drv, NULL); +} + + +static void wpa_driver_ndis_poll(void *priv) +{ + struct wpa_driver_ndis_data *drv = priv; + eloop_cancel_timeout(wpa_driver_ndis_poll_timeout, drv, drv->ctx); + wpa_driver_ndis_poll_timeout(drv, NULL); +} + + +/* Called when driver generates Media Connect Event by calling + * NdisMIndicateStatus() with NDIS_STATUS_MEDIA_CONNECT */ +void wpa_driver_ndis_event_connect(struct wpa_driver_ndis_data *drv) +{ + wpa_printf(MSG_DEBUG, "NDIS: Media Connect Event"); + if (wpa_driver_ndis_get_bssid(drv, drv->bssid) == 0) { + wpa_driver_ndis_get_associnfo(drv); + wpa_supplicant_event(drv->ctx, EVENT_ASSOC, NULL); + } +} + + +/* Called when driver generates Media Disconnect Event by calling + * NdisMIndicateStatus() with NDIS_STATUS_MEDIA_DISCONNECT */ +void wpa_driver_ndis_event_disconnect(struct wpa_driver_ndis_data *drv) +{ + wpa_printf(MSG_DEBUG, "NDIS: Media Disconnect Event"); + memset(drv->bssid, 0, ETH_ALEN); + wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, NULL); +} + + +static void wpa_driver_ndis_event_auth(struct wpa_driver_ndis_data *drv, + const u8 *data, size_t data_len) +{ + NDIS_802_11_AUTHENTICATION_REQUEST *req; + int pairwise = 0, group = 0; + union wpa_event_data event; + + if (data_len < sizeof(*req)) { + wpa_printf(MSG_DEBUG, "NDIS: Too short Authentication Request " + "Event (len=%d)", data_len); + return; + } + req = (NDIS_802_11_AUTHENTICATION_REQUEST *) data; + + wpa_printf(MSG_DEBUG, "NDIS: Authentication Request Event: " + "Bssid " MACSTR " Flags 0x%x", + MAC2STR(req->Bssid), (int) req->Flags); + + if ((req->Flags & NDIS_802_11_AUTH_REQUEST_PAIRWISE_ERROR) == + NDIS_802_11_AUTH_REQUEST_PAIRWISE_ERROR) + pairwise = 1; + else if ((req->Flags & NDIS_802_11_AUTH_REQUEST_GROUP_ERROR) == + NDIS_802_11_AUTH_REQUEST_GROUP_ERROR) + group = 1; + + if (pairwise || group) { + memset(&event, 0, sizeof(event)); + event.michael_mic_failure.unicast = pairwise; + wpa_supplicant_event(drv->ctx, EVENT_MICHAEL_MIC_FAILURE, + &event); + } +} + + +static void wpa_driver_ndis_event_pmkid(struct wpa_driver_ndis_data *drv, + const u8 *data, size_t data_len) +{ + NDIS_802_11_PMKID_CANDIDATE_LIST *pmkid; + int i; + union wpa_event_data event; + + if (data_len < 8) { + wpa_printf(MSG_DEBUG, "NDIS: Too short PMKID Candidate List " + "Event (len=%d)", data_len); + return; + } + pmkid = (NDIS_802_11_PMKID_CANDIDATE_LIST *) data; + wpa_printf(MSG_DEBUG, "NDIS: PMKID Candidate List Event - Version %d " + "NumCandidates %d", + (int) pmkid->Version, (int) pmkid->NumCandidates); + + if (pmkid->Version != 1) { + wpa_printf(MSG_DEBUG, "NDIS: Unsupported PMKID Candidate List " + "Version %d", (int) pmkid->Version); + return; + } + + if (data_len < 8 + pmkid->NumCandidates * sizeof(PMKID_CANDIDATE)) { + wpa_printf(MSG_DEBUG, "NDIS: PMKID Candidate List underflow"); + return; + } + + memset(&event, 0, sizeof(event)); + for (i = 0; i < pmkid->NumCandidates; i++) { + PMKID_CANDIDATE *p = &pmkid->CandidateList[i]; + wpa_printf(MSG_DEBUG, "NDIS: %d: " MACSTR " Flags 0x%x", + i, MAC2STR(p->BSSID), (int) p->Flags); + memcpy(event.pmkid_candidate.bssid, p->BSSID, ETH_ALEN); + event.pmkid_candidate.index = i; + event.pmkid_candidate.preauth = + p->Flags & NDIS_802_11_PMKID_CANDIDATE_PREAUTH_ENABLED; + wpa_supplicant_event(drv->ctx, EVENT_PMKID_CANDIDATE, + &event); + } +} + + +/* Called when driver calls NdisMIndicateStatus() with + * NDIS_STATUS_MEDIA_SPECIFIC_INDICATION */ +void wpa_driver_ndis_event_media_specific(struct wpa_driver_ndis_data *drv, + const u8 *data, size_t data_len) +{ + NDIS_802_11_STATUS_INDICATION *status; + + if (data == NULL || data_len < sizeof(*status)) + return; + + wpa_hexdump(MSG_DEBUG, "NDIS: Media Specific Indication", + data, data_len); + + status = (NDIS_802_11_STATUS_INDICATION *) data; + data += sizeof(status); + data_len -= sizeof(status); + + switch (status->StatusType) { + case Ndis802_11StatusType_Authentication: + wpa_driver_ndis_event_auth(drv, data, data_len); + break; + case Ndis802_11StatusType_PMKID_CandidateList: + wpa_driver_ndis_event_pmkid(drv, data, data_len); + break; + default: + wpa_printf(MSG_DEBUG, "NDIS: Unknown StatusType %d", + (int) status->StatusType); + break; + } +} + + +static void +wpa_driver_ndis_get_wpa_capability(struct wpa_driver_ndis_data *drv) +{ + wpa_printf(MSG_DEBUG, "NDIS: verifying driver WPA capability"); + + if (ndis_set_auth_mode(drv, Ndis802_11AuthModeWPA) == 0 && + ndis_get_auth_mode(drv) == Ndis802_11AuthModeWPA) { + wpa_printf(MSG_DEBUG, "NDIS: WPA key management supported"); + drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA; + } + + if (ndis_set_auth_mode(drv, Ndis802_11AuthModeWPAPSK) == 0 && + ndis_get_auth_mode(drv) == Ndis802_11AuthModeWPAPSK) { + wpa_printf(MSG_DEBUG, "NDIS: WPA-PSK key management " + "supported"); + drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK; + } + + if (ndis_set_encr_status(drv, Ndis802_11Encryption3Enabled) == 0 && + ndis_get_encr_status(drv) == Ndis802_11Encryption3KeyAbsent) { + wpa_printf(MSG_DEBUG, "NDIS: CCMP encryption supported"); + drv->capa.enc |= WPA_DRIVER_CAPA_ENC_CCMP; + } + + if (ndis_set_encr_status(drv, Ndis802_11Encryption2Enabled) == 0 && + ndis_get_encr_status(drv) == Ndis802_11Encryption2KeyAbsent) { + wpa_printf(MSG_DEBUG, "NDIS: TKIP encryption supported"); + drv->capa.enc |= WPA_DRIVER_CAPA_ENC_TKIP; + } + + if (ndis_set_encr_status(drv, Ndis802_11Encryption1Enabled) == 0 && + ndis_get_encr_status(drv) == Ndis802_11Encryption1KeyAbsent) { + wpa_printf(MSG_DEBUG, "NDIS: WEP encryption supported"); + drv->capa.enc |= WPA_DRIVER_CAPA_ENC_WEP40 | + WPA_DRIVER_CAPA_ENC_WEP104; + } + + if (ndis_set_auth_mode(drv, Ndis802_11AuthModeShared) == 0 && + ndis_get_auth_mode(drv) == Ndis802_11AuthModeShared) { + drv->capa.auth |= WPA_DRIVER_AUTH_SHARED; + } + + if (ndis_set_auth_mode(drv, Ndis802_11AuthModeOpen) == 0 && + ndis_get_auth_mode(drv) == Ndis802_11AuthModeOpen) { + drv->capa.auth |= WPA_DRIVER_AUTH_OPEN; + } + + ndis_set_encr_status(drv, Ndis802_11EncryptionDisabled); + + /* Could also verify OID_802_11_ADD_KEY error reporting and + * support for OID_802_11_ASSOCIATION_INFORMATION. */ + + if (drv->capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA && + drv->capa.enc & (WPA_DRIVER_CAPA_ENC_TKIP | + WPA_DRIVER_CAPA_ENC_CCMP)) { + wpa_printf(MSG_DEBUG, "NDIS: driver supports WPA"); + drv->has_capability = 1; + } else { + wpa_printf(MSG_DEBUG, "NDIS: no WPA support found"); + } + + wpa_printf(MSG_DEBUG, "NDIS: driver capabilities: key_mgmt 0x%x " + "enc 0x%x auth 0x%x", + drv->capa.key_mgmt, drv->capa.enc, drv->capa.auth); +} + + +static void wpa_driver_ndis_get_capability(struct wpa_driver_ndis_data *drv) +{ + char buf[512]; + int len, i; + NDIS_802_11_CAPABILITY *c; + + drv->capa.flags = WPA_DRIVER_FLAGS_DRIVER_IE | + WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC; + + len = ndis_get_oid(drv, OID_802_11_CAPABILITY, buf, sizeof(buf)); + if (len < 0) { + wpa_driver_ndis_get_wpa_capability(drv); + return; + } + + wpa_hexdump(MSG_MSGDUMP, "OID_802_11_CAPABILITY", buf, len); + c = (NDIS_802_11_CAPABILITY *) buf; + if (len < sizeof(*c) || c->Version != 2) { + wpa_printf(MSG_DEBUG, "NDIS: unsupported " + "OID_802_11_CAPABILITY data"); + return; + } + wpa_printf(MSG_DEBUG, "NDIS: Driver supports OID_802_11_CAPABILITY - " + "NoOfPMKIDs %d NoOfAuthEncrPairs %d", + (int) c->NoOfPMKIDs, (int) c->NoOfAuthEncryptPairSupported); + drv->has_capability = 1; + drv->no_of_pmkid = c->NoOfPMKIDs; + for (i = 0; i < c->NoOfAuthEncryptPairSupported; i++) { + NDIS_802_11_AUTHENTICATION_ENCRYPTION *ae; + ae = &c->AuthenticationEncryptionSupported[i]; + if ((char *) (ae + 1) > buf + len) { + wpa_printf(MSG_DEBUG, "NDIS: auth/encr pair list " + "overflow"); + break; + } + wpa_printf(MSG_MSGDUMP, "NDIS: %d - auth %d encr %d", + i, (int) ae->AuthModeSupported, + (int) ae->EncryptStatusSupported); + switch (ae->AuthModeSupported) { + case Ndis802_11AuthModeOpen: + drv->capa.auth |= WPA_DRIVER_AUTH_OPEN; + break; + case Ndis802_11AuthModeShared: + drv->capa.auth |= WPA_DRIVER_AUTH_SHARED; + break; + case Ndis802_11AuthModeWPA: + drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA; + break; + case Ndis802_11AuthModeWPAPSK: + drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK; + break; + case Ndis802_11AuthModeWPA2: + drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA2; + break; + case Ndis802_11AuthModeWPA2PSK: + drv->capa.key_mgmt |= + WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK; + break; + case Ndis802_11AuthModeWPANone: + drv->capa.key_mgmt |= + WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE; + break; + default: + break; + } + switch (ae->EncryptStatusSupported) { + case Ndis802_11Encryption1Enabled: + drv->capa.enc |= WPA_DRIVER_CAPA_ENC_WEP40; + drv->capa.enc |= WPA_DRIVER_CAPA_ENC_WEP104; + break; + case Ndis802_11Encryption2Enabled: + drv->capa.enc |= WPA_DRIVER_CAPA_ENC_TKIP; + break; + case Ndis802_11Encryption3Enabled: + drv->capa.enc |= WPA_DRIVER_CAPA_ENC_CCMP; + break; + default: + break; + } + } + + wpa_printf(MSG_DEBUG, "NDIS: driver capabilities: key_mgmt 0x%x " + "enc 0x%x auth 0x%x", + drv->capa.key_mgmt, drv->capa.enc, drv->capa.auth); +} + + +static int wpa_driver_ndis_get_capa(void *priv, struct wpa_driver_capa *capa) +{ + struct wpa_driver_ndis_data *drv = priv; + if (!drv->has_capability) + return -1; + memcpy(capa, &drv->capa, sizeof(*capa)); + return 0; +} + + +static const char * wpa_driver_ndis_get_ifname(void *priv) +{ + struct wpa_driver_ndis_data *drv = priv; + return drv->ifname; +} + + +static const u8 * wpa_driver_ndis_get_mac_addr(void *priv) +{ + struct wpa_driver_ndis_data *drv = priv; + return drv->own_addr; +} + + +static int wpa_driver_ndis_get_names(struct wpa_driver_ndis_data *drv) +{ + PTSTR names, pos; + ULONG len; + BOOLEAN res; + const int MAX_ADAPTERS = 16; + char *name[MAX_ADAPTERS]; + char *desc[MAX_ADAPTERS]; + int num_name, num_desc, i, found_name, found_desc; + size_t dlen; + + len = 1024; + names = malloc(len); + if (names == NULL) + return -1; + memset(names, 0, len); + + res = PacketGetAdapterNames(names, &len); + if (!res && len > 1024) { + free(names); + names = malloc(len); + if (names == NULL) + return -1; + memset(names, 0, len); + res = PacketGetAdapterNames(names, &len); + if (!res) { + free(names); + return -1; + } + } + + /* wpa_hexdump_ascii(MSG_DEBUG, "NDIS: AdapterNames", names, len); */ + + num_name = 0; + pos = names; + while (pos < names + len) { + name[num_name] = pos; + while (*pos && pos < names + len) + pos++; + if (pos + 1 >= names + len) { + free(names); + return -1; + } + pos++; + num_name++; + if (num_name >= MAX_ADAPTERS) { + wpa_printf(MSG_DEBUG, "NDIS: Too many adapters"); + free(names); + return -1; + } + if (*pos == '\0') { + wpa_printf(MSG_DEBUG, "NDIS: %d adapter names found", + num_name); + pos++; + break; + } + } + + num_desc = 0; + while (pos < names + len) { + desc[num_desc] = pos; + while (*pos && pos < names + len) + pos++; + if (pos + 1 >= names + len) { + free(names); + return -1; + } + pos++; + num_desc++; + if (num_desc >= MAX_ADAPTERS) { + wpa_printf(MSG_DEBUG, "NDIS: Too many adapter " + "descriptions"); + free(names); + return -1; + } + if (*pos == '\0') { + wpa_printf(MSG_DEBUG, "NDIS: %d adapter descriptions " + "found", num_name); + pos++; + break; + } + } + + if (num_name != num_desc) { + wpa_printf(MSG_DEBUG, "NDIS: mismatch in adapter name and " + "description counts (%d != %d)", + num_name, num_desc); + free(names); + return -1; + } + + found_name = found_desc = -1; + for (i = 0; i < num_name; i++) { + wpa_printf(MSG_DEBUG, "NDIS: %d - %s - %s", + i, name[i], desc[i]); + if (found_name == -1 && strcmp(name[i], drv->ifname) == 0) { + found_name = i; + } + if (found_desc == -1 && + strncmp(desc[i], drv->ifname, strlen(drv->ifname)) == 0) { + found_desc = i; + } + } + + if (found_name < 0 && found_desc >= 0) { + wpa_printf(MSG_DEBUG, "NDIS: Matched interface '%s' based on " + "description '%s'", + name[found_desc], desc[found_desc]); + found_name = found_desc; + strncpy(drv->ifname, name[found_desc], sizeof(drv->ifname)); + } + + if (found_name < 0) { + wpa_printf(MSG_DEBUG, "NDIS: Could not find interface '%s'", + drv->ifname); + free(names); + return -1; + } + + i = found_name; + pos = strchr(desc[i], '('); + if (pos) { + dlen = pos - desc[i]; + pos--; + if (pos > desc[i] && *pos == ' ') + dlen--; + } else { + dlen = strlen(desc[i]); + } + drv->adapter_desc = malloc(dlen + 1); + if (drv->adapter_desc) { + memcpy(drv->adapter_desc, desc[i], dlen); + drv->adapter_desc[dlen] = '\0'; + } + + free(names); + + if (drv->adapter_desc == NULL) + return -1; + + wpa_printf(MSG_DEBUG, "NDIS: Adapter description prefix '%s'", + drv->adapter_desc); + + return 0; +} + + +static void * wpa_driver_ndis_init(void *ctx, const char *ifname) +{ + struct wpa_driver_ndis_data *drv; + u32 mode; + + drv = malloc(sizeof(*drv)); + if (drv == NULL) + return NULL; + memset(drv, 0, sizeof(*drv)); + drv->ctx = ctx; + strncpy(drv->ifname, ifname, sizeof(drv->ifname)); + drv->event_sock = -1; + + if (wpa_driver_ndis_get_names(drv) < 0) { + free(drv); + return NULL; + } + + drv->adapter = PacketOpenAdapter(drv->ifname); + if (drv->adapter == NULL) { + wpa_printf(MSG_DEBUG, "NDIS: PacketOpenAdapter failed for " + "'%s'", drv->ifname); + free(drv); + return NULL; + } + + if (ndis_get_oid(drv, OID_802_3_CURRENT_ADDRESS, + drv->own_addr, ETH_ALEN) < 0) { + wpa_printf(MSG_DEBUG, "NDIS: Get OID_802_3_CURRENT_ADDRESS " + "failed"); + PacketCloseAdapter(drv->adapter); + free(drv); + return NULL; + } + wpa_driver_ndis_get_capability(drv); + + /* Make sure that the driver does not have any obsolete PMKID entries. + */ + wpa_driver_ndis_flush_pmkid(drv); + + eloop_register_timeout(1, 0, wpa_driver_ndis_poll_timeout, drv, NULL); + + wpa_driver_register_event_cb(drv); + + /* Set mode here in case card was configured for ad-hoc mode + * previously. */ + mode = Ndis802_11Infrastructure; + if (ndis_set_oid(drv, OID_802_11_INFRASTRUCTURE_MODE, + (char *) &mode, sizeof(mode)) < 0) { + wpa_printf(MSG_DEBUG, "NDIS: Failed to set " + "OID_802_11_INFRASTRUCTURE_MODE (%d)", + (int) mode); + /* Try to continue anyway */ + } + + return drv; +} + + +static void wpa_driver_ndis_deinit(void *priv) +{ + struct wpa_driver_ndis_data *drv = priv; + eloop_cancel_timeout(wpa_driver_ndis_poll_timeout, drv, drv->ctx); + wpa_driver_ndis_flush_pmkid(drv); + wpa_driver_ndis_disconnect(drv); + if (wpa_driver_ndis_radio_off(drv) < 0) { + wpa_printf(MSG_DEBUG, "NDIS: failed to disassociate and turn " + "radio off"); + } + if (drv->event_sock >= 0) { + eloop_unregister_read_sock(drv->event_sock); + close(drv->event_sock); + } + if (drv->adapter) + PacketCloseAdapter(drv->adapter); + + free(drv->adapter_desc); + free(drv); +} + + +struct wpa_driver_ops wpa_driver_ndis_ops = { + .name = "ndis", + .desc = "Windows NDIS driver", + .init = wpa_driver_ndis_init, + .deinit = wpa_driver_ndis_deinit, + .set_wpa = wpa_driver_ndis_set_wpa, + .scan = wpa_driver_ndis_scan, + .get_scan_results = wpa_driver_ndis_get_scan_results, + .get_bssid = wpa_driver_ndis_get_bssid, + .get_ssid = wpa_driver_ndis_get_ssid, + .set_key = wpa_driver_ndis_set_key, + .associate = wpa_driver_ndis_associate, + .deauthenticate = wpa_driver_ndis_deauthenticate, + .disassociate = wpa_driver_ndis_disassociate, + .poll = wpa_driver_ndis_poll, + .add_pmkid = wpa_driver_ndis_add_pmkid, + .remove_pmkid = wpa_driver_ndis_remove_pmkid, + .flush_pmkid = wpa_driver_ndis_flush_pmkid, + .get_capa = wpa_driver_ndis_get_capa, + .get_ifname = wpa_driver_ndis_get_ifname, + .get_mac_addr = wpa_driver_ndis_get_mac_addr, +}; diff --git a/contrib/wpa_supplicant/driver_ndis.h b/contrib/wpa_supplicant/driver_ndis.h new file mode 100644 index 000000000000..a79afcbfcf2d --- /dev/null +++ b/contrib/wpa_supplicant/driver_ndis.h @@ -0,0 +1,26 @@ +#ifndef DRIVER_NDIS_H +#define DRIVER_NDIS_H + +struct ndis_pmkid_entry { + struct ndis_pmkid_entry *next; + u8 bssid[ETH_ALEN]; + u8 pmkid[16]; +}; + +struct wpa_driver_ndis_data { + void *ctx; + char ifname[100]; + u8 own_addr[ETH_ALEN]; + LPADAPTER adapter; + u8 bssid[ETH_ALEN]; + + int has_capability; + int no_of_pmkid; + int radio_enabled; + struct wpa_driver_capa capa; + struct ndis_pmkid_entry *pmkid; + int event_sock; + char *adapter_desc; +}; + +#endif /* DRIVER_NDIS_H */ diff --git a/contrib/wpa_supplicant/driver_ndis_.c b/contrib/wpa_supplicant/driver_ndis_.c new file mode 100644 index 000000000000..e71b13be9a7a --- /dev/null +++ b/contrib/wpa_supplicant/driver_ndis_.c @@ -0,0 +1,164 @@ +/* + * WPA Supplicant - Windows/NDIS driver interface - event processing + * Copyright (c) 2004-2005, Jouni Malinen + * + * 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 +#include +#include +#include +#include +#ifndef CONFIG_NATIVE_WINDOWS +#include +#include +#endif /* CONFIG_NATIVE_WINDOWS */ + +#include "common.h" +#include "driver.h" +#include "wpa_supplicant.h" +#include "l2_packet.h" +#include "eloop.h" +#include "wpa.h" + +/* Keep this event processing in a separate file and without WinPcap headers to + * avoid conflicts with some of the header files. */ +struct _ADAPTER; +typedef struct _ADAPTER * LPADAPTER; +#include "driver_ndis.h" + + +void wpa_driver_ndis_event_connect(struct wpa_driver_ndis_data *drv); +void wpa_driver_ndis_event_disconnect(struct wpa_driver_ndis_data *drv); +void wpa_driver_ndis_event_media_specific(struct wpa_driver_ndis_data *drv, + const u8 *data, size_t data_len); + + +enum event_types { EVENT_CONNECT, EVENT_DISCONNECT, + EVENT_MEDIA_SPECIFIC }; + +/* Event data: + * enum event_types (as int, i.e., 4 octets) + * InstanceName length (1 octet) + * InstanceName (variable len) + * data length (1 octet, optional) + * data (variable len, optional) + */ + + +static void wpa_driver_ndis_event_cb(int sock, void *eloop_ctx, void *sock_ctx) +{ + struct wpa_driver_ndis_data *drv = eloop_ctx; + u8 buf[512], *pos, *data = NULL; + int res, i, desc_len; + enum event_types type; + unsigned char instance_len; + char *instance; + size_t data_len = 0; + + res = recv(sock, buf, sizeof(buf), 0); + if (res < 0) { + perror("wpa_driver_ndis_event_cb - recv"); + return; + } + wpa_hexdump(MSG_MSGDUMP, "NDIS: received event data", buf, res); + if (res < sizeof(int) + 1) + return; + type = *((int *) buf); + pos = buf + sizeof(int); + wpa_printf(MSG_DEBUG, "NDIS: event - type %d", type); + instance_len = *pos++; + if (instance_len > buf + res - pos) { + wpa_printf(MSG_DEBUG, "NDIS: event InstanceName overflow"); + return; + } + instance = pos; + pos += instance_len; + wpa_hexdump_ascii(MSG_MSGDUMP, "NDIS: event InstanceName", + instance, instance_len); + + if (buf + res - pos > 1) { + data_len = *pos++; + if (data_len > buf + res - pos) { + wpa_printf(MSG_DEBUG, "NDIS: event data overflow"); + return; + } + data = pos; + wpa_hexdump(MSG_MSGDUMP, "NDIS: event data", data, data_len); + } + + if (drv->adapter_desc) { + desc_len = strlen(drv->adapter_desc); + if (instance_len < desc_len || + strncmp(drv->adapter_desc, instance, desc_len)) { + wpa_printf(MSG_DEBUG, "NDIS: ignored event for " + "another adapter"); + return; + } + + /* InstanceName: + * # + * # - Miniport + */ + for (i = desc_len + 1; i < instance_len; i++) { + if (instance[i] == '-') { + wpa_printf(MSG_DEBUG, "NDIS: ignored event " + "for intermediate miniport"); + return; + } + } + } + + switch (type) { + case EVENT_CONNECT: + wpa_driver_ndis_event_connect(drv); + break; + case EVENT_DISCONNECT: + wpa_driver_ndis_event_disconnect(drv); + break; + case EVENT_MEDIA_SPECIFIC: + wpa_driver_ndis_event_media_specific(drv, data, data_len); + break; + } +} + + +int wpa_driver_register_event_cb(struct wpa_driver_ndis_data *drv) +{ + struct sockaddr_in addr; + + drv->event_sock = socket(PF_INET, SOCK_DGRAM, 0); + if (drv->event_sock < 0) { + perror("socket"); + return -1; + } + + /* These events are received from an external program, ndis_events, + * which is converting WMI events to more "UNIX-like" input for + * wpa_supplicant, i.e., UDP packets that can be received through the + * eloop mechanism. */ + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = htonl((127 << 24) | 1); + addr.sin_port = htons(9876); + if (bind(drv->event_sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) + { + perror("bind"); + close(drv->event_sock); + drv->event_sock = -1; + return -1; + } + + eloop_register_read_sock(drv->event_sock, wpa_driver_ndis_event_cb, + drv, NULL); + + return 0; +}