From 8047b186914d303310783f1efd841321e0b96447 Mon Sep 17 00:00:00 2001 From: Jay Katabathuni Date: Sat, 25 Aug 2012 15:58:30 +0300 Subject: [PATCH] Interworking: Add advertising of NAI Realm list Signed-hostap: Jouni Malinen --- hostapd/config_file.c | 144 ++++++++++++++++++++++++++++++++++++++++++ hostapd/hostapd.conf | 25 ++++++++ src/ap/ap_config.c | 1 + src/ap/ap_config.h | 20 ++++++ src/ap/gas_serv.c | 59 +++++++++++++++++ src/ap/gas_serv.h | 2 + 6 files changed, 251 insertions(+) diff --git a/hostapd/config_file.c b/hostapd/config_file.c index e4da5b99f..0c644fc72 100644 --- a/hostapd/config_file.c +++ b/hostapd/config_file.c @@ -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) { diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf index b6e338054..d27cc475c 100644 --- a/hostapd/hostapd.conf +++ b/hostapd/hostapd.conf @@ -1388,6 +1388,31 @@ own_ip_addr=127.0.0.1 # format: [;][;...] #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: +# 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: [:<[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 diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c index e88548124..0b22875eb 100644 --- a/src/ap/ap_config.c +++ b/src/ap/ap_config.c @@ -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); diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h index 13f725530..ce9690dd9 100644 --- a/src/ap/ap_config.h +++ b/src/ap/ap_config.h @@ -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; diff --git a/src/ap/gas_serv.c b/src/ap/gas_serv.c index 4699a3aa9..564dabcaa 100644 --- a/src/ap/gas_serv.c +++ b/src/ap/gas_serv.c @@ -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", diff --git a/src/ap/gas_serv.h b/src/ap/gas_serv.h index 1d6db3631..373b64289 100644 --- a/src/ap/gas_serv.h +++ b/src/ap/gas_serv.h @@ -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 \