Home | History | Annotate | Download | only in drivers
      1 /*
      2  * WPA Supplicant - iPhone/iPod touch Apple80211 driver interface
      3  * Copyright (c) 2007, Jouni Malinen <j (at) w1.fi>
      4  *
      5  * This program is free software; you can redistribute it and/or modify
      6  * it under the terms of the GNU General Public License version 2 as
      7  * published by the Free Software Foundation.
      8  *
      9  * Alternatively, this software may be distributed under the terms of BSD
     10  * license.
     11  *
     12  * See README and COPYING for more details.
     13  */
     14 
     15 #include "includes.h"
     16 #define Boolean __DummyBoolean
     17 #include <CoreFoundation/CoreFoundation.h>
     18 #undef Boolean
     19 
     20 #include "common.h"
     21 #include "driver.h"
     22 #include "eloop.h"
     23 #include "ieee802_11_defs.h"
     24 
     25 #include "MobileApple80211.h"
     26 
     27 struct wpa_driver_iphone_data {
     28 	void *ctx;
     29 	Apple80211Ref wireless_ctx;
     30 	CFArrayRef scan_results;
     31 	int ctrl_power;
     32 };
     33 
     34 
     35 static const void * cfdict_get_key_str(CFDictionaryRef dict, const char *key)
     36 {
     37 	const void *res;
     38 	CFStringRef str = CFStringCreateWithCString(kCFAllocatorDefault, key,
     39 						    kCFStringEncodingMacRoman);
     40 	if (str == NULL)
     41 		return NULL;
     42 
     43 	res = CFDictionaryGetValue(dict, str);
     44 	CFRelease(str);
     45 	return res;
     46 }
     47 
     48 
     49 static int wpa_driver_iphone_get_ssid(void *priv, u8 *ssid)
     50 {
     51 	struct wpa_driver_iphone_data *drv = priv;
     52 	CFDataRef data;
     53 	int err, len;
     54 
     55 	err = Apple80211CopyValue(drv->wireless_ctx, APPLE80211_VALUE_SSID, 0,
     56 				  &data);
     57 	if (err != 0) {
     58 		wpa_printf(MSG_DEBUG, "iPhone: Apple80211CopyValue(SSID) "
     59 			   "failed: %d", err);
     60 		return -1;
     61 	}
     62 
     63 	len = CFDataGetLength(data);
     64 	if (len > 32) {
     65 		CFRelease(data);
     66 		return -1;
     67 	}
     68 	os_memcpy(ssid, CFDataGetBytePtr(data), len);
     69 	CFRelease(data);
     70 
     71 	return len;
     72 }
     73 
     74 
     75 static int wpa_driver_iphone_get_bssid(void *priv, u8 *bssid)
     76 {
     77 	struct wpa_driver_iphone_data *drv = priv;
     78 	CFStringRef data;
     79 	int err;
     80 	int a1, a2, a3, a4, a5, a6;
     81 
     82 	err = Apple80211CopyValue(drv->wireless_ctx, APPLE80211_VALUE_BSSID, 0,
     83 				  &data);
     84 	if (err != 0) {
     85 		wpa_printf(MSG_DEBUG, "iPhone: Apple80211CopyValue(BSSID) "
     86 			   "failed: %d", err);
     87 		return -1;
     88 	}
     89 
     90 	sscanf(CFStringGetCStringPtr(data, kCFStringEncodingMacRoman),
     91 	       "%x:%x:%x:%x:%x:%x", &a1, &a2, &a3, &a4, &a5, &a6);
     92 	bssid[0] = a1;
     93 	bssid[1] = a2;
     94 	bssid[2] = a3;
     95 	bssid[3] = a4;
     96 	bssid[4] = a5;
     97 	bssid[5] = a6;
     98 
     99 	CFRelease(data);
    100 
    101 	return 0;
    102 }
    103 
    104 
    105 static void wpa_driver_iphone_scan_timeout(void *eloop_ctx, void *timeout_ctx)
    106 {
    107 	wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL);
    108 }
    109 
    110 
    111 static int wpa_driver_iphone_scan(void *priv, const u8 *ssid, size_t ssid_len)
    112 {
    113 	struct wpa_driver_iphone_data *drv = priv;
    114 	int err;
    115 
    116 	if (drv->scan_results) {
    117 		CFRelease(drv->scan_results);
    118 		drv->scan_results = NULL;
    119 	}
    120 
    121 	err = Apple80211Scan(drv->wireless_ctx, &drv->scan_results, NULL);
    122 	if (err) {
    123 		wpa_printf(MSG_DEBUG, "iPhone: Apple80211Scan failed: %d",
    124 			   err);
    125 		return -1;
    126 	}
    127 
    128 	eloop_register_timeout(0, 0, wpa_driver_iphone_scan_timeout, drv,
    129 			       drv->ctx);
    130 	return 0;
    131 }
    132 
    133 
    134 static int wpa_driver_iphone_get_scan_results(void *priv,
    135 					      struct wpa_scan_result *results,
    136 					      size_t max_size)
    137 {
    138 	struct wpa_driver_iphone_data *drv = priv;
    139 	size_t i, num;
    140 
    141 	if (drv->scan_results == NULL)
    142 		return 0;
    143 
    144 	num = CFArrayGetCount(drv->scan_results);
    145 	if (num > max_size)
    146 		num = max_size;
    147 	os_memset(results, 0, num * sizeof(struct wpa_scan_result));
    148 
    149 	for (i = 0; i < num; i++) {
    150 		struct wpa_scan_result *res = &results[i];
    151 		CFDictionaryRef dict =
    152 			CFArrayGetValueAtIndex(drv->scan_results, i);
    153 		CFDataRef data;
    154 		CFStringRef str;
    155 		CFNumberRef num;
    156 		int val;
    157 
    158 		data = cfdict_get_key_str(dict, "SSID");
    159 		if (data) {
    160 			res->ssid_len = CFDataGetLength(data);
    161 			if (res->ssid_len > 32)
    162 				res->ssid_len = 32;
    163 			os_memcpy(res->ssid, CFDataGetBytePtr(data),
    164 				  res->ssid_len);
    165 		}
    166 
    167 		str = cfdict_get_key_str(dict, "BSSID");
    168 		if (str) {
    169 			int a1, a2, a3, a4, a5, a6;
    170 			sscanf(CFStringGetCStringPtr(
    171 				       str, kCFStringEncodingMacRoman),
    172 			       "%x:%x:%x:%x:%x:%x",
    173 			       &a1, &a2, &a3, &a4, &a5, &a6);
    174 			res->bssid[0] = a1;
    175 			res->bssid[1] = a2;
    176 			res->bssid[2] = a3;
    177 			res->bssid[3] = a4;
    178 			res->bssid[4] = a5;
    179 			res->bssid[5] = a6;
    180 		}
    181 
    182 		num = cfdict_get_key_str(dict, "CAPABILITIES");
    183 		if (num) {
    184 			if (CFNumberGetValue(num, kCFNumberSInt32Type, &val))
    185 				res->caps = val;
    186 		}
    187 
    188 		num = cfdict_get_key_str(dict, "CHANNEL");
    189 		if (num) {
    190 			if (CFNumberGetValue(num, kCFNumberSInt32Type, &val))
    191 				res->freq = 2407 + val * 5;
    192 		}
    193 
    194 		num = cfdict_get_key_str(dict, "RSSI");
    195 		if (num) {
    196 			if (CFNumberGetValue(num, kCFNumberSInt32Type, &val))
    197 				res->level = val;
    198 		}
    199 
    200 		num = cfdict_get_key_str(dict, "NOISE");
    201 		if (num) {
    202 			if (CFNumberGetValue(num, kCFNumberSInt32Type, &val))
    203 				res->noise = val;
    204 		}
    205 
    206 		data = cfdict_get_key_str(dict, "IE");
    207 		if (data) {
    208 			u8 *ptr = (u8 *) CFDataGetBytePtr(data);
    209 			int len = CFDataGetLength(data);
    210 			u8 *pos = ptr, *end = ptr + len;
    211 
    212 			while (pos + 2 < end) {
    213 				if (pos + 2 + pos[1] > end)
    214 					break;
    215 				if (pos[0] == WLAN_EID_RSN &&
    216 				    pos[1] <= SSID_MAX_WPA_IE_LEN) {
    217 					os_memcpy(res->rsn_ie, pos,
    218 						  2 + pos[1]);
    219 					res->rsn_ie_len = 2 + pos[1];
    220 				}
    221 				if (pos[0] == WLAN_EID_VENDOR_SPECIFIC &&
    222 				    pos[1] > 4 && pos[2] == 0x00 &&
    223 				    pos[3] == 0x50 && pos[4] == 0xf2 &&
    224 				    pos[5] == 0x01) {
    225 					os_memcpy(res->wpa_ie, pos,
    226 						  2 + pos[1]);
    227 					res->wpa_ie_len = 2 + pos[1];
    228 				}
    229 
    230 				pos = pos + 2 + pos[1];
    231 			}
    232 		}
    233 	}
    234 
    235 	return num;
    236 }
    237 
    238 
    239 static void wpa_driver_iphone_assoc_timeout(void *eloop_ctx, void *timeout_ctx)
    240 {
    241 	struct wpa_driver_iphone_data *drv = eloop_ctx;
    242 	u8 bssid[ETH_ALEN];
    243 
    244 	if (wpa_driver_iphone_get_bssid(drv, bssid) != 0) {
    245 		eloop_register_timeout(1, 0, wpa_driver_iphone_assoc_timeout,
    246 				       drv, drv->ctx);
    247 		return;
    248 	}
    249 
    250 	wpa_supplicant_event(timeout_ctx, EVENT_ASSOC, NULL);
    251 }
    252 
    253 
    254 static int wpa_driver_iphone_associate(
    255 	void *priv, struct wpa_driver_associate_params *params)
    256 {
    257 	struct wpa_driver_iphone_data *drv = priv;
    258 	int i, num, err;
    259 	size_t ssid_len;
    260 	CFDictionaryRef bss = NULL;
    261 
    262 	/*
    263 	 * TODO: Consider generating parameters instead of just using an entry
    264 	 * from scan results in order to support ap_scan=2.
    265 	 */
    266 
    267 	if (drv->scan_results == NULL) {
    268 		wpa_printf(MSG_DEBUG, "iPhone: No scan results - cannot "
    269 			   "associate");
    270 		return -1;
    271 	}
    272 
    273 	num = CFArrayGetCount(drv->scan_results);
    274 
    275 	for (i = 0; i < num; i++) {
    276 		CFDictionaryRef dict =
    277 			CFArrayGetValueAtIndex(drv->scan_results, i);
    278 		CFDataRef data;
    279 
    280 		data = cfdict_get_key_str(dict, "SSID");
    281 		if (data == NULL)
    282 			continue;
    283 
    284 		ssid_len = CFDataGetLength(data);
    285 		if (ssid_len != params->ssid_len ||
    286 		    os_memcmp(CFDataGetBytePtr(data), params->ssid, ssid_len)
    287 		    != 0)
    288 			continue;
    289 
    290 		bss = dict;
    291 		break;
    292 	}
    293 
    294 	if (bss == NULL) {
    295 		wpa_printf(MSG_DEBUG, "iPhone: Could not find SSID from scan "
    296 			   "results - cannot associate");
    297 		return -1;
    298 	}
    299 
    300 	wpa_printf(MSG_DEBUG, "iPhone: Trying to associate with a BSS found "
    301 		   "from scan results");
    302 
    303 	err = Apple80211Associate(drv->wireless_ctx, bss, NULL);
    304 	if (err) {
    305 		wpa_printf(MSG_DEBUG, "iPhone: Apple80211Associate() failed: "
    306 			   "%d", err);
    307 		return -1;
    308 	}
    309 
    310 	/*
    311 	 * Driver is actually already associated; report association from an
    312 	 * eloop callback.
    313 	 */
    314 	eloop_cancel_timeout(wpa_driver_iphone_assoc_timeout, drv, drv->ctx);
    315 	eloop_register_timeout(0, 0, wpa_driver_iphone_assoc_timeout, drv,
    316 			       drv->ctx);
    317 
    318 	return 0;
    319 }
    320 
    321 
    322 static int wpa_driver_iphone_set_key(void *priv, wpa_alg alg, const u8 *addr,
    323 				     int key_idx, int set_tx, const u8 *seq,
    324 				     size_t seq_len, const u8 *key,
    325 				     size_t key_len)
    326 {
    327 	/*
    328 	 * TODO: Need to either support configuring PMK for 4-way handshake or
    329 	 * PTK for TKIP/CCMP.
    330 	 */
    331 	return -1;
    332 }
    333 
    334 
    335 static int wpa_driver_iphone_get_capa(void *priv, struct wpa_driver_capa *capa)
    336 {
    337 	os_memset(capa, 0, sizeof(*capa));
    338 
    339 	capa->key_mgmt = WPA_DRIVER_CAPA_KEY_MGMT_WPA |
    340 		WPA_DRIVER_CAPA_KEY_MGMT_WPA2 |
    341 		WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK |
    342 		WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK;
    343 	capa->enc = WPA_DRIVER_CAPA_ENC_WEP40 | WPA_DRIVER_CAPA_ENC_WEP104 |
    344 		WPA_DRIVER_CAPA_ENC_TKIP | WPA_DRIVER_CAPA_ENC_CCMP;
    345 	capa->auth = WPA_DRIVER_AUTH_OPEN | WPA_DRIVER_AUTH_SHARED |
    346 		WPA_DRIVER_AUTH_LEAP;
    347 	capa->flags = WPA_DRIVER_FLAGS_4WAY_HANDSHAKE;
    348 
    349 	return 0;
    350 }
    351 
    352 
    353 static void * wpa_driver_iphone_init(void *ctx, const char *ifname)
    354 {
    355 	struct wpa_driver_iphone_data *drv;
    356 	int err;
    357 	char power;
    358 	CFStringRef name;
    359 	CFDictionaryRef dict;
    360 
    361 	drv = os_zalloc(sizeof(*drv));
    362 	if (drv == NULL)
    363 		return NULL;
    364 	drv->ctx = ctx;
    365 	err = Apple80211Open(&drv->wireless_ctx);
    366 	if (err) {
    367 		wpa_printf(MSG_ERROR, "iPhone: Apple80211Open failed: %d",
    368 			   err);
    369 		os_free(drv);
    370 		return NULL;
    371 	}
    372 
    373 	name = CFStringCreateWithCString(kCFAllocatorDefault, ifname,
    374 					 kCFStringEncodingISOLatin1);
    375 	if (name == NULL) {
    376 		wpa_printf(MSG_ERROR, "iPhone: ifname -> CFString failed");
    377 		Apple80211Close(drv->wireless_ctx);
    378 		os_free(drv);
    379 		return NULL;
    380 	}
    381 
    382 	err = Apple80211BindToInterface(drv->wireless_ctx, name);
    383 	CFRelease(name);
    384 
    385 	if (err) {
    386 		wpa_printf(MSG_ERROR, "iPhone: Apple80211BindToInterface "
    387 			   "failed: %d", err);
    388 		Apple80211Close(drv->wireless_ctx);
    389 		os_free(drv);
    390 		return NULL;
    391 	}
    392 
    393 	err = Apple80211GetPower(drv->wireless_ctx, &power);
    394 	if (err)
    395 		wpa_printf(MSG_DEBUG, "iPhone: Apple80211GetPower failed: %d",
    396 			   err);
    397 
    398 	wpa_printf(MSG_DEBUG, "iPhone: Power=%d", power);
    399 
    400 	if (!power) {
    401 		drv->ctrl_power = 1;
    402 		err = Apple80211SetPower(drv->wireless_ctx, 1);
    403 		if (err) {
    404 			wpa_printf(MSG_DEBUG, "iPhone: Apple80211SetPower "
    405 				   "failed: %d", err);
    406 			Apple80211Close(drv->wireless_ctx);
    407 			os_free(drv);
    408 			return NULL;
    409 		}
    410 	}
    411 
    412 	err = Apple80211GetInfoCopy(drv->wireless_ctx, &dict);
    413 	if (err == 0) {
    414 		CFShow(dict);
    415 		CFRelease(dict);
    416 	} else {
    417 		printf("Apple80211GetInfoCopy: %d\n", err);
    418 	}
    419 
    420 	return drv;
    421 }
    422 
    423 
    424 static void wpa_driver_iphone_deinit(void *priv)
    425 {
    426 	struct wpa_driver_iphone_data *drv = priv;
    427 	int err;
    428 
    429 	eloop_cancel_timeout(wpa_driver_iphone_scan_timeout, drv, drv->ctx);
    430 	eloop_cancel_timeout(wpa_driver_iphone_assoc_timeout, drv, drv->ctx);
    431 
    432 	if (drv->ctrl_power) {
    433 		wpa_printf(MSG_DEBUG, "iPhone: Power down the interface");
    434 		err = Apple80211SetPower(drv->wireless_ctx, 0);
    435 		if (err) {
    436 			wpa_printf(MSG_DEBUG, "iPhone: Apple80211SetPower(0) "
    437 				   "failed: %d", err);
    438 		}
    439 	}
    440 
    441 	err = Apple80211Close(drv->wireless_ctx);
    442 	if (err) {
    443 		wpa_printf(MSG_DEBUG, "iPhone: Apple80211Close failed: %d",
    444 			   err);
    445 	}
    446 
    447 	if (drv->scan_results)
    448 		CFRelease(drv->scan_results);
    449 
    450 	os_free(drv);
    451 }
    452 
    453 
    454 const struct wpa_driver_ops wpa_driver_iphone_ops = {
    455 	.name = "iphone",
    456 	.desc = "iPhone/iPod touch Apple80211 driver",
    457 	.get_ssid = wpa_driver_iphone_get_ssid,
    458 	.get_bssid = wpa_driver_iphone_get_bssid,
    459 	.init = wpa_driver_iphone_init,
    460 	.deinit = wpa_driver_iphone_deinit,
    461 	.scan = wpa_driver_iphone_scan,
    462 	.get_scan_results = wpa_driver_iphone_get_scan_results,
    463 	.associate = wpa_driver_iphone_associate,
    464 	.set_key = wpa_driver_iphone_set_key,
    465 	.get_capa = wpa_driver_iphone_get_capa,
    466 };
    467