File import-udev-acl-tool-from-udev.patch of Package ConsoleKit

From d491e4017d3a098b6a2a4fe5a73989c172dfa035 Mon Sep 17 00:00:00 2001
From: Michael Biebl <biebl@debian.org>
Date: Mon, 19 Mar 2012 01:25:57 +0100
Subject: [PATCH] Import udev-acl tool from udev

The udev-acl tool has been removed from udev in version 182.
Add this tool to ConsoleKit for systems which don't use systemd-logind.
---
 configure.ac            |   21 +++
 tools/.gitignore        |    1 +
 tools/70-udev-acl.rules |   76 +++++++++
 tools/Makefile.am       |   11 ++
 tools/udev-acl.c        |  430 +++++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 539 insertions(+)
 create mode 100644 tools/70-udev-acl.rules
 create mode 100644 tools/udev-acl.c

Index: ConsoleKit-0.4.5/configure.ac
===================================================================
--- ConsoleKit-0.4.5.orig/configure.ac
+++ ConsoleKit-0.4.5/configure.ac
@@ -16,6 +16,7 @@ AC_GNU_SOURCE
 
 AC_ISC_POSIX
 AC_PROG_CC
+AM_PROG_CC_C_O
 AC_STDC_HEADERS
 AC_DISABLE_STATIC
 AC_PROG_LIBTOOL
@@ -233,6 +234,27 @@ if test "x$enable_pam_module" = "xyes";
 fi
 AM_CONDITIONAL(ENABLE_PAM_MODULE, test "x$enable_pam_module" = "xyes")
 
+dnl ------------------------------------------------------------------------------
+dnl udev-acl - apply ACLs for users with local forground sessions
+dnl ------------------------------------------------------------------------------
+AC_ARG_ENABLE([udev-acl],
+	AS_HELP_STRING([--enable-udev-acl], [enable local user acl permissions support @<:@default=disabled@:>@]),
+	[], [enable_udev_acl=no])
+AS_IF([test "x$enable_udev_acl" = "xyes"], [
+
+	PKG_CHECK_MODULES([UDEV_ACL], [glib-2.0 >= 2.22.0 gobject-2.0 >= 2.22.0 libudev])
+	AC_CHECK_LIB([acl], [acl_init], [UDEV_ACL_LIBS="$UDEV_ACL_LIBS -lacl"], AC_MSG_ERROR([libacl not found]))
+	AC_CHECK_HEADER([acl/libacl.h], [:], AC_MSG_ERROR([libacl header not found]))
+	_udevdir=$($PKG_CONFIG --variable=udevdir udev)
+	if test -z "$_udevdir"; then
+		UDEVDIR="$_udevdir"
+	else
+		UDEVDIR="/usr/lib/udev"
+	fi
+])
+AM_CONDITIONAL([ENABLE_UDEV_ACL], [test "x$enable_udev_acl" = "xyes"])
+
+
 dnl ---------------------------------------------------------------------------
 dnl - Install directory for PAM security module
 dnl ---------------------------------------------------------------------------
@@ -445,6 +467,7 @@ echo "
         Build backend:            ${CK_BACKEND}
         PAM module dir:           ${PAM_MODULE_DIR}
         Build PAM module:         ${msg_pam_module}
+        Build udev-acl:           ${enable_udev_acl}
         Build docs:               ${enable_docbook_docs}
 
         PolicyKit support         ${have_polkit}
