helpers: allow [A-F] range in operator-specific ICCID account number

There are operators (e.g. the Chinese CMCC operator) that abuse the
fact that 4 bits are used to store the BCD encoded numbers, and also
use the [A-F] range as valid characters for the ICCID in the operator
specific account number part. Haven't seen any documentation where
this format with [A-F] characters is explicitly allowed, but I have
seen multiple real cases where it happens. E.g.:

  898602F9091830030220
  898602C0123456789012

This patch also removes the 'last F' validation, used when reading
19-digit ICCIDs with +CRSM, as it no longer applies.
This commit is contained in:
Aleksander Morgado
2018-09-12 17:17:36 +00:00
committed by Dan Williams
parent eb01914bd0
commit 099d54a4bc
+22 -19
View File
@@ -3500,14 +3500,21 @@ mm_3gpp_get_ip_family_from_pdp_type (const gchar *pdp_type)
}
/*************************************************************************/
/* ICCID validation */
/*
* 89: telecom (2 digits)
* cc: country (2 digits)
* oo: operator (2 digits)
* aaaaaaaaaaaaa: operator-specific account number (13 digits)
* x: checksum (1 digit)
*/
char *
mm_3gpp_parse_iccid (const char *raw_iccid, GError **error)
{
gboolean swap;
char *buf, *swapped = NULL;
gsize len = 0;
int f_pos = -1, i;
int i;
g_return_val_if_fail (raw_iccid != NULL, NULL);
@@ -3518,13 +3525,20 @@ mm_3gpp_parse_iccid (const char *raw_iccid, GError **error)
/* Make sure the buffer is only digits or 'F' */
buf = g_strdup (raw_iccid);
for (len = 0; buf[len]; len++) {
if (isdigit (buf[len]))
/* Digit values allowed anywhere */
if (g_ascii_isdigit (buf[len]))
continue;
if (buf[len] == 'F' || buf[len] == 'f') {
buf[len] = 'F'; /* canonicalize the F */
f_pos = len;
/* There are operators (e.g. the Chinese CMCC operator) that abuse the
* fact that 4 bits are used to store the BCD encoded numbers, and also
* use the [A-F] range as valid characters for the ICCID. Explicitly
* allow this range in the operator-specific part. */
if (len >= 6 && g_ascii_isxdigit (buf[len])) {
/* canonicalize hex digit */
buf[len] = g_ascii_toupper (buf[len]);
continue;
}
if (buf[len] == '\"') {
buf[len] = 0;
break;
@@ -3532,8 +3546,8 @@ mm_3gpp_parse_iccid (const char *raw_iccid, GError **error)
/* Invalid character */
g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
"ICCID response contained invalid character '%c'",
buf[len]);
"ICCID response contained invalid character '%c' at index '%zu'",
buf[len], len);
goto error;
}
@@ -3568,17 +3582,6 @@ mm_3gpp_parse_iccid (const char *raw_iccid, GError **error)
goto error;
}
/* Ensure if there's an 'F' that it's second-to-last if swap = TRUE,
* otherwise last if swap = FALSE. Also fail if an F is found within a
* 19-digit reported ICCID. */
if (f_pos >= 0) {
if ((len != 20) || (swap && (f_pos != len - 2)) || (!swap && (f_pos != len - 1))) {
g_set_error_literal (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
"Invalid ICCID length (unexpected F position)");
goto error;
}
}
if (swap) {
/* Swap digits in the ICCID response to get the actual ICCID, each
* group of 2 digits is reversed.