hal: Branch 'master'
David Zeuthen
david at kemper.freedesktop.org
Sat Feb 17 14:24:56 PST 2007
fdi/policy/10osvendor/20-acl-management.fdi | 35
hald/ck-tracker.c | 8
hald/hald_dbus.c | 42 +
tools/Makefile.am | 6
tools/hal-acl-add.c | 97 --
tools/hal-acl-remove.c | 16
tools/hal-acl-tool.c | 1051 ++++++++++++++++++++++++++++
tools/hal-storage-shared.c | 1
8 files changed, 1121 insertions(+), 135 deletions(-)
New commits:
diff-tree d7d9e1100f9f26da1004597ee52814fd41610c08 (from 573c40932cb08837d75f1981904e493734765338)
Author: David Zeuthen <davidz at redhat.com>
Date: Sat Feb 17 17:24:54 2007 -0500
more ACL management work; now it's actually usuable
diff --git a/fdi/policy/10osvendor/20-acl-management.fdi b/fdi/policy/10osvendor/20-acl-management.fdi
index ca1f21a..8033521 100644
--- a/fdi/policy/10osvendor/20-acl-management.fdi
+++ b/fdi/policy/10osvendor/20-acl-management.fdi
@@ -5,33 +5,40 @@
<!-- allow local sessions to access ALSA device files -->
<match key="info.capabilities" contains="alsa">
- <merge key="acl.is_managed" type="bool">true</merge>
- <merge key="acl.grant_local_session" type="bool">true</merge>
- <merge key="acl.file" type="copy_property">alsa.device_file</merge>
+ <append key="info.capabilities" type="strlist">access_control</append>
+ <merge key="access_control.grant_local_session" type="bool">true</merge>
+ <merge key="access_control.file" type="copy_property">alsa.device_file</merge>
</match>
<!-- allow local sessions to access OSS device files -->
<match key="info.capabilities" contains="oss">
- <merge key="acl.is_managed" type="bool">true</merge>
- <merge key="acl.grant_local_session" type="bool">true</merge>
- <merge key="acl.file" type="copy_property">oss.device_file</merge>
+ <append key="info.capabilities" type="strlist">access_control</append>
+ <merge key="access_control.grant_local_session" type="bool">true</merge>
+ <merge key="access_control.file" type="copy_property">oss.device_file</merge>
</match>
<!-- TODO: add more rules for adding/removing ACL's (e.g. webcam, cd drive etc.) -->
- <!-- add and remove ACL's when devices are added and removed -->
- <match key="acl.is_managed" bool="true">
- <append key="info.callouts.add" type="strlist">hal-acl-add --device</append>
- <append key="info.callouts.remove" type="strlist">hal-acl-remove --device</append>
+ <!-- add / remove ACL's when devices are added and removed -->
+ <match key="info.capabilities" contains="access_control">
+ <append key="info.callouts.add" type="strlist">hal-acl-tool --add-device</append>
+ <append key="info.callouts.remove" type="strlist">hal-acl-tool --remove-device</append>
</match>
<match key="info.udi" string="/org/freedesktop/Hal/devices/computer">
<!-- remove all previously added ACL's on start-up -->
- <append key="info.callouts.add" type="strlist">hal-acl-remove --remove-all</append>
+ <append key="info.callouts.add" type="strlist">hal-acl-tool --remove-all</append>
+
+ <!-- reconfigure all ACL's sessions are added and removed -->
+ <append key="info.callouts.session_add" type="strlist">hal-acl-tool --reconfigure</append>
+ <append key="info.callouts.session_remove" type="strlist">hal-acl-tool --reconfigure</append>
+
+ <!-- reconfigure all ACL's when a session becomes active -->
+ <append key="info.callouts.session_active" type="strlist">hal-acl-tool --reconfigure</append>
+
+ <!-- reconfigure all ACL's when a session becomes inactive -->
+ <append key="info.callouts.session_inactive" type="strlist">hal-acl-tool --reconfigure</append>
- <!-- add and remove ACL's when sessions are added and removed -->
- <append key="info.callouts.session_add" type="strlist">hal-acl-add --session</append>
- <append key="info.callouts.session_remove" type="strlist">hal-acl-remove --session</append>
</match>
</device>
diff --git a/hald/ck-tracker.c b/hald/ck-tracker.c
index b943729..7276ad6 100644
--- a/hald/ck-tracker.c
+++ b/hald/ck-tracker.c
@@ -496,10 +496,6 @@ ck_tracker_process_system_bus_message (C
session = (CKSession *) i->data;
if (strcmp (session->session_objpath, session_objpath) == 0) {
- if (tracker->session_removed_cb != NULL) {
- tracker->session_removed_cb (tracker, session, tracker->user_data);
- }
-
if (session->seat == NULL) {
HAL_ERROR (("Session '%s' to be removed is not attached to a seat",
session_objpath));
@@ -509,6 +505,10 @@ ck_tracker_process_system_bus_message (C
tracker->sessions = g_slist_remove (tracker->sessions, session);
ck_session_unref (session);
+ if (tracker->session_removed_cb != NULL) {
+ tracker->session_removed_cb (tracker, session, tracker->user_data);
+ }
+
break;
}
}
diff --git a/hald/hald_dbus.c b/hald/hald_dbus.c
index 51089a5..24d0d53 100644
--- a/hald/hald_dbus.c
+++ b/hald/hald_dbus.c
@@ -4215,7 +4215,7 @@ hald_dbus_filter_function (DBusConnectio
goto out;
}
- HAL_INFO (("NameOwnerChanged name=%s old=%s new=%s", name, old_service_name, new_service_name));
+ /*HAL_INFO (("NameOwnerChanged name=%s old=%s new=%s", name, old_service_name, new_service_name));*/
ci_tracker_name_owner_changed (name, old_service_name, new_service_name);
@@ -4497,9 +4497,49 @@ out:
static void
hald_dbus_session_active_changed (CKTracker *tracker, CKSession *session, void *user_data)
{
+ HalDevice *d;
+ char **programs;
+ char *extra_env[5] = {"HALD_ACTION=session_remove",
+ NULL /* "HALD_SESSION_ACTIVE_CHANGED_SESSION_ID=" */,
+ NULL /* "HALD_SESSION_ACTIVE_CHANGED_SESSION_UID=" */,
+ NULL /* "HALD_SESSION_ACTIVE_CHANGED_SESSION_IS_ACTIVE=" */,
+ NULL};
+
HAL_INFO (("In hald_dbus_session_active_changed for session '%s': %s",
ck_session_get_id (session),
ck_session_is_active (session) ? "ACTIVE" : "INACTIVE"));
+
+ d = hal_device_store_find (hald_get_gdl (), "/org/freedesktop/Hal/devices/computer");
+ if (d == NULL) {
+ d = hal_device_store_find (hald_get_tdl (), "/org/freedesktop/Hal/devices/computer");
+ }
+ if (d == NULL) {
+ goto out;
+ }
+
+ programs = hal_device_property_dup_strlist_as_strv (d,
+ ck_session_is_active (session) ?
+ "info.callouts.session_active" :
+ "info.callouts.session_inactive");
+ if (programs == NULL) {
+ goto out;
+ }
+
+ extra_env[1] = g_strdup_printf ("HALD_SESSION_ACTIVE_CHANGED_SESSION_ID=%s", ck_session_get_id (session));
+ extra_env[2] = g_strdup_printf ("HALD_SESSION_ACTIVE_CHANGED_SESSION_UID=%d", ck_session_get_user (session));
+ extra_env[3] = g_strdup_printf ("HALD_SESSION_ACTIVE_CHANGED_SESSION_IS_ACTIVE=%s",
+ ck_session_is_active (session) ? "true" : "false");
+ hal_callout_device (d,
+ NULL /* callback */,
+ NULL /* userdata1 */,
+ NULL /* userdata2 */,
+ programs,
+ extra_env);
+ g_free (extra_env[1]);
+ g_free (extra_env[2]);
+ g_free (extra_env[3]);
+out:
+ ;
}
gboolean
diff --git a/tools/Makefile.am b/tools/Makefile.am
index 96844ce..b5332e3 100644
--- a/tools/Makefile.am
+++ b/tools/Makefile.am
@@ -63,10 +63,10 @@ libexec_PROGRAMS += \
endif
if HAVE_ACLMGMT
-libexec_PROGRAMS += hal-acl-add hal-acl-remove
+libexec_PROGRAMS += hal-acl-tool
-hal_acl_add_SOURCES = hal-acl-add.c
-hal_acl_remove_SOURCES = hal-acl-remove.c
+hal_acl_tool_SOURCES = hal-acl-tool.c
+hal_acl_tool_LDADD = @GLIB_LIBS@ $(top_builddir)/libhal/libhal.la
endif
diff --git a/tools/hal-acl-add.c b/tools/hal-acl-add.c
deleted file mode 100644
index 4d358d5..0000000
--- a/tools/hal-acl-add.c
+++ /dev/null
@@ -1,97 +0,0 @@
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <sys/file.h>
-#include <sys/types.h>
-#include <unistd.h>
-#include <errno.h>
-
-#include <glib.h>
-
-#if 0
-static int lock_acl_fd = -1;
-
-static gboolean
-lock_hal_acl (void)
-{
- if (lock_acl_fd >= 0)
- return TRUE;
-
- printf ("%d: attempting to get lock on /var/lib/hal/acl-list\n", getpid ());
-
- lock_acl_fd = open ("/var/lib/hal/acl-list", O_CREAT | O_RDWR);
-
- if (lock_acl_fd < 0)
- return FALSE;
-
-tryagain:
-#if sun
- if (lockf (lock_acl_fd, F_LOCK, 0) != 0) {
-#else
- if (flock (lock_acl_fd, LOCK_EX) != 0) {
-#endif
- if (errno == EINTR)
- goto tryagain;
- return FALSE;
- }
-
- printf ("%d: got lock on /var/lib/hal/acl-list\n", getpid ());
-
-
- return TRUE;
-}
-
-static void
-unlock_hal_acl (void)
-{
-#if sun
- lockf (lock_acl_fd, F_ULOCK, 0);
-#else
- flock (lock_acl_fd, LOCK_UN);
-#endif
- close (lock_acl_fd);
- lock_acl_fd = -1;
- printf ("%d: released lock on /var/lib/hal/acl-list\n", getpid ());
-}
-
-/* Each entry here represents a line in the acl-list file
- *
- * <device-file>\t<hal-udi>\t<uid-as-number>\t<gid-as-number>\t<session-id>
- *
- * example:
- *
- * /dev/snd/controlC0\t/org/freedesktop/Hal/devices/pci_8086_27d8_alsa_control__1\t500\t\t/org/freedesktop/ConsoleKit/Session0
- *
- * This means that the
- */
-typedef struct HalACL_s {
- const char *device;
- const char *uid;
- uid_t uid; /* 0 if unset */
- gid_t gid; /* 0 if unset */
- const char *session; /* NULL if unset */
-} HalACL;
-
-/* hal-acl-grant can run in two modes of operation;
- *
- * 1) as a hal callout via info.callouts.add - this will set ACL's on the device file pointed to by acl.file
- * using the information provided by the other acl.* properties to determine what ACL's to add to the given
- * device file
- *
- * 2)
- *
- */
-#endif
-
-int
-main (int argc, char *argv[])
-{
- int i;
-
- fprintf (stderr, "hal-acl-add %d\n", argc);
- for (i = 0; i < argc; i++) {
- fprintf (stderr, " arg %2d: %s\n", i, argv[i]);
- }
- system ("env |sort");
- return 0;
-}
diff --git a/tools/hal-acl-remove.c b/tools/hal-acl-remove.c
deleted file mode 100644
index 83f1c7c..0000000
--- a/tools/hal-acl-remove.c
+++ /dev/null
@@ -1,16 +0,0 @@
-
-#include <stdio.h>
-#include <stdlib.h>
-
-int
-main (int argc, char *argv[])
-{
- int i;
-
- fprintf (stderr, "hal-acl-remove %d\n", argc);
- for (i = 0; i < argc; i++) {
- fprintf (stderr, " arg %2d: %s\n", i, argv[i]);
- }
- system ("env |sort");
- return 0;
-}
diff --git a/tools/hal-acl-tool.c b/tools/hal-acl-tool.c
new file mode 100644
index 0000000..d49d097
--- /dev/null
+++ b/tools/hal-acl-tool.c
@@ -0,0 +1,1051 @@
+/***************************************************************************
+ *
+ * hal-acl-tool.c : Manage ACL's on device nodes
+ *
+ * Copyright (C) 2007 David Zeuthen, <david at fubar.dk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ **************************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/file.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <pwd.h>
+#include <grp.h>
+
+#include <glib.h>
+#include <libhal.h>
+
+/* How this works (or "An introduction to this code")
+ *
+ * - all ACL's granted by this tool is kept in /var/lib/hal/acl-list
+ *
+ * - every time tool is launched we read this file and keep each line
+ * as an ACLCurrent instance. These are kept in a list.
+ *
+ * - we do locking so only one instance of this tool is doing work
+ * at one time. This is essential as we maintain ACL's in a file.
+ *
+ * - there's an overarching --reconfigure method that basically
+ * - finds all devices of capability 'access_control'
+ * - computes what ACL's these devices should have
+ * - modifies the in-memory acl-current-list
+ * - ACL's to be removed are tagged with setting remove -> TRUE
+ * - ACL's to be added are appended to the list and add -> TRUE
+ * - we then compute the argument vector to setfacl(1) for adding /
+ * removing ACL's
+ * - if setfacl(1) succeeds (rc == 0) then we write the new acl-current-list
+ *
+ * Notably, the HAL daemon will invoke us with --reconfigure on every
+ * - session add
+ * - session remove
+ * - session inactive
+ * - session active
+ *
+ * event. Also, when devices are added we're invoked with --add-device
+ * respectively --remove-device. When the HAL daemon starts we're invoked
+ * with --remove-all.
+ *
+ * Optimizations
+ *
+ * - the HAL daemon exports the ConsoleKit seat + session
+ * configuration in CK_* environment variables. So we don't
+ * need to do IPC to the ConsoleKit daemon to learn about
+ * about seats and sessions.
+ *
+ * - special casing for --add-device and --remove-device; here we
+ * don't need roundtrips to the HAL daemon. Only --reconfigure
+ * requires that. As such no IPC is required for these cases
+ * which is fortunate as --add-device is invoked quite a lot
+ * on startup
+ *
+ */
+
+/* Each entry here represents a line in the /var/lib/hal/acl-list file
+ * of ACL's that have been set by HAL and as such are currently
+ * applied
+ *
+ * <device-file> <type> <uid-or-gid>
+ *
+ * where <type>='u'|'g' for respectively uid and gid (the spacing represent tabs).
+ *
+ * Example:
+ *
+ * /dev/snd/controlC0 u 500
+ * /dev/snd/controlC0 u 501
+ * /dev/snd/controlC0 g 1001
+ */
+typedef struct ACLCurrent_s {
+ char *device;
+ int type;
+ union {
+ uid_t uid;
+ gid_t gid;
+ } v;
+
+ gboolean remove;
+ gboolean add;
+} ACLCurrent;
+
+/* used for type member in ACLCurrent */
+enum {
+ HAL_ACL_UID,
+ HAL_ACL_GID
+};
+
+static void
+hal_acl_free (ACLCurrent *ha)
+{
+ g_free (ha->device);
+ g_free (ha);
+}
+
+static int
+ha_sort (ACLCurrent *a, ACLCurrent *b)
+{
+ return strcmp (a->device, b->device);
+}
+
+static gboolean
+acl_apply_changes (GSList *new_acl_list, gboolean only_update_acllist, gboolean missing_files_ok)
+{
+ GString *str;
+ GString *setfacl_cmdline;
+ char *new_acl_file_contents = NULL;
+ char *setfacl_cmdline_str = NULL;
+ GSList *i;
+ gboolean ret;
+ GError *error = NULL;
+ int exit_status;
+
+ ret = FALSE;
+
+ new_acl_list = g_slist_sort (new_acl_list, (GCompareFunc) ha_sort);
+
+ /* first compute the contents of the new acl-file
+ * and build up the command line for setfacl(1)
+ */
+ str = g_string_new ("");
+ setfacl_cmdline = g_string_new ("");
+ for (i = new_acl_list; i != NULL; i = g_slist_next (i)) {
+ ACLCurrent *ha = (ACLCurrent *) i->data;
+
+ if (ha->remove) {
+ if (setfacl_cmdline->len > 0) {
+ g_string_append_c (setfacl_cmdline, ' ');
+ }
+ g_string_append_printf (setfacl_cmdline, "-x %c:%d %s",
+ (ha->type == HAL_ACL_UID) ? 'u' : 'g',
+ (ha->type == HAL_ACL_UID) ? ha->v.uid : ha->v.gid,
+ ha->device);
+ continue;
+ }
+
+ if (ha->add) {
+ if (setfacl_cmdline->len > 0) {
+ g_string_append_c (setfacl_cmdline, ' ');
+ }
+ g_string_append_printf (setfacl_cmdline, "-m %c:%d:rw %s",
+ (ha->type == HAL_ACL_UID) ? 'u' : 'g',
+ (ha->type == HAL_ACL_UID) ? ha->v.uid : ha->v.gid,
+ ha->device);
+ }
+
+ g_string_append_printf (str,
+ "%s\t%c\t%d\n",
+ ha->device,
+ (ha->type == HAL_ACL_UID) ? 'u' : 'g',
+ (ha->type == HAL_ACL_UID) ? ha->v.uid : ha->v.gid);
+ }
+ new_acl_file_contents = g_string_free (str, FALSE);
+
+ /* TODO FIXME NOTE XXX WARNING:
+ *
+ * The variable 'only_update_acllist' is set to TRUE only on
+ * device_remove events. It effectively means "only update the
+ * /var/lib/hal/acl-list, do not apply ACL's on disk". So this
+ * is done for systems where /dev is dynamic and we know for
+ * sure that the device file is gone.
+ *
+ * So if you're running a static /dev you need to comment the
+ * only_update_acllist conditional out below so the ACL's are
+ * removed accordingly. As this is uncommon and against all
+ * recommendations for a system using HAL (we depend on recent
+ * udev versions!) we don't bother the syscall overhead of
+ * stat(2)'ing every file.
+ */
+
+ if (setfacl_cmdline->len == 0 || only_update_acllist) {
+ g_string_free (setfacl_cmdline, TRUE);
+ setfacl_cmdline_str = NULL;
+ } else {
+ g_string_prepend (setfacl_cmdline, "setfacl ");
+ setfacl_cmdline_str = g_string_free (setfacl_cmdline, FALSE);
+
+ printf ("%d: invoking '%s'\n", getpid (), setfacl_cmdline_str);
+ if (!g_spawn_command_line_sync (setfacl_cmdline_str, NULL, NULL, &exit_status, &error)) {
+ printf ("%d: Error - couldn't invoke setfacl(1): %s\n", getpid (), error->message);
+ g_error_free (error);
+ goto out;
+ }
+
+ if (exit_status != 0) {
+ if (!missing_files_ok) {
+ printf ("%d: setfacl(1) exit code != 0 but OK as some missing files are expected\n",
+ getpid ());
+ } else {
+ printf ("%d: Error - setfacl(1) failed\n", getpid ());
+ goto out;
+ }
+ }
+ }
+
+ /* success; now atomically set the new list */
+ g_file_set_contents ("/var/lib/hal/acl-list",
+ new_acl_file_contents,
+ strlen (new_acl_file_contents),
+ NULL);
+
+ ret = TRUE;
+
+out:
+ if (new_acl_file_contents != NULL)
+ g_free (new_acl_file_contents);
+
+ if (setfacl_cmdline_str != NULL)
+ g_free (setfacl_cmdline_str);
+
+ return ret;
+}
+
+
+static gboolean
+get_current_acl_list (GSList **l)
+{
+ FILE *f;
+ gboolean ret;
+ char buf[1024];
+
+ *l = NULL;
+ f = NULL;
+ ret = FALSE;
+
+ f = fopen ("/var/lib/hal/acl-list", "r");
+ if (f == NULL) {
+ printf ("%d: cannot open /var/lib/hal/acl-list\n", getpid ());
+ goto out;
+ }
+
+ while (fgets (buf, sizeof(buf), f) != NULL) {
+ ACLCurrent *ha;
+ char **val;
+ char *endptr;
+
+ ha = g_new0 (ACLCurrent, 1);
+ val = g_strsplit(buf, "\t", 0);
+ if (g_strv_length (val) != 3) {
+ printf ("Line is malformed: '%s'\n", buf);
+ g_strfreev (val);
+ goto out;
+ }
+
+ ha->device = g_strdup (val[0]);
+ if (strcmp (val[1], "u") == 0) {
+ ha->type = HAL_ACL_UID;
+ ha->v.uid = strtol (val[2], &endptr, 10);
+ if (*endptr != '\0' && *endptr != '\n') {
+ printf ("Line is malformed: '%s'\n", buf);
+ g_strfreev (val);
+ goto out;
+ }
+ } else if (strcmp (val[1], "g") == 0) {
+ ha->type = HAL_ACL_GID;
+ ha->v.gid = strtol (val[2], &endptr, 10);
+ if (*endptr != '\0' && *endptr != '\n') {
+ printf ("Line is malformed: '%s'\n", buf);
+ g_strfreev (val);
+ goto out;
+ }
+ } else {
+ printf ("Line is malformed: '%s'\n", buf);
+ g_strfreev (val);
+ goto out;
+ }
+ g_strfreev (val);
+
+ /*printf (" ACL '%s' %d %d\n", ha->device, ha->type, ha->v.uid);*/
+
+ *l = g_slist_prepend (*l, ha);
+ }
+
+ ret = TRUE;
+
+out:
+ if (f != NULL)
+ fclose (f);
+ return ret;
+}
+
+typedef void (*SeatSessionVisitor) (const char *seat_id,
+ gboolean seat_is_local,
+ int num_sessions_on_seat,
+ const char *session_id, /* may be NULL */
+ uid_t session_uid,
+ gboolean session_is_active,
+ gpointer user_data);
+
+/* Visits all seats and sessions.
+ *
+ * NOTE: when a seat is visited session_id will be NULL and session_uid, session_is_active are undefined.
+ */
+static gboolean
+visit_seats_and_sessions (SeatSessionVisitor visitor_cb, gpointer user_data)
+{
+ int i;
+ int j;
+ char *s;
+ char *p;
+ char **seats;
+ gboolean ret;
+
+ ret = FALSE;
+
+ if ((s = getenv ("CK_SEATS")) == NULL) {
+ printf ("%d: CK_SEATS is not set!\n", getpid());
+ goto out;
+ }
+ seats = g_strsplit (s, "\t", 0);
+ /* for all seats */
+ for (i = 0; seats[i] != NULL; i++) {
+ char *seat = seats[i];
+ char **sessions;
+ int num_sessions_on_seat;
+ gboolean seat_is_local;
+
+ p = g_strdup_printf ("CK_SEAT_IS_LOCAL_%s", seat);
+ if ((s = getenv (p)) == NULL) {
+ printf ("%d: CK_SEAT_IS_LOCAL_%s is not set!\n", getpid(), seat);
+ g_free (p);
+ goto out;
+ }
+ g_free (p);
+ seat_is_local = (strcmp (s, "true") == 0);
+
+ p = g_strdup_printf ("CK_SEAT_%s", seat);
+ if ((s = getenv (p)) == NULL) {
+ printf ("%d: CK_SEAT_%s is not set!\n", getpid(), seat);
+ g_free (p);
+ goto out;
+ }
+ g_free (p);
+ sessions = g_strsplit (s, "\t", 0);
+ num_sessions_on_seat = g_strv_length (sessions);
+
+ visitor_cb (seat, seat_is_local, num_sessions_on_seat, NULL, 0, FALSE, user_data);
+
+ /* for all sessions on seat */
+ for (j = 0; sessions[j] != NULL; j++) {
+ char *session = sessions[j];
+ gboolean session_is_active;
+ uid_t session_uid;
+ char *endptr;
+
+ p = g_strdup_printf ("CK_SESSION_IS_ACTIVE_%s", session);
+ if ((s = getenv (p)) == NULL) {
+ printf ("%d: CK_SESSION_IS_ACTIVE_%s is not set!\n", getpid(), session);
+ g_free (p);
+ goto out;
+ }
+ g_free (p);
+ session_is_active = (strcmp (s, "true") == 0);
+
+ p = g_strdup_printf ("CK_SESSION_UID_%s", session);
+ if ((s = getenv (p)) == NULL) {
+ printf ("%d: CK_SESSION_UID_%s is not set!\n", getpid(), session);
+ g_free (p);
+ goto out;
+ }
+ g_free (p);
+ session_uid = strtol (s, &endptr, 10);
+ if (*endptr != '\0') {
+ printf ("%d: CK_SESSION_UID_%s set to '%s' is malformed!\n", getpid(), session, s);
+ goto out;
+ }
+
+ visitor_cb (seat, seat_is_local, num_sessions_on_seat,
+ session, session_uid, session_is_active, user_data);
+
+ }
+ g_strfreev (sessions);
+ }
+ g_strfreev (seats);
+
+ ret = TRUE;
+
+out:
+ return ret;
+}
+
+/* this data structure is for collecting what ACL's a device should have */
+typedef struct {
+ /* identifying the device */
+ char *udi; /* HAL UDI of device */
+ char *device; /* device file */
+
+ /* policy for how to apply ACL's (must be set by the caller prior to visiting the device) */
+
+ /* access is granted to any session on a local seat */
+ gboolean grant_to_local_seat;
+
+ /* access is granted only to active sessions on local seats */
+ gboolean grant_to_local_seat_active_only;
+
+ /* will be set by the visitor */
+ GSList *uid; /* list of uid's (int) that should have access to this device */
+ GSList *gid; /* list of gid's (int) that should have access to this device */
+} ACLForDevice;
+
+static void
+afd_grant_to_uid (ACLForDevice *afd, uid_t uid)
+{
+ GSList *i;
+
+ for (i = afd->uid; i != NULL; i = g_slist_next (i)) {
+ uid_t iuid = GPOINTER_TO_INT (i->data);
+ if (uid == iuid)
+ goto out;
+ }
+ afd->uid = g_slist_prepend (afd->uid, GINT_TO_POINTER (uid));
+out:
+ ;
+}
+
+static void
+afd_grant_to_gid (ACLForDevice *afd, gid_t gid)
+{
+ GSList *i;
+
+ for (i = afd->gid; i != NULL; i = g_slist_next (i)) {
+ gid_t igid = GPOINTER_TO_INT (i->data);
+ if (gid == igid)
+ goto out;
+ }
+ afd->gid = g_slist_prepend (afd->gid, GINT_TO_POINTER (gid));
+out:
+ ;
+}
+
+static uid_t
+util_name_to_uid (const char *username, gid_t *default_gid)
+{
+ int rc;
+ uid_t res;
+ char *buf = NULL;
+ unsigned int bufsize;
+ struct passwd pwd;
+ struct passwd *pwdp;
+
+ res = (uid_t) -1;
+
+ bufsize = sysconf (_SC_GETPW_R_SIZE_MAX);
+ buf = g_new0 (char, bufsize);
+
+ rc = getpwnam_r (username, &pwd, buf, bufsize, &pwdp);
+ if (rc != 0 || pwdp == NULL) {
+ /*g_warning ("getpwnam_r() returned %d", rc);*/
+ goto out;
+ }
+
+ res = pwdp->pw_uid;
+ if (default_gid != NULL)
+ *default_gid = pwdp->pw_gid;
+
+out:
+ g_free (buf);
+ return res;
+}
+
+static gid_t
+util_name_to_gid (const char *groupname)
+{
+ int rc;
+ gid_t res;
+ char *buf = NULL;
+ unsigned int bufsize;
+ struct group gbuf;
+ struct group *gbufp;
+
+ res = (gid_t) -1;
+
+ bufsize = sysconf (_SC_GETGR_R_SIZE_MAX);
+ buf = g_new0 (char, bufsize);
+
+ rc = getgrnam_r (groupname, &gbuf, buf, bufsize, &gbufp);
+ if (rc != 0 || gbufp == NULL) {
+ /*g_warning ("getgrnam_r() returned %d", rc);*/
+ goto out;
+ }
+
+ res = gbufp->gr_gid;
+
+out:
+ g_free (buf);
+ return res;
+}
+
+static void
+afd_grant_to_uid_from_userlist (ACLForDevice *afd, char **sv)
+{
+ int i;
+ uid_t uid;
+ char *endptr;
+
+ for (i = 0; sv[i] != NULL; i++) {
+ uid = strtol (sv[i], &endptr, 10);
+ if (*endptr != '\0') {
+ uid = util_name_to_uid (sv[i], NULL);
+ if ((int) uid == -1) {
+ printf ("%d: warning; username '%s' is unknown\n", getpid (), sv[i]);
+ continue;
+ }
+ }
+ afd_grant_to_uid (afd, uid);
+ }
+}
+
+static void
+afd_grant_to_gid_from_grouplist (ACLForDevice *afd, char **sv)
+{
+ int i;
+ gid_t gid;
+ char *endptr;
+
+ for (i = 0; sv[i] != NULL; i++) {
+ gid = strtol (sv[i], &endptr, 10);
+ if (*endptr != '\0') {
+ gid = util_name_to_gid (sv[i]);
+ if ((int) gid == -1) {
+ printf ("%d: warning; group '%s' is unknown\n", getpid (), sv[i]);
+ continue;
+ }
+ }
+ afd_grant_to_gid (afd, gid);
+ }
+}
+
+static ACLForDevice *
+acl_for_device_new (const char *udi)
+{
+ ACLForDevice *afd;
+
+ afd = g_new0 (ACLForDevice, 1);
+ afd->udi = g_strdup (udi);
+
+ return afd;
+}
+
+static void
+acl_for_device_set_device (ACLForDevice *afd, const char *device)
+{
+ afd->device = g_strdup (device);
+}
+
+static void
+acl_for_device_free (ACLForDevice* afd)
+{
+ g_free (afd->udi);
+ g_free (afd->device);
+ g_slist_free (afd->uid);
+ g_slist_free (afd->gid);
+ g_free (afd);
+}
+
+static void
+acl_device_added_visitor (const char *seat_id,
+ gboolean seat_is_local,
+ int num_sessions_on_seat,
+ const char *session_id,
+ uid_t session_uid,
+ gboolean session_is_active,
+ gpointer user_data)
+{
+ GSList *i;
+ GSList *afd_list = (GSList *) user_data;
+
+#if 0
+ if (session_id == NULL) {
+ /* means we're just visiting the seat; each session on the seat will be visited accordingly */
+ printf ("Visiting seat '%s' (is_local=%d) with %d sessions\n",
+ seat_id, seat_is_local, num_sessions_on_seat);
+ } else {
+ printf (" %s: Visiting session '%s' with uid %d (is_active=%d)\n",
+ seat_id, session_id, session_uid, session_is_active);
+ }
+#endif
+
+ /* for each entry ACLForDevice in afd_list, add to the uid and
+ * gid lists for users and groups that should have access to
+ * the device in question
+ */
+ for (i = afd_list; i != NULL; i = g_slist_next (i)) {
+ ACLForDevice *afd = (ACLForDevice *) i->data;
+
+ if (session_id == NULL) {
+ /* we only grant access to sessions - someone suggested that if a device is tied
+ * to a seat.. the owner might want to give access to user 'dilbert' if, and only
+ * if, no sessions is occuring at that seat. Cuz then the user 'dilbert' could
+ * have a cron job running that takes a webcam shot every 5 minutes or so..
+ *
+ * this can be achieved by testing num_sessions_on_seat==0
+ */
+ continue;
+ }
+
+
+ /* apply the policy defined by grant_to_local_seat and grant_to_local_seat_active_only */
+
+ /* we only grant access to local seats... */
+ if (!seat_is_local)
+ continue;
+
+ if (afd->grant_to_local_seat)
+ afd_grant_to_uid (afd, session_uid);
+ else {
+ if (afd->grant_to_local_seat_active_only) {
+ if (session_is_active) {
+ afd_grant_to_uid (afd, session_uid);
+ }
+ }
+ }
+ }
+
+}
+
+static void
+acl_compute_changes (GSList *afd_list, gboolean only_update_acllist)
+{
+ GSList *current_acl_list = NULL;
+ GSList *i;
+ GSList *j;
+ GSList *k;
+
+ /* get the list of ACL's currently applied */
+ if (!get_current_acl_list (¤t_acl_list)) {
+ printf ("Error getting ACL's currently applied\n");
+ goto out;
+ }
+
+ /* for each entry in ACLForDevice, we need to modify current_acl_list
+ * such that it matches the entry. This is achieved by
+ *
+ * - setting the 'remove' boolean to TRUE for existing
+ * entries that should be removed
+ *
+ * - adding a new entry with the 'add' boolean set to TRUE,
+ * in the front, for entries we need to have added
+ */
+ for (i = afd_list; i != NULL; i = g_slist_next (i)) {
+ ACLForDevice *afd = (ACLForDevice *) i->data;
+
+ /* OK, so we have the ACL's we want for a device - this is expressed in afd
+ *
+ * go through all the ACL's that we've *already*
+ * added..
+ *
+ * if an ACL we want is already there
+ * we simply remove it from afd (the ACL's we want).
+ *
+ * If we've already got an ACL that we don't want
+ * tag the current list with this for removal.
+ *
+ * At the end the afd will express the ACL's that we
+ * need to add.. so make entries in the current_list
+ * with 'add' set to TRUE.
+ */
+ for (j = current_acl_list; j != NULL; j = g_slist_next (j)) {
+ ACLCurrent *ha = (ACLCurrent *) j->data;
+
+ if (strcmp (afd->device, ha->device) == 0) {
+ switch (ha->type) {
+ case HAL_ACL_UID:
+ /* see if this is already in the ACLForDevice entry */
+ for (k = afd->uid; k != NULL; k = g_slist_next (k)) {
+ uid_t uid = GPOINTER_TO_INT (k->data);
+ if (uid == ha->v.uid) {
+ /* yup, so we're all good - remove it from the afd_list
+ * since we don't need it to be added later...
+ */
+ afd->uid = g_slist_delete_link (afd->uid, k);
+ break;
+ }
+ }
+ if (k == NULL) {
+ /* nope, element wasn't there so this ACLCurrent should be removed */
+ ha->remove = TRUE;
+ }
+ break;
+ case HAL_ACL_GID:
+ /* see if this is already in the ACLForDevice entry */
+ for (k = afd->gid; k != NULL; k = g_slist_next (k)) {
+ gid_t gid = GPOINTER_TO_INT (k->data);
+ if (gid == ha->v.gid) {
+ /* yup, so we're all good - remove it from the afd_list
+ * since we don't need it to be added later...
+ */
+ afd->gid = g_slist_delete_link (afd->gid, k);
+ break;
+ }
+ }
+ if (k == NULL) {
+ /* nope, element wasn't there so this ACLCurrent should be removed */
+ ha->remove = TRUE;
+ }
+ break;
+ }
+ }
+
+ }
+
+ /* now go through remaining entries in afd->uid and afd->gid
+ * and create ACLCurrent entries
+ */
+ for (j = afd->uid; j != NULL; j = g_slist_next (j)) {
+ ACLCurrent *ha;
+ ha = g_new0 (ACLCurrent, 1);
+ ha->add = TRUE;
+ ha->device = g_strdup (afd->device);
+ ha->type = HAL_ACL_UID;
+ ha->v.uid = GPOINTER_TO_INT (j->data);
+ current_acl_list = g_slist_prepend (current_acl_list, ha);
+ }
+ for (j = afd->gid; j != NULL; j = g_slist_next (j)) {
+ ACLCurrent *ha;
+ ha = g_new0 (ACLCurrent, 1);
+ ha->add = TRUE;
+ ha->device = g_strdup (afd->device);
+ ha->type = HAL_ACL_GID;
+ ha->v.gid = GPOINTER_TO_INT (j->data);
+ current_acl_list = g_slist_prepend (current_acl_list, ha);
+ }
+ }
+
+#if 0
+ printf ("====================================\n");
+ for (i = current_acl_list; i != NULL; i = g_slist_next (i)) {
+ ACLCurrent *ha = (ACLCurrent *) i->data;
+
+ printf (" ACL '%s' %d %d rem=%d add=%d\n", ha->device, ha->type, ha->v.uid, ha->remove, ha->add);
+
+ }
+ printf ("====================================\n");
+#endif
+
+ acl_apply_changes (current_acl_list, only_update_acllist, FALSE);
+
+out:
+ if (current_acl_list != NULL) {
+ g_slist_foreach (current_acl_list, (GFunc) hal_acl_free, NULL);
+ g_slist_free (current_acl_list);
+ }
+}
+
+
+static void
+acl_device_added (void)
+{
+ char *s;
+ char *udi;
+ char *device;
+ GSList *afd_list = NULL;
+ ACLForDevice *afd = NULL;
+
+ /* we can avoid round-trips to the HAL daemon by using what's in the environment */
+
+ if ((udi = getenv ("UDI")) == NULL)
+ goto out;
+
+ if ((device = getenv ("HAL_PROP_ACCESS_CONTROL_FILE")) == NULL)
+ goto out;
+
+ afd = acl_for_device_new (udi);
+ acl_for_device_set_device (afd, device);
+ afd_list = g_slist_prepend (NULL, afd);
+
+ /* get ACL granting policy from HAL properties */
+ if ((s = getenv ("HAL_PROP_ACCESS_CONTROL_GRANT_LOCAL_SESSION")) != NULL) {
+ afd->grant_to_local_seat = (strcmp (s, "true") == 0);
+ }
+ if ((s = getenv ("HAL_PROP_ACCESS_CONTROL_GRANT_LOCAL_ACTIVE_SESSION")) != NULL) {
+ afd->grant_to_local_seat_active_only = (strcmp (s, "true") == 0);
+ }
+ if ((s = getenv ("HAL_PROP_ACCESS_CONTROL_GRANT_USER")) != NULL) {
+ char **sv;
+ sv = g_strsplit (s, "\t", 0);
+ afd_grant_to_uid_from_userlist (afd, sv);
+ g_strfreev (sv);
+ }
+ if ((s = getenv ("HAL_PROP_ACCESS_CONTROL_GRANT_GROUP")) != NULL) {
+ char **sv;
+ sv = g_strsplit (s, "\t", 0);
+ afd_grant_to_gid_from_grouplist (afd, sv);
+ g_strfreev (sv);
+ }
+
+ /* determine what ACL's we want to put on the given device
+ * files; e.g. apply the seat / session policy
+ *
+ * (entries in afd_list will be modified, see ACLForDevice
+ * data structure)
+ */
+ if (!visit_seats_and_sessions (acl_device_added_visitor, (gpointer) afd_list)) {
+ printf ("Error visiting seats and sessions\n");
+ goto out;
+ }
+
+ printf ("%d: adding ACL's for %s\n", getpid (), device);
+
+ acl_compute_changes (afd_list, FALSE);
+
+out:
+ if (afd != NULL)
+ acl_for_device_free (afd);
+ if (afd_list != NULL)
+ g_slist_free (afd_list);
+}
+
+static void
+acl_device_removed (void)
+{
+ char *udi;
+ char *device;
+ GSList *afd_list = NULL;
+ ACLForDevice *afd = NULL;
+
+ /* we can avoid round-trips to the HAL daemon by using what's in the environment */
+
+ if ((udi = getenv ("UDI")) == NULL)
+ goto out;
+
+ if ((device = getenv ("HAL_PROP_ACCESS_CONTROL_FILE")) == NULL)
+ goto out;
+
+ afd = acl_for_device_new (udi);
+ acl_for_device_set_device (afd, device);
+ afd_list = g_slist_prepend (NULL, afd);
+
+ /* since this device is to be removed don't set policy - this means "grant it to no-one"; and since
+ * there is no-one to grant it to.. we don't need to visit any seats
+ */
+ printf ("%d: removing ACL's for %s\n", getpid (), device);
+
+ /* only update the ACL list, don't invoke setfacl(1) on the
+ * files (see note in acl_apply_changes())
+ */
+ acl_compute_changes (afd_list, TRUE);
+
+out:
+ if (afd != NULL)
+ acl_for_device_free (afd);
+ if (afd_list != NULL)
+ g_slist_free (afd_list);
+}
+
+static void
+acl_reconfigure_all (void)
+{
+ int i;
+ int num_devices;
+ char **udis;
+ DBusError error;
+ LibHalContext *hal_ctx;
+ GSList *afd_list = NULL;
+
+ printf ("%d: reconfiguring all ACL's\n", getpid ());
+
+ dbus_error_init (&error);
+ if ((hal_ctx = libhal_ctx_init_direct (&error)) == NULL) {
+ printf ("%d: Cannot connect to hald: %s: %s\n", getpid (), error.name, error.message);
+ LIBHAL_FREE_DBUS_ERROR (&error);
+ goto out;
+ }
+
+ if ((udis = libhal_find_device_by_capability (hal_ctx, "access_control", &num_devices, &error)) == NULL) {
+ printf ("%d: Cannot get list of devices of capability 'acl'\n", getpid ());
+ goto out;
+ }
+
+ for (i = 0; udis[i] != NULL; i++) {
+ LibHalPropertySet *props;
+ LibHalPropertySetIterator psi;
+ char *device = NULL;
+ ACLForDevice *afd;
+ char **sv;
+
+ if ((props = libhal_device_get_all_properties (hal_ctx, udis[i], &error)) == NULL) {
+ printf ("%d: Cannot get list properties for '%s'\n", getpid (), udis[1]);
+ goto out;
+ }
+
+ afd = acl_for_device_new (udis[i]);
+
+ libhal_psi_init (&psi, props);
+ while (libhal_psi_has_more (&psi)) {
+ char *key;
+ key = libhal_psi_get_key (&psi);
+ if (strcmp (key, "access_control.file") == 0) {
+ device = libhal_psi_get_string (&psi);
+ } else if (strcmp (key, "access_control.grant_local_session") == 0) {
+ afd->grant_to_local_seat = libhal_psi_get_bool (&psi);
+ } else if (strcmp (key, "access_control.grant_local_active_session") == 0) {
+ afd->grant_to_local_seat_active_only = libhal_psi_get_bool (&psi);
+ } else if (strcmp (key, "access_control.grant_user") == 0) {
+ sv = libhal_psi_get_strlist (&psi);
+ afd_grant_to_uid_from_userlist (afd, sv);
+ } else if (strcmp (key, "access_control.grant_group") == 0) {
+ sv = libhal_psi_get_strlist (&psi);
+ afd_grant_to_gid_from_grouplist (afd, sv);
+ }
+ libhal_psi_next (&psi);
+ }
+
+ if (device == NULL) {
+ printf ("%d: access_control.file not set for '%s'\n", getpid (), udis[i]);
+ goto out;
+ } else {
+ acl_for_device_set_device (afd, device);
+ afd_list = g_slist_prepend (afd_list, afd);
+ }
+
+ libhal_free_property_set (props);
+ }
+ libhal_free_string_array (udis);
+
+ if (g_slist_length (afd_list) > 0) {
+ if (!visit_seats_and_sessions (acl_device_added_visitor, (gpointer) afd_list)) {
+ printf ("Error visiting seats and sessions\n");
+ goto out;
+ }
+ acl_compute_changes (afd_list, FALSE);
+ }
+
+out:
+ ;
+}
+
+static void
+acl_remove_all (void)
+{
+ GSList *current_acl_list = NULL;
+ GSList *i;
+
+ if (!get_current_acl_list (¤t_acl_list)) {
+ printf ("Error getting ACL's currently applied\n");
+ goto out;
+ }
+
+ for (i = current_acl_list; i != NULL; i = g_slist_next (i)) {
+ ACLCurrent *ha = (ACLCurrent *) i->data;
+ ha->remove = TRUE;
+ }
+
+ acl_apply_changes (current_acl_list, FALSE, FALSE);
+
+out:
+ if (current_acl_list != NULL) {
+ g_slist_foreach (current_acl_list, (GFunc) hal_acl_free, NULL);
+ g_slist_free (current_acl_list);
+ }
+}
+
+static int lock_acl_fd = -1;
+
+static gboolean
+acl_lock (void)
+{
+ if (lock_acl_fd >= 0)
+ return TRUE;
+
+ printf ("%d: attempting to get lock on /var/lib/hal/acl-list\n", getpid ());
+
+ lock_acl_fd = open ("/var/lib/hal/acl-list", O_CREAT | O_RDWR);
+ /* TODO: set correct mode, owner etc. */
+
+ if (lock_acl_fd < 0) {
+ printf ("%d: error opening/creating /var/lib/hal/acl-list\n", getpid ());
+ return FALSE;
+ }
+
+tryagain:
+#if sun
+ if (lockf (lock_acl_fd, F_LOCK, 0) != 0)
+#else
+ if (flock (lock_acl_fd, LOCK_EX) != 0)
+#endif
+ {
+ if (errno == EINTR)
+ goto tryagain;
+ return FALSE;
+ }
+
+ printf ("%d: got lock on /var/lib/hal/acl-list\n", getpid ());
+ return TRUE;
+}
+
+static void
+acl_unlock (void)
+{
+#if sun
+ lockf (lock_acl_fd, F_ULOCK, 0);
+#else
+ flock (lock_acl_fd, LOCK_UN);
+#endif
+ close (lock_acl_fd);
+ lock_acl_fd = -1;
+ printf ("%d: released lock on /var/lib/hal/acl-list\n", getpid ());
+}
+
+
+int
+main (int argc, char *argv[])
+{
+ if (argc != 2) {
+ printf ("hal-acl-tool should only be invoked by hald\n");
+ goto out;
+ }
+
+ if (!acl_lock ()) {
+ goto out;
+ }
+
+ if (strcmp (argv[1], "--add-device") == 0) {
+ acl_device_added ();
+ } else if (strcmp (argv[1], "--remove-device") == 0) {
+ acl_device_removed ();
+ } else if (strcmp (argv[1], "--reconfigure") == 0) {
+ acl_reconfigure_all ();
+ } else if (strcmp (argv[1], "--remove-all") == 0) {
+ acl_remove_all ();
+ }
+
+ acl_unlock ();
+
+out:
+ return 0;
+}
diff --git a/tools/hal-storage-shared.c b/tools/hal-storage-shared.c
index 19e1819..11ec3d1 100644
--- a/tools/hal-storage-shared.c
+++ b/tools/hal-storage-shared.c
@@ -573,6 +573,7 @@ lock_hal_mtab (void)
printf ("%d: XYA attempting to get lock on /media/.hal-mtab-lock\n", getpid ());
lock_mtab_fd = open ("/media/.hal-mtab-lock", O_CREAT | O_RDWR);
+ /* TODO: set correct mode, owner etc. */
if (lock_mtab_fd < 0)
return FALSE;
More information about the hal-commit
mailing list