]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
Interworking: Add advertising of NAI Realm list
authorJay Katabathuni <jkatabat@qca.qualcomm.com>
Sat, 25 Aug 2012 12:58:30 +0000 (15:58 +0300)
committerJouni Malinen <j@w1.fi>
Sun, 26 Aug 2012 15:59:12 +0000 (18:59 +0300)
Signed-hostap: Jouni Malinen <jouni@qca.qualcomm.com>

hostapd/config_file.c
hostapd/hostapd.conf
src/ap/ap_config.c
src/ap/ap_config.h
src/ap/gas_serv.c
src/ap/gas_serv.h

index e4da5b99ff822c182fecd774355ccd0022b05ea1..0c644fc72a96e96972a4511d8f6674a8d2a0ab1e 100644 (file)
@@ -1412,6 +1412,147 @@ fail:
        return -1;
 }
 
+
+static int parse_nai_realm(struct hostapd_bss_config *bss, char *buf, int line)
+{
+       struct hostapd_nai_realm_data *realm;
+       size_t i, j, len;
+       int *offsets;
+       char *pos, *end, *rpos;
+
+       offsets = os_calloc(bss->nai_realm_count * MAX_NAI_REALMS,
+                           sizeof(int));
+       if (offsets == NULL)
+               return -1;
+
+       for (i = 0; i < bss->nai_realm_count; i++) {
+               realm = &bss->nai_realm_data[i];
+               for (j = 0; j < MAX_NAI_REALMS; j++) {
+                       offsets[i * MAX_NAI_REALMS + j] =
+                               realm->realm[j] ?
+                               realm->realm[j] - realm->realm_buf : -1;
+               }
+       }
+
+       realm = os_realloc_array(bss->nai_realm_data, bss->nai_realm_count + 1,
+                                sizeof(struct hostapd_nai_realm_data));
+       if (realm == NULL) {
+               os_free(offsets);
+               return -1;
+       }
+       bss->nai_realm_data = realm;
+
+       /* patch the pointers after realloc */
+       for (i = 0; i < bss->nai_realm_count; i++) {
+               realm = &bss->nai_realm_data[i];
+               for (j = 0; j < MAX_NAI_REALMS; j++) {
+                       int offs = offsets[i * MAX_NAI_REALMS + j];
+                       if (offs >= 0)
+                               realm->realm[j] = realm->realm_buf + offs;
+                       else
+                               realm->realm[j] = NULL;
+               }
+       }
+       os_free(offsets);
+
+       realm = &bss->nai_realm_data[bss->nai_realm_count];
+       os_memset(realm, 0, sizeof(*realm));
+
+       pos = buf;
+       realm->encoding = atoi(pos);
+       pos = os_strchr(pos, ',');
+       if (pos == NULL)
+               goto fail;
+       pos++;
+
+       end = os_strchr(pos, ',');
+       if (end) {
+               len = end - pos;
+               *end = '\0';
+       } else {
+               len = os_strlen(pos);
+       }
+
+       if (len > MAX_NAI_REALMLEN) {
+               wpa_printf(MSG_ERROR, "Too long a realm string (%d > max %d "
+                          "characters)", (int) len, MAX_NAI_REALMLEN);
+               goto fail;
+       }
+       os_memcpy(realm->realm_buf, pos, len);
+
+       if (end)
+               pos = end + 1;
+       else
+               pos = NULL;
+
+       while (pos && *pos) {
+               struct hostapd_nai_realm_eap *eap;
+
+               if (realm->eap_method_count >= MAX_NAI_EAP_METHODS) {
+                       wpa_printf(MSG_ERROR, "Too many EAP methods");
+                       goto fail;
+               }
+
+               eap = &realm->eap_method[realm->eap_method_count];
+               realm->eap_method_count++;
+
+               end = os_strchr(pos, ',');
+               if (end == NULL)
+                       end = pos + os_strlen(pos);
+
+               eap->eap_method = atoi(pos);
+               for (;;) {
+                       pos = os_strchr(pos, '[');
+                       if (pos == NULL || pos > end)
+                               break;
+                       pos++;
+                       if (eap->num_auths >= MAX_NAI_AUTH_TYPES) {
+                               wpa_printf(MSG_ERROR, "Too many auth params");
+                               goto fail;
+                       }
+                       eap->auth_id[eap->num_auths] = atoi(pos);
+                       pos = os_strchr(pos, ':');
+                       if (pos == NULL || pos > end)
+                               goto fail;
+                       pos++;
+                       eap->auth_val[eap->num_auths] = atoi(pos);
+                       pos = os_strchr(pos, ']');
+                       if (pos == NULL || pos > end)
+                               goto fail;
+                       pos++;
+                       eap->num_auths++;
+               }
+
+               if (*end != ',')
+                       break;
+
+               pos = end + 1;
+       }
+
+       /* Split realm list into null terminated realms */
+       rpos = realm->realm_buf;
+       i = 0;
+       while (*rpos) {
+               if (i >= MAX_NAI_REALMS) {
+                       wpa_printf(MSG_ERROR, "Too many realms");
+                       goto fail;
+               }
+               realm->realm[i++] = rpos;
+               rpos = os_strchr(rpos, ';');
+               if (rpos == NULL)
+                       break;
+               *rpos++ = '\0';
+       }
+
+       bss->nai_realm_count++;
+
+       return 0;
+
+fail:
+       wpa_printf(MSG_ERROR, "Line %d: invalid nai_realm '%s'", line, buf);
+       return -1;
+}
+
 #endif /* CONFIG_INTERWORKING */
 
 
