diff options
author | Dan Williams <dcbw@redhat.com> | 2010-10-25 17:41:08 -0500 |
---|---|---|
committer | Dan Williams <dcbw@redhat.com> | 2010-10-25 17:41:08 -0500 |
commit | 1684d8b1734be69700a93a9995b99fac6ffb9763 (patch) | |
tree | 29b0fd07d0b864bddf23d036ab1c223ccf15a625 | |
parent | 46106660fe6aae1018f6f6c45281ccef837e78f8 (diff) |
gsm: add SimIdentifier property
An obfuscated SimIdentifier that may be available before the PIN has
been entered, for use in auto-unlocking a device. If this value is
present, it should be used in preference to DeviceIdentifier as it
is SIM-specific like the PIN code.
-rw-r--r-- | introspection/mm-modem-gsm-card.xml | 8 | ||||
-rw-r--r-- | plugins/mm-modem-sierra-gsm.c | 74 | ||||
-rw-r--r-- | src/mm-generic-gsm.c | 198 | ||||
-rw-r--r-- | src/mm-generic-gsm.h | 6 | ||||
-rw-r--r-- | src/mm-modem-gsm-card.c | 8 | ||||
-rw-r--r-- | src/mm-modem-gsm-card.h | 1 | ||||
-rw-r--r-- | src/mm-serial-parsers.c | 7 | ||||
-rwxr-xr-x | test/info.py | 15 |
8 files changed, 312 insertions, 5 deletions
diff --git a/introspection/mm-modem-gsm-card.xml b/introspection/mm-modem-gsm-card.xml index 9c9fdc19..d4811576 100644 --- a/introspection/mm-modem-gsm-card.xml +++ b/introspection/mm-modem-gsm-card.xml @@ -109,6 +109,14 @@ </arg> </method> + <property name="SimIdentifier" type="s" access="read"> + <tp:docstring> + An obfuscated SIM identifier based on the IMSI or the ICCID. This may + be available before the PIN has been entered depending on the device + itself. + </tp:docstring> + </property> + <property name="SupportedBands" type="u" access="read" tp:type="MM_MODEM_GSM_BAND"> <tp:docstring> Bands supported by the card. (Note for plugin writers: diff --git a/plugins/mm-modem-sierra-gsm.c b/plugins/mm-modem-sierra-gsm.c index c86c1c5e..b33af6b2 100644 --- a/plugins/mm-modem-sierra-gsm.c +++ b/plugins/mm-modem-sierra-gsm.c @@ -15,6 +15,7 @@ */ #include <config.h> +#include <ctype.h> #include <stdlib.h> #include <stdio.h> #include <string.h> @@ -242,6 +243,78 @@ get_access_technology (MMGenericGsm *modem, mm_at_serial_port_queue_command (port, "*CNTI=0", 3, get_act_request_done, info); } +static void +get_sim_iccid_done (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = user_data; + const char *p; + char buf[21]; + int i; + + if (error) { + info->error = g_error_copy (error); + goto done; + } + + p = mm_strip_tag (response->str, "!ICCID:"); + if (!p) { + info->error = g_error_new_literal (MM_MODEM_ERROR, + MM_MODEM_ERROR_GENERAL, + "Failed to parse !ICCID response"); + goto done; + } + + memset (buf, 0, sizeof (buf)); + for (i = 0; i < 20; i++) { + if (!isdigit (p[i]) && (p[i] != 'F') && (p[i] == 'f')) { + info->error = g_error_new (MM_MODEM_ERROR, + MM_MODEM_ERROR_GENERAL, + "CRSM ICCID response contained invalid character '%c'", + p[i]); + goto done; + } + if (p[i] == 'F' || p[i] == 'f') { + buf[i] = 0; + break; + } + buf[i] = p[i]; + } + + if (i == 19 || i == 20) + mm_callback_info_set_result (info, g_strdup (buf), g_free); + else { + info->error = g_error_new (MM_MODEM_ERROR, + MM_MODEM_ERROR_GENERAL, + "Invalid +CRSM ICCID response size (was %d, expected 19 or 20)", + i); + } + +done: + mm_callback_info_schedule (info); +} + +static void +get_sim_iccid (MMGenericGsm *modem, + MMModemStringFn callback, + gpointer user_data) +{ + MMAtSerialPort *port; + MMCallbackInfo *info; + + info = mm_callback_info_string_new (MM_MODEM (modem), callback, user_data); + + port = mm_generic_gsm_get_best_at_port (modem, &info->error); + if (!port) { + mm_callback_info_schedule (info); + return; + } + + mm_at_serial_port_queue_command (port, "!ICCID?", 3, get_sim_iccid_done, info); +} + /*****************************************************************************/ /* Modem class override functions */ /*****************************************************************************/ @@ -355,5 +428,6 @@ mm_modem_sierra_gsm_class_init (MMModemSierraGsmClass *klass) gsm_class->set_allowed_mode = set_allowed_mode; gsm_class->get_allowed_mode = get_allowed_mode; gsm_class->get_access_technology = get_access_technology; + gsm_class->get_sim_iccid = get_sim_iccid; } diff --git a/src/mm-generic-gsm.c b/src/mm-generic-gsm.c index b7b8e84b..23d6c551 100644 --- a/src/mm-generic-gsm.c +++ b/src/mm-generic-gsm.c @@ -69,6 +69,7 @@ typedef struct { gboolean pin_checked; guint32 pin_check_tries; guint pin_check_timeout; + char *simid; MMModemGsmAllowedMode allowed_mode; @@ -555,6 +556,188 @@ initial_info_check (MMGenericGsm *self) } } +static void +get_iccid_done (MMModem *modem, + const char *response, + GError *error, + gpointer user_data) +{ + MMGenericGsmPrivate *priv; + const char *p = response; + GChecksum *sum; + + if (error || !response || !strlen (response)) + return; + + sum = g_checksum_new (G_CHECKSUM_SHA1); + + /* Make sure it looks like an ICCID */ + while (*p) { + if (!isdigit (*p)) { + g_warning ("%s: invalid ICCID format (not a digit)", __func__); + goto out; + } + g_checksum_update (sum, (const guchar *) p++, 1); + } + + priv = MM_GENERIC_GSM_GET_PRIVATE (modem); + g_free (priv->simid); + priv->simid = g_strdup (g_checksum_get_string (sum)); + + if (mm_options_debug ()) { + g_debug ("SIM ID source '%s'", response); + g_debug ("SIM ID '%s'", priv->simid); + } + + g_object_notify (G_OBJECT (modem), MM_MODEM_GSM_CARD_SIM_IDENTIFIER); + +out: + g_checksum_free (sum); +} + +static void +real_get_iccid_done (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + const char *str; + int sw1, sw2; + gboolean success = FALSE; + char buf[21], swapped[21]; + + if (error) { + info->error = g_error_copy (error); + goto done; + } + + memset (buf, 0, sizeof (buf)); + str = mm_strip_tag (response->str, "+CRSM:"); + if (sscanf (str, "%d,%d,\"%20c\"", &sw1, &sw2, (char *) &buf) == 3) + success = TRUE; + else { + /* May not include quotes... */ + if (sscanf (str, "%d,%d,%20c", &sw1, &sw2, (char *) &buf) == 3) + success = TRUE; + } + + if (!success) { + info->error = g_error_new_literal (MM_MODEM_ERROR, + MM_MODEM_ERROR_GENERAL, + "Could not parse the CRSM response"); + goto done; + } + + if ((sw1 == 0x90 && sw2 == 0x00) || (sw1 == 0x91) || (sw1 == 0x92) || (sw1 == 0x9f)) { + gsize len = 0; + int f_pos = -1, i; + + /* Make sure the buffer is only digits or 'F' */ + for (len = 0; len < sizeof (buf) && buf[len]; len++) { + if (isdigit (buf[len])) + continue; + if (buf[len] == 'F' || buf[len] == 'f') { + buf[len] = 'F'; /* canonicalize the F */ + f_pos = len; + continue; + } + if (buf[len] == '\"') { + buf[len] = 0; + break; + } + + /* Invalid character */ + info->error = g_error_new (MM_MODEM_ERROR, + MM_MODEM_ERROR_GENERAL, + "CRSM ICCID response contained invalid character '%c'", + buf[len]); + goto done; + } + + /* BCD encoded ICCIDs are 20 digits long */ + if (len != 20) { + info->error = g_error_new (MM_MODEM_ERROR, + MM_MODEM_ERROR_GENERAL, + "Invalid +CRSM ICCID response size (was %zd, expected 20)", + len); + goto done; + } + + /* Ensure if there's an 'F' that it's second-to-last */ + if ((f_pos >= 0) && (f_pos != len - 2)) { + info->error = g_error_new_literal (MM_MODEM_ERROR, + MM_MODEM_ERROR_GENERAL, + "Invalid +CRSM ICCID length (unexpected F)"); + goto done; + } + + /* Swap digits in the EFiccid response to get the actual ICCID, each + * group of 2 digits is reversed in the +CRSM response. i.e.: + * + * 21436587 -> 12345678 + */ + memset (swapped, 0, sizeof (swapped)); + for (i = 0; i < 10; i++) { + swapped[i * 2] = buf[(i * 2) + 1]; + swapped[(i * 2) + 1] = buf[i * 2]; + } + + /* Zero out the F for 19 digit ICCIDs */ + if (swapped[len - 1] == 'F') + swapped[len - 1] = 0; + + mm_callback_info_set_result (info, g_strdup (swapped), g_free); + } else { + info->error = g_error_new (MM_MODEM_ERROR, + MM_MODEM_ERROR_GENERAL, + "SIM failed to handle CRSM request (sw1 %d sw2 %d)", + sw1, sw2); + } + +done: + mm_callback_info_schedule (info); +} + +static void +real_get_sim_iccid (MMGenericGsm *self, + MMModemStringFn callback, + gpointer callback_data) +{ + MMCallbackInfo *info; + MMAtSerialPort *port; + GError *error = NULL; + + port = mm_generic_gsm_get_best_at_port (self, &error); + if (!port) { + callback (MM_MODEM (self), NULL, error, callback_data); + g_clear_error (&error); + return; + } + + if (!mm_serial_port_open (MM_SERIAL_PORT (port), &error)) { + callback (MM_MODEM (self), NULL, error, callback_data); + g_clear_error (&error); + return; + } + + info = mm_callback_info_string_new (MM_MODEM (self), callback, callback_data); + + /* READ BINARY of EFiccid (ICC Identification) ETSI TS 102.221 section 13.2 */ + mm_at_serial_port_queue_command (port, + "+CRSM=176,12258,0,0,10", + 3, + real_get_iccid_done, + info); +} + +static void +initial_iccid_check (MMGenericGsm *self) +{ + g_assert (MM_GENERIC_GSM_GET_CLASS (self)->get_sim_iccid); + MM_GENERIC_GSM_GET_CLASS (self)->get_sim_iccid (self, get_iccid_done, NULL); +} + static gboolean owns_port (MMModem *modem, const char *subsys, const char *name) { @@ -608,9 +791,12 @@ mm_generic_gsm_grab_port (MMGenericGsm *self, /* Get the modem's general info */ initial_info_check (self); - /* Get modem's IMEI number */ + /* Get modem's IMEI */ initial_imei_check (self); + /* Try to get the SIM's ICCID */ + initial_iccid_check (self); + /* Get modem's initial lock/unlock state */ initial_pin_check (self); @@ -4184,6 +4370,7 @@ set_property (GObject *object, guint prop_id, case MM_GENERIC_GSM_PROP_SUPPORTED_MODES: case MM_GENERIC_GSM_PROP_ALLOWED_MODE: case MM_GENERIC_GSM_PROP_ACCESS_TECHNOLOGY: + case MM_GENERIC_GSM_PROP_SIM_IDENTIFIER: #if LOCATION_API case MM_GENERIC_GSM_PROP_LOC_CAPABILITIES: case MM_GENERIC_GSM_PROP_LOC_ENABLED: @@ -4250,6 +4437,9 @@ get_property (GObject *object, guint prop_id, else g_value_set_uint (value, MM_MODEM_GSM_ACCESS_TECH_UNKNOWN); break; + case MM_GENERIC_GSM_PROP_SIM_IDENTIFIER: + g_value_set_string (value, priv->simid); + break; #if LOCATION_API case MM_GENERIC_GSM_PROP_LOC_CAPABILITIES: g_value_set_uint (value, priv->loc_caps); @@ -4303,6 +4493,7 @@ finalize (GObject *object) g_free (priv->oper_code); g_free (priv->oper_name); + g_free (priv->simid); G_OBJECT_CLASS (mm_generic_gsm_parent_class)->finalize (object); } @@ -4323,6 +4514,7 @@ mm_generic_gsm_class_init (MMGenericGsmClass *klass) klass->do_enable = real_do_enable; klass->do_enable_power_up_done = real_do_enable_power_up_done; klass->do_disconnect = real_do_disconnect; + klass->get_sim_iccid = real_get_sim_iccid; /* Properties */ g_object_class_override_property (object_class, @@ -4349,6 +4541,10 @@ mm_generic_gsm_class_init (MMGenericGsmClass *klass) MM_GENERIC_GSM_PROP_ACCESS_TECHNOLOGY, MM_MODEM_GSM_NETWORK_ACCESS_TECHNOLOGY); + g_object_class_override_property (object_class, + MM_GENERIC_GSM_PROP_SIM_IDENTIFIER, + MM_MODEM_GSM_CARD_SIM_IDENTIFIER); + #if LOCATION_API g_object_class_override_property (object_class, MM_GENERIC_GSM_PROP_LOC_CAPABILITIES, diff --git a/src/mm-generic-gsm.h b/src/mm-generic-gsm.h index be75ceed..175cf9a0 100644 --- a/src/mm-generic-gsm.h +++ b/src/mm-generic-gsm.h @@ -55,6 +55,7 @@ typedef enum { MM_GENERIC_GSM_PROP_LOC_SIGNAL, MM_GENERIC_GSM_PROP_LOC_LOCATION, #endif + MM_GENERIC_GSM_PROP_SIM_IDENTIFIER, } MMGenericGsmProp; typedef enum { @@ -128,6 +129,11 @@ typedef struct { void (*loc_get_capabilities) (MMGenericGsm *self, MMModemUIntFn callback, gpointer user_data); + + /* Called by the generic class to retrieve the SIM's ICCID */ + void (*get_sim_iccid) (MMGenericGsm *self, + MMModemStringFn callback, + gpointer user_data); } MMGenericGsmClass; GType mm_generic_gsm_get_type (void); diff --git a/src/mm-modem-gsm-card.c b/src/mm-modem-gsm-card.c index e03b964d..d04f0146 100644 --- a/src/mm-modem-gsm-card.c +++ b/src/mm-modem-gsm-card.c @@ -581,6 +581,14 @@ mm_modem_gsm_card_init (gpointer g_iface) g_object_interface_install_property (g_iface, + g_param_spec_string (MM_MODEM_GSM_CARD_SIM_IDENTIFIER, + "SimIdentifier", + "An obfuscated identifier of the SIM", + NULL, + G_PARAM_READABLE)); + + g_object_interface_install_property + (g_iface, g_param_spec_uint (MM_MODEM_GSM_CARD_SUPPORTED_BANDS, "Supported Modes", "Supported frequency bands of the card", diff --git a/src/mm-modem-gsm-card.h b/src/mm-modem-gsm-card.h index 584d7344..e617d8fe 100644 --- a/src/mm-modem-gsm-card.h +++ b/src/mm-modem-gsm-card.h @@ -26,6 +26,7 @@ #define MM_MODEM_GSM_CARD_SUPPORTED_BANDS "supported-bands" #define MM_MODEM_GSM_CARD_SUPPORTED_MODES "supported-modes" +#define MM_MODEM_GSM_CARD_SIM_IDENTIFIER "sim-identifier" #define MM_MODEM_GSM_CARD_SIM_PIN "sim-pin" #define MM_MODEM_GSM_CARD_SIM_PIN2 "sim-pin2" diff --git a/src/mm-serial-parsers.c b/src/mm-serial-parsers.c index 00fe9dce..735ebd97 100644 --- a/src/mm-serial-parsers.c +++ b/src/mm-serial-parsers.c @@ -33,6 +33,13 @@ response_clean (GString *response) s -= 2; } + /* Contains duplicate '<CR><CR>' */ + s = response->str; + while ((response->len >= 2) && (*s == '\r') && (*(s + 1) == '\r')) { + g_string_erase (response, 0, 1); + s = response->str; + } + /* Starts with one or more '<CR><LF>' */ s = response->str; while ((response->len >= 2) && (*s == '\r') && (*(s + 1) == '\n')) { diff --git a/test/info.py b/test/info.py index 347f8fe8..6659a3a4 100755 --- a/test/info.py +++ b/test/info.py @@ -167,6 +167,13 @@ def gsm_inspect(proxy, props): # Gsm.Card interface card = dbus.Interface(proxy, dbus_interface=MM_DBUS_INTERFACE_MODEM_GSM_CARD) + simid = "<unavailable>" + try: + simid = props.Get(MM_DBUS_INTERFACE_MODEM_GSM_CARD, "SimIdentifier") + except dbus.exceptions.DBusException: + pass + print "SIM ID: %s" % simid + imei = "<unavailable>" try: imei = card.GetImei() @@ -229,10 +236,10 @@ if mtype == 1: elif mtype == 2: print "Type: CDMA" -print "Driver: '%s'" % (props.Get(MM_DBUS_INTERFACE_MODEM, 'Driver')) -print "Modem device: '%s'" % (props.Get(MM_DBUS_INTERFACE_MODEM, 'MasterDevice')) -print "Data device: '%s'" % (props.Get(MM_DBUS_INTERFACE_MODEM, 'Device')) -print "Device ID: '%s'" % (props.Get(MM_DBUS_INTERFACE_MODEM, 'DeviceIdentifier')) +print "Driver: %s" % (props.Get(MM_DBUS_INTERFACE_MODEM, 'Driver')) +print "Modem device: %s" % (props.Get(MM_DBUS_INTERFACE_MODEM, 'MasterDevice')) +print "Data device: %s" % (props.Get(MM_DBUS_INTERFACE_MODEM, 'Device')) +print "Device ID: %s" % (props.Get(MM_DBUS_INTERFACE_MODEM, 'DeviceIdentifier')) print "" modem = dbus.Interface(proxy, dbus_interface=MM_DBUS_INTERFACE_MODEM) |