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