@@ -2679,6 +2820,9 @@ static int hostapd_config_fill(struct hostapd_config *conf,
                } else if (os_strcmp(buf, "anqp_3gpp_cell_net") == 0) {
                        if (parse_3gpp_cell_net(bss, pos, line) < 0)
                                errors++;
+               } else if (os_strcmp(buf, "nai_realm") == 0) {
+                       if (parse_nai_realm(bss, pos, line) < 0)
+                               errors++;
                } else if (os_strcmp(buf, "gas_frag_limit") == 0) {
                        bss->gas_frag_limit = atoi(pos);
                } else if (os_strcmp(buf, "gas_comeback_delay") == 0) {
index b6e3380540066d724dd8dd3451f5d9df6412d564..d27cc475cef4c399840c2ad6ecbae55bf4e817dc 100644 (file)
@@ -1388,6 +1388,31 @@ own_ip_addr=127.0.0.1
 # format: <MCC1,MNC1>[;<MCC2,MNC2>][;...]
 #anqp_3gpp_cell_net=244,91;310,026;234,56
 
+# NAI Realm information
+# One or more realm can be advertised. Each nai_realm line adds a new realm to
+# the set. These parameters provide information for stations using Interworking
+# network selection to allow automatic connection to a network based on
+# credentials.
+# format: <encoding>,<NAI Realm(s)>[,<EAP Method 1>][,<EAP Method 2>][,...]
+# encoding:
+#      0 = Realm formatted in accordance with IETF RFC 4282
+#      1 = UTF-8 formatted character string that is not formatted in
+#          accordance with IETF RFC 4282
+# NAI Realm(s): Semi-colon delimited NAI Realm(s)
+# EAP Method: <EAP Method>[:<[AuthParam1:Val1]>][<[AuthParam2:Val2]>][...]
+# AuthParam (Table 8-188 in IEEE Std 802.11-2012):
+# ID 2 = Non-EAP Inner Authentication Type
+#      1 = PAP, 2 = CHAP, 3 = MSCHAP, 4 = MSCHAPV2
+# ID 3 = Inner authentication EAP Method Type
+# ID 5 = Credential Type
+#      1 = SIM, 2 = USIM, 3 = NFC Secure Element, 4 = Hardware Token,
+#      5 = Softoken, 6 = Certificate, 7 = username/password, 9 = Anonymous,
+#      10 = Vendor Specific
+#nai_realm=0,example.com;example.net
+# EAP methods EAP-TLS with certificate and EAP-TTLS/MSCHAPv2 with
+# username/password
+#nai_realm=0,example.org,13[5:6],21[2:4][5:7]
+
 ##### Hotspot 2.0 #############################################################
 
 # Enable Hotspot 2.0 support
index e8854812450a9cee10bc5ba7863e4f7168fa9da0..0b22875eb3f066dcc93f65ae5d252a04206b43e7 100644 (file)
@@ -498,6 +498,7 @@ static void hostapd_config_free_bss(struct hostapd_bss_config *conf)
 
        os_free(conf->roaming_consortium);
        os_free(conf->venue_name);
+       os_free(conf->nai_realm_data);
        os_free(conf->network_auth_type);
        os_free(conf->anqp_3gpp_cell_net);
        os_free(conf->domain_name);
index 13f725530ca32ec53c16c2c19c4b7828455a5524..ce9690dd9310b42c46a9139efeef0862b2103b70 100644 (file)
@@ -151,6 +151,23 @@ struct hostapd_lang_string {
        u8 name[252];
 };
 
+#define MAX_NAI_REALMS 10
+#define MAX_NAI_REALMLEN 255
+#define MAX_NAI_EAP_METHODS 5
+#define MAX_NAI_AUTH_TYPES 4
+struct hostapd_nai_realm_data {
+       u8 encoding;
+       char realm_buf[MAX_NAI_REALMLEN + 1];
+       char *realm[MAX_NAI_REALMS];
+       u8 eap_method_count;
+       struct hostapd_nai_realm_eap {
+               u8 eap_method;
+               u8 num_auths;
+               u8 auth_id[MAX_NAI_AUTH_TYPES];
+               u8 auth_val[MAX_NAI_AUTH_TYPES];
+       } eap_method[MAX_NAI_EAP_METHODS];
+};
+
 /**
  * struct hostapd_bss_config - Per-BSS configuration
  */
@@ -404,6 +421,9 @@ struct hostapd_bss_config {
        u8 *domain_name;
        size_t domain_name_len;
 
+       unsigned int nai_realm_count;
+       struct hostapd_nai_realm_data *nai_realm_data;
+
        u16 gas_comeback_delay;
        int gas_frag_limit;
 
index 4699a3aa983c73f654b1311f211fa0d1b486bf4b..564dabcaa785b200f14ba35724f1407a1d9355fa 100644 (file)
@@ -166,6 +166,8 @@ static void anqp_add_capab_list(struct hostapd_data *hapd,
                wpabuf_put_le16(buf, ANQP_ROAMING_CONSORTIUM);
        if (hapd->conf->ipaddr_type_configured)
                wpabuf_put_le16(buf, ANQP_IP_ADDR_TYPE_AVAILABILITY);
+       if (hapd->conf->nai_realm_data)
+               wpabuf_put_le16(buf, ANQP_NAI_REALM);
        if (hapd->conf->anqp_3gpp_cell_net)
                wpabuf_put_le16(buf, ANQP_3GPP_CELLULAR_NETWORK);
        if (hapd->conf->domain_name)
@@ -235,6 +237,56 @@ static void anqp_add_ip_addr_type_availability(struct hostapd_data *hapd,
 }
 
 
+static void anqp_add_nai_realm_eap(struct wpabuf *buf,
+                                  struct hostapd_nai_realm_data *realm)
+{
+       unsigned int i, j;
+
+       wpabuf_put_u8(buf, realm->eap_method_count);
+
+       for (i = 0; i < realm->eap_method_count; i++) {
+               struct hostapd_nai_realm_eap *eap = &realm->eap_method[i];
+               wpabuf_put_u8(buf, 2 + (3 * eap->num_auths));
+               wpabuf_put_u8(buf, eap->eap_method);
+               wpabuf_put_u8(buf, eap->num_auths);
+               for (j = 0; j < eap->num_auths; j++) {
+                       wpabuf_put_u8(buf, eap->auth_id[j]);
+                       wpabuf_put_u8(buf, 1);
+                       wpabuf_put_u8(buf, eap->auth_val[j]);
+               }
+       }
+}
+
+
+static void anqp_add_nai_realm(struct hostapd_data *hapd, struct wpabuf *buf)
+{
+       if (hapd->conf->nai_realm_data) {
+               u8 *len;
+               unsigned int i, j;
+               len = gas_anqp_add_element(buf, ANQP_NAI_REALM);
+               wpabuf_put_le16(buf, hapd->conf->nai_realm_count);
+               for (i = 0; i < hapd->conf->nai_realm_count; i++) {
+                       u8 *realm_data_len, *realm_len;
+                       struct hostapd_nai_realm_data *realm;
+
+                       realm = &hapd->conf->nai_realm_data[i];
+                       realm_data_len = wpabuf_put(buf, 2);
+                       wpabuf_put_u8(buf, realm->encoding);
+                       realm_len = wpabuf_put(buf, 1);
+                       for (j = 0; realm->realm[j]; j++) {
+                               if (j > 0)
+                                       wpabuf_put_u8(buf, ';');
+                               wpabuf_put_str(buf, realm->realm[j]);
+                       }
+                       *realm_len = (u8 *) wpabuf_put(buf, 0) - realm_len - 1;
+                       anqp_add_nai_realm_eap(buf, realm);
+                       gas_anqp_set_element_len(buf, realm_data_len);
+               }
+               gas_anqp_set_element_len(buf, len);
+       }
+}
+
+
 static void anqp_add_3gpp_cellular_network(struct hostapd_data *hapd,
                                           struct wpabuf *buf)
 {
@@ -351,6 +403,8 @@ gas_serv_build_gas_resp_payload(struct hostapd_data *hapd,
                anqp_add_roaming_consortium(hapd, buf);
        if (request & ANQP_REQ_IP_ADDR_TYPE_AVAILABILITY)
                anqp_add_ip_addr_type_availability(hapd, buf);
+       if (request & ANQP_REQ_NAI_REALM)
+               anqp_add_nai_realm(hapd, buf);
        if (request & ANQP_REQ_3GPP_CELLULAR_NETWORK)
                anqp_add_3gpp_cellular_network(hapd, buf);
        if (request & ANQP_REQ_DOMAIN_NAME)
@@ -436,6 +490,11 @@ static void rx_anqp_query_list_id(struct hostapd_data *hapd, u16 info_id,
                             hapd->conf->ipaddr_type_configured,
                             0, 0, qi);
                break;
+       case ANQP_NAI_REALM:
+               set_anqp_req(ANQP_REQ_NAI_REALM, "NAI Realm",
+                            hapd->conf->nai_realm_data != NULL,
+                            0, 0, qi);
+               break;
        case ANQP_3GPP_CELLULAR_NETWORK:
                set_anqp_req(ANQP_REQ_3GPP_CELLULAR_NETWORK,
                             "3GPP Cellular Network",
index 1d6db36316ca291111e5a99fe238b9178e388bc2..373b64289f3fce187eb7580fae0ebaa3d3fd5abe 100644 (file)
@@ -19,6 +19,8 @@
        (1 << (ANQP_ROAMING_CONSORTIUM - ANQP_QUERY_LIST))
 #define ANQP_REQ_IP_ADDR_TYPE_AVAILABILITY \
        (1 << (ANQP_IP_ADDR_TYPE_AVAILABILITY - ANQP_QUERY_LIST))
+#define ANQP_REQ_NAI_REALM \
+       (1 << (ANQP_NAI_REALM - ANQP_QUERY_LIST))
 #define ANQP_REQ_3GPP_CELLULAR_NETWORK \
        (1 << (ANQP_3GPP_CELLULAR_NETWORK - ANQP_QUERY_LIST))
 #define ANQP_REQ_DOMAIN_NAME \