diff --git a/wpa_supplicant/interworking.c b/wpa_supplicant/interworking.c index 2f3524054..36f75a166 100644 --- a/wpa_supplicant/interworking.c +++ b/wpa_supplicant/interworking.c @@ -599,19 +599,29 @@ static struct nai_realm_eap * nai_realm_find_eap(struct wpa_cred *cred, static int plmn_id_match(struct wpabuf *anqp, const char *imsi, int mnc_len) { - u8 plmn[3]; + u8 plmn[3], plmn2[3]; const u8 *pos, *end; u8 udhl; - /* See Annex A of 3GPP TS 24.234 v8.1.0 for description */ + /* + * See Annex A of 3GPP TS 24.234 v8.1.0 for description. The network + * operator is allowed to include only two digits of the MNC, so allow + * matches based on both two and three digit MNC assumptions. Since some + * SIM/USIM cards may not expose MNC length conveniently, we may be + * provided the default MNC length 3 here and as such, checking with MNC + * length 2 is justifiable even though 3GPP TS 24.234 does not mention + * that case. Anyway, MCC/MNC pair where both 2 and 3 digit MNC is used + * with otherwise matching values would not be good idea in general, so + * this should not result in selecting incorrect networks. + */ + /* Match with 3 digit MNC */ plmn[0] = (imsi[0] - '0') | ((imsi[1] - '0') << 4); - plmn[1] = imsi[2] - '0'; - /* default to MNC length 3 if unknown */ - if (mnc_len != 2) - plmn[1] |= (imsi[5] - '0') << 4; - else - plmn[1] |= 0xf0; + plmn[1] = (imsi[2] - '0') | ((imsi[5] - '0') << 4); plmn[2] = (imsi[3] - '0') | ((imsi[4] - '0') << 4); + /* Match with 2 digit MNC */ + plmn2[0] = (imsi[0] - '0') | ((imsi[1] - '0') << 4); + plmn2[1] = (imsi[2] - '0') | 0xf0; + plmn2[2] = (imsi[3] - '0') | ((imsi[4] - '0') << 4); if (anqp == NULL) return 0; @@ -631,6 +641,10 @@ static int plmn_id_match(struct wpabuf *anqp, const char *imsi, int mnc_len) } end = pos + udhl; + wpa_printf(MSG_DEBUG, "Interworking: Matching against MCC/MNC alternatives: %02x:%02x:%02x or %02x:%02x:%02x (IMSI %s, MNC length %d)", + plmn[0], plmn[1], plmn[2], plmn2[0], plmn2[1], plmn2[2], + imsi, mnc_len); + while (pos + 2 <= end) { u8 iei, len; const u8 *l_end; @@ -643,14 +657,20 @@ static int plmn_id_match(struct wpabuf *anqp, const char *imsi, int mnc_len) if (iei == 0 && len > 0) { /* PLMN List */ u8 num, i; + wpa_hexdump(MSG_DEBUG, "Interworking: PLMN List information element", + pos, len); num = *pos++; for (i = 0; i < num; i++) { - if (pos + 3 > end) + if (pos + 3 > l_end) break; - if (os_memcmp(pos, plmn, 3) == 0) + if (os_memcmp(pos, plmn, 3) == 0 || + os_memcmp(pos, plmn2, 3) == 0) return 1; /* Found matching PLMN */ pos += 3; } + } else { + wpa_hexdump(MSG_DEBUG, "Interworking: Unrecognized 3GPP information element", + pos, len); } pos = l_end; @@ -1337,6 +1357,8 @@ static struct wpa_cred * interworking_credentials_available_3gpp( char *sep; const char *imsi; int mnc_len; + char imsi_buf[16]; + size_t msin_len; #ifdef PCSC_FUNCS if (cred->pcsc && wpa_s->conf->pcsc_reader && wpa_s->scard && @@ -1363,7 +1385,14 @@ static struct wpa_cred * interworking_credentials_available_3gpp( (sep - cred->imsi != 5 && sep - cred->imsi != 6)) continue; mnc_len = sep - cred->imsi - 3; - imsi = cred->imsi; + os_memcpy(imsi_buf, cred->imsi, 3 + mnc_len); + sep++; + msin_len = os_strlen(cred->imsi); + if (3 + mnc_len + msin_len >= sizeof(imsi_buf) - 1) + msin_len = sizeof(imsi_buf) - 3 - mnc_len - 1; + os_memcpy(&imsi_buf[3 + mnc_len], sep, msin_len); + imsi_buf[3 + mnc_len + msin_len] = '\0'; + imsi = imsi_buf; #if defined(PCSC_FUNCS) || defined(CONFIG_EAP_PROXY) compare: