automake build system
[mech_eap.orig] / src / drivers / driver_iphone.m
1 /*
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
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 };