>From ac872582e335ad6433f99576b6dc23803d0f0c5e Mon Sep 17 00:00:00 2001
From: Bastien Nocera <hadess@hadess.net>
Date: Wed, 21 May 2008 14:36:36 +0100
Subject: [PATCH] Add PolicyKit checking

Add PolicyKit checks to all the public functions, grouped
in 2 main groups: Verify and Enroll
By default, only the user is able to enroll new fingers,
or verify themselves.
You need to be allowed at least one of those 2 actions
to be allowed to claim or release the device.

We also add a new SetUsername function, for administration
functions. Users will need to be authenticate as admins to
be allowed to change the username on which the actions will
be taken. Any prints loaded before the change of username will
be unloaded.
---
 Makefile.am                                  |    2 +-
 autogen.sh                                   |    1 +
 configure.ac                                 |    6 +-
 data/Makefile.am                             |   12 ++-
 data/net.reactivated.fprint.device.policy.in |   42 +++++++
 src/device.c                                 |  156 ++++++++++++++++++++++----
 src/device.xml                               |    5 +
 7 files changed, 199 insertions(+), 25 deletions(-)
 create mode 100644 data/net.reactivated.fprint.device.policy.in

diff --git a/Makefile.am b/Makefile.am
index 62e9de8..095c889 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,4 +1,4 @@
 AUTOMAKE_OPTIONS = dist-bzip2
 SUBDIRS = src data tests po
-EXTRA_DIST = TODO
+EXTRA_DIST = TODO intltool-extract.in intltool-merge.in intltool-update.in
 
diff --git a/autogen.sh b/autogen.sh
index 096ac20..16bc9ec 100755
--- a/autogen.sh
+++ b/autogen.sh
@@ -2,6 +2,7 @@
 aclocal || exit 1
 autoheader || exit 1
 glib-gettextize -f -c || exit 1
+intltoolize -c -f || exit 1
 autoconf || exit 1
 automake -a -c || exit 1
 ./configure $*
diff --git a/configure.ac b/configure.ac
index 59d6394..2c2665c 100644
--- a/configure.ac
+++ b/configure.ac
@@ -11,6 +11,7 @@ GETTEXT_PACKAGE=fprintd
 AC_DEFINE_UNQUOTED(GETTEXT_PACKAGE, ["$GETTEXT_PACKAGE"], [Define to the Gettext package name])
 AC_SUBST(GETTEXT_PACKAGE)
 AM_GLIB_GNU_GETTEXT
+IT_PROG_INTLTOOL([0.35.0])
 
 PKG_CHECK_MODULES(FPRINT, [libfprint > 0.1.0])
 AC_SUBST(FPRINT_LIBS)
@@ -24,10 +25,13 @@ PKG_CHECK_MODULES(DBUS_GLIB, "dbus-glib-1")
 AC_SUBST(DBUS_GLIB_LIBS)
 AC_SUBST(DBUS_GLIB_CFLAGS)
 
-PKG_CHECK_MODULES(POLKIT, "polkit")
+PKG_CHECK_MODULES(POLKIT, "polkit polkit-dbus")
 AC_SUBST(POLKIT_LIBS)
 AC_SUBST(POLKIT_CFLAGS)
 
+AC_CHECK_PROG([POLKIT_POLICY_FILE_VALIDATE],
+	      [polkit-policy-file-validate], [polkit-policy-file-validate])
+
 AS_AC_EXPAND(DATADIR, $datadir)
 
 DBUS_SERVICES_DIR="$DATADIR/dbus-1/services"
diff --git a/data/Makefile.am b/data/Makefile.am
index 0243423..98c9afa 100644
--- a/data/Makefile.am
+++ b/data/Makefile.am
@@ -1,15 +1,21 @@
 
 dbus_servicesdir = $(datadir)/dbus-1/system-services
-dbus_confdir = $(sysconfdir)/dbus-1/system.d
-
 dbus_services_in_files = net.reactivated.Fprint.service.in
-
 dbus_services_DATA = $(dbus_services_in_files:.service.in=.service)
 
 $(dbus_services_DATA): $(dbus_services_in_files)
 	sed -e "s|\@LIBEXECDIR\@|$(libexecdir)|" $< > $@
 
+dbus_confdir = $(sysconfdir)/dbus-1/system.d
 dbus_conf_DATA = net.reactivated.Fprint.conf
 
