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