blob: 8213fdacc32e8482487193c04e174103c08c9f07 [file] [log] [blame]
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001/*
2 * WPA Supplicant - iPhone/iPod touch Apple80211 driver interface
3 * Copyright (c) 2007, Jouni Malinen <j@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 "common/ieee802_11_defs.h"
24
25#include "MobileApple80211.h"
26
27struct wpa_driver_iphone_data {
28 void *ctx;
29 Apple80211Ref wireless_ctx;
30 CFArrayRef scan_results;
31 int ctrl_power;
32};
33
34
35static 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
49static 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
75static 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
105static 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
111static 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
134static 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
239static 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
254static 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
322static 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
335static 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
353static 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
424static 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
454const 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};