diff --git a/NEWS b/NEWS index 81c3225..cf06a31 100644 --- a/NEWS +++ b/NEWS @@ -1,4 +1,11 @@ ============== +Version 0.4.5 +============== + + * Loop around opening /dev/console to deal with BKL-less kernels (Colin Watson) + * systemd fixes (Lennart Poettering) + +============== Version 0.4.4 ============== diff --git a/configure.ac b/configure.ac index 0efc489..7a3b33f 100644 --- a/configure.ac +++ b/configure.ac @@ -1,7 +1,7 @@ -AC_PREREQ(2.59c) +AC_PREREQ([2.68]) AC_INIT([ConsoleKit], - [0.4.5], + [0.4.6], [https://bugs.freedesktop.org/enter_bug.cgi?product=ConsoleKit], [ConsoleKit]) @@ -12,13 +12,14 @@ m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) AM_MAINTAINER_MODE # for O_NOFOLLOW support -AC_GNU_SOURCE +AC_USE_SYSTEM_EXTENSIONS -AC_ISC_POSIX +AC_SEARCH_LIBS([strerror],[cposix]) AC_PROG_CC -AC_STDC_HEADERS +AM_PROG_CC_C_O +AC_HEADER_STDC AC_DISABLE_STATIC -AC_PROG_LIBTOOL +LT_INIT AC_HEADER_STDC @@ -88,50 +89,15 @@ case "$host" in ;; esac -# Find out the version of DBUS we're using - -dbus_version=`pkg-config --modversion dbus-1` -DBUS_VERSION_MAJOR=`echo $dbus_version | awk -F. '{print $1}'` -DBUS_VERSION_MINOR=`echo $dbus_version | awk -F. '{print $2}'` -DBUS_VERSION_MICRO=`echo $dbus_version | awk -F. '{print $3}'` -if test "z$DBUS_VERSION_MAJOR" = "z"; then - DBUS_VERSION_MAJOR="0" -fi -if test "z$DBUS_VERSION_MINOR" = "z"; then - DBUS_VERSION_MINOR="0" -fi -if test "z$DBUS_VERSION_MICRO" = "z"; then - DBUS_VERSION_MICRO="0" -fi - -if test "z$DBUS_VERSION_MAJOR" = "z0" -a "z$DBUS_VERSION_MINOR" = "z0" -a "z$DBUS_VERSION_MICRO" = "z0"; then echo "Error: Couldn't determine the version of your DBUS package." - echo " This is probably an error in this script, please report it" - echo " along with the following information:" - echo " Base DBUS version ='$dbus_version'" - echo " DBUS_VERSION_MAJOR='$DBUS_VERSION_MAJOR'" - echo " DBUS_VERSION_MINOR='$DBUS_VERSION_MINOR'" - echo " DBUS_VERSION_MICRO='$DBUS_VERSION_MICRO'" - exit 1 -else - - echo "Your dbus version is $DBUS_VERSION_MAJOR,$DBUS_VERSION_MINOR,$DBUS_VERSION_MICRO." - DBUS_CFLAGS="$DBUS_CFLAGS -DDBUS_VERSION_MAJOR=$DBUS_VERSION_MAJOR" - DBUS_CFLAGS="$DBUS_CFLAGS -DDBUS_VERSION_MINOR=$DBUS_VERSION_MINOR" - DBUS_CFLAGS="$DBUS_CFLAGS -DDBUS_VERSION_MICRO=$DBUS_VERSION_MICRO" - - AC_SUBST(DBUS_CFLAGS) -fi dnl --------------------------------------------------------------------------- dnl - Are we specifying a different dbus root ? dnl --------------------------------------------------------------------------- AC_ARG_WITH(dbus-sys, - [AC_HELP_STRING([--with-dbus-sys=], - [where D-BUS system.d directory is])]) + [AS_HELP_STRING([--with-dbus-sys=],[where D-BUS system.d directory is])]) AC_ARG_WITH(dbus-services, - [AC_HELP_STRING([--with-dbus-services=], - [where D-BUS services directory is])]) + [AS_HELP_STRING([--with-dbus-services=],[where D-BUS services directory is])]) if ! test -z "$with_dbus_sys" ; then DBUS_SYS_DIR="$with_dbus_sys" else @@ -158,8 +124,7 @@ dnl - PID file dnl --------------------------------------------------------------------------- AC_ARG_WITH(pid-file, - [AC_HELP_STRING([--with-pid-file=], - [pid file location])]) + [AS_HELP_STRING([--with-pid-file=],[pid file location])]) if ! test -z "$with_pid_file"; then CONSOLE_KIT_PID_FILE=$with_pid_file @@ -190,6 +155,9 @@ case "$host" in *-*-solaris*) CK_BACKEND="solaris" ;; + *-*-gnu*) + CK_BACKEND="gnu" + ;; *) AC_MSG_ERROR([No sysdeps back-end implemented for host $host]) ;; @@ -200,6 +168,7 @@ AC_SUBST(KVM_LIBS) AM_CONDITIONAL(CK_COMPILE_LINUX, test x$CK_BACKEND = xlinux, [Compiling for Linux]) AM_CONDITIONAL(CK_COMPILE_FREEBSD, test x$CK_BACKEND = xfreebsd, [Compiling for FreeBSD]) AM_CONDITIONAL(CK_COMPILE_SOLARIS, test x$CK_BACKEND = xsolaris, [Compiling for Solaris]) +AM_CONDITIONAL(CK_COMPILE_GNU, test x$CK_BACKEND = xgnu, [Compiling for GNU]) AC_SUBST(CK_BACKEND) dnl --------------------------------------------------------------------------- @@ -221,8 +190,7 @@ AC_CHECK_LIB(pam, pam_syslog, [AC_DEFINE(HAVE_PAM_SYSLOG, [], [Define to 1 if yo # Check if we should build the PAM module msg_pam_module=no AC_ARG_ENABLE(pam-module, - [AC_HELP_STRING([--enable-pam-module], - [build PAM module])], + [AS_HELP_STRING([--enable-pam-module],[build PAM module])], , enable_pam_module=no) if test "x$enable_pam_module" = "xyes"; then if test "x$have_pam" = "xno"; then @@ -233,13 +201,32 @@ if test "x$enable_pam_module" = "xyes"; then 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="/lib/udev" + fi + AC_SUBST(UDEVDIR) +]) +AM_CONDITIONAL([ENABLE_UDEV_ACL], [test "x$enable_udev_acl" = "xyes"]) + + dnl --------------------------------------------------------------------------- dnl - Install directory for PAM security module dnl --------------------------------------------------------------------------- AC_ARG_WITH(pam-module-dir, - [AC_HELP_STRING([--with-pam-module-dir=], - [directory to install PAM security module])]) + [AS_HELP_STRING([--with-pam-module-dir=],[directory to install PAM security module])]) if ! test -z "$with_pam_module_dir"; then PAM_MODULE_DIR="$with_pam_module_dir" else @@ -252,8 +239,7 @@ dnl - DocBook Documentation dnl --------------------------------------------------------------------------- AC_ARG_ENABLE(docbook-docs, - [AC_HELP_STRING([--enable-docbook-docs], - [build documentation (requires xmlto)])], + [AS_HELP_STRING([--enable-docbook-docs],[build documentation (requires xmlto)])], enable_docbook_docs=$enableval,enable_docbook_docs=no) AC_PATH_PROG(XMLTO, xmlto, no) AC_MSG_CHECKING([whether to build DocBook documentation]) @@ -308,8 +294,7 @@ dnl --------------------------------------------------------------------------- msg_rbac_shutdown=no AC_ARG_ENABLE(rbac-shutdown, - [AC_HELP_STRING([--enable-rbac-shutdown=], - [Build with RBAC support specifying shutdown/reboot RBAC authentication key])], + [AS_HELP_STRING([--enable-rbac-shutdown=],[Build with RBAC support specifying shutdown/reboot RBAC authentication key])], enable_rbac_shutdown=$enableval,enable_rbac_shutdown=no) if test "x$enable_rbac_shutdown" != "xno"; then RBAC_LIBS="-lsecdb -lsocket -lnsl" @@ -326,8 +311,7 @@ dnl --------------------------------------------------------------------------- # Turn on the additional warnings last, so -Werror doesn't affect other tests. AC_ARG_ENABLE(more-warnings, - [AC_HELP_STRING([--enable-more-warnings], - [Maximum compiler warnings])], + [AS_HELP_STRING([--enable-more-warnings],[Maximum compiler warnings])], set_more_warnings="$enableval",[ if test -d $srcdir/.git; then set_more_warnings=yes @@ -349,9 +333,7 @@ if test "$GCC" = "yes" -a "$set_more_warnings" != "no"; then SAVE_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $option" AC_MSG_CHECKING([whether gcc understands $option]) - AC_TRY_COMPILE([], [], - has_option=yes, - has_option=no,) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[]])],[has_option=yes],[has_option=no]) if test $has_option = no; then CFLAGS="$SAVE_CFLAGS" fi @@ -368,8 +350,7 @@ fi # Enable Debug # AC_ARG_ENABLE(debug, - [AC_HELP_STRING([--enable-debug], - [turn on debugging])], + [AS_HELP_STRING([--enable-debug],[turn on debugging])], , enable_debug=yes) if test "$enable_debug" = "yes"; then DEBUG_CFLAGS="-DG_ENABLE_DEBUG" @@ -395,10 +376,9 @@ AC_SUBST(LDFLAGS) AC_ARG_WITH([systemdsystemunitdir], AS_HELP_STRING([--with-systemdsystemunitdir=DIR], [Directory for systemd service files]), - [], - [with_systemdsystemunitdir=$($PKG_CONFIG --variable=systemdsystemunitdir systemd)]) -AC_SUBST([systemdsystemunitdir], [$with_systemdsystemunitdir]) -AM_CONDITIONAL(HAVE_SYSTEMD, [test -n "$with_systemdsystemunitdir"]) + [], [with_systemdsystemunitdir=$($PKG_CONFIG --variable=systemdsystemunitdir systemd)]) +AS_IF([test "x$with_systemdsystemunitdir" != "xno"], [ AC_SUBST([systemdsystemunitdir], [$with_systemdsystemunitdir])]) +AM_CONDITIONAL(HAVE_SYSTEMD, [test -n "$with_systemdsystemunitdir" -a "x$with_systemdsystemunitdir" != "xno"]) # Files @@ -445,6 +425,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} diff --git a/pam-ck-connector/pam-ck-connector.c b/pam-ck-connector/pam-ck-connector.c index 9bdef51..949a9b0 100644 --- a/pam-ck-connector/pam-ck-connector.c +++ b/pam-ck-connector/pam-ck-connector.c @@ -244,12 +244,12 @@ pam_sm_open_session (pam_handle_t *pamh, const char *s; uid_t uid; char buf[256]; - char ttybuf[PATH_MAX]; + char *ttybuf; DBusError error; dbus_bool_t is_local; ret = PAM_IGNORE; - + ttybuf = NULL; is_local = TRUE; _parse_pam_args (pamh, flags, argc, argv); @@ -295,7 +295,13 @@ pam_sm_open_session (pam_handle_t *pamh, x11_display = display_device; display_device = ""; } else if (strncmp (_PATH_DEV, display_device, 5) != 0) { - snprintf (ttybuf, sizeof (ttybuf), _PATH_DEV "%s", display_device); + int len = strlen (_PATH_DEV) + strlen (display_device) + 1; + ttybuf = malloc (len); + if (ttybuf == NULL) { + ck_pam_syslog (pamh, LOG_ERR, "oom allocating ttybuf"); + goto out; + } + snprintf (ttybuf, len, _PATH_DEV "%s", display_device); display_device = ttybuf; } @@ -411,6 +417,8 @@ pam_sm_open_session (pam_handle_t *pamh, ret = PAM_SUCCESS; out: + free (ttybuf); + return ret; } diff --git a/src/Makefile.am b/src/Makefile.am index 869decd..217e531 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -57,11 +57,18 @@ libck_la_SOURCES += \ $(NULL) libck_la_LIBADD = $(KVM_LIBS) endif +if CK_COMPILE_GNU +libck_la_SOURCES += \ + ck-sysdeps-gnu.c \ + $(NULL) +libck_la_LIBADD = -lps +endif EXTRA_libck_la_SOURCES = \ ck-sysdeps-linux.c \ ck-sysdeps-solaris.c \ ck-sysdeps-freebsd.c \ + ck-sysdeps-gnu.c \ $(NULL) sbin_PROGRAMS = \ diff --git a/src/ck-sysdeps-gnu.c b/src/ck-sysdeps-gnu.c new file mode 100644 index 0000000..254f7ef --- /dev/null +++ b/src/ck-sysdeps-gnu.c @@ -0,0 +1,402 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2009 Pino Toscano + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#include "config.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#ifdef HAVE_PATHS_H +#include +#endif /* HAVE_PATHS_H */ + +#include "ck-sysdeps.h" + +struct _CkProcessStat +{ + struct proc_stat *ps; /* the statistics of a process */ +}; + +static struct ps_context *pc = NULL; + +static gboolean +get_proc_stat_from_pid (pid_t pid, + ps_flags_t flags, + struct proc_stat **res_ps) +{ + error_t err; + struct proc_stat *ps; + + g_assert (pid >= 0); + g_assert (res_ps != NULL); + + if (pc == NULL) { + err = ps_context_create (getproc (), &pc); + if (err) { + return FALSE; + } + } + + err = _proc_stat_create (pid, pc, &ps); + if (err) { + return FALSE; + } + + err = proc_stat_set_flags (ps, PSTAT_PID | flags); + if (err) { + return FALSE; + } + + *res_ps = ps; + return TRUE; +} + + +pid_t +ck_process_stat_get_ppid (CkProcessStat *stat) +{ + g_return_val_if_fail (stat != NULL, -1); + + return proc_stat_pid (stat->ps); +} + +char * +ck_process_stat_get_cmd (CkProcessStat *stat) +{ + g_return_val_if_fail (stat != NULL, NULL); + + return g_strdup (proc_stat_args (stat->ps)); +} + +char * +ck_process_stat_get_tty (CkProcessStat *stat) +{ + struct ps_tty *tty; + + g_return_val_if_fail (stat != NULL, NULL); + + tty = proc_stat_tty (stat->ps); + + return tty ? g_strdup (ps_tty_name (tty)) : NULL; +} + +gboolean +ck_process_stat_new_for_unix_pid (pid_t pid, + CkProcessStat **stat, + GError **error) +{ + gboolean res; + struct proc_stat *ps; + CkProcessStat *proc; + + g_return_val_if_fail (pid > 1, FALSE); + + if (stat == NULL) { + return FALSE; + } + + *stat = NULL; + + res = get_proc_stat_from_pid (pid, PSTAT_ARGS | PSTAT_TTY, &ps); + if (!res) { + return FALSE; + } + + proc = g_new0 (CkProcessStat, 1); + proc->ps = ps; + *stat = proc; + + return TRUE; +} + +void +ck_process_stat_free (CkProcessStat *stat) +{ + _proc_stat_free (stat->ps); + + g_free (stat); +} + +GHashTable * +ck_unix_pid_get_env_hash (pid_t pid) +{ + struct proc_stat *ps; + char *env_p; + size_t env_index; + size_t env_l; + gboolean res; + GHashTable *hash; + + g_return_val_if_fail (pid > 1, NULL); + + res = get_proc_stat_from_pid (pid, PSTAT_ENV, &ps); + if (!res) { + return NULL; + } + + hash = g_hash_table_new_full (g_str_hash, + g_str_equal, + g_free, + g_free); + + env_index = 0; + env_l = 0; + env_p = proc_stat_env (ps); + while (env_index < proc_stat_env_len (ps)) { + env_l = strlen (env_p); + env_index += env_l + 1; + if (env_l) { + char **vals; + vals = g_strsplit (env_p, "=", 2); + if (vals != NULL) { + g_hash_table_insert (hash, + g_strdup (vals[0]), + g_strdup (vals[1])); + g_strfreev (vals); + } + } + env_p = env_p + env_l + 1; + } + + _proc_stat_free (ps); + + return hash; +} + +char * +ck_unix_pid_get_env (pid_t pid, + const char *var) +{ + struct proc_stat *ps; + char *env_p; + size_t env_index; + size_t env_l; + char *prefix; + int prefix_len; + char *val; + gboolean res; + + g_return_val_if_fail (pid > 1, NULL); + + val = NULL; + + res = get_proc_stat_from_pid (pid, PSTAT_ENV, &ps); + if (!res) { + return NULL; + } + + prefix = g_strdup_printf ("%s=", var); + prefix_len = strlen (prefix); + + env_index = 0; + env_l = 0; + env_p = proc_stat_env (ps); + while (env_index < proc_stat_env_len (ps)) { + env_l = strlen (env_p); + env_index += env_l + 1; + if (env_l && g_str_has_prefix (env_p, prefix)) { + val = g_strdup (env_p + prefix_len); + break; + } + env_p = env_p + env_l + 1; + } + + g_free (prefix); + + _proc_stat_free (ps); + + return val; +} + +uid_t +ck_unix_pid_get_uid (pid_t pid) +{ + struct proc_stat *ps; + gboolean res; + uid_t uid; + + g_return_val_if_fail (pid > 1, 0); + + res = get_proc_stat_from_pid (pid, PSTAT_OWNER_UID, &ps); + if (!res) { + return 0; + } + + uid = proc_stat_owner_uid (ps); + + _proc_stat_free (ps); + + return uid; +} + +pid_t +ck_unix_pid_get_ppid (pid_t pid) +{ + struct proc_stat *ps; + gboolean res; + pid_t ppid; + + g_return_val_if_fail (pid > 1, 0); + + res = get_proc_stat_from_pid (pid, PSTAT_PROC_INFO, &ps); + if (!res) { + return 0; + } + + ppid = proc_stat_proc_info (ps)->ppid; + + _proc_stat_free (ps); + + return ppid; +} + +gboolean +ck_unix_pid_get_login_session_id (pid_t pid, + char **idp) +{ + g_return_val_if_fail (pid > 1, FALSE); + + return FALSE; +} + +gboolean +ck_get_max_num_consoles (guint *num) +{ + int max_consoles; + int res; + gboolean ret; + struct ttyent *t; + + ret = FALSE; + max_consoles = 0; + + res = setttyent (); + if (res == 0) { + goto done; + } + + while ((t = getttyent ()) != NULL) { + if (t->ty_status & TTY_ON && strncmp (t->ty_name, "tty", 3) == 0) + max_consoles++; + } + + /* Increment one more so that all consoles are properly counted + * this is arguable a bug in vt_add_watches(). + */ + max_consoles++; + + ret = TRUE; + + endttyent (); + +done: + if (num != NULL) { + *num = max_consoles; + } + + return ret; +} + +gboolean +ck_supports_activatable_consoles (void) +{ + return TRUE; +} + +char * +ck_get_console_device_for_num (guint num) +{ + char *device; + + device = g_strdup_printf (_PATH_TTY "%u", num); + + return device; +} + +gboolean +ck_get_console_num_from_device (const char *device, + guint *num) +{ + guint n; + gboolean ret; + + n = 0; + ret = FALSE; + + if (device == NULL) { + return FALSE; + } + + if (sscanf (device, _PATH_TTY "%u", &n) == 1) { + ret = TRUE; + } + + if (num != NULL) { + *num = n; + } + + return ret; +} + +gboolean +ck_get_active_console_num (int console_fd, + guint *num) +{ + gboolean ret; + int res; + long cur_active; + char buf[30]; + guint active; + + g_assert (console_fd != -1); + + active = 0; + ret = FALSE; + + res = readlink ("/dev/cons/vcs", buf, sizeof (buf)); + if (res > 0) { + /* the resolved path is like "/dev/vcs/$number", so skip + the non-number part at the start */ + const char *p = buf; + while ((*p) && ((*p < '0') || (*p > '9'))) { + ++p; + } + if (*p) { + cur_active = strtol (p, NULL, 10); + g_debug ("Current VT: tty%ld", cur_active); + active = cur_active; + ret = TRUE; + } + } + + if (num != NULL) { + *num = active; + } + + return ret; +} diff --git a/tools/70-udev-acl.rules b/tools/70-udev-acl.rules new file mode 100644 index 0000000..2dac283 --- /dev/null +++ b/tools/70-udev-acl.rules @@ -0,0 +1,76 @@ +# do not edit this file, it will be overwritten on update + +# Do not use TAG+="udev-acl" 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+="udev-acl" + +# digicams with proprietary protocol +ENV{ID_GPHOTO2}=="*?", TAG+="udev-acl" + +# SCSI and USB scanners +ENV{libsane_matched}=="yes", TAG+="udev-acl" + +# HPLIP devices (necessary for ink level check and HP tool maintenance) +ENV{ID_HPLIP}=="1", TAG+="udev-acl" + +# optical drives +SUBSYSTEM=="block", ENV{ID_CDROM}=="1", TAG+="udev-acl" +SUBSYSTEM=="scsi_generic", SUBSYSTEMS=="scsi", ATTRS{type}=="4|5", TAG+="udev-acl" + +# sound devices +SUBSYSTEM=="sound", TAG+="udev-acl" + +# ffado is an userspace driver for firewire sound cards +SUBSYSTEM=="firewire", ENV{ID_FFADO}=="1", TAG+="udev-acl" + +# webcams, frame grabber, TV cards +SUBSYSTEM=="video4linux", TAG+="udev-acl" +SUBSYSTEM=="dvb", TAG+="udev-acl" + +# IIDC devices: industrial cameras and some webcams +SUBSYSTEM=="firewire", ATTR{units}=="*0x00a02d:0x00010*", TAG+="udev-acl" +SUBSYSTEM=="firewire", ATTR{units}=="*0x00b09d:0x00010*", TAG+="udev-acl" +# AV/C devices: camcorders, set-top boxes, TV sets, audio devices, and more +SUBSYSTEM=="firewire", ATTR{units}=="*0x00a02d:0x010001*", TAG+="udev-acl" +SUBSYSTEM=="firewire", ATTR{units}=="*0x00a02d:0x014001*", TAG+="udev-acl" + +# DRI video devices +SUBSYSTEM=="drm", KERNEL=="card*", TAG+="udev-acl" + +# KVM +SUBSYSTEM=="misc", KERNEL=="kvm", TAG+="udev-acl" + +# smart-card readers +ENV{ID_SMARTCARD_READER}=="*?", TAG+="udev-acl" + +# PDA devices +ENV{ID_PDA}=="*?", TAG+="udev-acl" + +# Programmable remote control +ENV{ID_REMOTE_CONTROL}=="1", TAG+="udev-acl" + +# joysticks +SUBSYSTEM=="input", ENV{ID_INPUT_JOYSTICK}=="?*", TAG+="udev-acl" + +# color measurement devices +ENV{COLOR_MEASUREMENT_DEVICE}=="*?", TAG+="udev-acl" + +# DDC/CI device, usually high-end monitors such as the DreamColor +ENV{DDC_DEVICE}=="*?", TAG+="udev-acl" + +# media player raw devices (for user-mode drivers, Android SDK, etc.) +SUBSYSTEM=="usb", ENV{ID_MEDIA_PLAYER}=="?*", TAG+="udev-acl" + +# apply ACL for all locally logged in users +LABEL="acl_apply", TAG=="udev-acl", TEST=="/var/run/ConsoleKit/database", \ + RUN+="udev-acl --action=$env{ACTION} --device=$env{DEVNAME}" + +LABEL="acl_end" diff --git a/tools/Makefile.am b/tools/Makefile.am index 13c191f..88f3c91 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -143,6 +143,21 @@ 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) + +install-exec-hook: + mkdir -p $(DESTDIR)$(prefix)/lib/ConsoleKit/run-seat.d + ln -sf $(UDEVDIR)/udev-acl $(DESTDIR)$(prefix)/lib/ConsoleKit/run-seat.d/udev-acl.ck +endif EXTRA_DIST = \ $(NULL) diff --git a/tools/ck-history.c b/tools/ck-history.c index d02caaa..85d9e6f 100644 --- a/tools/ck-history.c +++ b/tools/ck-history.c @@ -804,7 +804,7 @@ generate_report_frequent (int uid, data = user_counts->data; username = get_user_name_for_uid (data->uid); - g_print ("%-8.8s %u\n", username, data->count); + g_print ("%-8s %u\n", username, data->count); g_free (data); user_counts = g_list_delete_link (user_counts, user_counts); g_free (username); diff --git a/tools/udev-acl.c b/tools/udev-acl.c new file mode 100644 index 0000000..628cfbe --- /dev/null +++ b/tools/udev-acl.c @@ -0,0 +1,430 @@ +/* + * Copyright (C) 2009 Kay Sievers + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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, "udev-acl"); + 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; +}