From 5eda94dda25bccda928c4b33c790dbe748573a22 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 24 May 2011 04:20:35 +0200 Subject: [PATCH] logind: implement ACL management --- .gitignore | 1 + Makefile.am | 24 +++- configure.ac | 38 ++++++ src/logind-acl.c | 282 +++++++++++++++++++++++++++++++++++++++++++ src/logind-acl.h | 40 ++++++ src/logind-seat.c | 45 ++++--- src/logind-seat.h | 4 +- src/logind-session.c | 7 +- src/logind.c | 34 +++++- src/logind.h | 5 +- src/uaccess.c | 87 +++++++++++++ 11 files changed, 533 insertions(+), 34 deletions(-) create mode 100644 src/logind-acl.c create mode 100644 src/logind-acl.h create mode 100644 src/uaccess.c diff --git a/.gitignore b/.gitignore index c2820b03e..7bd22c521 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 79ccbbe6e..f455f2110 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 eb5fb6a97..0dd185b25 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 000000000..3df104ff2 --- /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 . +***/ + +#include +#include +#include +#include +#include + +#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 000000000..9c88a8064 --- /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 . +***/ + +#include +#include +#include + +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 315490043..743ded66b 100644 --- a/src/logind-seat.c +++ b/src/logind-seat.c @@ -25,8 +25,10 @@ #include #include #include +#include #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 2fe7949bd..4b6b3401d 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 7bdf487e8..c10f5e62c 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 0c26aaddb..7e9b706d1 100644 --- a/src/logind.c +++ b/src/logind.c @@ -26,6 +26,8 @@ #include #include #include +#include +#include #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 0d3bd899b..be5dab746 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 000000000..e55ab51f3 --- /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 . +***/ + +#include +#include + +#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; +} -- 2.30.2