+polkitdir = $(datadir)/PolicyKit/policy
+polkit_in_files = net.reactivated.fprint.device.policy.in
+
+@INTLTOOL_POLICY_RULE@
+polkit_DATA = $(polkit_in_files:.policy.in=.policy)
+
+check:
+	$(POLKIT_POLICY_FILE_VALIDATE) $(polkit_DATA)
 
 
diff --git a/data/net.reactivated.fprint.device.policy.in b/data/net.reactivated.fprint.device.policy.in
new file mode 100644
index 0000000..af2eddb
--- /dev/null
+++ b/data/net.reactivated.fprint.device.policy.in
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE policyconfig PUBLIC
+ "-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN"
+ "http://www.freedesktop.org/standards/PolicyKit/1.0/policyconfig.dtd">
+
+<policyconfig>
+
+  <vendor>The FPrint Project</vendor>
+  <vendor_url>http://reactivated.net/fprint/</vendor_url>
+  <icon_name>fprint</icon_name>
+
+  <action id="net.reactivated.fprint.device.verify">
+    <_description>Verify a fingerprint</_description>
+    <_message>Privileges are required to verify fingerprints.</_message>
+    <defaults>
+      <allow_any>no</allow_any>
+      <allow_inactive>no</allow_inactive>
+      <allow_active>yes</allow_active>
+    </defaults>
+  </action>
+
+  <action id="net.reactivated.fprint.device.enroll">
+    <_description>Enroll new fingerprints</_description>
+    <_message>Privileges are required to enroll new fingerprints.</_message>
+    <defaults>
+      <allow_any>no</allow_any>
+      <allow_inactive>no</allow_inactive>
+      <allow_active>yes</allow_active>
+    </defaults>
+  </action>
+
+  <action id="net.reactivated.fprint.device.setusername">
+    <_description>Select a user to enroll</_description>
+    <_message>Privileges are required to enroll new fingerprints for other users.</_message>
+    <defaults>
+      <allow_any>no</allow_any>
+      <allow_inactive>no</allow_inactive>
+      <allow_active>auth_admin_keep_always</allow_active>
+    </defaults>
+  </action>
+
+</policyconfig>
diff --git a/src/device.c b/src/device.c
index 391b4d3..26552a1 100644
--- a/src/device.c
+++ b/src/device.c
@@ -34,6 +34,9 @@
 
 extern DBusGConnection *fprintd_dbus_conn;
 
