aboutsummaryrefslogtreecommitdiff
path: root/security/trustees
diff options
context:
space:
mode:
authorDavid Barksdale <amatus@amatus.name>2014-08-13 16:14:13 -0500
committerDavid Barksdale <amatus@amatus.name>2014-08-13 16:14:13 -0500
commitace6c6d243016e272050787c14e27a83ecd94a25 (patch)
treec837edb1ca98b2552fbc7edba47aeb63f98ca1f0 /security/trustees
parent1b6e1688bd215cd7c9cb75650fa815a1ec6567e1 (diff)
Diffstat (limited to 'security/trustees')
-rwxr-xr-xsecurity/trustees/Makefile7
-rwxr-xr-xsecurity/trustees/fs.c273
-rwxr-xr-xsecurity/trustees/funcs.c812
-rwxr-xr-xsecurity/trustees/init.c57
-rwxr-xr-xsecurity/trustees/internal.h101
-rwxr-xr-xsecurity/trustees/security.c417
6 files changed, 1667 insertions, 0 deletions
diff --git a/security/trustees/Makefile b/security/trustees/Makefile
new file mode 100755
index 00000000000..0613ea613c9
--- /dev/null
+++ b/security/trustees/Makefile
@@ -0,0 +1,7 @@
+ifeq ($(CONFIG_SECURITY_TRUSTEES_DEBUG),y)
+ EXTRA_CFLAGS += -DTRUSTEES_DEBUG
+endif
+
+obj-$(CONFIG_SECURITY_TRUSTEES) := trustees.o
+
+trustees-y := security.o fs.o init.o funcs.o
diff --git a/security/trustees/fs.c b/security/trustees/fs.c
new file mode 100755
index 00000000000..6463c64631a
--- /dev/null
+++ b/security/trustees/fs.c
@@ -0,0 +1,273 @@
+/*
+ * Trustees ACL Project
+ *
+ * Copyright (c) 1999-2000 Vyacheslav Zavadsky
+ * Copyright (c) 2004 Andrew Ruder (aeruder@ksu.edu)
+ *
+ * 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, version 2.
+ *
+ * This code handles the virtual filesystem for trustees.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/vmalloc.h>
+#include <linux/security.h>
+#include <asm/atomic.h>
+#include <asm/uaccess.h>
+
+#include "internal.h"
+
+
+/* initialization code for the trustees filesystem */
+
+/* File operations
+ *
+ * this is all the code for handling the file operations done on the few files
+ * in the trustees filesystem
+ */
+static int trustees_open(struct inode *inode, struct file *filp);
+static ssize_t trustees_read_bogus(struct file *filp, char __user * buf,
+ size_t count, loff_t * offset);
+static ssize_t trustees_write_bogus(struct file *filp,
+ const char __user * buf, size_t count,
+ loff_t * offset);
+static ssize_t trustees_read_status(struct file *filp, char __user * buf,
+ size_t count, loff_t * offset);
+static ssize_t trustees_read_apiversion(struct file *filp, char __user * buf,
+ size_t count, loff_t * offset);
+static ssize_t trustees_write_trustees(struct file *filp,
+ const char __user * buf,
+ size_t count, loff_t * offset);
+static int trustees_open_trustees(struct inode *inode, struct file *file);
+static int trustees_release_trustees(struct inode *inode, struct file *file);
+
+/* Various structs
+ */
+
+static struct file_operations trustees_ops_apiversion = {
+ .open = trustees_open,
+ .read = trustees_read_apiversion,
+ .write = trustees_write_bogus,
+};
+
+static struct file_operations trustees_ops_status = {
+ .open = trustees_open,
+ .read = trustees_read_status,
+ .write = trustees_write_bogus
+};
+
+static struct file_operations trustees_ops_trustees = {
+ .open = trustees_open_trustees,
+ .read = trustees_read_bogus,
+ .write = trustees_write_trustees,
+ .release = trustees_release_trustees
+};
+
+static struct trustees_file_info {
+ const char *name;
+ struct file_operations *fops;
+ int mode;
+ struct dentry *dentry;
+} trustees_files[] = {
+ {.name = "device",
+ .fops = &trustees_ops_trustees,
+ .mode = S_IWUSR,
+ .dentry = 0
+ },
+ {.name = "status",
+ .fops = &trustees_ops_status,
+ .mode = S_IRUSR,
+ .dentry = 0
+ },
+ {.name = "apiversion",
+ .fops = &trustees_ops_apiversion,
+ .mode = S_IRUSR | S_IRGRP | S_IROTH,
+ .dentry = 0
+ },
+ {"", NULL, 0, 0}
+};
+
+struct trustee_command_reader {
+ struct trustee_command command;
+ unsigned curarg;
+ void *arg[TRUSTEE_MAX_ARGS];
+ size_t argsize[TRUSTEE_MAX_ARGS];
+};
+
+
+static struct dentry *toplevel = NULL;
+
+int trustees_init_fs(void)
+{
+ struct trustees_file_info *iter;
+ toplevel = securityfs_create_dir("trustees", NULL);
+ if (!toplevel) trustees_deinit_fs();
+ for (iter = trustees_files; iter->fops && toplevel; iter++) {
+ iter->dentry = securityfs_create_file(
+ iter->name, iter->mode, toplevel, NULL, iter->fops);
+ if (!iter->dentry) trustees_deinit_fs();
+ }
+ return !toplevel;
+}
+
+void trustees_deinit_fs(void)
+{
+ struct trustees_file_info *iter;
+ for (iter = trustees_files; iter->fops; iter++) {
+ securityfs_remove(iter->dentry);
+ iter->dentry = NULL;
+ }
+ securityfs_remove(toplevel);
+ toplevel = NULL;
+}
+
+/*
+ * They're opening the file...
+ */
+
+static int trustees_open(struct inode *inode, struct file *filp)
+{
+ return 0;
+}
+
+static int trustees_open_trustees(struct inode *inode, struct file *file)
+{
+ file->private_data = vmalloc(sizeof(struct trustee_command_reader));
+ if (!file->private_data)
+ return -ENOMEM;
+
+ memset(file->private_data, 0, sizeof(struct trustee_command_reader));
+
+ return 0;
+}
+
+static int trustees_release_trustees(struct inode *inode, struct file *file)
+{
+ vfree(file->private_data);
+ return 0;
+}
+
+/* Do a read on a bogus file. Just return nothing :) */
+static ssize_t trustees_read_bogus(struct file *filp, char __user * buf,
+ size_t count, loff_t * offset)
+{
+ return 0;
+}
+
+/* Similar way to handle writes. Just say we wrote the data and return */
+static ssize_t trustees_write_bogus(struct file *filp,
+ const char __user * buf, size_t count,
+ loff_t * offset)
+{
+ return count;
+}
+
+/* Function for handling reading of the status. */
+static ssize_t trustees_read_status(struct file *filp, char __user * buf,
+ size_t count, loff_t * offset)
+{
+ static const char msg[] = "Damnit, it works, OK?!\n";
+ unsigned long nocopy;
+
+ if (*offset >= (sizeof(msg) - 1)) {
+ return 0;
+ }
+
+ if (count > (sizeof(msg) - 1 - *offset)) {
+ count = sizeof(msg) - 1 - *offset;
+ }
+ nocopy = copy_to_user(buf, msg, count);
+ (*offset) += count;
+ (*offset) -= nocopy;
+
+ return count;
+}
+
+/* Function for handling reading of the apiversion. */
+static ssize_t trustees_read_apiversion(struct file *filp, char __user * buf,
+ size_t count, loff_t * offset)
+{
+ static const char msg[] = TRUSTEES_APIVERSION_STR "\n";
+ unsigned long nocopy;
+
+ if (*offset >= (sizeof(msg) - 1)) {
+ return 0;
+ }
+
+ if (count > (sizeof(msg) - 1 - *offset)) {
+ count = sizeof(msg) - 1 - *offset;
+ }
+ nocopy = copy_to_user(buf, msg, count);
+ (*offset) += count;
+ (*offset) -= nocopy;
+
+ return count;
+}
+
+/* Cleanup our reader (deallocate all the allocated memory) */
+static void cleanup_reader(struct trustee_command_reader *reader) {
+ int z;
+ if (!reader) {
+ TS_ERR_MSG("How does reader disappear on us?\n");
+ return;
+ }
+
+ for (z = reader->curarg - 1; z >= 0; z--) {
+ vfree(reader->arg[z]);
+ reader->argsize[z] = 0;
+ }
+ reader->command.command = 0;
+ reader->curarg = 0;
+}
+
+static ssize_t trustees_write_trustees(struct file *filp,
+ const char __user * buf,
+ size_t count, loff_t * offset)
+{
+ struct trustee_command_reader *reader = filp->private_data;
+
+ if (reader->command.command == 0) {
+ reader->curarg = 0;
+ if (count != sizeof(struct trustee_command)) {
+ return -EIO;
+ }
+ if (copy_from_user(&reader->command, buf, count)) {
+ reader->command.command = 0;
+ TS_ERR_MSG("copy_from_user failed on command\n");
+ return -EIO;
+ }
+ if (reader->command.numargs > TRUSTEE_MAX_ARGS) {
+ TS_ERR_MSG("Too many arguments specified for command %d\n",
+ reader->command.command);
+ return -EIO;
+ }
+ } else {
+ unsigned curarg = reader->curarg;
+ if (!(reader->arg[curarg] = vmalloc(count+1))) {
+ cleanup_reader(reader);
+ return -EIO;
+ }
+ reader->argsize[curarg] = count;
+ ((char *)reader->arg[curarg])[count] = '\0';
+ reader->curarg++;
+ if (copy_from_user(reader->arg[curarg], buf, count)) {
+ cleanup_reader(reader);
+ TS_ERR_MSG("copy_from_user failed on arg\n");
+ return -EIO;
+ }
+ }
+
+ if (reader->command.command && reader->curarg == reader->command.numargs) {
+ int ret = trustees_process_command(reader->command, reader->arg,
+ reader->argsize);
+ cleanup_reader(reader);
+ if (ret) return -EIO;
+ }
+
+ return count;
+}
diff --git a/security/trustees/funcs.c b/security/trustees/funcs.c
new file mode 100755
index 00000000000..fe152d75df2
--- /dev/null
+++ b/security/trustees/funcs.c
@@ -0,0 +1,812 @@
+/*
+ * Trustees ACL Project
+ *
+ * Copyright (c) 1999-2000 Vyacheslav Zavadsky
+ * Copyright (c) 2004 Andrew Ruder (aeruder@ksu.edu)
+ *
+ * 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, version 2.
+ *
+ * This code contains the functions for handling the actual trustees data
+ * and returning the permissions for a given file, etc.
+ *
+ *
+ */
+
+#include <linux/fs.h>
+#include <linux/mount.h>
+#include <linux/dcache.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/poll.h>
+#include <linux/sched.h>
+#include <linux/limits.h>
+#include <linux/list.h>
+#include <linux/vmalloc.h>
+#include <linux/ctype.h>
+#include <linux/cred.h>
+
+#include "internal.h"
+
+/*
+ * This is a hash of all the trustee_names currently added. These values
+ * are hashed on a combination of device/filename. Before reading/writing
+ * be sure to take care of the locking of trustee_hash_lock.
+ */
+rwlock_t trustee_hash_lock;
+static struct hlist_head *trustee_hash = NULL;
+
+/*
+ * This is the deepest level trustee. When calculating filenames, we can
+ * skip several of the levels in many case since we know it won't be any
+ * deeper than this.
+ *
+ * Kept up to date by calculate_deepest_level
+ *
+ * / => 0
+ * /test => 1
+ * /test/blah => 2
+ */
+static int deepest_level = 0;
+
+/*
+ * A list of filesystems that need to have their case
+ * ignored. This is protected by trustee_hash_lock.
+ */
+static LIST_HEAD(trustee_ic_list);
+
+
+/* The calling method needs to free the buffer created by this function
+ * This method returns the filename for a dentry. This is, of course,
+ * relative to the device. The filename can be truncated to be as deep as
+ * the deepest trustee. The depth returned in d will always be the true
+ * depth, however.
+ *
+ * Args:
+ * dentry: The dentry we are interested in.
+ * d: a pointer to the place where the depth can be stored.
+ * trunc: ok to truncate the name to the longest that needs to be figured out.
+ */
+
+#define FN_CHUNK_SIZE 64
+char *trustees_filename_for_dentry(struct dentry *dentry, int *d, int trunc)
+{
+ char *buffer = NULL, *tmpbuf = NULL;
+ int bufsize = FN_CHUNK_SIZE;
+ char c;
+ int i, j, k;
+ int depth = 0;
+ struct dentry *temp_dentry;
+
+ if (dentry->d_parent == NULL) {
+ TS_ERR_MSG("d_parent is null\n");
+ return NULL;
+ }
+
+ if (dentry->d_name.name == NULL) {
+ TS_ERR_MSG("name is null\n");
+ return NULL;
+ }
+
+ buffer = kmalloc(FN_CHUNK_SIZE, GFP_KERNEL);
+ if (!buffer) {
+ TS_ERR_MSG("could not allocate filename buffer\n");
+ return NULL;
+ }
+
+ buffer[0] = '/';
+ buffer[i = 1] = '\0';
+ for (temp_dentry = dentry; !IS_ROOT(temp_dentry); temp_dentry = temp_dentry->d_parent)
+ depth++;
+ if (d) *d = depth;
+ if (deepest_level <= 0) return buffer;
+
+ for (;;) {
+ if (IS_ROOT(dentry))
+ break;
+ if (depth-- > deepest_level) continue;
+
+ j = i + strlen(dentry->d_name.name);
+ if ((j + 2) > bufsize) { /* reallocate - won't fit */
+ bufsize = (((j + 2) / FN_CHUNK_SIZE) + 1) * FN_CHUNK_SIZE;
+ tmpbuf = kmalloc(bufsize, GFP_KERNEL);
+ if (!tmpbuf) {
+ kfree(buffer);
+ TS_ERR_MSG
+ ("Out of memory allocating tmpbuf\n");
+ return NULL;
+ }
+ memcpy(tmpbuf, buffer, i);
+ kfree(buffer);
+ buffer = tmpbuf;
+ }
+ /* Throw the name in there backward */
+ for (k = 0; dentry->d_name.name[k]; k++) {
+ buffer[j - 1 - k] = dentry->d_name.name[k];
+ }
+ i = j;
+ buffer[i++] = '/';
+ dentry = dentry->d_parent;
+ }
+ buffer[i] = 0;
+
+ /* buffer is backwards, reverse it */
+ for (j = 0; j < (i / 2); ++j) {
+ c = buffer[j];
+ buffer[j] = buffer[i - j - 1];
+ buffer[i - j - 1] = c;
+ }
+
+ return buffer;
+}
+
+/**
+ * Allocate memory using vmalloc and return a duplicate of the passed in string.
+ * Returns NULL if a problem occurs
+ */
+static char *vmalloc_strdup(const char *str, size_t len)
+{
+ char *r;
+
+ if (!str) return NULL;
+ len = strlen(str);
+ r = vmalloc(len + 1);
+ if (!r) return NULL;
+ memcpy(r, str, len + 1);
+
+ return r;
+}
+
+/*
+ * Add a filesystem as a ignored-case dev.
+ */
+static inline void add_ic_dev(u32 dev, char *devname)
+{
+ char *devname2;
+ struct trustee_ic *ic;
+ size_t dev_len;
+
+ dev_len = strlen(devname);
+
+ if (dev_len > PATH_MAX) {
+ TS_ERR_MSG("devname bad, add_ic_dev ignored.\n");
+ return;
+ }
+
+ if (!dev_len) {
+ TS_ERR_MSG("No devname specified in add_ic_dev.\n");
+ return;
+ }
+
+ devname2 = vmalloc_strdup(devname, dev_len);
+ if (!devname2) {
+ TS_ERR_MSG
+ ("Seems that we have ran out of memory adding ic dev!\n");
+ return;
+ }
+
+ ic = vmalloc(sizeof(struct trustee_ic));
+ if (!ic) {
+ TS_ERR_MSG
+ ("Seems that we ran out of memory allocating ic!\n");
+ return;
+ }
+
+ ic->dev = new_decode_dev(dev);
+ ic->devname = devname2;
+
+ write_lock(&trustee_hash_lock);
+ list_add(&ic->ic_list, &trustee_ic_list);
+ write_unlock(&trustee_hash_lock);
+}
+
+/*
+ * Remove all ignored-case filesystems.
+ */
+static inline void remove_ic_devs(void)
+{
+ struct trustee_ic *ic, *temp_ic;
+ struct list_head temp_ic_list;
+
+ INIT_LIST_HEAD(&temp_ic_list);
+ list_splice_init(&trustee_ic_list, &temp_ic_list);
+
+ list_for_each_entry_safe(ic, temp_ic, &temp_ic_list, ic_list) {
+ vfree(ic->devname);
+ vfree(ic);
+ }
+}
+
+/*
+ * This frees all the capsules in a trustee element.
+ */
+static inline void free_hash_element_list(struct trustee_hash_element *e)
+{
+ struct trustee_permission_capsule *capsule, *temp;
+
+ list_for_each_entry_safe(capsule, temp, &e->perm_list, perm_list) {
+ list_del(&capsule->perm_list);
+ vfree(capsule);
+ }
+}
+
+/*
+ * Free a trustee name. This frees the devname and the filename
+ */
+static inline void free_trustee_name(struct trustee_name *name)
+{
+ vfree(name->filename);
+ vfree(name->devname);
+}
+
+/*
+ * Frees the capsules, and the filenames for a trustee hash element.
+ * Also marks it as unused in the hash.
+ */
+static inline void free_hash_element(struct trustee_hash_element *e)
+{
+ free_hash_element_list(e);
+ free_trustee_name(&e->name);
+ vfree(e);
+}
+
+/**
+ * Copies from src to dest (duplicating the strings in the
+ * trustee_name structure. Returns zero for unsuccesful.
+ */
+static int copy_trustee_name(struct trustee_name *dst, struct trustee_name *src)
+{
+ *dst = *src;
+ if (dst->filename) {
+ dst->filename = vmalloc_strdup(src->filename, strlen(src->filename));
+ if (!dst->filename) {
+ TS_ERR_MSG("Ran out of memory duplicating src->filename\n");
+ return 0;
+ }
+ }
+
+ if (dst->devname) {
+ dst->devname = vmalloc_strdup(src->devname, strlen(src->devname));
+ if (!dst->devname) {
+ TS_ERR_MSG("Ran out of memory duplicating src->devname\n");
+ vfree(dst->filename);
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+
+/*
+ * hashing function researched by Karl Nelson <kenelson @ ece ucdavis edu>
+ * and is used in glib.
+ */
+static inline unsigned int hash_string(const char *s)
+{
+ unsigned int v = 0;
+
+ while (*s) {
+ v = (v << 5) - v + tolower(*s);
+ s++;
+ }
+
+ return v;
+}
+
+/*
+ * Return the hash for a device.
+ */
+static inline unsigned int hash_device(const char *name, dev_t device)
+{
+ if (MAJOR(device) == 0) {
+ return hash_string(name);
+ }
+
+ return new_encode_dev(device);
+}
+
+/*
+ * Return the hash for a file. This is a combination of the
+ * hash of the filename and the hash for the device.
+ */
+static inline unsigned int hash(const struct trustee_name *name)
+{
+ return hash_string(name->filename) ^
+ hash_device(name->devname, name->dev);
+}
+
+/*
+ * Return the slot in the trustees_hash where a trustee is located
+ */
+static inline unsigned int hash_slot(const struct trustee_name *name)
+{
+ return hash(name) % trustee_hash_size;
+}
+
+/*
+ * Compare two devices. Return 1 if they are equal otherwise return 0
+ */
+static inline int trustee_dev_cmp(dev_t dev1, dev_t dev2, const char *devname1,
+ const char *devname2)
+{
+ if ((MAJOR(dev1) == 0) && (MAJOR(dev2) == 0))
+ return (strcmp(devname1, devname2) == 0);
+ else if ((MAJOR(dev1) != 0) && (MAJOR(dev2) != 0))
+ return (dev1 == dev2);
+ return 0;
+}
+
+/*
+ * Compare two trustee_name's. Returns 1 if they are are equal
+ * otherwise return 0
+ */
+static inline int trustee_name_cmp(const struct trustee_name *n1,
+ const struct trustee_name *n2,
+ unsigned ignore_case)
+{
+ if (trustee_dev_cmp(n1->dev, n2->dev, n1->devname, n2->devname))
+ return ignore_case ?
+ (strnicmp(n1->filename, n2->filename, PATH_MAX) == 0) :
+ (strcmp(n1->filename, n2->filename) == 0);
+ return 0;
+}
+
+/*
+ * Calculate the deepest level.
+ */
+static inline void calculate_deepest_level(const struct trustee_name *name)
+{
+ const char *fn = name->filename;
+ const char *x;
+ int level = 0;
+
+ for (x = fn; *x; ++x) {
+ if (*x == '/')
+ ++level;
+ }
+
+ /* If it is the root, it should have
+ * a level of 0.
+ */
+ if (x == (fn + 1)) level = 0;
+
+ if (level > deepest_level) deepest_level = level;
+}
+
+/*
+ * Return the trustee element for a name.
+ * This should be called with a lock on the trustee_hash (which should
+ * not be released until you are done with the returned hash_element)!
+ */
+static struct trustee_hash_element *get_trustee_for_name(const struct trustee_name *name,
+ unsigned ignore_case)
+{
+ struct trustee_hash_element *item = NULL;
+ struct hlist_node *iter = NULL;
+
+ hlist_for_each_entry(item, iter, &trustee_hash[hash_slot(name)], hash_list) {
+ if (trustee_name_cmp(&item->name, name, ignore_case))
+ return item;
+ }
+
+ return NULL;
+}
+
+/**
+ * Add a new blank trustee to the hash.
+ *
+ * If this returns zero, then the adding failed and name should be freed
+ * (assuming must_copy is 0), otherwise assume we used its memory.
+ */
+static unsigned add_trustee(struct trustee_name *name, int must_copy) {
+ struct trustee_name newname;
+ struct trustee_name rootname;
+ unsigned is_root = 1;
+ unsigned r = 0;
+ struct trustee_hash_element *new;
+ struct trustee_hash_element *root;
+
+ if (!name->filename || !name->filename[0]) goto err0;
+
+ if (!copy_trustee_name(&rootname, name)) goto err0;
+ rootname.filename[1] = '\0';
+
+ if (strlen(name->filename) > 1 && strcmp(name->filename, "/")) {
+ add_trustee(&rootname, 1);
+ is_root = 0;
+ }
+
+ if (must_copy) {
+ if (!copy_trustee_name(&newname, name)) goto err1;
+ } else {
+ newname = *name;
+ }
+
+ new = vmalloc(sizeof(struct trustee_hash_element));
+ if (!new) goto err2;
+ new->name = newname;
+ INIT_HLIST_NODE(&new->hash_list);
+ INIT_LIST_HEAD(&new->perm_list);
+ INIT_LIST_HEAD(&new->device_list);
+
+ write_lock(&trustee_hash_lock);
+ if (get_trustee_for_name(&newname, 0)) goto err3;
+
+ if (is_root) {
+ root = NULL;
+ } else if (!(root = get_trustee_for_name(&rootname, 0))) {
+ TS_ERR_MSG("Root trustee disappeared on us!\n");
+ goto err3;
+ }
+ hlist_add_head(&new->hash_list, &trustee_hash[hash_slot(name)]);
+ if (!is_root) {
+ list_add_tail(&new->device_list, &root->device_list);
+ }
+ calculate_deepest_level(&newname);
+ TS_DEBUG_MSG("Created '%s' trustee\n", newname.filename);
+ r = 1;
+err3:
+ write_unlock(&trustee_hash_lock);
+ if (!r) vfree(new);
+err2:
+ if (must_copy && !r) free_trustee_name(&newname);
+err1:
+ free_trustee_name(&rootname);
+err0:
+ return r;
+}
+
+/**
+ * Add a permissions module to the trustee specified by name.
+ */
+static unsigned add_trustee_perm
+ (struct trustee_name *name, struct trustee_permission acl)
+{
+ struct trustee_hash_element *r = NULL;
+ struct trustee_permission_capsule *capsule;
+
+ capsule = vmalloc(sizeof(struct trustee_permission_capsule));
+ if (!capsule) {
+ TS_ERR_MSG
+ ("Can not allocate memory for trustee capsule\n");
+ return 0;
+ }
+ capsule->permission = acl;
+
+ write_lock(&trustee_hash_lock);
+ r = get_trustee_for_name(name, 0);
+
+ if (r) {
+ list_add_tail(&capsule->perm_list, &r->perm_list);
+ write_unlock(&trustee_hash_lock);
+ TS_DEBUG_MSG("Added permission capsule to '%s' trustee\n", name->filename);
+ return 1;
+ }
+ write_unlock(&trustee_hash_lock);
+ TS_ERR_MSG("trustee disappeared under us while trying to add perms\n");
+ vfree(capsule);
+
+ return 0;
+}
+
+/*
+ * Get the mask for a trustee name.
+ * This should be called with a lock on the trustee_hash (which should
+ * not be released until you are done with the returned hash_element)!
+ */
+static int get_trustee_mask_for_name(struct trustee_name *name,
+ int oldmask, int height,
+ struct trustee_hash_element **element,
+ unsigned ignore_case)
+{
+ struct trustee_hash_element *e;
+ int m;
+ struct trustee_permission_capsule *l;
+ int appl;
+ e = get_trustee_for_name(name, ignore_case);
+ if (!e) {
+ return oldmask;
+ }
+ list_for_each_entry(l, &e->perm_list, perm_list) {
+ if ((height < 0)
+ && (l->permission.mask & TRUSTEE_ONE_LEVEL_MASK))
+ continue;
+ if (element) {
+ *element = e;
+ element = NULL;
+ }
+ appl = ((!(l->permission.mask & TRUSTEE_IS_GROUP_MASK))
+ && (current_fsuid() == l->permission.u.uid))
+ || (((l->permission.mask & TRUSTEE_IS_GROUP_MASK))
+ && (in_group_p(l->permission.u.gid)))
+ || (l->permission.mask & TRUSTEE_ALL_MASK);
+ if (l->permission.mask & TRUSTEE_NOT_MASK)
+ appl = !appl;
+
+ if (!appl)
+ continue;
+
+ m = l->permission.mask & TRUSTEE_ACL_MASK;
+
+ if (l->permission.mask & TRUSTEE_ALLOW_DENY_MASK)
+ m <<= TRUSTEE_NUM_ACL_BITS;
+
+ oldmask =
+ l->permission.
+ mask & TRUSTEE_CLEAR_SET_MASK ? (oldmask & (~m))
+ : (oldmask | m);
+ }
+
+ return oldmask;
+}
+
+/*
+ * Return non-zero if a trustee exists in a subpath.
+ *
+ * WARNING!
+ * This function requires that you lock/unlock the trustees_hash_lock
+ */
+int trustee_has_child(struct vfsmount *mnt, char *file_name)
+{
+ struct trustee_name trustee_name;
+ char tempchar;
+ unsigned ignore_case = 0;
+ struct trustee_hash_element *root;
+ size_t len;
+ struct trustee_ic *iter;
+ struct trustee_hash_element *r;
+
+ if (!file_name || !*file_name) return 0;
+
+ trustee_name.dev = mnt->mnt_sb->s_dev;
+ trustee_name.devname = mnt->mnt_devname;
+ trustee_name.filename = file_name;
+
+ list_for_each_entry(iter, &trustee_ic_list, ic_list) {
+ if (trustee_dev_cmp
+ (iter->dev, trustee_name.dev, iter->devname,
+ trustee_name.devname)) {
+ ignore_case = 1;
+ break;
+ }
+ }
+
+ tempchar = file_name[1];
+ file_name[1] = '\0';
+
+ root = get_trustee_for_name(&trustee_name, ignore_case);
+ if (!root) return 0;
+
+ file_name[1] = tempchar;
+
+ len = strlen(file_name);
+
+ list_for_each_entry(r, &root->device_list, device_list) {
+ size_t this_len = strlen(r->name.filename);
+ if (this_len <= len) continue;
+ if (!strncmp(file_name, r->name.filename, len) &&
+ r->name.filename[len] != '\0')
+ return 1;
+ }
+
+ return 0;
+}
+
+/*
+ * Return the mask for a file.
+ *
+ * WARNING!
+ * This function requires that you lock/unlock the trustees_hash_lock
+ */
+int trustee_perm(struct path *path,
+ char *file_name, int unix_ret, int depth, int is_dir,
+ struct trustee_hash_element **deepest)
+{
+ static char dbl_nul_slash[3] = { '/', '\0', '\0' };
+ int oldmask = trustee_default_acl;
+ int height = 0;
+ char *filecount;
+ char c;
+ struct trustee_name trustee_name;
+ struct trustee_ic *iter;
+ unsigned ignore_case = 0;
+
+ trustee_name.dev = path->mnt->mnt_sb->s_dev;
+ trustee_name.devname = path->mnt->mnt_devname;
+ trustee_name.filename = file_name;
+
+ list_for_each_entry(iter, &trustee_ic_list, ic_list) {
+ if (trustee_dev_cmp
+ (iter->dev, trustee_name.dev, iter->devname,
+ trustee_name.devname)) {
+ ignore_case = 1;
+ break;
+ }
+ }
+
+ if (deepest) *deepest = NULL;
+
+ filecount = file_name + 1;
+ /* Try to handle the unlikely case where the string will be '/'
+ * out here to simplify the logic inside the loop. We do this
+ * by giving it a string with two nul byte terminators so that it
+ * will gracefully (and safely) make it through the loop below.
+ */
+ if (*filecount == '\0') {
+ file_name = dbl_nul_slash;
+ filecount = file_name + 1;
+ }
+ do {
+ c = *filecount;
+ *filecount = 0;
+ oldmask =
+ get_trustee_mask_for_name(&trustee_name, oldmask,
+ height - depth + !is_dir,
+ deepest, ignore_case);
+ height++;
+ *filecount = c;
+ ++filecount;
+ while ((*filecount) && (*filecount != '/')) filecount++;
+
+ } while(*filecount);
+
+ return oldmask;
+}
+
+/* Clear out the hash of trustees and release the hash itself.
+ * Also gets rid of the ignore-case list
+ */
+static void trustees_clear_all(void)
+{
+ struct trustee_hash_element *item = NULL;
+ struct hlist_node *iter, *temp = NULL;
+ unsigned i;
+ write_lock(&trustee_hash_lock);
+
+ for (i = 0; i < trustee_hash_size; i++) {
+ hlist_for_each_entry_safe(item, iter, temp, &trustee_hash[i], hash_list) {
+ free_hash_element(item);
+ }
+ INIT_HLIST_HEAD(&trustee_hash[i]);
+ }
+
+ deepest_level = 0;
+
+ remove_ic_devs();
+
+ write_unlock(&trustee_hash_lock);
+}
+
+/*
+ * Initialize globals
+ */
+int trustees_funcs_init_globals(void)
+{
+ unsigned int iter;
+
+ if (trustee_hash_size <= 0)
+ return 1;
+
+ rwlock_init(&trustee_hash_lock);
+
+ trustee_hash = vmalloc(sizeof(*trustee_hash) * trustee_hash_size);
+ if (!trustee_hash)
+ return 1;
+
+ for (iter = 0; iter < trustee_hash_size; iter++)
+ INIT_HLIST_HEAD(trustee_hash + iter);
+
+ return 0;
+}
+
+/*
+ * Clear globals
+ */
+int trustees_funcs_cleanup_globals(void)
+{
+ trustees_clear_all();
+
+ vfree(trustee_hash);
+
+ return 0;
+}
+
+/*
+ * Prepare a trustee name from a passed in trustee name.
+ */
+static int prepare_trustee_name(u32 device, char *devname, char *filename, struct trustee_name *name)
+{
+ size_t devl, filel;
+ char *devb = NULL, *fileb = NULL;
+
+ if ((!name))
+ return 0;
+
+ filel = strlen(filename);
+ devl = strlen(devname);
+
+ if (devl > PATH_MAX) {
+ TS_ERR_MSG("device name bad, command ignored.\n");
+ return 0;
+ }
+ if (filel > PATH_MAX) {
+ TS_ERR_MSG("file name bad, command ignored.\n");
+ return 0;
+ }
+
+ devb = vmalloc_strdup(devname, devl);
+ if (!devb) {
+ TS_ERR_MSG("Couldn't allocate mem for devb.\n");
+ return 0;
+ }
+
+ fileb = vmalloc_strdup(filename, filel);
+ if (!fileb) {
+ TS_ERR_MSG("Couldn't allocate mem for fileb.\n");
+ return 0;
+ }
+
+ name->devname = devb;
+ name->filename = fileb;
+
+ name->dev = new_decode_dev(device);
+
+ return 1;
+}
+
+/*
+ * Process a user command
+ */
+extern int trustees_process_command(struct trustee_command command,
+ void **arg,
+ size_t *argsize)
+{
+ int r = -ENOSYS;
+ int must_free = 0;
+ struct trustee_name name;
+
+ if ((current_euid() != 0) && !capable(CAP_SYS_ADMIN)) {
+ r = -EACCES;
+ return r;
+ }
+
+ switch (command.command) {
+ case TRUSTEE_COMMAND_MAKE_IC:
+ if (command.numargs != 2 ||
+ argsize[1] != sizeof(u32)) goto unlk;
+ add_ic_dev(*(u32 *)arg[1], arg[0]);
+ r = 0;
+ break;
+ case TRUSTEE_COMMAND_REMOVE_ALL:
+ if (command.numargs != 0) goto unlk;
+ trustees_clear_all();
+ r = 0;
+ break;
+ case TRUSTEE_COMMAND_ADD:
+ if (command.numargs != 4 ||
+ argsize[3] != sizeof(u32) ||
+ argsize[1] != sizeof(struct trustee_permission))
+ goto unlk;
+ if (!prepare_trustee_name(*(u32 *)arg[3], arg[2], arg[0], &name)) {
+ r = -ENOMEM;
+ goto unlk;
+ }
+ if (!add_trustee(&name, 0)) {
+ must_free = 1;
+ }
+ if (!add_trustee_perm(&name, *(struct trustee_permission *)arg[1]))
+ r = -ENOMEM;
+ else
+ r = 0;
+
+ if (must_free) free_trustee_name(&name);
+ break;
+ }
+ unlk:
+
+ return r;
+}
diff --git a/security/trustees/init.c b/security/trustees/init.c
new file mode 100755
index 00000000000..38eb964c382
--- /dev/null
+++ b/security/trustees/init.c
@@ -0,0 +1,57 @@
+/*
+ * Trustees ACL Project
+ *
+ * Copyright (c) 1999-2000 Vyacheslav Zavadsky
+ * Copyright (c) 2004 Andrew Ruder (aeruder@ksu.edu)
+ *
+ * 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, version 2.
+ *
+ * Module initialization and cleanup
+ *
+ * History:
+ * 2002-12-16 trustees 2.10 released by Vyacheslav Zavadsky
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/security.h>
+#include <linux/capability.h>
+
+#include "internal.h"
+
+unsigned int trustee_hash_size = 256;
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Trustees ACL System");
+MODULE_AUTHOR("Vyacheslav Zavadsky and Andrew E. Ruder <aeruder@ksu.edu>");
+MODULE_VERSION("2.11");
+
+MODULE_PARM_DESC(hash_size, "Trustees hash size");
+module_param_named(hash_size, trustee_hash_size, uint, 0444);
+
+
+static int __init trustees_init(void)
+{
+ if (trustees_funcs_init_globals() != 0) {
+ return -EINVAL;
+ }
+
+ if (trustees_init_fs() != 0) {
+ trustees_funcs_cleanup_globals();
+ return -EINVAL;
+ }
+
+ if (trustees_init_security() != 0) {
+ trustees_deinit_fs();
+ trustees_funcs_cleanup_globals();
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+fs_initcall(trustees_init);
diff --git a/security/trustees/internal.h b/security/trustees/internal.h
new file mode 100755
index 00000000000..f7203a05fc1
--- /dev/null
+++ b/security/trustees/internal.h
@@ -0,0 +1,101 @@
+/*
+ * Trustees ACL Project
+ *
+ * Copyright (c) 1999-2000 Vyacheslav Zavadsky
+ * Copyright (c) 2004 Andrew Ruder (aeruder@ksu.edu)
+ *
+ * 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, version 2.
+ *
+ * Private methods and definitions used only within the module.
+ *
+ */
+
+#ifndef _LINUX_TRUSTEES_H
+#define _LINUX_TRUSTEES_H
+#include <linux/types.h>
+#include <linux/dcache.h>
+#include <linux/kdev_t.h>
+#include <linux/list.h>
+#include <linux/version.h>
+#include <linux/trustees.h>
+#include <linux/path.h>
+
+#define TRUSTEE_DEFAULT_MASK TRUSTEE_USE_UNIX_MASK
+
+struct trustee_ic {
+ dev_t dev;
+ char *devname; /* ONLY if MAJOR(dev)==0 */
+ struct list_head ic_list;
+};
+
+struct trustee_name {
+ dev_t dev;
+ char *filename;
+ const char *devname; /* ONLY if MAJOR(dev)==0 */
+};
+
+struct trustee_permission_capsule {
+ struct list_head perm_list;
+ struct trustee_permission permission;
+};
+
+/* For the usage field */
+#define TRUSTEE_HASH_ELEMENT_USED 2
+#define TRUSTEE_HASH_ELEMENT_DELETED 1
+#define TRUSTEE_HASH_ELEMENT_NOTUSED 0
+
+struct trustee_hash_element {
+ struct trustee_name name;
+ struct list_head perm_list;
+ struct hlist_node hash_list;
+ struct list_head device_list;
+};
+
+extern char *trustees_filename_for_dentry(struct dentry *dentry, int *d, int trunc);
+
+extern int trustees_funcs_init_globals(void);
+extern int trustees_funcs_cleanup_globals(void);
+
+int trustee_has_child(struct vfsmount *mnt, char *file_name);
+int trustee_perm(struct path *path,
+ char *file_name, int unix_ret, int depth, int is_dir,
+ struct trustee_hash_element **deepest);
+
+extern int trustees_process_command(struct trustee_command command,
+ void **arg, size_t *argsize);
+
+extern unsigned int trustee_hash_size;
+extern rwlock_t trustee_hash_lock;
+
+#define TRUSTEE_INITIAL_NAME_BUFFER 256
+#define TRUSTEE_HASDEVNAME(TNAME) (MAJOR((TNAME).dev)==0)
+
+#define TS_ERR_MSG(...) printk(KERN_ERR "Trustees: " __VA_ARGS__)
+
+#ifdef TRUSTEES_DEBUG
+#define TS_DEBUG_MSG(...) printk(KERN_ERR "Trustees: " __VA_ARGS__)
+#else
+#define TS_DEBUG_MSG(...)
+#endif
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,15)
+#define NAMESPACE_SEM(_ns) (namespace_sem)
+#else
+#define NAMESPACE_SEM(_ns) ((_ns)->sem)
+#endif
+
+/*
+ * Magic number!
+ *
+ * FIXME: Do I just make this up or is there some system for coming
+ * up with magic numbers?
+ */
+#define TRUSTEES_MAGIC 0x32236975
+
+int trustees_init_fs(void);
+void trustees_deinit_fs(void);
+
+int trustees_init_security(void);
+#endif /* _LINUX_TRUSTEES_H */
diff --git a/security/trustees/security.c b/security/trustees/security.c
new file mode 100755
index 00000000000..bbc98886243
--- /dev/null
+++ b/security/trustees/security.c
@@ -0,0 +1,417 @@
+/*
+ * Trustees ACL Project
+ *
+ * Copyright (c) 1999-2000 Vyacheslav Zavadsky
+ * Copyright (c) 2004 Andrew Ruder (aeruder@ksu.edu)
+ *
+ * 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, version 2.
+ *
+ * The security module (LSM API) component of the trustees system
+ *
+ * One quick note: generally security modules with the LSM are supposed
+ * to be solely restrictive modules. Unless the trustees module were to
+ * require that people set all files rwx by all, it could not function
+ * as it is meant to function as a solely restrictive module.
+ *
+ * To compensate, every process is given the capability CAP_DAC_OVERRIDE.
+ * In other words, every process is first given full rights to the filesystem.
+ * This is the only non-restricting portion of this module, since it -does-
+ * in fact give additional permissions. However, in the inode_permission hook,
+ * any rights the user should not have are taken away.
+ *
+ * Side effects: Posix ACLs or other filesystem-specific permissions are not
+ * honored. Trustees ACLs can (and do) take into account the standard unix
+ * permissions, but any permissions further than that are difficult, to say
+ * the least, to take into account. I, personally, do not find this to
+ * be a problem since if you are using Trustees ACLs, why also require the use
+ * of another ACL system?
+ */
+
+#include <linux/security.h>
+#include <linux/capability.h>
+#include <linux/mount.h>
+#include <linux/namei.h>
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/nsproxy.h>
+#include <linux/cred.h>
+#include <linux/mnt_namespace.h>
+
+#include "internal.h"
+
+static int trustees_capable(struct task_struct *tsk, const struct cred *cred,
+ int cap,
+ int audit);
+static int trustees_inode_permission(struct inode *inode, int mask);
+
+/* Checks if user has access to the inode due to root status
+ */
+static inline int has_root_perm(struct inode *inode, int mask)
+{
+ umode_t mode = inode->i_mode;
+
+ if (!(mask & MAY_EXEC) || (mode & S_IXUGO) || S_ISDIR(mode))
+ if (current_fsuid() == 0)
+ return 0;
+
+ return -EACCES;
+}
+
+/* The logic for this was mostly stolen from vfs_permission. The security API
+ * doesn't give a good way to use the actual vfs_permission for this since our
+ * CAP_DAC_OVERRIDE causes it to always return 0. But if we didn't return
+ * CAP_DAC_OVERRIDE, we'd never get to handle permissions! Since we don't need
+ * to handle capabilities and dealing with ACLs with trustees loaded isn't an
+ * issue for me, the function ends up being pretty simple.
+ */
+
+static inline int has_unix_perm(struct inode *inode, int mask)
+{
+ umode_t mode = inode->i_mode;
+ mask &= ~MAY_APPEND;
+
+ if (current_fsuid() == inode->i_uid)
+ mode >>= 6;
+ else if (in_group_p(inode->i_gid))
+ mode >>= 3;
+
+ if (((mode & mask & (MAY_READ | MAY_WRITE | MAY_EXEC)) == mask))
+ return 0;
+
+ return -EACCES;
+}
+
+/* Find a vfsmount given an inode */
+static inline struct vfsmount *find_inode_mnt(struct inode *inode)
+{
+ struct mnt_namespace *ns = NULL;
+ struct vfsmount *mnt = NULL;
+
+ if (!inode->i_sb) {
+ TS_ERR_MSG("Inode without a i_sb entry?");
+ return NULL;
+ }
+
+ /* Okay, we need to find the vfsmount by looking
+ * at the namespace now.
+ */
+ task_lock(current);
+ if (current->nsproxy) {
+ ns = current->nsproxy->mnt_ns;
+ if (ns)
+ get_mnt_ns(ns);
+ }
+ task_unlock(current);
+
+ if (!ns)
+ return NULL;
+
+ list_for_each_entry(mnt, &ns->list, mnt_list) {
+ if (mnt->mnt_sb == inode->i_sb && mnt->mnt_devname) {
+ mntget(mnt);
+ goto out;
+ }
+ }
+
+ out:
+ put_mnt_ns(ns);
+
+ return mnt;
+}
+
+/* Find a dentry given an inode */
+static inline struct dentry *find_inode_dentry(struct inode *inode)
+{
+ struct dentry *dentry;
+
+ dentry = d_find_alias(inode);
+
+ return dentry;
+}
+
+/* Find a path (dentry/vfsmount pair) given an inode
+ */
+static inline int find_inode_path(struct inode *inode, struct path *path)
+{
+ int ret = 0;
+
+ path->dentry = NULL;
+ path->mnt = NULL;
+
+ path->mnt = find_inode_mnt(inode);
+ if (unlikely(!path->mnt)) {
+ TS_ERR_MSG("inode does not have a mnt!\n");
+ goto error_out;
+ }
+
+ path->dentry = find_inode_dentry(inode);
+ if (unlikely(!path->dentry)) {
+ /* Most of the time when this happens, it is the /
+ * If it is not, we need to dump as much information
+ * as possible on it and dump it to logs, because
+ * I'm really not sure how it happens.
+ */
+ if (path->mnt->mnt_root && inode == path->mnt->mnt_root->d_inode) {
+ path->dentry = dget(path->mnt->mnt_root);
+ } else {
+ /* I have seen this happen once but I did not have any
+ * way to see what caused it. I am gonna dump_stack
+ * until I have that happen again to see if the cause
+ * is something that I need to worry about.
+ */
+ dump_stack(); /* DEBUG FIXME */
+ TS_ERR_MSG("Inode number: %ld\n", inode->i_ino);
+ TS_ERR_MSG("dentry does not exist!\n");
+ goto error_mnt_out;
+ }
+ }
+
+ goto out;
+
+error_mnt_out:
+ mntput(path->mnt);
+ path->mnt = NULL;
+
+error_out:
+ ret = 1;
+
+out:
+ return ret;
+}
+
+/*
+ * Return 1 if they are under the same set of trustees
+ * otherwise return 0. In the case that we are handling
+ * a directory, we also check to see if there are subdirectories
+ * with trustees.
+ */
+static inline int have_same_trustees_rename(struct path *path1, struct path *path2)
+{
+ char *filename1, *filename2;
+ int depth1, depth2;
+ struct trustee_hash_element *deep1, *deep2;
+ int is_dir;
+ int ret = 0;
+
+ filename1 = trustees_filename_for_dentry(path1->dentry, &depth1, 1);
+ if (!filename1) {
+ TS_ERR_MSG("Couldn't allocate filename\n");
+ goto out;
+ }
+
+ filename2 = trustees_filename_for_dentry(path2->dentry, &depth2, 1);
+ if (!filename2) {
+ TS_ERR_MSG("Couldn't allocate filename\n");
+ goto out_file_name;
+ }
+
+ is_dir = S_ISDIR(path1->dentry->d_inode->i_mode);
+
+ read_lock(&trustee_hash_lock);
+ trustee_perm(path1, filename1, ret, depth1, is_dir, &deep1);
+ trustee_perm(path2, filename2, ret, depth2, is_dir, &deep2);
+ if (deep1 == deep2) {
+ ret = 1;
+ if (is_dir) {
+ if (trustee_has_child(path1->mnt, filename1) ||
+ trustee_has_child(path2->mnt, filename2)) ret = 0;
+ }
+ }
+ read_unlock(&trustee_hash_lock);
+
+ kfree(filename2);
+ out_file_name:
+ kfree(filename1);
+ out:
+
+ return ret;
+}
+
+static int trustees_inode_rename(struct inode *old_dir,
+ struct dentry *old_dentry,
+ struct inode *new_dir,
+ struct dentry *new_dentry);
+static int trustees_inode_link(struct dentry *old_dentry,
+ struct inode *dir,
+ struct dentry *new_dentry);
+
+/* Structure where we fill in the various hooks we are implementing in this module
+ */
+struct security_operations trustees_security_ops = {
+ .capable = trustees_capable,
+ .inode_permission = trustees_inode_permission,
+ .inode_link = trustees_inode_link,
+ .inode_rename = trustees_inode_rename
+};
+
+#define ALL_MAYS (MAY_WRITE | MAY_EXEC | MAY_READ)
+/* Converts a trustee_mask to a normal unix mask
+ */
+static int inline trustee_mask_to_normal_mask(int mask, int isdir)
+{
+ int r = 0;
+ if ((mask & TRUSTEE_READ_MASK) && !isdir)
+ r |= MAY_READ;
+ if ((mask & TRUSTEE_READ_DIR_MASK) && isdir)
+ r |= MAY_READ;
+ if (mask & TRUSTEE_WRITE_MASK)
+ r |= MAY_WRITE;
+ if ((mask & TRUSTEE_BROWSE_MASK) && isdir)
+ r |= MAY_EXEC;
+ if ((mask & TRUSTEE_EXECUTE_MASK) && !isdir)
+ r |= MAY_EXEC;
+ return r;
+}
+
+/* This is the meat of the permissions checking. First it checks for root,
+ * otherwise it first checks for any errors finding the dentry/vfsmount for
+ * the inode, and then it looks up the dentry in the trustees hash.
+ */
+static int trustees_inode_permission(struct inode *inode, int mask)
+{
+ struct path path;
+ char *file_name;
+ int is_dir;
+ int ret;
+ int depth;
+ int amask;
+ int dmask;
+ umode_t mode = inode->i_mode;
+
+ if (has_root_perm(inode, mask) == 0)
+ return 0;
+
+ ret = has_unix_perm(inode, mask);
+
+ if (find_inode_path(inode, &path)) {
+ return -EACCES;
+ }
+
+ file_name = trustees_filename_for_dentry(path.dentry, &depth, 1);
+ if (!file_name) {
+ TS_ERR_MSG("Couldn't allocate filename\n");
+ ret = -EACCES;
+ goto out_path;
+ }
+
+ is_dir = S_ISDIR(inode->i_mode);
+
+ read_lock(&trustee_hash_lock);
+ amask = trustee_perm(&path, file_name, ret, depth, is_dir,
+ (struct trustee_hash_element **)NULL);
+ read_unlock(&trustee_hash_lock);
+ dmask = amask >> TRUSTEE_NUM_ACL_BITS;
+
+ /* no permission if denied */
+ if (trustee_mask_to_normal_mask(dmask, is_dir) & mask & ALL_MAYS) {
+ ret = -EACCES;
+ goto out;
+ }
+ /* use unix perms */
+ if (!(dmask & TRUSTEE_USE_UNIX_MASK) &&
+ (amask & TRUSTEE_USE_UNIX_MASK) && (!ret))
+ goto out;
+
+ /* if the file isn't executable, then the trustees shouldn't
+ * make it executable
+ */
+ if ((mask & MAY_EXEC) && !(mode & S_IXOTH) &&
+ !((mode >> 3) & S_IXOTH) & !((mode >> 6) & S_IXOTH) &&
+ (!is_dir)) {
+ ret = -EACCES;
+ goto out;
+ }
+ /* Check trustees for permission
+ */
+ if ((trustee_mask_to_normal_mask(amask, is_dir) & mask & ALL_MAYS)
+ == mask) {
+ ret = 0;
+ goto out;
+ } else
+ ret = -EACCES;
+
+out:
+ kfree(file_name);
+out_path:
+ path_put(&path);
+
+ return ret;
+}
+
+/* We should only allow hard links under one of two conditions:
+ * 1. Its in the same trustee
+ * - if the two dentries are covered by the same trustee, there shouldn't
+ * be much of a problem with allowing the hardlink to occur.
+ * 2. fsuid = 0
+ */
+static int trustees_inode_link(struct dentry *old_dentry,
+ struct inode *dir,
+ struct dentry *new_dentry)
+{
+ int ret = -EXDEV;
+ struct path path1;
+ struct path path2;
+
+ if (current_fsuid() == 0)
+ return 0;
+
+ path1.dentry = dget(old_dentry);
+ path1.mnt = find_inode_mnt(old_dentry->d_inode);
+ path2.dentry = dget(new_dentry);
+ path2.mnt = mntget(path1.mnt);
+
+ if (have_same_trustees_rename(&path1, &path2))
+ ret = 0;
+
+ path_put(&path1);
+ path_put(&path2);
+
+ return ret;
+}
+
+/* We have a few renames to protect against:
+ * 1. Any file or directory that is affected by different trustees at its
+ * old location than at its new location.
+ * 2. In the case of a directory, we should protect against moving a directory
+ * that has trustees set inside of it.
+ *
+ * In any case above, we return -EXDEV which signifies to the calling program that
+ * the files are on different devices, and assuming the program is written correctly
+ * it should then handle the situation by copying the files and removing the originals
+ * ( which will then use the trustees permissions as they are meant to be used )
+ */
+static int trustees_inode_rename(struct inode *old_dir,
+ struct dentry *old_dentry,
+ struct inode *new_dir,
+ struct dentry *new_dentry)
+{
+ return trustees_inode_link(old_dentry, new_dir, new_dentry);
+}
+
+/* Return CAP_DAC_OVERRIDE on everything. We want to handle our own
+ * permissions (overriding those normally allowed by unix permissions)
+ */
+static int trustees_capable(struct task_struct *tsk, const struct cred *cred,
+ int cap,
+ int audit)
+{
+ if (cap == CAP_DAC_OVERRIDE)
+ return 0;
+
+ return cap_capable(tsk, cred, cap, audit);
+}
+
+/* Register the security module
+ */
+int trustees_init_security(void)
+{
+ if (register_security(&trustees_security_ops)) {
+ TS_ERR_MSG("Could not register security component\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}