chiark / gitweb /
udev-acl: move from udev-extras
authorKay Sievers <kay.sievers@vrfy.org>
Tue, 16 Jun 2009 15:43:12 +0000 (17:43 +0200)
committerKay Sievers <kay.sievers@vrfy.org>
Tue, 16 Jun 2009 15:43:12 +0000 (17:43 +0200)
The build of extras with larger external dependencies is wrapped in:
      --enable-extras

Makefile.am
autogen.sh
configure.ac
extras/Makefile.am
extras/udev-acl/.gitignore [new file with mode: 0644]
extras/udev-acl/70-acl.rules [new file with mode: 0644]
extras/udev-acl/Makefile.am [new file with mode: 0644]
extras/udev-acl/udev-acl.c [new file with mode: 0644]

index fd75386..fbaf2ae 100644 (file)
@@ -20,7 +20,9 @@ CLEANFILES = \
 
 ACLOCAL_AMFLAGS = -I m4
 
-DISTCHECK_CONFIGURE_FLAGS=--enable-gtk-doc
+DISTCHECK_CONFIGURE_FLAGS = \
+       --enable-gtk-doc \
+       --enable-extras
 
 clean-local:
        rm -rf udev-test-install
index 83af29c..e41bae0 100755 (executable)
@@ -13,7 +13,7 @@ CFLAGS="-g -Wall \
 libdirname=$(basename $(cd /lib/$(gcc -print-multi-os-directory); pwd))
 args="--prefix=/usr --exec-prefix= --sysconfdir=/etc \
 --libdir=/usr/$libdirname --with-libdir-name=$libdirname \
---with-selinux --enable-gtk-doc
+--with-selinux --enable-gtk-doc --enable-extras"
 
 export CFLAGS="$CFLAGS -O2"
 ./configure $args $@
index 0df0f60..9571c90 100644 (file)
@@ -58,6 +58,16 @@ if test "x$enable_logging" = "xyes"; then
        AC_DEFINE(ENABLE_LOGGING, [1], [System logging.])
 fi
 