Index: ConsoleKit-0.4.5/tools/70-udev-acl.rules
===================================================================
--- /dev/null
+++ ConsoleKit-0.4.5/tools/70-udev-acl.rules
@@ -0,0 +1,76 @@
+# do not edit this file, it will be overwritten on update
+
+# Do not use TAG+="uaccess" outside of this file. This variable is private to
+# udev-acl of this udev release and may be replaced at any time.
+
+ENV{MAJOR}=="", GOTO="acl_end"
+ACTION=="remove", GOTO="acl_apply"
+
+# systemd replaces udev-acl entirely, skip if active
+TEST=="/sys/fs/cgroup/systemd", TAG=="uaccess", GOTO="acl_end"
+
+# PTP/MTP protocol devices, cameras, portable media players
+SUBSYSTEM=="usb", ENV{ID_USB_INTERFACES}=="*:060101:*", TAG+="uaccess"
+
+# digicams with proprietary protocol
+ENV{ID_GPHOTO2}=="*?", TAG+="uaccess"
+
+# SCSI and USB scanners
+ENV{libsane_matched}=="yes", TAG+="uaccess"
+
+# HPLIP devices (necessary for ink level check and HP tool maintenance)
+ENV{ID_HPLIP}=="1", TAG+="uaccess"
+
+# optical drives
+SUBSYSTEM=="block", ENV{ID_CDROM}=="1", TAG+="uaccess"
+SUBSYSTEM=="scsi_generic", SUBSYSTEMS=="scsi", ATTRS{type}=="4|5", TAG+="uaccess"
+
+# sound devices
+SUBSYSTEM=="sound", TAG+="uaccess"
+
+# ffado is an userspace driver for firewire sound cards
+SUBSYSTEM=="firewire", ENV{ID_FFADO}=="1", TAG+="uaccess"
+
+# webcams, frame grabber, TV cards
+SUBSYSTEM=="video4linux", TAG+="uaccess"
+SUBSYSTEM=="dvb", TAG+="uaccess"
+
+# IIDC devices: industrial cameras and some webcams
+SUBSYSTEM=="firewire", ATTR{units}=="*0x00a02d:0x00010*",  TAG+="uaccess"
+SUBSYSTEM=="firewire", ATTR{units}=="*0x00b09d:0x00010*",  TAG+="uaccess"
+# AV/C devices: camcorders, set-top boxes, TV sets, audio devices, and more
+SUBSYSTEM=="firewire", ATTR{units}=="*0x00a02d:0x010001*", TAG+="uaccess"
+SUBSYSTEM=="firewire", ATTR{units}=="*0x00a02d:0x014001*", TAG+="uaccess"
+
+# DRI video devices
+SUBSYSTEM=="drm", KERNEL=="card*", TAG+="uaccess"
+
+# KVM
+SUBSYSTEM=="misc", KERNEL=="kvm", TAG+="uaccess"
+
+# smart-card readers
+ENV{ID_SMARTCARD_READER}=="*?", TAG+="uaccess"
+
+# PDA devices
+ENV{ID_PDA}=="*?", TAG+="uaccess"
+
+# Programmable remote control
+ENV{ID_REMOTE_CONTROL}=="1", TAG+="uaccess"
+
+# joysticks
+SUBSYSTEM=="input", ENV{ID_INPUT_JOYSTICK}=="?*", TAG+="uaccess"
+
+# color measurement devices
+ENV{COLOR_MEASUREMENT_DEVICE}=="*?", TAG+="uaccess"
+
+# DDC/CI device, usually high-end monitors such as the DreamColor
+ENV{DDC_DEVICE}=="*?", TAG+="uaccess"
+
+# media player raw devices (for user-mode drivers, Android SDK, etc.)
+SUBSYSTEM=="usb", ENV{ID_MEDIA_PLAYER}=="?*", TAG+="uaccess"
+
+# apply ACL for all locally logged in users
+LABEL="acl_apply", TAG=="uaccess", TEST=="/var/run/ConsoleKit/database", \
+  RUN+="udev-acl --action=$env{ACTION} --device=$env{DEVNAME}"
+
+LABEL="acl_end"
Index: ConsoleKit-0.4.5/tools/Makefile.am
===================================================================
--- ConsoleKit-0.4.5.orig/tools/Makefile.am
+++ ConsoleKit-0.4.5/tools/Makefile.am
@@ -143,6 +143,17 @@ ck_get_x11_display_device_LDADD =	\
 	$(top_builddir)/src/libck.la	\
 	$(NULL)
 
+if ENABLE_UDEV_ACL
+udevdir = $(UDEVDIR)
+udevrulesdir = $(UDEVDIR)/rules.d
+
+dist_udevrules_DATA = 70-udev-acl.rules
+udev_PROGRAMS = udev-acl
+
+udev_acl_SOURCES = udev-acl.c
+udev_acl_LDADD = $(UDEV_ACL_LIBS)
+udev_acl_CFLAGS = $(UDEV_ACL_CFLAGS)
+endif
 
 EXTRA_DIST =				\
 	$(NULL)
