diff options
author | Lennart Poettering <lennart@poettering.net> | 2011-05-24 04:20:35 +0200 |
---|---|---|
committer | Lennart Poettering <lennart@poettering.net> | 2011-06-21 19:29:44 +0200 |
commit | 5eda94dda25bccda928c4b33c790dbe748573a22 (patch) | |
tree | dd5402db68b03e448873b97c3c2e0cd9582272db /src/logind-acl.c | |
parent | 90821c935e5f4258dc21fc515cf721beb0914a85 (diff) |
logind: implement ACL management
Diffstat (limited to 'src/logind-acl.c')
-rw-r--r-- | src/logind-acl.c | 282 |
1 files changed, 282 insertions, 0 deletions
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; +} |