logind: implement ACL management

This commit is contained in:
Lennart Poettering 2011-05-24 04:20:35 +02:00
parent 90821c935e
commit 5eda94dda2
11 changed files with 535 additions and 36 deletions

1
.gitignore vendored
View file

@ -1,3 +1,4 @@
systemd-uaccess
systemd-logind
systemd-hostnamed
systemd-binfmt

View file

@ -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 \

View file

@ -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}

282
src/logind-acl.c Normal file
View file

@ -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;
}

40
src/logind-acl.h Normal file
View file

@ -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

View file

@ -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) {
int seat_apply_acls(Seat *s, Session *old_active) {
int r;
assert(s);
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);
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);
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;
if (vt_is_busy(vtnr) == 0)
manager_spawn_autovt(s->manager, vtnr);
seat_apply_acls(s, old_active);
manager_spawn_autovt(s->manager, vtnr);
return 0;
}
int seat_stop(Seat *s) {

View file

@ -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);

View file

@ -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) {

View file

@ -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();

View file

@ -36,11 +36,12 @@
*
* recreate VTs when disallocated
* udev rules
* spawn user systemd
* non-local X11 server
* udev-acl
* reboot/shutdown halt management
* PAM rewrite
* spawn user systemd
* dbus API
*
* non-local X11 server
* reboot/shutdown halt management
*/
typedef struct Manager Manager;

87
src/uaccess.c Normal file
View file

@ -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;
}