+static void fprint_device_set_username(FprintDevice *rdev,
+	const char *username,
+	DBusGMethodInvocation *context);
 static void fprint_device_claim(FprintDevice *rdev,
 	DBusGMethodInvocation *context);
 static void fprint_device_release(FprintDevice *rdev,
@@ -89,9 +92,8 @@ struct FprintDevicePrivate {
 	char *sender;
 
 	/* Either the current user of the device, or if allowed,
-	 * what was set using SetCurrentUid */
+	 * what was set using SetUsername */
 	char *username;
-	uid_t uid;
 
 	/* type of storage */
 	int storage_type;
@@ -257,18 +259,15 @@ _fprint_device_check_claimed (FprintDevice *rdev,
 }
 
 static gboolean
-_check_polkit_for_action (FprintDevice *rdev, DBusGMethodInvocation *context, const char *action)
+_fprint_device_check_polkit_for_action (FprintDevice *rdev, DBusGMethodInvocation *context, const char *action, GError **error)
 {
 	FprintDevicePrivate *priv = DEVICE_GET_PRIVATE(rdev);
 	const char *sender;
-	GError *error;
 	DBusError dbus_error;
 	PolKitCaller *pk_caller;
 	PolKitAction *pk_action;
 	PolKitResult pk_result;
 
-	error = NULL;
-
 	/* Check that caller is privileged */
 	sender = dbus_g_method_get_sender (context);
 	dbus_error_init (&dbus_error);
@@ -277,38 +276,103 @@ _check_polkit_for_action (FprintDevice *rdev, DBusGMethodInvocation *context, co
 	    sender, 
 	    &dbus_error);
 	if (pk_caller == NULL) {
-		error = g_error_new (FPRINT_ERROR,
-				     FPRINT_ERROR_INTERNAL,
-				     "Error getting information about caller: %s: %s",
-				     dbus_error.name, dbus_error.message);
+		g_set_error (error, FPRINT_ERROR,
+			     FPRINT_ERROR_INTERNAL,
+			     "Error getting information about caller: %s: %s",
+			     dbus_error.name, dbus_error.message);
 		dbus_error_free (&dbus_error);
-		dbus_g_method_return_error (context, error);
-		g_error_free (error);
 		return FALSE;
 	}
 
 	pk_action = polkit_action_new ();
 	polkit_action_set_action_id (pk_action, action);
 	pk_result = polkit_context_is_caller_authorized (priv->pol_ctx, pk_action, pk_caller,
-							 FALSE, NULL);
+							 TRUE, NULL);
 	polkit_caller_unref (pk_caller);
 	polkit_action_unref (pk_action);
 
 	if (pk_result != POLKIT_RESULT_YES) {
-		error = g_error_new (FPRINT_ERROR,
-				     FPRINT_ERROR_INTERNAL,
-				     "%s %s <-- (action, result)",
-				     action,
-				     polkit_result_to_string_representation (pk_result));
+		g_set_error (error, FPRINT_ERROR,
+			     FPRINT_ERROR_INTERNAL,
+			     "%s %s <-- (action, result)",
+			     action,
+			     polkit_result_to_string_representation (pk_result));
 		dbus_error_free (&dbus_error);
-		dbus_g_method_return_error (context, error);
-		g_error_free (error);
 		return FALSE;
 	}
 
 	return TRUE;
 }
 
+static gboolean
+_fprint_device_check_polkit_for_actions (FprintDevice *rdev,
+					 DBusGMethodInvocation *context,
+					 const char *action1,
+					 const char *action2,
+					 GError **error)
+{
+	if (_fprint_device_check_polkit_for_action (rdev, context, action1, error) != FALSE)
+		return TRUE;
+
+	g_error_free (*error);
+	*error = NULL;
+
+	return _fprint_device_check_polkit_for_action (rdev, context, action2, error);
+}
+
+static void
+fprint_device_set_username (FprintDevice *rdev,
+			    const char *username,
+			    DBusGMethodInvocation *context)
+{
+	FprintDevicePrivate *priv = DEVICE_GET_PRIVATE(rdev);
+	GError *error = NULL;
+	struct session_data *session = priv->session;
+	GSList *elem = session->loaded_prints;
+
+	if (_fprint_device_check_claimed(rdev, context, &error) == FALSE) {
+		dbus_g_method_return_error (context, error);
+		return;
+	}
+
+	if (_fprint_device_check_polkit_for_action (rdev, context, "net.reactivated.fprint.device.setusername", &error) == FALSE) {
+		dbus_g_method_return_error (context, error);
+		return;
+	}
+
+	if (username == NULL) {
+		dbus_g_method_return (context);
+		return;
+	}
+
+	/* We already have a username, check if the one we're
+	 * setting is the same */
+	if (g_str_equal (username, priv->username) != FALSE) {
+		dbus_g_method_return (context);
+		return;
+	}
+
+	g_free (priv->username);
+	priv->username = g_strdup (username);
+
+	/* Any fingerprints to unload? */
+	if (!elem) {
+		dbus_g_method_return (context);
+		return;
+	}
+
+	/* Empty the fingerprints, as we have a different user */
+	do {
+		struct loaded_print *print = elem->data;
+
+		session->loaded_prints = g_slist_delete_link(session->loaded_prints,
+			elem);
+		g_slice_free(struct loaded_print, print);
+	} while ((elem = g_slist_next(elem)) != NULL);
+
+	dbus_g_method_return (context);
+}
+
 static void dev_open_cb(struct fp_dev *dev, int status, void *user_data)
 {
 	FprintDevice *rdev = user_data;
@@ -353,6 +417,14 @@ static void fprint_device_claim(FprintDevice *rdev,
 		return;
 	}
 
+	if (_fprint_device_check_polkit_for_actions (rdev, context,
+						     "net.reactivated.fprint.device.verify",
+						     "net.reactivated.fprint.device.enroll",
+						     &error) == FALSE) {
+		dbus_g_method_return_error (context, error);
+		return;
+	}
+
 	/* Get details about the current sender, and username/uid */
 	conn = dbus_g_connection_get_connection (fprintd_dbus_conn);
 	sender = dbus_g_method_get_sender (context);
@@ -430,6 +502,15 @@ static void fprint_device_release(FprintDevice *rdev,
 		return;
 	}
 
+	/* People that can claim can also release */
+	if (_fprint_device_check_polkit_for_actions (rdev, context,
+						     "net.reactivated.fprint.device.verify",
+						     "net.reactivated.fprint.device.enroll",
+						     &error) == FALSE) {
+		dbus_g_method_return_error (context, error);
+		return;
+	}
+
 	/* Unload any loaded prints */
 	if (elem) {
 		do
@@ -455,6 +536,11 @@ static void fprint_device_unload_print_data(FprintDevice *rdev,
 		return;
 	}
 
+	if (_fprint_device_check_polkit_for_action (rdev, context, "net.reactivated.fprint.device.verify", &error) == FALSE) {
+		dbus_g_method_return_error (context, error);
+		return;
+	}
+
 	g_message("unload print data %d for device %d", print_id, priv->id);
 	if (!elem) {
 		g_set_error(&error, FPRINT_ERROR, FPRINT_ERROR_NO_SUCH_LOADED_PRINT,
@@ -505,6 +591,11 @@ static void fprint_device_verify_start(FprintDevice *rdev,
 		return;
 	}
 
+	if (_fprint_device_check_polkit_for_action (rdev, context, "net.reactivated.fprint.device.verify", &error) == FALSE) {
+		dbus_g_method_return_error (context, error);
+		return;
+	}
+
 	g_message("start verification device %d print %d", priv->id, print_id);
 	if (!elem) {
 		g_set_error(&error, FPRINT_ERROR, FPRINT_ERROR_NO_SUCH_LOADED_PRINT,
@@ -557,6 +648,11 @@ static void fprint_device_verify_stop(FprintDevice *rdev,
 		return;
 	}
 
+	if (_fprint_device_check_polkit_for_action (rdev, context, "net.reactivated.fprint.device.verify", &error) == FALSE) {
+		dbus_g_method_return_error (context, error);
+		return;
+	}
+
 	r = fp_async_verify_stop(priv->dev, verify_stop_cb, context);
 	if (r < 0) {
 		g_set_error(&error, FPRINT_ERROR, FPRINT_ERROR_VERIFY_STOP,
@@ -598,6 +694,11 @@ static void fprint_device_enroll_start(FprintDevice *rdev,
 		return;
 	}
 
+	if (_fprint_device_check_polkit_for_action (rdev, context, "net.reactivated.fprint.device.enroll", &error) == FALSE) {
+		dbus_g_method_return_error (context, error);
+		return;
+	}
+
 	g_message("start enrollment device %d finger %d", priv->id, finger_num);
 	session->enroll_finger = finger_num;
 	
@@ -629,6 +730,11 @@ static void fprint_device_enroll_stop(FprintDevice *rdev,
 		return;
 	}
 
+	if (_fprint_device_check_polkit_for_action (rdev, context, "net.reactivated.fprint.device.enroll", &error) == FALSE) {
+		dbus_g_method_return_error (context, error);
+		return;
+	}
+
 	r = fp_async_enroll_stop(priv->dev, enroll_stop_cb, context);
 	if (r < 0) {
 		g_set_error(&error, FPRINT_ERROR, FPRINT_ERROR_ENROLL_STOP,
@@ -666,6 +772,11 @@ static void fprint_device_list_enrolled_fingers(FprintDevice *rdev,
 		return;
 	}
 
+	if (_fprint_device_check_polkit_for_action (rdev, context, "net.reactivated.fprint.device.verify", &error) == FALSE) {
+		dbus_g_method_return_error (context, error);
+		return;
+	}
+
 	prints = storages[priv->storage_type].discover_prints(priv->dev, priv->username);
 	if (!prints) {
 		g_set_error(&error, FPRINT_ERROR, FPRINT_ERROR_DISCOVER_PRINTS,
@@ -700,6 +811,11 @@ static void fprint_device_load_print_data(FprintDevice *rdev,
 		return;
 	}
 
+	if (_fprint_device_check_polkit_for_action (rdev, context, "net.reactivated.fprint.device.verify", &error) == FALSE) {
+		dbus_g_method_return_error (context, error);
+		return;
+	}
+
 	r = storages[priv->storage_type].print_data_load(priv->dev, (enum fp_finger)finger_num, 
 		&data, priv->username);
 
diff --git a/src/device.xml b/src/device.xml
index 6ef5801..f7d1656 100644
--- a/src/device.xml
+++ b/src/device.xml
@@ -60,6 +60,11 @@
 			<annotation name="org.freedesktop.DBus.GLib.Async" value="" />
 		</method>
 
+		<method name="SetUsername">
+			<arg type="s" name="username" direction="in" />
+			<annotation name="org.freedesktop.DBus.GLib.Async" value="" />
+		</method>
+
 	</interface>
 </node>
 
-- 
1.5.4.5

