hostapd/src/drivers/driver_iphone.m

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,
};