Index: ConsoleKit-0.4.5/tools/udev-acl.c
===================================================================
--- /dev/null
+++ ConsoleKit-0.4.5/tools/udev-acl.c
@@ -0,0 +1,430 @@
+/*
+ * 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 <acl/libacl.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <getopt.h>
+#include <glib.h>
+#include <inttypes.h>
+#include <libudev.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+static int debug;
+
+enum{
+        ACTION_NONE = 0,
+        ACTION_REMOVE,
+        ACTION_ADD,
+        ACTION_CHANGE
+};
+
+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;
+
+        /* don't touch ACLs for root */
+        if (uid == 0)
+                return 0;
+
+        /* 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 *ck_action, uid_t *uid, uid_t *uid2, const char **remove_session_id, int *action)
+{
+        int a = ACTION_NONE;
+        uid_t u = 0;
+        uid_t u2 = 0;
+        const char *s;
+        const char *s2;
+        const char *old_session = NULL;
+
+        if (ck_action == NULL || strcmp(ck_action, "seat_active_session_changed") != 0)
+                return -1;
+
+        /* We can have one of: remove, add, change, no-change */
+        s = getenv("CK_SEAT_OLD_SESSION_ID");
+        s2 = getenv("CK_SEAT_SESSION_ID");
+        if (s == NULL && s2 == NULL) {
+                return -1;
+        } else if (s2 == NULL) {
+                a = ACTION_REMOVE;
+        } else if (s == NULL) {
+                a = ACTION_ADD;
+        } else {
+                a = ACTION_CHANGE;
+        }
+
+        switch (a) {
+        case ACTION_ADD:
+                s = getenv("CK_SEAT_SESSION_USER_UID");
+                if (s == NULL)
+                        return -1;
+                u = strtoul(s, NULL, 10);
+
+                s = getenv("CK_SEAT_SESSION_IS_LOCAL");
+                if (s == NULL)
+                        return -1;
+                if (strcmp(s, "true") != 0)
+                        return 0;
+
+                break;
+        case ACTION_REMOVE:
+                s = getenv("CK_SEAT_OLD_SESSION_USER_UID");
+                if (s == NULL)
+                        return -1;
+                u = strtoul(s, NULL, 10);
+
+                s = getenv("CK_SEAT_OLD_SESSION_IS_LOCAL");
+                if (s == NULL)
+                        return -1;
+                if (strcmp(s, "true") != 0)
+                        return 0;
+
+                old_session = getenv("CK_SEAT_OLD_SESSION_ID");
+                if (old_session == NULL)
+                        return -1;
+
+                break;
+        case ACTION_CHANGE:
+                s = getenv("CK_SEAT_OLD_SESSION_USER_UID");
+                if (s == NULL)
+                        return -1;
+                u = strtoul(s, NULL, 10);
+                s = getenv("CK_SEAT_SESSION_USER_UID");
+                if (s == NULL)
+                        return -1;
+                u2 = strtoul(s, NULL, 10);
+
+                s = getenv("CK_SEAT_OLD_SESSION_IS_LOCAL");
+                s2 = getenv("CK_SEAT_SESSION_IS_LOCAL");
+                if (s == NULL || s2 == NULL)
+                        return -1;
+                /* don't process non-local session changes */
+                if (strcmp(s, "true") != 0 && strcmp(s2, "true") != 0)
+                        return 0;
+
+                if (strcmp(s, "true") == 0 && strcmp(s, "true") == 0) {
+                        /* process the change */
+                        if (u == u2) {
+                                /* special case: we noop if we are
+                                 * changing between local sessions for
+                                 * the same uid */
+                                a = ACTION_NONE;
+                        }
+                        old_session = getenv("CK_SEAT_OLD_SESSION_ID");
+                        if (old_session == NULL)
+                                return -1;
+                } else if (strcmp(s, "true") == 0) {
+                        /* only process the removal */
+                        a = ACTION_REMOVE;
+                        old_session = getenv("CK_SEAT_OLD_SESSION_ID");
+                        if (old_session == NULL)
+                                return -1;
+                } else if (strcmp(s2, "true") == 0) {
+                        /* only process the addition */
+                        a = ACTION_ADD;
+                        u = u2;
+                }
+                break;
+        }
+
+        *remove_session_id = old_session;
+        *uid = u;
+        *uid2 = u2;
+        *action = 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_tag(enumerate, "uaccess");
+        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) {
+                        udev_device_unref(device);
+                        continue;
+                }
+                set_facl(node, uid, add);
+                udev_device_unref(device);
+        }
+        udev_enumerate_unref(enumerate);
+        udev_unref(udev);
+}
+
+static void
+remove_uid (uid_t uid, const char *remove_session_id)
+{
+        /*
+         * 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(remove_session_id);
+        if (!uid_in_list(list, uid))
+                apply_acl_to_devices(uid, 0);
+        g_slist_free(list);
+}
+
+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 action = -1;
+        const char *device = NULL;
+        bool uid_given = false;
+        uid_t uid = 0;
+        uid_t uid2 = 0;
+        const char* remove_session_id = 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, "remove") == 0)
+                                action = ACTION_REMOVE;
+                        else
+                                action = ACTION_ADD;
+                        break;
+                case 'D':
+                        device = optarg;
+                        break;
+                case 'u':
+                        uid_given = true;
+                        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");
+                        goto out;
+                }
+        }
+
+        if (action < 0 && device == NULL && !uid_given)
+                if (!consolekit_called(argv[optind], &uid, &uid2, &remove_session_id, &action))
+                        uid_given = true;
+
+        if (action < 0) {
+                fprintf(stderr, "missing action\n\n");
+                rc = 2;
+                goto out;
+        }
+
+        if (device != NULL && uid_given) {
+                fprintf(stderr, "only one option, --device=DEVICEFILE or --user=UID expected\n\n");
+                rc = 3;
+                goto out;
+        }
+
+        if (uid_given) {
+                switch (action) {
+                case ACTION_ADD:
+                        /* Add ACL for given uid to all matching devices. */
+                        apply_acl_to_devices(uid, 1);
+                        break;
+                case ACTION_REMOVE:
+                        remove_uid(uid, remove_session_id);
+                        break;
+                case ACTION_CHANGE:
+                        remove_uid(uid, remove_session_id);
+                        apply_acl_to_devices(uid2, 1);
+                        break;
+                case ACTION_NONE:
+                        goto out;
+                        break;
+                default:
+                        g_assert_not_reached();
+                        break;
+                }
+        } 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 (action == ACTION_ADD || !uid_in_list(list, u))
+                                set_facl(device, u, action == ACTION_ADD);
+                }
+                g_slist_free(list);
+        } else {
+                fprintf(stderr, "--device=DEVICEFILE or --user=UID expected\n\n");
+                rc = 3;
+        }
+out:
+        return rc;
+}