remove @EAP_LDFLAGS@, no longer exists
[mech_eap.git] / libeap / src / drivers / driver_iphone.m
diff --git a/libeap/src/drivers/driver_iphone.m b/libeap/src/drivers/driver_iphone.m
new file mode 100644 (file)
index 0000000..8213fda
--- /dev/null
@@ -0,0 +1,466 @@
+/*
+ * 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 "common/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,
+};