]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
Interworking: Support for using EAP-SIM credentials in network selection
authorJouni Malinen <jouni@qca.qualcomm.com>
Thu, 6 Oct 2011 19:26:33 +0000 (22:26 +0300)
committerJouni Malinen <j@w1.fi>
Sun, 16 Oct 2011 20:55:34 +0000 (23:55 +0300)
New configuration parameters home_imsi and home_milenage can be used
to configure SIM/USIM simulator parameters for network selection based
on SIM/USIM credentials.

home_imsi=(MCC | MNC | '-' | rest of IMSI)
home_milenage=(Ki):(OPc):(SQN)

For example:
home_imsi=310026-000000000
home_milenage=90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581:000000000123

wpa_supplicant/config.c
wpa_supplicant/config.h
wpa_supplicant/config_file.c
wpa_supplicant/interworking.c

index 6811244a1840a9b6eac0cf8b3d5ee95a5fdc217f..ff054c2d7832544bd80b880e946f63a8ba14e85f 100644 (file)
@@ -1730,6 +1730,8 @@ void wpa_config_free(struct wpa_config *config)
        os_free(config->home_username);
        os_free(config->home_password);
        os_free(config->home_ca_cert);
+       os_free(config->home_imsi);
+       os_free(config->home_milenage);
        os_free(config);
 }
 
@@ -2489,6 +2491,8 @@ static const struct global_parse_data global_fields[] = {
        { STR(home_username), 0 },
        { STR(home_password), 0 },
        { STR(home_ca_cert), 0 },
+       { STR(home_imsi), 0 },
+       { STR(home_milenage), 0 },
        { INT_RANGE(interworking, 0, 1), 0 },
        { FUNC(hessid), 0 }
 };
index 62cac75ac704773685ef4fcac434d73de6d1438c..3741b9b716ab80dc8d9834d64dcf79bbc4b40f71 100644 (file)
@@ -460,6 +460,17 @@ struct wpa_config {
         * home_ca_cert - CA certificate for Interworking network selection
         */
        char *home_ca_cert;
+
+       /**
+        * home_imsi - IMSI in <MCC> | <MNC> | '-' | <MSIN> format
+        */
+       char *home_imsi;
+
+       /**
+        * home_milenage - Milenage parameters for SIM/USIM simulator in
+        *      <Ki>:<OPc>:<SQN> format
+        */
+       char *home_milenage;
 };
 
 
index ba546a2c913f229e6838b4924b89ee899563607f..6c3aa9c378f5933f1d275ab7f513d6828e2effb7 100644 (file)
@@ -712,6 +712,10 @@ static void wpa_config_write_global(FILE *f, struct wpa_config *config)
                fprintf(f, "home_password=%s\n", config->home_password);
        if (config->home_ca_cert)
                fprintf(f, "home_ca_cert=%s\n", config->home_ca_cert);
+       if (config->home_imsi)
+               fprintf(f, "home_imsi=%s\n", config->home_imsi);
+       if (config->home_milenage)
+               fprintf(f, "home_milenage=%s\n", config->home_milenage);
        if (config->interworking)
                fprintf(f, "interworking=%u\n", config->interworking);
        if (!is_zero_ether_addr(config->hessid))
index e7fcd83654e78757f57bd51298c70a31646a85c7..545c951e625d2539384cbb813ff5a8f37b39082c 100644 (file)
 #include "interworking.h"
 
 
+#if defined(EAP_SIM) | defined(EAP_SIM_DYNAMIC)
+#define INTERWORKING_3GPP
+#else
+#if defined(EAP_AKA) | defined(EAP_AKA_DYNAMIC)
+#define INTERWORKING_3GPP
+#else
+#if defined(EAP_AKA_PRIME) | defined(EAP_AKA_PRIME_DYNAMIC)
+#define INTERWORKING_3GPP
+#endif
+#endif
+#endif
+
 static void interworking_next_anqp_fetch(struct wpa_supplicant *wpa_s);
 
 
@@ -343,7 +355,7 @@ static int nai_realm_match(struct nai_realm *realm, const char *home_realm)
        char *tmp, *pos, *end;
        int match = 0;
 
-       if (realm->realm == NULL)
+       if (realm->realm == NULL || home_realm == NULL)
                return 0;
 
        if (os_strchr(realm->realm, ';') == NULL)
@@ -431,6 +443,187 @@ struct nai_realm_eap * nai_realm_find_eap(struct wpa_supplicant *wpa_s,
 }
 
 
