summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2011-05-24 04:20:35 +0200
committerLennart Poettering <lennart@poettering.net>2011-06-21 19:29:44 +0200
commit5eda94dda25bccda928c4b33c790dbe748573a22 (patch)
treedd5402db68b03e448873b97c3c2e0cd9582272db
parent90821c935e5f4258dc21fc515cf721beb0914a85 (diff)
logind: implement ACL management
-rw-r--r--.gitignore1
-rw-r--r--Makefile.am24
-rw-r--r--configure.ac38
-rw-r--r--src/logind-acl.c282
-rw-r--r--src/logind-acl.h40
-rw-r--r--src/logind-seat.c45
-rw-r--r--src/logind-seat.h4
-rw-r--r--src/logind-session.c7
-rw-r--r--src/logind.c34
-rw-r--r--src/logind.h5
-rw-r--r--src/uaccess.c87
11 files changed, 533 insertions, 34 deletions
diff --git a/.gitignore b/.gitignore
index c2820b03e2..7bd22c5211 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
+systemd-uaccess
systemd-logind
systemd-hostnamed
systemd-binfmt
diff --git a/Makefile.am b/Makefile.am
index 79ccbbe6e6..f455f21109 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -159,7 +159,8 @@ rootlibexec_PROGRAMS = \
systemd-detect-virt \
systemd-sysctl \
systemd-hostnamed \
- systemd-logind
+ systemd-logind \
+ systemd-uaccess
if ENABLE_BINFMT
rootlibexec_PROGRAMS += \
@@ -813,6 +814,7 @@ systemd_logind_SOURCES = \
src/logind-seat.c \
src/logind-session.c \
src/logind-user.c \
+ src/logind-acl.c \
src/dbus-common.c \
src/dbus-loop.c \
src/cgroup-util.c
@@ -820,13 +822,29 @@ systemd_logind_SOURCES = \
systemd_logind_CFLAGS = \
$(AM_CFLAGS) \
$(DBUS_CFLAGS) \
- $(UDEV_CFLAGS)
+ $(UDEV_CFLAGS) \
+ $(ACL_CFLAGS)
systemd_logind_LDADD = \
libsystemd-basic.la \
libsystemd-daemon.la \
$(DBUS_LIBS) \
- $(UDEV_LIBS)
+ $(UDEV_LIBS) \
+ $(ACL_LIBS)
+
+systemd_uaccess_SOURCES = \
+ src/uaccess.c \
+ src/logind-acl.c
+
+systemd_uaccess_CFLAGS = \
+ $(AM_CFLAGS) \
+ $(UDEV_CFLAGS) \
+ $(ACL_CFLAGS)
+
+systemd_uaccess_LDADD = \
+ libsystemd-basic.la \
+ $(UDEV_LIBS) \
+ $(ACL_LIBS)
systemd_shutdown_SOURCES = \
src/mount-setup.c \
diff --git a/configure.ac b/configure.ac
index eb5fb6a975..0dd185b258 100644
--- a/configure.ac
+++ b/configure.ac
@@ -193,6 +193,43 @@ fi
AC_SUBST(PAM_LIBS)
AM_CONDITIONAL([HAVE_PAM], [test "x$have_pam" != xno])
+AC_ARG_ENABLE([acl],
+ AS_HELP_STRING([--disable-acl],[Disable optional ACL support]),
+ [case "${enableval}" in
+ yes) have_acl=yes ;;
+ no) have_acl=no ;;
+ *) AC_MSG_ERROR(bad value ${enableval} for --disable-acl) ;;
+ esac],
+ [have_acl=auto])
+
+if test "x${have_acl}" != xno ; then
+ AC_CHECK_HEADERS(
+ [sys/acl.h acl/libacl.h],
+ [have_acl=yes],
+ [if test "x$have_acl" = xyes ; then
+ AC_MSG_ERROR([*** ACL headers not found.])
+ fi])
+
+ AC_CHECK_LIB(
+ [acl],
+ [acl_get_file],
+ [have_acl=yes],
+ [if test "x$have_acl" = xyes ; then
+ AC_MSG_ERROR([*** libacl not found.])
+ fi])
+
+ if test "x$have_acl" = xyes ; then
+ ACL_LIBS="-lacl"
+ AC_DEFINE(HAVE_ACL, 1, [ACL available])
+ else
+ have_acl=no
+ fi
+else
+ ACL_LIBS=
+fi
+AC_SUBST(ACL_LIBS)
+AM_CONDITIONAL([HAVE_ACL], [test "x$have_acl" != xno])
+
AC_ARG_ENABLE([audit],
AS_HELP_STRING([--disable-audit],[Disable optional AUDIT support]),
[case "${enableval}" in
@@ -496,6 +533,7 @@ echo "
PAM: ${have_pam}
AUDIT: ${have_audit}
SELinux: ${have_selinux}
+ ACL: ${have_acl}
binfmt: ${have_binfmt}
prefix: ${prefix}
root dir: ${with_rootdir}
diff --git a/src/logind-acl.c b/src/logind-acl.c
new file mode 100644
index 0000000000..3df104ff2c
--- /dev/null
+++ b/src/logind-acl.c
@@ -0,0 +1,282 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2011 Lennart Poettering
+
+ systemd 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.
+
+ systemd 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 systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <assert.h>
+#include <sys/acl.h>
+#include <acl/libacl.h>
+#include <errno.h>
+#include <string.h>
+
+#include "logind-acl.h"
+#include "util.h"
+
+static int find_acl(acl_t acl, uid_t uid, acl_entry_t *entry) {
+ acl_entry_t i;
+ int found;
+
+ assert(acl);
+ assert(entry);
+
+ for (found = acl_get_entry(acl, ACL_FIRST_ENTRY, &i);
+ found > 0;
+ found = acl_get_entry(acl, ACL_NEXT_ENTRY, &i)) {
+
+ acl_tag_t tag;
+ uid_t *u;
+ bool b;
+
+ if (acl_get_tag_type(i, &tag) < 0)
+ return -errno;
+
+ if (tag != ACL_USER)
+ continue;
+
+ u = acl_get_qualifier(i);
+ if (!u)
+ return -errno;
+
+ b = *u == uid;
+ free(u);
+
+ if (b) {
+ *entry = i;
+ return 1;
+ }
+ }
+
+ if (found < 0)
+ return -errno;
+
+ return 0;
+}
+
+static int flush_acl(acl_t acl) {
+ acl_entry_t i;
+ int found;
+ bool changed = false;
+
+ assert(acl);
+
+ for (found = acl_get_entry(acl, ACL_FIRST_ENTRY, &i);
+ found > 0;
+ found = acl_get_entry(acl, ACL_NEXT_ENTRY, &i)) {
+
+ acl_tag_t tag;
+
+ if (acl_get_tag_type(i, &tag) < 0)
+ return -errno;
+
+ if (tag != ACL_USER)
+ continue;
+
+ if (acl_delete_entry(acl, i) < 0)
+ return -errno;
+
+ changed = true;
+ }
+
+ if (found < 0)
+ return -errno;
+
+ return changed;
+}
+
+int devnode_acl(const char *path,
+ bool flush,
+ bool del, uid_t old_uid,
+ bool add, uid_t new_uid) {
+
+ acl_t acl;
+ int r;
+ bool changed = false;
+
+ assert(path);
+
+ acl = acl_get_file(path, ACL_TYPE_ACCESS);
+ if (!acl)
+ return -errno;
+
+ if (flush) {
+
+ r = flush_acl(acl);
+ if (r < 0)
+ goto finish;
+ if (r > 0)
+ changed = true;
+
+ } else if (del && old_uid > 0) {
+ acl_entry_t entry;
+
+ r = find_acl(acl, old_uid, &entry);
+ if (r < 0)
+ goto finish;
+
+ if (r > 0) {
+ if (acl_delete_entry(acl, entry) < 0) {
+ r = -errno;
+ goto finish;
+ }
+
+ changed = true;
+ }
+ }
+
+ if (add && new_uid > 0) {
+ acl_entry_t entry;
+ acl_permset_t permset;
+ int rd, wt;
+
+ r = find_acl(acl, new_uid, &entry);
+ if (r < 0)
+ goto finish;
+
+ if (r == 0) {
+ if (acl_create_entry(&acl, &entry) < 0) {
+ r = -errno;
+ goto finish;
+ }
+
+ if (acl_set_tag_type(entry, ACL_USER) < 0 ||
+ acl_set_qualifier(entry, &new_uid) < 0) {
+ r = -errno;
+ goto finish;
+ }
+ }
+
+ if (acl_get_permset(entry, &permset) < 0) {
+ r = -errno;
+ goto finish;
+ }
+
+ rd = acl_get_perm(permset, ACL_READ);
+ if (rd < 0) {
+ r = -errno;
+ goto finish;
+ }
+
+ wt = acl_get_perm(permset, ACL_WRITE);
+ if (wt < 0) {
+ r = -errno;
+ goto finish;
+ }
+
+ if (!rd || !wt) {
+
+ if (acl_add_perm(permset, ACL_READ|ACL_WRITE) < 0) {
+ r = -errno;
+ goto finish;
+ }
+
+ changed = true;
+ }
+ }
+
+ if (!changed)
+ goto finish;
+
+ if (acl_calc_mask(&acl) < 0) {
+ r = -errno;
+ goto finish;
+ }
+
+ if (acl_set_file(path, ACL_TYPE_ACCESS, acl) < 0) {
+ r = -errno;
+ goto finish;
+ }
+
+ r = 0;
+
+finish:
+ acl_free(acl);
+
+ return r;
+}
+
+int devnode_acl_all(struct udev *udev,
+ const char *seat,
+ bool flush,
+ bool del, uid_t old_uid,
+ bool add, uid_t new_uid) {
+
+ struct udev_list_entry *item = NULL, *first = NULL;
+ struct udev_enumerate *e;
+ int r;
+
+ assert(udev);
+
+ if (!seat)
+ seat = "seat0";
+
+ e = udev_enumerate_new(udev);
+ if (!e)
+ return -ENOMEM;
+
+ r = udev_enumerate_add_match_tag(e, "uaccess");
+ if (r < 0)
+ goto finish;
+
+ r = udev_enumerate_add_match_tag(e, seat);
+ if (r < 0)
+ goto finish;
+
+ r = udev_enumerate_scan_devices(e);
+ if (r < 0)
+ goto finish;
+
+ first = udev_enumerate_get_list_entry(e);
+ udev_list_entry_foreach(item, first) {
+ struct udev_device *d;
+ const char *node, *sn;
+
+ d = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item));
+ if (!d) {
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ sn = udev_device_get_property_value(d, "SEAT");
+ if (!sn)
+ sn = "seat0";
+
+ if (!streq(seat, sn)) {
+ udev_device_unref(d);
+ continue;
+ }
+
+ node = udev_device_get_devnode(d);
+ udev_device_unref(d);
+
+ if (!node) {
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ r = devnode_acl(node, flush, del, old_uid, add, new_uid);
+ if (r < 0)
+ goto finish;
+ }
+
+finish:
+ if (e)
+ udev_enumerate_unref(e);
+
+ return r;
+}
diff --git a/src/logind-acl.h b/src/logind-acl.h
new file mode 100644
index 0000000000..9c88a80644
--- /dev/null
+++ b/src/logind-acl.h
@@ -0,0 +1,40 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef foologindaclhfoo
+#define foologindaclhfoo
+
+/***
+ This file is part of systemd.
+
+ Copyright 2011 Lennart Poettering
+
+ systemd 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.
+
+ systemd 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 systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <sys/types.h>
+#include <stdbool.h>
+#include <libudev.h>
+
+int devnode_acl(const char *path,
+ bool flush,
+ bool del, uid_t old_uid,
+ bool add, uid_t new_uid);
+
+int devnode_acl_all(struct udev *udev,
+ const char *seat,
+ bool flush,
+ bool del, uid_t old_uid,
+ bool add, uid_t new_uid);
+
+#endif
diff --git a/src/logind-seat.c b/src/logind-seat.c
index 315490043d..743ded66bd 100644
--- a/src/logind-seat.c
+++ b/src/logind-seat.c
@@ -25,8 +25,10 @@
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/vt.h>
+#include <string.h>
#include "logind-seat.h"
+#include "logind-acl.h"
#include "util.h"
Seat *seat_new(Manager *m, const char *id) {
@@ -179,40 +181,32 @@ int seat_preallocate_vts(Seat *s) {
return r;
}
-int seat_apply_acls(Seat *s) {
- assert(s);
-
-
- return 0;
-}
-
-static int vt_is_busy(int vtnr) {
- struct vt_stat vt_stat;
- int r = 0, fd;
-
- assert(vtnr >= 1);
+int seat_apply_acls(Seat *s, Session *old_active) {
+ int r;
- fd = open_terminal("/dev/tty0", O_RDWR|O_NOCTTY|O_CLOEXEC);
- if (fd < 0)
- return -errno;
+ assert(s);
- if (ioctl(fd, VT_GETSTATE, &vt_stat) < 0)
- r = -errno;
- else
- r = !!(vt_stat.v_state & (1 << vtnr));
+ r = devnode_acl_all(s->manager->udev,
+ s->id,
+ false,
+ !!old_active, old_active ? old_active->user->uid : 0,
+ !!s->active, s->active ? s->active->user->uid : 0);
- close_nointr_nofail(fd);
+ if (r < 0)
+ log_error("Failed to apply ACLs: %s", strerror(-r));
return r;
}
-void seat_active_vt_changed(Seat *s, int vtnr) {
+int seat_active_vt_changed(Seat *s, int vtnr) {
Session *i;
+ Session *old_active;
assert(s);
assert(vtnr >= 1);
assert(s->manager->vtconsole == s);
+ old_active = s->active;
s->active = NULL;
LIST_FOREACH(sessions_by_seat, i, s->sessions)
@@ -221,10 +215,13 @@ void seat_active_vt_changed(Seat *s, int vtnr) {
break;
}
- seat_apply_acls(s);
+ if (old_active == s->active)
+ return 0;
+
+ seat_apply_acls(s, old_active);
+ manager_spawn_autovt(s->manager, vtnr);
- if (vt_is_busy(vtnr) == 0)
- manager_spawn_autovt(s->manager, vtnr);
+ return 0;
}
int seat_stop(Seat *s) {
diff --git a/src/logind-seat.h b/src/logind-seat.h
index 2fe7949bd9..4b6b3401d2 100644
--- a/src/logind-seat.h
+++ b/src/logind-seat.h
@@ -45,8 +45,8 @@ struct Seat {
Seat *seat_new(Manager *m, const char *id);
void seat_free(Seat *s);
int seat_preallocate_vts(Seat *s);
-void seat_active_vt_changed(Seat *s, int vtnr);
-int seat_apply_acls(Seat *s);
+int seat_active_vt_changed(Seat *s, int vtnr);
+int seat_apply_acls(Seat *s, Session *old_active);
int seat_stop(Seat *s);
int seat_save(Seat *s);
int seat_load(Seat *s);
diff --git a/src/logind-session.c b/src/logind-session.c
index 7bdf487e8d..c10f5e62c9 100644
--- a/src/logind-session.c
+++ b/src/logind-session.c
@@ -174,6 +174,7 @@ int session_load(Session *s) {
int session_activate(Session *s) {
int r;
+ Session *old_active;
assert(s);
@@ -192,9 +193,13 @@ int session_activate(Session *s) {
if (r < 0)
return r;
+ old_active = s->seat->active;
s->seat->active = s;
- return seat_apply_acls(s->seat);
+ seat_apply_acls(s->seat, old_active);
+ manager_spawn_autovt(s->manager, s->vtnr);
+
+ return 0;
}
bool x11_display_is_local(const char *display) {
diff --git a/src/logind.c b/src/logind.c
index 0c26aaddbf..7e9b706d13 100644
--- a/src/logind.c
+++ b/src/logind.c
@@ -26,6 +26,8 @@
#include <string.h>
#include <unistd.h>
#include <sys/epoll.h>
+#include <sys/ioctl.h>
+#include <linux/vt.h>
#include "logind.h"
#include "dbus-common.h"
@@ -290,7 +292,7 @@ int manager_enumerate_devices(Manager *m) {
e = udev_enumerate_new(m->udev);
if (!e)
- return -ENOMEM;
+ goto finish;
if (udev_enumerate_add_match_subsystem(e, "graphics") < 0)
goto finish;
@@ -627,9 +629,37 @@ int manager_dispatch_console(Manager *m) {
return 0;
}
+static int vt_is_busy(int vtnr) {
+ struct vt_stat vt_stat;
+ int r = 0, fd;
+
+ assert(vtnr >= 1);
+
+ fd = open_terminal("/dev/tty0", O_RDWR|O_NOCTTY|O_CLOEXEC);
+ if (fd < 0)
+ return -errno;
+
+ if (ioctl(fd, VT_GETSTATE, &vt_stat) < 0)
+ r = -errno;
+ else
+ r = !!(vt_stat.v_state & (1 << vtnr));
+
+ close_nointr_nofail(fd);
+
+ return r;
+}
+
int manager_spawn_autovt(Manager *m, int vtnr) {
+ int r;
+
assert(m);
+ r = vt_is_busy(vtnr);
+ if (r != 0)
+ return r;
+
+ /* ... */
+
return 0;
}
@@ -849,7 +879,7 @@ int manager_run(Manager *m) {
int main(int argc, char *argv[]) {
Manager *m = NULL;
- int r = 0;
+ int r;
log_set_target(LOG_TARGET_AUTO);
log_parse_environment();
diff --git a/src/logind.h b/src/logind.h
index 0d3bd899b2..be5dab7464 100644
--- a/src/logind.h
+++ b/src/logind.h
@@ -36,11 +36,12 @@
*
* recreate VTs when disallocated
* udev rules
+ * PAM rewrite
* spawn user systemd
+ * dbus API
+ *
* non-local X11 server
- * udev-acl
* reboot/shutdown halt management
- * PAM rewrite
*/
typedef struct Manager Manager;
diff --git a/src/uaccess.c b/src/uaccess.c
new file mode 100644
index 0000000000..e55ab51f34
--- /dev/null
+++ b/src/uaccess.c
@@ -0,0 +1,87 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2011 Lennart Poettering
+
+ systemd 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.
+
+ systemd 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 systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <errno.h>
+#include <string.h>
+
+#include "logind-acl.h"
+#include "util.h"
+#include "log.h"
+
+int main(int argc, char *argv[]) {
+ int r;
+ const char *path, *seat;
+ char *p, *active_uid = NULL;
+ unsigned long ul;
+
+ log_set_target(LOG_TARGET_AUTO);
+ log_parse_environment();
+ log_open();
+
+ if (argc != 2) {
+ log_error("This program expects two argument.");
+ r = -EINVAL;
+ goto finish;
+ }
+
+ path = argv[1];
+ seat = argv[2];
+
+ p = strappend("/run/systemd/seat/", seat);
+ if (!p) {
+ log_error("Out of memory.");
+ goto finish;
+ }
+
+ r = parse_env_file(p, NEWLINE,
+ "ACTIVE_UID", &active_uid,
+ NULL);
+ free(p);
+
+ if (r < 0) {
+ if (errno == ENOENT) {
+ r = 0;
+ goto finish;
+ }
+
+ log_error("Failed to read seat data for %s: %s", seat, strerror(-r));
+ goto finish;
+ }
+
+ r = safe_atolu(active_uid, &ul);
+ if (r < 0) {
+ log_error("Failed to parse active UID value %s: %s", active_uid, strerror(-r));
+ goto finish;
+ }
+
+ r = devnode_acl(path, true, false, 0, true, (uid_t) ul);
+ if (r < 0) {
+ log_error("Failed to apply ACL on %s: %s", path, strerror(-r));
+ goto finish;
+ }
+
+ r = 0;
+
+finish:
+ free(active_uid);
+
+ return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+}