+AC_ARG_ENABLE([extras],
+       AS_HELP_STRING([--enable-extras], [enable extras with external dependencies]),
+       [], [enable_extras=no])
+if test "x$enable_extras" = xyes; then
+       PKG_CHECK_MODULES([GLIB], [glib-2.0 >= 2.7.0 gobject-2.0 >= 2.7.0])
+       AC_SUBST([GLIB_CFLAGS])
+       AC_SUBST([GLIB_LIBS])
+fi
+AM_CONDITIONAL([ENABLE_EXTRAS], [test "x$enable_extras" = xyes])
+
 AC_CONFIG_HEADERS(config.h)
 AC_CONFIG_FILES([
        Makefile
@@ -79,6 +89,7 @@ AC_CONFIG_FILES([
        extras/rule_generator/Makefile
        extras/scsi_id/Makefile
        extras/usb_id/Makefile
+       extras/udev-acl/Makefile
 ])
 
 AC_OUTPUT
@@ -102,5 +113,7 @@ AC_MSG_RESULT([
        cflags:                 ${CFLAGS}
        ldflags:                ${LDFLAGS}
 
+       extras:                 ${enable_extras}
+
        xsltproc:               ${XSLTPROC}
 ])
index 49a23d9..4c9b204 100644 (file)
@@ -12,3 +12,8 @@ SUBDIRS = \
        rule_generator \
        scsi_id \
        usb_id
+
+if ENABLE_EXTRAS
+SUBDIRS += \
+       udev-acl
+endif
diff --git a/extras/udev-acl/.gitignore b/extras/udev-acl/.gitignore
new file mode 100644 (file)
index 0000000..08891fe
--- /dev/null
@@ -0,0 +1 @@
+udev-acl
diff --git a/extras/udev-acl/70-acl.rules b/extras/udev-acl/70-acl.rules
new file mode 100644 (file)
index 0000000..fc27039
--- /dev/null
@@ -0,0 +1,45 @@
+# do not edit this file, it will be overwritten on update
+
+ENV{MAJOR}=="", GOTO="acl_end"
+ACTION!="add|change", GOTO="acl_apply"
+
+# PTP/MTP protocol devices, cameras, portable media players
+SUBSYSTEM=="usb", ENV{ID_USB_INTERFACES}=="", ENV{DEVTYPE}=="usb_device", IMPORT{program}="usb_id --export %p"
+SUBSYSTEM=="usb", ENV{ID_USB_INTERFACES}=="*:060101:*", ENV{ACL_MANAGE}="1"
+
+# digicams with proprietary protocol
+ENV{ID_GPHOTO2}=="*?", ENV{ACL_MANAGE}="1"
+
+# SCSI scanners
+KERNEL=="sg[0-9]*", ATTRS{type}=="6", ENV{ACL_MANAGE}="1"
+KERNEL=="sg[0-9]*", ATTRS{type}=="3", ATTRS{vendor}=="HP|EPSON|Epson", ENV{ACL_MANAGE}="1"
+
+# USB scanners
+ENV{libsane_matched}=="yes", ENV{ACL_MANAGE}="1"
+
+# optical drives
+SUBSYSTEM=="block", ENV{ID_CDROM}=="1", ENV{ACL_MANAGE}="1"
+
+# sound devices
+SUBSYSTEM=="sound", ENV{ACL_MANAGE}="1"
+# sound jack-sense
+SUBSYSTEM=="input", SUBSYSTEMS=="sound", ENV{ACL_MANAGE}="1"
+
+# webcams, frame grabber, TV cards
+SUBSYSTEM=="video4linux", ENV{ACL_MANAGE}="1"
+SUBSYSTEM=="dvb", ENV{ACL_MANAGE}="1"
+
+# fingerprint readers
+SUBSYSTEM=="usb", ATTR{idVendor}=="0483", ATTR{idProduct}=="2016", ENV{ACL_MANAGE}="1"
+
+# DRI video devices
+SUBSYSTEM=="drm", KERNEL=="card*", ENV{ACL_MANAGE}="1"
+
+# KVM
+SUBSYSTEM=="misc", KERNEL=="kvm", ENV{ACL_MANAGE}="1"
+
+# apply ACL for all locally logged in users
+LABEL="acl_apply", ENV{ACL_MANAGE}=="?*", TEST=="/var/run/ConsoleKit/database", \
+  RUN+="udev-acl --action=$env{ACTION} --device=$env{DEVNAME}"
+
+LABEL="acl_end"
diff --git a/extras/udev-acl/Makefile.am b/extras/udev-acl/Makefile.am
new file mode 100644 (file)
index 0000000..f1a5fbd
--- /dev/null
@@ -0,0 +1,15 @@
+include $(top_srcdir)/Makefile.am.inc
+
+udevhomedir = $(udev_prefix)/lib/udev
+udevhome_PROGRAMS = udev-acl
+
+udev_acl_SOURCES = udev-acl.c
+udev_acl_CPPFLAGS = $(AM_CPPFLAGS) $(GLIB_CFLAGS)
+udev_acl_LDADD = -lacl $(top_builddir)/libudev/libudev.la $(GLIB_LIBS)
+
+udevrulesdir = $(udev_prefix)/lib/udev/rules.d
+dist_udevrules_DATA = 70-acl.rules
+
+install-exec-hook:
+       mkdir -p $(DESTDIR)$(prefix)/lib/ConsoleKit/run-session.d
+       ln -sf $(udevhomedir)/udev-acl $(DESTDIR)$(prefix)/lib/ConsoleKit/run-session.d/udev-acl.ck
diff --git a/extras/udev-acl/udev-acl.c b/extras/udev-acl/udev-acl.c
new file mode 100644 (file)
index 0000000..ec95025
--- /dev/null
@@ -0,0 +1,334 @@
+/*
+ * Copyright (C) 2009 Kay Sievers <kay.sievers@vrfy.org>
+ *
+ * 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:
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <inttypes.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <getopt.h>
+#include <sys/stat.h>
+#include <glib.h>
+#include <acl/libacl.h>
+#include <libudev.h>
+
+static int debug;
+
+static int set_facl(const char* filename, uid_t uid, int add)
+{
+       int get;
+       acl_t acl;
+       acl_entry_t entry = NULL;
+       acl_entry_t e;
+       acl_permset_t permset;
+       int ret;
+
+       /* read current record */
+       acl = acl_get_file(filename, ACL_TYPE_ACCESS);
+       if (!acl)
+               return -1;
+
+       /* locate ACL_USER entry for uid */
+       get = acl_get_entry(acl, ACL_FIRST_ENTRY, &e);
+       while (get == 1) {
+               acl_tag_t t;
+
+               acl_get_tag_type(e, &t);
+               if (t == ACL_USER) {
+                       uid_t *u;
+
+                       u = (uid_t*)acl_get_qualifier(e);
+                       if (u == NULL) {
+                               ret = -1;
+                               goto out;
+                       }
+                       if (*u == uid) {
+                               entry = e;
+                               acl_free(u);
+                               break;
+                       }
+                       acl_free(u);
+               }
+
+               get = acl_get_entry(acl, ACL_NEXT_ENTRY, &e);
+       }
+
+       /* remove ACL_USER entry for uid */
+       if (!add) {
+               if (entry == NULL) {
+                       ret = 0;
+                       goto out;
+               }
+               acl_delete_entry(acl, entry);
+               goto update;
+       }
+
+       /* create ACL_USER entry for uid */
+       if (entry == NULL) {
+               ret = acl_create_entry(&acl, &entry);
+               if (ret != 0)
+                       goto out;
+               acl_set_tag_type(entry, ACL_USER);
+               acl_set_qualifier(entry, &uid);
+       }
+
+       /* add permissions for uid */
+       acl_get_permset(entry, &permset);
+       acl_add_perm(permset, ACL_READ|ACL_WRITE);
+update:
+       /* update record */
+       if (debug)
+               printf("%c%u %s\n", add ? '+' : '-', uid, filename);
+       acl_calc_mask(&acl);
+       ret = acl_set_file(filename, ACL_TYPE_ACCESS, acl);
+       if (ret != 0)
+               goto out;
+out:
+       acl_free(acl);
+       return ret;
+}
+
+/* check if a given uid is listed */
+static int uid_in_list(GSList *list, uid_t uid)
+{
+       GSList *l;
+
+       for (l = list; l != NULL; l = g_slist_next(l))
+               if (uid == GPOINTER_TO_UINT(l->data))
+                       return 1;
+       return 0;
+}
+
+/* return list of current uids of local active sessions */
+static GSList *uids_with_local_active_session(const char *own_id)
+{
+       GSList *list = NULL;
+       GKeyFile *keyfile;
+
+       keyfile = g_key_file_new();
+       if (g_key_file_load_from_file(keyfile, "/var/run/ConsoleKit/database", 0, NULL)) {
+               gchar **groups;
+
+               groups = g_key_file_get_groups(keyfile, NULL);
+               if (groups != NULL) {
+                       int i;
+
+                       for (i = 0; groups[i] != NULL; i++) {
+                               uid_t u;
+
+                               if (!g_str_has_prefix(groups[i], "Session "))
+                                       continue;
+                               if (own_id != NULL &&g_str_has_suffix(groups[i], own_id))
+                                       continue;
+                               if (!g_key_file_get_boolean(keyfile, groups[i], "is_local", NULL))
+                                       continue;
+                               if (!g_key_file_get_boolean(keyfile, groups[i], "is_active", NULL))
+                                       continue;
+                               u = g_key_file_get_integer(keyfile, groups[i], "uid", NULL);
+                               if (u > 0 && !uid_in_list(list, u))
+                                       list = g_slist_prepend(list, GUINT_TO_POINTER(u));
+                       }
+                       g_strfreev(groups);
+               }
+       }
+       g_key_file_free(keyfile);
+
+       return list;
+}
+
+/* ConsoleKit calls us with special variables */
+static int consolekit_called(const char *action, uid_t *uid, const char **own_session, int *add)
+{
+       int a;
+       uid_t u;
+       const char *s;
+       const char *session;
+
+       if (strcmp(action, "session_active_changed") != 0)
+               return -1;
+
+       s = getenv("CK_SESSION_IS_LOCAL");
+       if (s == NULL)
+               return -1;
+       if (strcmp(s, "true") != 0)
+               return 0;
+
+       s = getenv("CK_SESSION_IS_ACTIVE");
+       if (s == NULL)
+               return -1;
+       if (strcmp(s, "true") == 0)
+               a = 1;
+       else
+               a = 0;
+
+       session = getenv("CK_SESSION_ID");
+       if (session == NULL)
+               return -1;
+
+       s = getenv("CK_SESSION_USER_UID");
+       if (s == NULL)
+               return -1;
+       u = strtoul(s, NULL, 10);
+       if (u == 0)
+               return 0;
+
+       *own_session = session;
+       *uid = u;
+       *add = a;
+       return 0;
+}
+
+/* add or remove a ACL for a given uid from all matching devices */
+static void apply_acl_to_devices(uid_t uid, int add)
+{
+       struct udev *udev;
+       struct udev_enumerate *enumerate;
+       struct udev_list_entry *list_entry;
+
+       /* iterate over all devices tagged with ACL_SET */
+       udev = udev_new();
+       enumerate = udev_enumerate_new(udev);
+       udev_enumerate_add_match_property(enumerate, "ACL_MANAGE", "*");
+       udev_enumerate_scan_devices(enumerate);
+       udev_list_entry_foreach(list_entry, udev_enumerate_get_list_entry(enumerate)) {
+               struct udev_device *device;
+               const char *node;
+
+               device = udev_device_new_from_syspath(udev_enumerate_get_udev(enumerate),
+                                                     udev_list_entry_get_name(list_entry));
+               if (device == NULL)
+                       continue;
+               node = udev_device_get_devnode(device);
+               if (node == NULL)
+                       continue;
+               set_facl(node, uid, add);
+               udev_device_unref(device);
+       }
+       udev_enumerate_unref(enumerate);
+       udev_unref(udev);
+}
+
+int main (int argc, char* argv[])
+{
+       static const struct option options[] = {
+               { "action", required_argument, NULL, 'a' },
+               { "device", required_argument, NULL, 'D' },
+               { "user", required_argument, NULL, 'u' },
+               { "debug", no_argument, NULL, 'd' },
+               { "help", no_argument, NULL, 'h' },
+               {}
+       };
+       int add = -1;
+       const char *device = NULL;
+       uid_t uid = 0;
+       const char* own_session = NULL;
+       int rc = 0;
+
+       /* valgrind is more important to us than a slice allocator */
+       g_slice_set_config (G_SLICE_CONFIG_ALWAYS_MALLOC, 1);
+
+       while (1) {
+               int option;
+
+               option = getopt_long(argc, argv, "+a:D:u:dh", options, NULL);
+               if (option == -1)
+                       break;
+
+               switch (option) {
+               case 'a':
+                       if (strcmp(optarg, "add") == 0 || strcmp(optarg, "change") == 0)
+                               add = 1;
+                       else if (strcmp(optarg, "remove") == 0)
+                               add = 0;
+                       else
+                               goto out;
+                       break;
+               case 'D':
+                       device = optarg;
+                       break;
+               case 'u':
+                       uid = strtoul(optarg, NULL, 10);
+                       break;
+               case 'd':
+                       debug = 1;
+                       break;
+               case 'h':
+                       printf("Usage: udev-acl --action=ACTION [--device=DEVICEFILE] [--user=UID]\n\n");
+               default:
+                       goto out;
+               }
+       }
+
+       if (add < 0 && device == NULL && uid == 0)
+               consolekit_called(argv[optind], &uid, &own_session, &add);
+
+       if (add < 0) {
+               fprintf(stderr, "missing action\n\n");
+               rc = 2;
+               goto out;
+       }
+
+       if (device != NULL && uid != 0) {
+               fprintf(stderr, "only one option, --device=DEVICEFILE or --user=UID expected\n\n");
+               rc = 3;
+               goto out;
+       }
+
+       if (uid != 0) {
+               if (add) {
+                       /* Add ACL for given uid to all matching devices. */
+                       apply_acl_to_devices(uid, 1);
+               } else {
+                       /*
+                        * Remove ACL for given uid from all matching devices
+                        * when there is currently no local active session.
+                        */
+                       GSList *list;
+
+                       list = uids_with_local_active_session(own_session);
+                       if (!uid_in_list(list, uid))
+                               apply_acl_to_devices(uid, 0);
+                       g_slist_free(list);
+               }
+       } else if (device != NULL) {
+               /*
+                * Add ACLs for all current session uids to a given device.
+                *
+                * Or remove ACLs for uids which do not have any current local
+                * active session. Remove is not really interesting, because in
+                * most cases the device node is removed anyway.
+                */
+               GSList *list;
+               GSList *l;
+
+               list = uids_with_local_active_session(NULL);
+               for (l = list; l != NULL; l = g_slist_next(l)) {
+                       uid_t u;
+
+                       u = GPOINTER_TO_UINT(l->data);
+                       if (add || !uid_in_list(list, u))
+                               set_facl(device, u, add);
+               }
+               g_slist_free(list);
+       } else {
+               fprintf(stderr, "--device=DEVICEFILE or --user=UID expected\n\n");
+               rc = 3;
+       }
+out:
+       return rc;
+}