+#ifdef INTERWORKING_3GPP
+
+static int plmn_id_match(struct wpabuf *anqp, const char *imsi)
+{
+       const char *sep;
+       u8 plmn[3];
+       const u8 *pos, *end;
+       u8 udhl;
+
+       sep = os_strchr(imsi, '-');
+       if (sep == NULL || (sep - imsi != 5 && sep - imsi != 6))
+               return 0;
+
+       /* See Annex A of 3GPP TS 24.234 v8.1.0 for description */
+       plmn[0] = (imsi[0] - '0') | ((imsi[1] - '0') << 4);
+       plmn[1] = imsi[2] - '0';
+       if (sep - imsi == 6)
+               plmn[1] |= (imsi[5] - '0') << 4;
+       else
+               plmn[1] |= 0xf0;
+       plmn[2] = (imsi[3] - '0') | ((imsi[4] - '0') << 4);
+
+       if (anqp == NULL)
+               return 0;
+       pos = wpabuf_head_u8(anqp);
+       end = pos + wpabuf_len(anqp);
+       if (pos + 2 > end)
+               return 0;
+       if (*pos != 0) {
+               wpa_printf(MSG_DEBUG, "Unsupported GUD version 0x%x", *pos);
+               return 0;
+       }
+       pos++;
+       udhl = *pos++;
+       if (pos + udhl > end) {
+               wpa_printf(MSG_DEBUG, "Invalid UDHL");
+               return 0;
+       }
+       end = pos + udhl;
+
+       while (pos + 2 <= end) {
+               u8 iei, len;
+               const u8 *l_end;
+               iei = *pos++;
+               len = *pos++ & 0x7f;
+               if (pos + len > end)
+                       break;
+               l_end = pos + len;
+
+               if (iei == 0 && len > 0) {
+                       /* PLMN List */
+                       u8 num, i;
+                       num = *pos++;
+                       for (i = 0; i < num; i++) {
+                               if (pos + 3 > end)
+                                       break;
+                               if (os_memcmp(pos, plmn, 3) == 0)
+                                       return 1; /* Found matching PLMN */
+                       }
+               }
+
+               pos = l_end;
+       }
+
+       return 0;
+}
+
+
+static int set_root_nai(struct wpa_ssid *ssid, const char *imsi, char prefix)
+{
+       const char *sep, *msin;
+       char nai[100], *end, *pos;
+       size_t msin_len, plmn_len;
+
+       /*
+        * TS 23.003, Clause 14 (3GPP to WLAN Interworking)
+        * Root NAI:
+        * <aka:0|sim:1><IMSI>@wlan.mnc<MNC>.mcc<MCC>.3gppnetwork.org
+        * <MNC> is zero-padded to three digits in case two-digit MNC is used
+        */
+
+       if (imsi == NULL || os_strlen(imsi) > 16) {
+               wpa_printf(MSG_DEBUG, "No valid IMSI available");
+               return -1;
+       }
+       sep = os_strchr(imsi, '-');
+       if (sep == NULL)
+               return -1;
+       plmn_len = sep - imsi;
+       if (plmn_len != 5 && plmn_len != 6)
+               return -1;
+       msin = sep + 1;
+       msin_len = os_strlen(msin);
+
+       pos = nai;
+       end = pos + sizeof(nai);
+       *pos++ = prefix;
+       os_memcpy(pos, imsi, plmn_len);
+       pos += plmn_len;
+       os_memcpy(pos, msin, msin_len);
+       pos += msin_len;
+       pos += os_snprintf(pos, end - pos, "@wlan.mnc");
+       if (plmn_len == 5) {
+               *pos++ = '0';
+               *pos++ = imsi[3];
+               *pos++ = imsi[4];
+       } else {
+               *pos++ = imsi[3];
+               *pos++ = imsi[4];
+               *pos++ = imsi[5];
+       }
+       pos += os_snprintf(pos, end - pos, ".mcc%c%c%c.3gppnetwork.org",
+                          imsi[0], imsi[1], imsi[2]);
+
+       return wpa_config_set_quoted(ssid, "identity", nai);
+}
+
+#endif /* INTERWORKING_3GPP */
+
+
+static int interworking_connect_3gpp(struct wpa_supplicant *wpa_s,
+                                    struct wpa_bss *bss)
+{
+#ifdef INTERWORKING_3GPP
+       struct wpa_ssid *ssid;
+       const u8 *ie;
+
+       ie = wpa_bss_get_ie(bss, WLAN_EID_SSID);
+       wpa_printf(MSG_DEBUG, "Interworking: Connect with " MACSTR " (3GPP)",
+                  MAC2STR(bss->bssid));
+
+       ssid = wpa_config_add_network(wpa_s->conf);
+       if (ssid == NULL)
+               return -1;
+
+       wpas_notify_network_added(wpa_s, ssid);
+       wpa_config_set_network_defaults(ssid);
+       ssid->temporary = 1;
+       ssid->ssid = os_zalloc(ie[1] + 1);
+       if (ssid->ssid == NULL)
+               goto fail;
+       os_memcpy(ssid->ssid, ie + 2, ie[1]);
+       ssid->ssid_len = ie[1];
+
+       /* TODO: figure out whether to use EAP-SIM, EAP-AKA, or EAP-AKA' */
+       if (wpa_config_set(ssid, "eap", "SIM", 0) < 0) {
+               wpa_printf(MSG_DEBUG, "EAP-SIM not supported");
+               goto fail;
+       }
+       if (set_root_nai(ssid, wpa_s->conf->home_imsi, '1') < 0) {
+               wpa_printf(MSG_DEBUG, "Failed to set Root NAI");
+               goto fail;
+       }
+
+       if (wpa_s->conf->home_milenage && wpa_s->conf->home_milenage[0]) {
+               if (wpa_config_set_quoted(ssid, "password",
+                                         wpa_s->conf->home_milenage) < 0)
+                       goto fail;
+       } else {
+               /* TODO: PIN */
+               if (wpa_config_set_quoted(ssid, "pcsc", "") < 0)
+                       goto fail;
+       }
+
+       if (wpa_s->conf->home_password && wpa_s->conf->home_password[0] &&
+           wpa_config_set_quoted(ssid, "password", wpa_s->conf->home_password)
+           < 0)
+               goto fail;
+
+       wpa_supplicant_select_network(wpa_s, ssid);
+
+       return 0;
+
+fail:
+       wpas_notify_network_removed(wpa_s, ssid);
+       wpa_config_remove_network(wpa_s->conf, ssid->id);
+#endif /* INTERWORKING_3GPP */
+       return -1;
+}
+
+
 int interworking_connect(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
 {
        struct wpa_ssid *ssid;
@@ -453,8 +646,7 @@ int interworking_connect(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
        if (realm == NULL) {
                wpa_printf(MSG_DEBUG, "Interworking: Could not parse NAI "
                           "Realm list from " MACSTR, MAC2STR(bss->bssid));
-               nai_realm_free(realm, count);
-               return -1;
+               count = 0;
        }
 
        for (i = 0; i < count; i++) {
@@ -466,6 +658,12 @@ int interworking_connect(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
        }
 
        if (!eap) {
+               if (interworking_connect_3gpp(wpa_s, bss) == 0) {
+                       if (realm)
+                               nai_realm_free(realm, count);
+                       return 0;
+               }
+
                wpa_printf(MSG_DEBUG, "Interworking: No matching credentials "
                           "and EAP method found for " MACSTR,
                           MAC2STR(bss->bssid));
@@ -564,8 +762,31 @@ fail:
 }
 
 
-static int interworking_credentials_available(struct wpa_supplicant *wpa_s,
-                                             struct wpa_bss *bss)
+static int interworking_credentials_available_3gpp(
+       struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
+{
+       int ret = 0;
+
+#ifdef INTERWORKING_3GPP
+       if (bss->anqp_3gpp == NULL)
+               return ret;
+
+       if (wpa_s->conf->home_imsi == NULL || !wpa_s->conf->home_imsi[0] ||
+           wpa_s->conf->home_milenage == NULL ||
+           !wpa_s->conf->home_milenage[0])
+               return ret;
+
+       wpa_printf(MSG_DEBUG, "Interworking: Parsing 3GPP info from " MACSTR,
+                  MAC2STR(bss->bssid));
+       ret = plmn_id_match(bss->anqp_3gpp, wpa_s->conf->home_imsi);
+       wpa_printf(MSG_DEBUG, "PLMN match %sfound", ret ? "" : "not ");
+#endif /* INTERWORKING_3GPP */
+       return ret;
+}
+
+
+static int interworking_credentials_available_realm(
+       struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
 {
        struct nai_realm *realm;
        u16 count, i;
@@ -601,6 +822,14 @@ static int interworking_credentials_available(struct wpa_supplicant *wpa_s,
 }
 
 
+static int interworking_credentials_available(struct wpa_supplicant *wpa_s,
+                                             struct wpa_bss *bss)
+{
+       return interworking_credentials_available_realm(wpa_s, bss) ||
+               interworking_credentials_available_3gpp(wpa_s, bss);
+}
+
+
 static void interworking_select_network(struct wpa_supplicant *wpa_s)
 {
        struct wpa_bss *bss, *selected = NULL;