diff --git a/wpa_supplicant/README-HS20 b/wpa_supplicant/README-HS20 index 0cc5f3949..7d30e23c6 100644 --- a/wpa_supplicant/README-HS20 +++ b/wpa_supplicant/README-HS20 @@ -199,7 +199,26 @@ Credentials can be pre-configured for automatic network selection: # be used to configure alternative FQDNs that will be considered home # networks. # +# home_ois: Home OI(s) +# This string field contains one or more comma delimited OIs (hexdump) +# identifying the access the access points that support authentication +# with this credential. There are an alternative to the use of the realm +# parameter. When using Home OIs to match the network, the EAP parameters +# need to be pre-configured with the credentials since the NAI Realm +# information may not be available or fetched. +# A successful authentication with the access point is possible as soon +# as at least one Home OI from the list matches an OI in the Roaming +# Consortium advertised by the access point. +# (Hotspot 2.0 PerProviderSubscription//HomeSP/HomeOIList//HomeOI) +# +# required_home_ois: Required Home OI(s) +# This string field contains the set of Home OI(s) (hexdump) that are +# required to be advertised by the AP for the credential to be considered +# matching. +# (Hotspot 2.0 PerProviderSubscription//HomeSP/HomeOIList//HomeOIRequired) +# # roaming_consortium: Roaming Consortium OI +# Deprecated: use home_ois instead. # If roaming_consortium_len is non-zero, this field contains the # Roaming Consortium OI that can be used to determine which access # points support authentication with this credential. This is an @@ -209,6 +228,7 @@ Credentials can be pre-configured for automatic network selection: # may not be available or fetched. # # required_roaming_consortium: Required Roaming Consortium OI +# Deprecated: use required_home_ois instead. # If required_roaming_consortium_len is non-zero, this field contains the # Roaming Consortium OI that is required to be advertised by the AP for # the credential to be considered matching. @@ -325,7 +345,7 @@ Credentials can be pre-configured for automatic network selection: # password="password" # ca_cert="/etc/wpa_supplicant/ca.pem" # domain="example.com" -# roaming_consortium=223344 +# home_ois="223344" # roaming_consortiums="112233,4455667788,aabbcc" # eap=TTLS # phase2="auth=MSCHAPV2" diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c index 82e3390ab..2754ad833 100644 --- a/wpa_supplicant/config.c +++ b/wpa_supplicant/config.c @@ -3532,53 +3532,62 @@ static int wpa_config_set_cred_req_conn_capab(struct wpa_cred *cred, } -static int wpa_config_set_cred_roaming_consortiums(struct wpa_cred *cred, - const char *value) +static int +wpa_config_set_cred_ois(u8 cred_ois[MAX_ROAMING_CONS][MAX_ROAMING_CONS_OI_LEN], + size_t cred_ois_len[MAX_ROAMING_CONS], + unsigned int *cred_num_ois, + const char *value) { - u8 roaming_consortiums[MAX_ROAMING_CONS][MAX_ROAMING_CONS_OI_LEN]; - size_t roaming_consortiums_len[MAX_ROAMING_CONS]; - unsigned int num_roaming_consortiums = 0; + u8 ois[MAX_ROAMING_CONS][MAX_ROAMING_CONS_OI_LEN]; + size_t ois_len[MAX_ROAMING_CONS]; + unsigned int num_ois = 0; const char *pos, *end; size_t len; - os_memset(roaming_consortiums, 0, sizeof(roaming_consortiums)); - os_memset(roaming_consortiums_len, 0, sizeof(roaming_consortiums_len)); + len = os_strlen(value); + if (len / 2 < 3) { + wpa_printf(MSG_ERROR, + "Invalid organisation identifier (OI) list: %s", + value); + return -1; + } + + os_memset(ois, 0, sizeof(ois)); + os_memset(ois_len, 0, sizeof(ois_len)); for (pos = value;;) { end = os_strchr(pos, ','); len = end ? (size_t) (end - pos) : os_strlen(pos); if (!end && len == 0) break; - if (len == 0 || (len & 1) != 0 || + if (len / 2 < 3 || (len & 1) != 0 || len / 2 > MAX_ROAMING_CONS_OI_LEN || hexstr2bin(pos, - roaming_consortiums[num_roaming_consortiums], + ois[num_ois], len / 2) < 0) { wpa_printf(MSG_INFO, - "Invalid roaming_consortiums entry: %s", + "Invalid organisation identifier (OI) entry: %s", pos); return -1; } - roaming_consortiums_len[num_roaming_consortiums] = len / 2; - num_roaming_consortiums++; + ois_len[num_ois] = len / 2; + num_ois++; if (!end) break; - if (num_roaming_consortiums >= MAX_ROAMING_CONS) { + if (num_ois >= MAX_ROAMING_CONS) { wpa_printf(MSG_INFO, - "Too many roaming_consortiums OIs"); + "Too many OIs"); return -1; } pos = end + 1; } - os_memcpy(cred->roaming_consortiums, roaming_consortiums, - sizeof(roaming_consortiums)); - os_memcpy(cred->roaming_consortiums_len, roaming_consortiums_len, - sizeof(roaming_consortiums_len)); - cred->num_roaming_consortiums = num_roaming_consortiums; + os_memcpy(cred_ois, ois, sizeof(ois)); + os_memcpy(cred_ois_len, ois_len, sizeof(ois_len)); + *cred_num_ois = num_ois; return 0; } @@ -3813,36 +3822,70 @@ int wpa_config_set_cred(struct wpa_cred *cred, const char *var, } if (os_strcmp(var, "roaming_consortium") == 0) { - if (len < 3 || len > sizeof(cred->roaming_consortium)) { + if (len < 3 || len > sizeof(cred->home_ois[0])) { wpa_printf(MSG_ERROR, "Line %d: invalid " "roaming_consortium length %d (3..15 " "expected)", line, (int) len); os_free(val); return -1; } - os_memcpy(cred->roaming_consortium, val, len); - cred->roaming_consortium_len = len; + wpa_printf(MSG_WARNING, + "Line %d: option roaming_consortium is deprecated and will be removed in the future", + line); + os_memcpy(cred->home_ois[0], val, len); + cred->home_ois_len[0] = len; + cred->num_home_ois = 1; os_free(val); return 0; } if (os_strcmp(var, "required_roaming_consortium") == 0) { - if (len < 3 || len > sizeof(cred->required_roaming_consortium)) - { + if (len < 3 || len > sizeof(cred->required_home_ois[0])) { wpa_printf(MSG_ERROR, "Line %d: invalid " "required_roaming_consortium length %d " "(3..15 expected)", line, (int) len); os_free(val); return -1; } - os_memcpy(cred->required_roaming_consortium, val, len); - cred->required_roaming_consortium_len = len; + wpa_printf(MSG_WARNING, + "Line %d: option required_roaming_consortium is deprecated and will be removed in the future", + line); + os_memcpy(cred->required_home_ois[0], val, len); + cred->required_home_ois_len[0] = len; + cred->num_required_home_ois = 1; os_free(val); return 0; } + if (os_strcmp(var, "home_ois") == 0) { + res = wpa_config_set_cred_ois(cred->home_ois, + cred->home_ois_len, + &cred->num_home_ois, + val); + if (res < 0) + wpa_printf(MSG_ERROR, "Line %d: invalid home_ois", + line); + os_free(val); + return res; + } + + if (os_strcmp(var, "required_home_ois") == 0) { + res = wpa_config_set_cred_ois(cred->required_home_ois, + cred->required_home_ois_len, + &cred->num_required_home_ois, + val); + if (res < 0) + wpa_printf(MSG_ERROR, + "Line %d: invalid required_home_ois", line); + os_free(val); + return res; + } + if (os_strcmp(var, "roaming_consortiums") == 0) { - res = wpa_config_set_cred_roaming_consortiums(cred, val); + res = wpa_config_set_cred_ois(cred->roaming_consortiums, + cred->roaming_consortiums_len, + &cred->num_roaming_consortiums, + val); if (res < 0) wpa_printf(MSG_ERROR, "Line %d: invalid roaming_consortiums", @@ -4154,14 +4197,14 @@ char * wpa_config_get_cred_no_key(struct wpa_cred *cred, const char *var) size_t buflen; char *buf; - if (!cred->roaming_consortium_len) + if (!cred->num_home_ois || !cred->home_ois_len[0]) return NULL; - buflen = cred->roaming_consortium_len * 2 + 1; + buflen = cred->home_ois_len[0] * 2 + 1; buf = os_malloc(buflen); if (buf == NULL) return NULL; - wpa_snprintf_hex(buf, buflen, cred->roaming_consortium, - cred->roaming_consortium_len); + wpa_snprintf_hex(buf, buflen, cred->home_ois[0], + cred->home_ois_len[0]); return buf; } @@ -4169,14 +4212,64 @@ char * wpa_config_get_cred_no_key(struct wpa_cred *cred, const char *var) size_t buflen; char *buf; - if (!cred->required_roaming_consortium_len) + if (!cred->num_required_home_ois || + !cred->required_home_ois_len[0]) return NULL; - buflen = cred->required_roaming_consortium_len * 2 + 1; + buflen = cred->required_home_ois_len[0] * 2 + 1; buf = os_malloc(buflen); if (buf == NULL) return NULL; - wpa_snprintf_hex(buf, buflen, cred->required_roaming_consortium, - cred->required_roaming_consortium_len); + wpa_snprintf_hex(buf, buflen, cred->required_home_ois[0], + cred->required_home_ois_len[0]); + return buf; + } + + if (os_strcmp(var, "home_ois") == 0) { + size_t buflen; + char *buf, *pos; + size_t i; + + if (!cred->num_home_ois) + return NULL; + buflen = cred->num_home_ois * MAX_ROAMING_CONS_OI_LEN * 2 + 1; + buf = os_malloc(buflen); + if (!buf) + return NULL; + pos = buf; + for (i = 0; i < cred->num_home_ois; i++) { + if (i > 0) + *pos++ = ','; + pos += wpa_snprintf_hex( + pos, buf + buflen - pos, + cred->home_ois[i], + cred->home_ois_len[i]); + } + *pos = '\0'; + return buf; + } + + if (os_strcmp(var, "required_home_ois") == 0) { + size_t buflen; + char *buf, *pos; + size_t i; + + if (!cred->num_required_home_ois) + return NULL; + buflen = cred->num_required_home_ois * + MAX_ROAMING_CONS_OI_LEN * 2 + 1; + buf = os_malloc(buflen); + if (!buf) + return NULL; + pos = buf; + for (i = 0; i < cred->num_required_home_ois; i++) { + if (i > 0) + *pos++ = ','; + pos += wpa_snprintf_hex( + pos, buf + buflen - pos, + cred->required_home_ois[i], + cred->required_home_ois_len[i]); + } + *pos = '\0'; return buf; } diff --git a/wpa_supplicant/config.h b/wpa_supplicant/config.h index e2394bfa7..33a609b9f 100644 --- a/wpa_supplicant/config.h +++ b/wpa_supplicant/config.h @@ -259,36 +259,50 @@ struct wpa_cred { size_t num_domain; /** - * roaming_consortium - Roaming Consortium OI + * home_ois - Home OIs * - * If roaming_consortium_len is non-zero, this field contains the - * Roaming Consortium OI that can be used to determine which access - * points support authentication with this credential. This is an - * alternative to the use of the realm parameter. When using Roaming - * Consortium to match the network, the EAP parameters need to be - * pre-configured with the credential since the NAI Realm information - * may not be available or fetched. + * If num_home_ois is non-zero, this field contains the set of Home OIs + * that can be use to determine which access points support + * authentication with this credential. These are an alternative to the + * use of the realm parameter. When using Home OIs to match the network, + * the EAP parameters need to be pre-configured with the credentials + * since the NAI Realm information may not be available or fetched. + * A successful authentication with the access point is possible as soon + * as at least one Home OI from the list matches an OI in the Roaming + * Consortium list advertised by the access point. + * (Hotspot 2.0 PerProviderSubscription//HomeSP/HomeOIList//HomeOI) */ - u8 roaming_consortium[15]; + u8 home_ois[MAX_ROAMING_CONS][MAX_ROAMING_CONS_OI_LEN]; /** - * roaming_consortium_len - Length of roaming_consortium + * home_ois_len - Length of home_ois[i] */ - size_t roaming_consortium_len; + size_t home_ois_len[MAX_ROAMING_CONS]; /** - * required_roaming_consortium - Required Roaming Consortium OI + * num_home_ois - Number of entries in home_ois + */ + unsigned int num_home_ois; + + /** + * required_home_ois - Required Home OI(s) * - * If required_roaming_consortium_len is non-zero, this field contains - * the Roaming Consortium OI that is required to be advertised by the AP - * for the credential to be considered matching. + * If required_home_ois_len is non-zero, this field contains the set of + * Home OI(s) that are required to be advertised by the AP for the + * credential to be considered matching. + * (Hotspot 2.0 PerProviderSubscription//HomeSP/HomeOIList//HomeOIRequired) */ - u8 required_roaming_consortium[15]; + u8 required_home_ois[MAX_ROAMING_CONS][MAX_ROAMING_CONS_OI_LEN]; /** - * required_roaming_consortium_len - Length of required_roaming_consortium + * required_home_ois_len - Length of required_home_ois */ - size_t required_roaming_consortium_len; + size_t required_home_ois_len[MAX_ROAMING_CONS]; + + /** + * num_required_home_ois - Number of entries in required_home_ois + */ + unsigned int num_required_home_ois; /** * roaming_consortiums - Roaming Consortium OI(s) memberships diff --git a/wpa_supplicant/config_file.c b/wpa_supplicant/config_file.c index 4e001dd00..dbc04e13d 100644 --- a/wpa_supplicant/config_file.c +++ b/wpa_supplicant/config_file.c @@ -922,12 +922,6 @@ static void wpa_config_write_cred(FILE *f, struct wpa_cred *cred) if (cred->domain_suffix_match) fprintf(f, "\tdomain_suffix_match=\"%s\"\n", cred->domain_suffix_match); - if (cred->roaming_consortium_len) { - fprintf(f, "\troaming_consortium="); - for (i = 0; i < cred->roaming_consortium_len; i++) - fprintf(f, "%02x", cred->roaming_consortium[i]); - fprintf(f, "\n"); - } if (cred->eap_method) { const char *name; name = eap_get_name(cred->eap_method[0].vendor, @@ -1003,12 +997,32 @@ static void wpa_config_write_cred(FILE *f, struct wpa_cred *cred) } } - if (cred->required_roaming_consortium_len) { - fprintf(f, "\trequired_roaming_consortium="); - for (i = 0; i < cred->required_roaming_consortium_len; i++) - fprintf(f, "%02x", - cred->required_roaming_consortium[i]); - fprintf(f, "\n"); + if (cred->num_home_ois) { + size_t j; + + fprintf(f, "\thome_ois=\""); + for (i = 0; i < cred->num_home_ois; i++) { + if (i > 0) + fprintf(f, ","); + for (j = 0; j < cred->home_ois_len[i]; j++) + fprintf(f, "%02x", + cred->home_ois[i][j]); + } + fprintf(f, "\"\n"); + } + + if (cred->num_required_home_ois) { + size_t j; + + fprintf(f, "\trequired_home_ois=\""); + for (i = 0; i < cred->num_required_home_ois; i++) { + if (i > 0) + fprintf(f, ","); + for (j = 0; j < cred->required_home_ois_len[i]; j++) + fprintf(f, "%02x", + cred->required_home_ois[i][j]); + } + fprintf(f, "\"\n"); } if (cred->num_roaming_consortiums) { diff --git a/wpa_supplicant/interworking.c b/wpa_supplicant/interworking.c index 6ae21737d..e33f3617a 100644 --- a/wpa_supplicant/interworking.c +++ b/wpa_supplicant/interworking.c @@ -144,9 +144,9 @@ static int cred_with_roaming_consortium(struct wpa_supplicant *wpa_s) struct wpa_cred *cred; for (cred = wpa_s->conf->cred; cred; cred = cred->next) { - if (cred->roaming_consortium_len) + if (cred->num_home_ois) return 1; - if (cred->required_roaming_consortium_len) + if (cred->num_required_home_ois) return 1; if (cred->num_roaming_consortiums) return 1; @@ -1097,8 +1097,7 @@ fail: } -static int roaming_consortium_element_match(const u8 *ie, const u8 *rc_id, - size_t rc_len) +static int oi_element_match(const u8 *ie, const u8 *oi, size_t oi_len) { const u8 *pos, *end; u8 lens; @@ -1123,24 +1122,24 @@ static int roaming_consortium_element_match(const u8 *ie, const u8 *rc_id, if ((lens & 0x0f) + (lens >> 4) > end - pos) return 0; - if ((lens & 0x0f) == rc_len && os_memcmp(pos, rc_id, rc_len) == 0) + if ((lens & 0x0f) == oi_len && os_memcmp(pos, oi, oi_len) == 0) return 1; pos += lens & 0x0f; - if ((lens >> 4) == rc_len && os_memcmp(pos, rc_id, rc_len) == 0) + if ((lens >> 4) == oi_len && os_memcmp(pos, oi, oi_len) == 0) return 1; pos += lens >> 4; - if (pos < end && (size_t) (end - pos) == rc_len && - os_memcmp(pos, rc_id, rc_len) == 0) + if (pos < end && (size_t) (end - pos) == oi_len && + os_memcmp(pos, oi, oi_len) == 0) return 1; return 0; } -static int roaming_consortium_anqp_match(const struct wpabuf *anqp, - const u8 *rc_id, size_t rc_len) +static int oi_anqp_match(const struct wpabuf *anqp, const u8 *oi, + size_t oi_len) { const u8 *pos, *end; u8 len; @@ -1156,7 +1155,7 @@ static int roaming_consortium_anqp_match(const struct wpabuf *anqp, len = *pos++; if (len > end - pos) break; - if (len == rc_len && os_memcmp(pos, rc_id, rc_len) == 0) + if (len == oi_len && os_memcmp(pos, oi, oi_len) == 0) return 1; pos += len; } @@ -1165,11 +1164,26 @@ static int roaming_consortium_anqp_match(const struct wpabuf *anqp, } -static int roaming_consortium_match(const u8 *ie, const struct wpabuf *anqp, - const u8 *rc_id, size_t rc_len) +static int oi_match(const u8 *ie, const struct wpabuf *anqp, + const u8 *oi, size_t oi_len) { - return roaming_consortium_element_match(ie, rc_id, rc_len) || - roaming_consortium_anqp_match(anqp, rc_id, rc_len); + return oi_element_match(ie, oi, oi_len) || + oi_anqp_match(anqp, oi, oi_len); +} + + +static int cred_home_ois_match(const u8 *ie, const struct wpabuf *anqp, + const struct wpa_cred *cred) { + unsigned int i; + + /* There's a match if at least one of the home OI matches. */ + for (i = 0; i < cred->num_home_ois; i++) { + if (oi_match(ie, anqp, cred->home_ois[i], + cred->home_ois_len[i])) + return 1; + } + + return 0; } @@ -1180,9 +1194,8 @@ static int cred_roaming_consortiums_match(const u8 *ie, unsigned int i; for (i = 0; i < cred->num_roaming_consortiums; i++) { - if (roaming_consortium_match(ie, anqp, - cred->roaming_consortiums[i], - cred->roaming_consortiums_len[i])) + if (oi_match(ie, anqp, cred->roaming_consortiums[i], + cred->roaming_consortiums_len[i])) return 1; } @@ -1193,8 +1206,9 @@ static int cred_roaming_consortiums_match(const u8 *ie, static int cred_no_required_oi_match(struct wpa_cred *cred, struct wpa_bss *bss) { const u8 *ie; + unsigned int i; - if (cred->required_roaming_consortium_len == 0) + if (cred->num_required_home_ois == 0) return 0; ie = wpa_bss_get_ie(bss, WLAN_EID_ROAMING_CONSORTIUM); @@ -1203,11 +1217,16 @@ static int cred_no_required_oi_match(struct wpa_cred *cred, struct wpa_bss *bss) (bss->anqp == NULL || bss->anqp->roaming_consortium == NULL)) return 1; - return !roaming_consortium_match(ie, - bss->anqp ? - bss->anqp->roaming_consortium : NULL, - cred->required_roaming_consortium, - cred->required_roaming_consortium_len); + /* According to Passpoint specification, there must be a match for + * each required home OI provided. */ + for (i = 0; i < cred->num_required_home_ois; i++) { + if (!oi_match(ie, bss->anqp ? + bss->anqp->roaming_consortium : NULL, + cred->required_home_ois[i], + cred->required_home_ois_len[i])) + return 1; + } + return 0; } @@ -1407,26 +1426,24 @@ static struct wpa_cred * interworking_credentials_available_roaming_consortium( return NULL; for (cred = wpa_s->conf->cred; cred; cred = cred->next) { - if (cred->roaming_consortium_len == 0 && + if (cred->num_home_ois == 0 && + cred->num_required_home_ois == 0 && cred->num_roaming_consortiums == 0) continue; if (!cred->eap_method) continue; - if ((cred->roaming_consortium_len == 0 || - !roaming_consortium_match(ie, anqp, - cred->roaming_consortium, - cred->roaming_consortium_len)) && - !cred_roaming_consortiums_match(ie, anqp, cred) && - (cred->required_roaming_consortium_len == 0 || - !roaming_consortium_match( - ie, anqp, cred->required_roaming_consortium, - cred->required_roaming_consortium_len))) + /* If there's required home OIs, there must be a match for each + * required OI (see Passpoint v3.2 - 9.1.2 - RequiredHomeOI). */ + if (cred->num_required_home_ois > 0 && + cred_no_required_oi_match(cred, bss)) continue; - if (cred_no_required_oi_match(cred, bss)) + if (!cred_home_ois_match(ie, anqp, cred) && + !cred_roaming_consortiums_match(ie, anqp, cred)) continue; + if (!ignore_bw && cred_below_min_backhaul(wpa_s, cred, bss)) continue; if (!ignore_bw && cred_over_max_bss_load(wpa_s, cred, bss)) @@ -1641,9 +1658,8 @@ static int interworking_connect_roaming_consortium( ie = wpa_bss_get_ie(bss, WLAN_EID_ROAMING_CONSORTIUM); anqp = bss->anqp ? bss->anqp->roaming_consortium : NULL; for (i = 0; (ie || anqp) && i < cred->num_roaming_consortiums; i++) { - if (!roaming_consortium_match( - ie, anqp, cred->roaming_consortiums[i], - cred->roaming_consortiums_len[i])) + if (!oi_match(ie, anqp, cred->roaming_consortiums[i], + cred->roaming_consortiums_len[i])) continue; ssid->roaming_consortium_selection = diff --git a/wpa_supplicant/wpa_supplicant.conf b/wpa_supplicant/wpa_supplicant.conf index 29e6a9710..1bb3cc18b 100644 --- a/wpa_supplicant/wpa_supplicant.conf +++ b/wpa_supplicant/wpa_supplicant.conf @@ -683,7 +683,26 @@ fast_reauth=1 # be used to configure alternative FQDNs that will be considered home # networks. # +# home_ois: Home OI(s) +# This string field contains one or more comma delimited OIs (hexdump) +# identifying the access the access points that support authentication +# with this credential. There are an alternative to the use of the realm +# parameter. When using Home OIs to match the network, the EAP parameters +# need to be pre-configured with the credentials since the NAI Realm +# information may not be available or fetched. +# A successful authentication with the access point is possible as soon +# as at least one Home OI from the list matches an OI in the Roaming +# Consortium advertised by the access point. +# (Hotspot 2.0 PerProviderSubscription//HomeSP/HomeOIList//HomeOI) +# +# required_home_ois: Required Home OI(s) +# This string field contains the set of Home OI(s) (hexdump) that are +# required to be advertised by the AP for the credential to be considered +# matching. +# (Hotspot 2.0 PerProviderSubscription//HomeSP/HomeOIList//HomeOIRequired) +# # roaming_consortium: Roaming Consortium OI +# Deprecated: use home_ois instead. # If roaming_consortium_len is non-zero, this field contains the # Roaming Consortium OI that can be used to determine which access # points support authentication with this credential. This is an @@ -693,6 +712,7 @@ fast_reauth=1 # may not be available or fetched. # # required_roaming_consortium: Required Roaming Consortium OI +# Deprecated: use required_home_ois instead. # If required_roaming_consortium_len is non-zero, this field contains the # Roaming Consortium OI that is required to be advertised by the AP for # the credential to be considered matching. @@ -798,7 +818,7 @@ fast_reauth=1 # password="password" # ca_cert="/etc/wpa_supplicant/ca.pem" # domain="example.com" -# roaming_consortium=223344 +# home_ois="223344" # eap=TTLS # phase2="auth=MSCHAPV2" #}