[network-manager-applet] gsm: auto-unlock using PINs stored in the keyring (bgo #618532) (lp:555467)



commit 7d837085c467675529a4e2be90f406e1cfcd6277
Author: Anders Feder <anders feder dk>
Date:   Wed Oct 12 18:29:42 2011 -0500

    gsm: auto-unlock using PINs stored in the keyring (bgo #618532) (lp:555467)
    
    Store successful PINs in the keyring and use the modem's
    DeviceIdentifier and SimIdentifier to look those PINs up
    when the modem is inserted and requires an unlock.
    
    (dcbw: leaks, cleanups, and async keyring calls)

 src/applet-device-gsm.c |  215 ++++++++++++++++++++++++++++++++++++++++++++--
 1 files changed, 205 insertions(+), 10 deletions(-)
---
diff --git a/src/applet-device-gsm.c b/src/applet-device-gsm.c
index d2740ef..e492cdb 100644
--- a/src/applet-device-gsm.c
+++ b/src/applet-device-gsm.c
@@ -73,6 +73,8 @@ typedef struct {
 	gboolean quality_valid;
 	guint32 quality;
 	char *unlock_required;
+	char *devid;
+	char *simid;
 	gboolean modem_enabled;
 	MMModemGsmAccessTech act;
 
@@ -90,6 +92,7 @@ typedef struct {
 
 	/* Unlock dialog stuff */
 	GtkWidget *dialog;
+	gpointer keyring_id;
 } GsmDeviceInfo;
 
 static void unlock_dialog_destroy (GsmDeviceInfo *info);
@@ -761,6 +764,56 @@ gsm_get_secrets (SecretsRequest *req, GError **error)
 /********************************************************************/
 
 static void
+save_pin_cb (GnomeKeyringResult result, guint32 val, gpointer user_data)
+{
+	if (result != GNOME_KEYRING_RESULT_OK)
+		g_warning ("%s: result %d", (const char *) user_data, result);
+}
+
+static void
+set_pin_in_keyring (const char *devid,
+                    const char *simid,
+                    const char *pin)
+{
+	GnomeKeyringAttributeList *attributes;
+	GnomeKeyringAttribute attr;
+	const char *name;
+	char *error_msg;
+
+	name = g_strdup_printf (_("PIN code for SIM card '%s' on '%s'"),
+	                        simid ? simid : "unknown",
+	                        devid);
+
+	attributes = gnome_keyring_attribute_list_new ();
+	attr.name = g_strdup ("devid");
+	attr.type = GNOME_KEYRING_ATTRIBUTE_TYPE_STRING;
+	attr.value.string = g_strdup (devid);
+	g_array_append_val (attributes, attr);
+
+	if (simid) {
+		attr.name = g_strdup ("simid");
+		attr.type = GNOME_KEYRING_ATTRIBUTE_TYPE_STRING;
+		attr.value.string = g_strdup (simid);
+		g_array_append_val (attributes, attr);
+	}
+
+	error_msg = g_strdup_printf ("Saving PIN code in keyring for devid:%s simid:%s failed",
+	                             devid, simid ? simid : "(unknown)");
+
+	gnome_keyring_item_create (NULL,
+	                           GNOME_KEYRING_ITEM_GENERIC_SECRET,
+	                           name,
+	                           attributes,
+	                           pin,
+	                           TRUE,
+	                           save_pin_cb,
+	                           error_msg,
+	                           (GDestroyNotify) g_free);
+
+	gnome_keyring_attribute_list_free (attributes);
+}
+
+static void
 unlock_dialog_destroy (GsmDeviceInfo *info)
 {
 	applet_mobile_pin_dialog_destroy (info->dialog);
@@ -772,9 +825,11 @@ unlock_pin_reply (DBusGProxy *proxy, DBusGProxyCall *call, gpointer user_data)
 {
 	GsmDeviceInfo *info = user_data;
 	GError *error = NULL;
-	const char *dbus_error, *msg = NULL;
+	const char *dbus_error, *msg = NULL, *code1;
 
 	if (dbus_g_proxy_end_call (proxy, call, &error, G_TYPE_INVALID)) {
+		code1 = applet_mobile_pin_dialog_get_entry1 (info->dialog);
+		set_pin_in_keyring (info->devid, info->simid, code1);
 		unlock_dialog_destroy (info);
 		return;
 	}
@@ -962,6 +1017,9 @@ gsm_device_info_free (gpointer data)
 	if (info->bus)
 		dbus_g_connection_unref (info->bus);
 
+	if (info->keyring_id)
+		gnome_keyring_cancel_request (info->keyring_id);
+
 	if (info->providers)
 		g_hash_table_destroy (info->providers);
 
@@ -971,6 +1029,8 @@ gsm_device_info_free (gpointer data)
 	if (info->dialog)
 		unlock_dialog_destroy (info);
 
+	g_free (info->devid);
+	g_free (info->simid);
 	g_free (info->op_code);
 	g_free (info->op_name);
 	memset (info, 0, sizeof (GsmDeviceInfo));
@@ -1193,7 +1253,86 @@ parse_unlock_required (GValue *value)
 }
 
 static void
-unlock_reply (DBusGProxy *proxy, DBusGProxyCall *call, gpointer user_data)
+keyring_unlock_pin_reply (DBusGProxy *proxy, DBusGProxyCall *call, gpointer user_data)
+{
+	GsmDeviceInfo *info = user_data;
+	GError *error = NULL;
+
+	if (!dbus_g_proxy_end_call (proxy, call, &error, G_TYPE_INVALID)) {
+		g_warning ("Failed to auto-unlock devid:%s simid:%s : (%s) %s",
+		           info->devid ? info->devid : "(unknown)",
+		           info->simid ? info->simid : "(unknown)",
+		           dbus_g_error_get_name (error),
+		           error->message);
+		/* Ask the user */
+		unlock_dialog_new (info->device, info);
+		g_clear_error (&error);
+	}
+}
+
+static void
+keyring_pin_check_cb (GnomeKeyringResult result, GList *list, gpointer user_data)
+{
+	GsmDeviceInfo *info = user_data;
+	GList *iter;
+	const char *pin = NULL;
+
+	info->keyring_id = NULL;
+
+	if (result != GNOME_KEYRING_RESULT_OK) {
+		/* No saved PIN, just ask the user */
+		unlock_dialog_new (info->device, info);
+		return;
+	}
+
+	/* Look for a result with a matching "simid" attribute since that's
+	 * better than just using a matching "devid".  The PIN is really tied
+	 * to the SIM, not the modem itself.
+	 */
+	for (iter = list;
+	     info->simid && (pin == NULL) && iter;
+	     iter = g_list_next (iter)) {
+		GnomeKeyringFound *found = iter->data;
+		int i;
+
+		/* Look for a matching "simid" attribute */
+		for (i = 0; (pin == NULL) && i < found->attributes->len; i++) {
+			GnomeKeyringAttribute attr = gnome_keyring_attribute_list_index (found->attributes, i);
+
+			if (   g_strcmp0 (attr.name, "simid") == 0
+			    && attr.type == GNOME_KEYRING_ATTRIBUTE_TYPE_STRING
+			    && g_strcmp0 (attr.value.string, info->simid) == 0) {
+				pin = found->secret;
+				break;
+			}
+		}
+	}
+
+	if (pin == NULL) {
+		/* Fall back to the first result's PIN */
+		pin = ((GnomeKeyringFound *) list->data)->secret;
+		if (pin == NULL) {
+			/* Should never get here */
+			g_warn_if_fail (pin != NULL);
+			unlock_dialog_new (info->device, info);
+			return;
+		}
+	}
+
+	/* Send the PIN code to ModemManager */
+	if (!dbus_g_proxy_begin_call_with_timeout (info->card_proxy, "SendPin",
+	                                           keyring_unlock_pin_reply, info, NULL,
+	                                           15000,  /* 15 seconds */
+	                                           G_TYPE_STRING, pin,
+	                                           G_TYPE_INVALID)) {
+		g_warning ("Failed to auto-unlock devid:%s simid:%s",
+		           info->devid ? info->devid : "(unknown)",
+		           info->simid ? info->simid : "(unknown)");
+	}
+}
+
+static void
+simid_reply (DBusGProxy *proxy, DBusGProxyCall *call, gpointer user_data)
 {
 	GsmDeviceInfo *info = user_data;
 	GError *error = NULL;
@@ -1203,15 +1342,72 @@ unlock_reply (DBusGProxy *proxy, DBusGProxyCall *call, gpointer user_data)
 	                           G_TYPE_VALUE, &value,
 	                           G_TYPE_INVALID)) {
 		if (G_VALUE_HOLDS_STRING (&value)) {
-			g_free (info->unlock_required);
-			info->unlock_required = parse_unlock_required (&value);
-
-			/* Show the unlock dialog if an unlock is now required */
-			if (info->unlock_required)
-				unlock_dialog_new (info->device, info);
+			g_free (info->simid);
+			info->simid = g_value_dup_string (&value);
 		}
 		g_value_unset (&value);
 	}
+	g_clear_error (&error);
+
+	/* Procure unlock code and apply it if an unlock is now required. */
+	if (info->unlock_required) {
+		/* If we have a device ID ask the keyring for any saved SIM-PIN codes */
+		if (info->devid && (g_strcmp0 (info->unlock_required, "sim-pin") == 0)) {
+			g_warn_if_fail (info->keyring_id == NULL);
+			info->keyring_id = gnome_keyring_find_itemsv (GNOME_KEYRING_ITEM_GENERIC_SECRET,
+			                                              keyring_pin_check_cb,
+			                                              info,
+			                                              NULL,
+			                                              "devid",
+			                                              GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
+			                                              info->devid,
+			                                              NULL);
+		} else {
+			/* Couldn't get a device ID, but unlock required; present dialog */
+			unlock_dialog_new (info->device, info);
+		}
+	}
+
+	check_start_polling (info);
+}
+
+#define MM_DBUS_INTERFACE_MODEM_GSM_CARD "org.freedesktop.ModemManager.Modem.Gsm.Card"
+
+static void
+unlock_reply (DBusGProxy *proxy, DBusGProxyCall *call, gpointer user_data)
+{
+	GsmDeviceInfo *info = user_data;
+	GError *error = NULL;
+	GHashTable *props = NULL;
+
+	if (dbus_g_proxy_end_call (proxy, call, &error,
+	                           DBUS_TYPE_G_MAP_OF_VARIANT, &props,
+	                           G_TYPE_INVALID)) {
+		GHashTableIter iter;
+		const char *prop_name;
+		GValue *value;
+
+		g_hash_table_iter_init (&iter, props);
+		while (g_hash_table_iter_next (&iter, (gpointer) &prop_name, (gpointer) &value)) {
+			if ((strcmp (prop_name, "UnlockRequired") == 0) && G_VALUE_HOLDS_STRING (value)) {
+				g_free (info->unlock_required);
+				info->unlock_required = parse_unlock_required (value);
+			}
+
+			if ((strcmp (prop_name, "DeviceIdentifier") == 0) && G_VALUE_HOLDS_STRING (value)) {
+				g_free (info->devid);
+				info->devid = g_value_dup_string (value);
+			}
+		}
+		g_hash_table_destroy (props);
+
+		/* Get SIM card identifier */
+		dbus_g_proxy_begin_call (info->props_proxy, "Get",
+		                         simid_reply, info, NULL,
+		                         G_TYPE_STRING, MM_DBUS_INTERFACE_MODEM_GSM_CARD,
+		                         G_TYPE_STRING, "SimIdentifier",
+		                         G_TYPE_INVALID);
+	}
 
 	g_clear_error (&error);
 	check_start_polling (info);
@@ -1465,10 +1661,9 @@ gsm_device_added (NMDevice *device, NMApplet *applet)
 	                             info, NULL);
 
 	/* Ask whether the device needs to be unlocked */
-	dbus_g_proxy_begin_call (info->props_proxy, "Get",
+	dbus_g_proxy_begin_call (info->props_proxy, "GetAll",
 	                         unlock_reply, info, NULL,
 	                         G_TYPE_STRING, MM_DBUS_INTERFACE_MODEM,
-	                         G_TYPE_STRING, "UnlockRequired",
 	                         G_TYPE_INVALID);
 
 	/* Ask whether the device is enabled */



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]