466 lines
11 KiB
Objective-C
466 lines
11 KiB
Objective-C
/*
|
|
* WPA Supplicant - iPhone/iPod touch Apple80211 driver interface
|
|
* Copyright (c) 2007, Jouni Malinen <j@w1.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 "includes.h"
|
|
#define Boolean __DummyBoolean
|
|
#include <CoreFoundation/CoreFoundation.h>
|
|
#undef Boolean
|
|
|
|
#include "common.h"
|
|
#include "driver.h"
|
|
#include "eloop.h"
|
|
#include "ieee802_11_defs.h"
|
|
|
|
#include "MobileApple80211.h"
|
|
|
|
struct wpa_driver_iphone_data {
|
|
void *ctx;
|
|
Apple80211Ref wireless_ctx;
|
|
CFArrayRef scan_results;
|
|
int ctrl_power;
|
|
};
|
|
|
|
|
|
static const void * cfdict_get_key_str(CFDictionaryRef dict, const char *key)
|
|
{
|
|
const void *res;
|
|
CFStringRef str = CFStringCreateWithCString(kCFAllocatorDefault, key,
|
|
kCFStringEncodingMacRoman);
|
|
if (str == NULL)
|
|
return NULL;
|
|
|
|
res = CFDictionaryGetValue(dict, str);
|
|
CFRelease(str);
|
|
return res;
|
|
}
|
|
|
|
|
|
static int wpa_driver_iphone_get_ssid(void *priv, u8 *ssid)
|
|
{
|
|
struct wpa_driver_iphone_data *drv = priv;
|
|
CFDataRef data;
|
|
int err, len;
|
|
|
|
err = Apple80211CopyValue(drv->wireless_ctx, APPLE80211_VALUE_SSID, 0,
|
|
&data);
|
|
if (err != 0) {
|
|
wpa_printf(MSG_DEBUG, "iPhone: Apple80211CopyValue(SSID) "
|
|
"failed: %d", err);
|
|
return -1;
|
|
}
|
|
|
|
len = CFDataGetLength(data);
|
|
if (len > 32) {
|
|
CFRelease(data);
|
|
return -1;
|
|
}
|
|
os_memcpy(ssid, CFDataGetBytePtr(data), len);
|
|
CFRelease(data);
|
|
|
|
return len;
|
|
}
|
|
|
|
|
|
static int wpa_driver_iphone_get_bssid(void *priv, u8 *bssid)
|
|
{
|
|
struct wpa_driver_iphone_data *drv = priv;
|
|
CFStringRef data;
|
|
int err;
|
|
int a1, a2, a3, a4, a5, a6;
|
|
|
|
err = Apple80211CopyValue(drv->wireless_ctx, APPLE80211_VALUE_BSSID, 0,
|
|
&data);
|
|
if (err != 0) {
|
|
wpa_printf(MSG_DEBUG, "iPhone: Apple80211CopyValue(BSSID) "
|
|
"failed: %d", err);
|
|
return -1;
|
|
}
|
|
|
|
sscanf(CFStringGetCStringPtr(data, kCFStringEncodingMacRoman),
|
|
"%x:%x:%x:%x:%x:%x", &a1, &a2, &a3, &a4, &a5, &a6);
|
|
bssid[0] = a1;
|
|
bssid[1] = a2;
|
|
bssid[2] = a3;
|
|
bssid[3] = a4;
|
|
bssid[4] = a5;
|
|
bssid[5] = a6;
|
|
|
|
CFRelease(data);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static void wpa_driver_iphone_scan_timeout(void *eloop_ctx, void *timeout_ctx)
|
|
{
|
|
wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL);
|
|
}
|
|
|
|
|
|
static int wpa_driver_iphone_scan(void *priv, const u8 *ssid, size_t ssid_len)
|
|
{
|
|
struct wpa_driver_iphone_data *drv = priv;
|
|
int err;
|
|
|
|
if (drv->scan_results) {
|
|
CFRelease(drv->scan_results);
|
|
drv->scan_results = NULL;
|
|
}
|
|
|
|
err = Apple80211Scan(drv->wireless_ctx, &drv->scan_results, NULL);
|
|
if (err) {
|
|
wpa_printf(MSG_DEBUG, "iPhone: Apple80211Scan failed: %d",
|
|
err);
|
|
return -1;
|
|
}
|
|
|
|
eloop_register_timeout(0, 0, wpa_driver_iphone_scan_timeout, drv,
|
|
drv->ctx);
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int wpa_driver_iphone_get_scan_results(void *priv,
|
|
struct wpa_scan_result *results,
|
|
size_t max_size)
|
|
{
|
|
struct wpa_driver_iphone_data *drv = priv;
|
|
size_t i, num;
|
|
|
|
if (drv->scan_results == NULL)
|
|
return 0;
|
|
|
|
num = CFArrayGetCount(drv->scan_results);
|
|
if (num > max_size)
|
|
num = max_size;
|
|
os_memset(results, 0, num * sizeof(struct wpa_scan_result));
|
|
|
|
for (i = 0; i < num; i++) {
|
|
struct wpa_scan_result *res = &results[i];
|
|
CFDictionaryRef dict =
|
|
CFArrayGetValueAtIndex(drv->scan_results, i);
|
|
CFDataRef data;
|
|
CFStringRef str;
|
|
CFNumberRef num;
|
|
int val;
|
|
|
|
data = cfdict_get_key_str(dict, "SSID");
|
|
if (data) {
|
|
res->ssid_len = CFDataGetLength(data);
|
|
if (res->ssid_len > 32)
|
|
res->ssid_len = 32;
|
|
os_memcpy(res->ssid, CFDataGetBytePtr(data),
|
|
res->ssid_len);
|
|
}
|
|
|
|
str = cfdict_get_key_str(dict, "BSSID");
|
|
if (str) {
|
|
int a1, a2, a3, a4, a5, a6;
|
|
sscanf(CFStringGetCStringPtr(
|
|
str, kCFStringEncodingMacRoman),
|
|
"%x:%x:%x:%x:%x:%x",
|
|
&a1, &a2, &a3, &a4, &a5, &a6);
|
|
res->bssid[0] = a1;
|
|
res->bssid[1] = a2;
|
|
res->bssid[2] = a3;
|
|
res->bssid[3] = a4;
|
|
res->bssid[4] = a5;
|
|
res->bssid[5] = a6;
|
|
}
|
|
|
|
num = cfdict_get_key_str(dict, "CAPABILITIES");
|
|
if (num) {
|
|
if (CFNumberGetValue(num, kCFNumberSInt32Type, &val))
|
|
res->caps = val;
|
|
}
|
|
|
|
num = cfdict_get_key_str(dict, "CHANNEL");
|
|
if (num) {
|
|
if (CFNumberGetValue(num, kCFNumberSInt32Type, &val))
|
|
res->freq = 2407 + val * 5;
|
|
}
|
|
|
|
num = cfdict_get_key_str(dict, "RSSI");
|
|
if (num) {
|
|
if (CFNumberGetValue(num, kCFNumberSInt32Type, &val))
|
|
res->level = val;
|
|
}
|
|
|
|
num = cfdict_get_key_str(dict, "NOISE");
|
|
if (num) {
|
|
if (CFNumberGetValue(num, kCFNumberSInt32Type, &val))
|
|
res->noise = val;
|
|
}
|
|
|
|
data = cfdict_get_key_str(dict, "IE");
|
|
if (data) {
|
|
u8 *ptr = (u8 *) CFDataGetBytePtr(data);
|
|
int len = CFDataGetLength(data);
|
|
u8 *pos = ptr, *end = ptr + len;
|
|
|
|
while (pos + 2 < end) {
|
|
if (pos + 2 + pos[1] > end)
|
|
break;
|
|
if (pos[0] == WLAN_EID_RSN &&
|
|
pos[1] <= SSID_MAX_WPA_IE_LEN) {
|
|
os_memcpy(res->rsn_ie, pos,
|
|
2 + pos[1]);
|
|
res->rsn_ie_len = 2 + pos[1];
|
|
}
|
|
if (pos[0] == WLAN_EID_VENDOR_SPECIFIC &&
|
|
pos[1] > 4 && pos[2] == 0x00 &&
|
|
pos[3] == 0x50 && pos[4] == 0xf2 &&
|
|
pos[5] == 0x01) {
|
|
os_memcpy(res->wpa_ie, pos,
|
|
2 + pos[1]);
|
|
res->wpa_ie_len = 2 + pos[1];
|
|
}
|
|
|
|
pos = pos + 2 + pos[1];
|
|
}
|
|
}
|
|
}
|
|
|
|
return num;
|
|
}
|
|
|
|
|
|
static void wpa_driver_iphone_assoc_timeout(void *eloop_ctx, void *timeout_ctx)
|
|
{
|
|
struct wpa_driver_iphone_data *drv = eloop_ctx;
|
|
u8 bssid[ETH_ALEN];
|
|
|
|
if (wpa_driver_iphone_get_bssid(drv, bssid) != 0) {
|
|
eloop_register_timeout(1, 0, wpa_driver_iphone_assoc_timeout,
|
|
drv, drv->ctx);
|
|
return;
|
|
}
|
|
|
|
wpa_supplicant_event(timeout_ctx, EVENT_ASSOC, NULL);
|
|
}
|
|
|
|
|
|
static int wpa_driver_iphone_associate(
|
|
void *priv, struct wpa_driver_associate_params *params)
|
|
{
|
|
struct wpa_driver_iphone_data *drv = priv;
|
|
int i, num, err;
|
|
size_t ssid_len;
|
|
CFDictionaryRef bss = NULL;
|
|
|
|
/*
|
|
* TODO: Consider generating parameters instead of just using an entry
|
|
* from scan results in order to support ap_scan=2.
|
|
*/
|
|
|
|
if (drv->scan_results == NULL) {
|
|
wpa_printf(MSG_DEBUG, "iPhone: No scan results - cannot "
|
|
"associate");
|
|
return -1;
|
|
}
|
|
|
|
num = CFArrayGetCount(drv->scan_results);
|
|
|
|
for (i = 0; i < num; i++) {
|
|
CFDictionaryRef dict =
|
|
CFArrayGetValueAtIndex(drv->scan_results, i);
|
|
CFDataRef data;
|
|
|
|
data = cfdict_get_key_str(dict, "SSID");
|
|
if (data == NULL)
|
|
continue;
|
|
|
|
ssid_len = CFDataGetLength(data);
|
|
if (ssid_len != params->ssid_len ||
|
|
os_memcmp(CFDataGetBytePtr(data), params->ssid, ssid_len)
|
|
!= 0)
|
|
continue;
|
|
|
|
bss = dict;
|
|
break;
|
|
}
|
|
|
|
if (bss == NULL) {
|
|
wpa_printf(MSG_DEBUG, "iPhone: Could not find SSID from scan "
|
|
"results - cannot associate");
|
|
return -1;
|
|
}
|
|
|
|
wpa_printf(MSG_DEBUG, "iPhone: Trying to associate with a BSS found "
|
|
"from scan results");
|
|
|
|
err = Apple80211Associate(drv->wireless_ctx, bss, NULL);
|
|
if (err) {
|
|
wpa_printf(MSG_DEBUG, "iPhone: Apple80211Associate() failed: "
|
|
"%d", err);
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* Driver is actually already associated; report association from an
|
|
* eloop callback.
|
|
*/
|
|
eloop_cancel_timeout(wpa_driver_iphone_assoc_timeout, drv, drv->ctx);
|
|
eloop_register_timeout(0, 0, wpa_driver_iphone_assoc_timeout, drv,
|
|
drv->ctx);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int wpa_driver_iphone_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)
|
|
{
|
|
/*
|
|
* TODO: Need to either support configuring PMK for 4-way handshake or
|
|
* PTK for TKIP/CCMP.
|
|
*/
|
|
return -1;
|
|
}
|
|
|
|
|
|
static int wpa_driver_iphone_get_capa(void *priv, struct wpa_driver_capa *capa)
|
|
{
|
|
os_memset(capa, 0, sizeof(*capa));
|
|
|
|
capa->key_mgmt = WPA_DRIVER_CAPA_KEY_MGMT_WPA |
|
|
WPA_DRIVER_CAPA_KEY_MGMT_WPA2 |
|
|
WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK |
|
|
WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK;
|
|
capa->enc = WPA_DRIVER_CAPA_ENC_WEP40 | WPA_DRIVER_CAPA_ENC_WEP104 |
|
|
WPA_DRIVER_CAPA_ENC_TKIP | WPA_DRIVER_CAPA_ENC_CCMP;
|
|
capa->auth = WPA_DRIVER_AUTH_OPEN | WPA_DRIVER_AUTH_SHARED |
|
|
WPA_DRIVER_AUTH_LEAP;
|
|
capa->flags = WPA_DRIVER_FLAGS_4WAY_HANDSHAKE;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static void * wpa_driver_iphone_init(void *ctx, const char *ifname)
|
|
{
|
|
struct wpa_driver_iphone_data *drv;
|
|
int err;
|
|
char power;
|
|
CFStringRef name;
|
|
CFDictionaryRef dict;
|
|
|
|
drv = os_zalloc(sizeof(*drv));
|
|
if (drv == NULL)
|
|
return NULL;
|
|
drv->ctx = ctx;
|
|
err = Apple80211Open(&drv->wireless_ctx);
|
|
if (err) {
|
|
wpa_printf(MSG_ERROR, "iPhone: Apple80211Open failed: %d",
|
|
err);
|
|
os_free(drv);
|
|
return NULL;
|
|
}
|
|
|
|
name = CFStringCreateWithCString(kCFAllocatorDefault, ifname,
|
|
kCFStringEncodingISOLatin1);
|
|
if (name == NULL) {
|
|
wpa_printf(MSG_ERROR, "iPhone: ifname -> CFString failed");
|
|
Apple80211Close(drv->wireless_ctx);
|
|
os_free(drv);
|
|
return NULL;
|
|
}
|
|
|
|
err = Apple80211BindToInterface(drv->wireless_ctx, name);
|
|
CFRelease(name);
|
|
|
|
if (err) {
|
|
wpa_printf(MSG_ERROR, "iPhone: Apple80211BindToInterface "
|
|
"failed: %d", err);
|
|
Apple80211Close(drv->wireless_ctx);
|
|
os_free(drv);
|
|
return NULL;
|
|
}
|
|
|
|
err = Apple80211GetPower(drv->wireless_ctx, &power);
|
|
if (err)
|
|
wpa_printf(MSG_DEBUG, "iPhone: Apple80211GetPower failed: %d",
|
|
err);
|
|
|
|
wpa_printf(MSG_DEBUG, "iPhone: Power=%d", power);
|
|
|
|
if (!power) {
|
|
drv->ctrl_power = 1;
|
|
err = Apple80211SetPower(drv->wireless_ctx, 1);
|
|
if (err) {
|
|
wpa_printf(MSG_DEBUG, "iPhone: Apple80211SetPower "
|
|
"failed: %d", err);
|
|
Apple80211Close(drv->wireless_ctx);
|
|
os_free(drv);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
err = Apple80211GetInfoCopy(drv->wireless_ctx, &dict);
|
|
if (err == 0) {
|
|
CFShow(dict);
|
|
CFRelease(dict);
|
|
} else {
|
|
printf("Apple80211GetInfoCopy: %d\n", err);
|
|
}
|
|
|
|
return drv;
|
|
}
|
|
|
|
|
|
static void wpa_driver_iphone_deinit(void *priv)
|
|
{
|
|
struct wpa_driver_iphone_data *drv = priv;
|
|
int err;
|
|
|
|
eloop_cancel_timeout(wpa_driver_iphone_scan_timeout, drv, drv->ctx);
|
|
eloop_cancel_timeout(wpa_driver_iphone_assoc_timeout, drv, drv->ctx);
|
|
|
|
if (drv->ctrl_power) {
|
|
wpa_printf(MSG_DEBUG, "iPhone: Power down the interface");
|
|
err = Apple80211SetPower(drv->wireless_ctx, 0);
|
|
if (err) {
|
|
wpa_printf(MSG_DEBUG, "iPhone: Apple80211SetPower(0) "
|
|
"failed: %d", err);
|
|
}
|
|
}
|
|
|
|
err = Apple80211Close(drv->wireless_ctx);
|
|
if (err) {
|
|
wpa_printf(MSG_DEBUG, "iPhone: Apple80211Close failed: %d",
|
|
err);
|
|
}
|
|
|
|
if (drv->scan_results)
|
|
CFRelease(drv->scan_results);
|
|
|
|
os_free(drv);
|
|
}
|
|
|
|
|
|
const struct wpa_driver_ops wpa_driver_iphone_ops = {
|
|
.name = "iphone",
|
|
.desc = "iPhone/iPod touch Apple80211 driver",
|
|
.get_ssid = wpa_driver_iphone_get_ssid,
|
|
.get_bssid = wpa_driver_iphone_get_bssid,
|
|
.init = wpa_driver_iphone_init,
|
|
.deinit = wpa_driver_iphone_deinit,
|
|
.scan = wpa_driver_iphone_scan,
|
|
.get_scan_results = wpa_driver_iphone_get_scan_results,
|
|
.associate = wpa_driver_iphone_associate,
|
|
.set_key = wpa_driver_iphone_set_key,
|
|
.get_capa = wpa_driver_iphone_get_capa,
|
|
};
|