All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC] Privilege dropping security module
@ 2009-09-23  0:56 Andy Spencer
  2009-09-23 20:46 ` Casey Schaufler
                   ` (2 more replies)
  0 siblings, 3 replies; 24+ messages in thread
From: Andy Spencer @ 2009-09-23  0:56 UTC (permalink / raw)
  To: linux-security-module, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 2885 bytes --]

I started work on a Linux Security Module called dpriv a few days ago
and would like to get some feedback. It's by no means ready to be
included in the kernel, but I'm sure there are many things that I could
have done better so suggestions are welcome.

The code is available from gitweb and the dpriv branch of my tree:

  git://lug.rose-hulman.edu/~spenceal/linux-dev dpriv

  http://lug.rose-hulman.edu/git/?p=~spenceal/linux-dev;f=security/dpriv;hb=refs/heads/dpriv


I'll start off with a quick FAQ
-------------------------------
Q: Why another LSM, don't we have enough already?
A: As far as I know there are several rather unique things about dpriv
   when compared to other LSMs. (it's also been a good way for me to
   familiarize myself with the kernel)

Q: So what's unique about dpriv?
A: - Dpriv can be used by any user, not just by root
   - The "policy" is created at runtime instead of fixed beforehand
   - It does *not* implement Mandatory Access Control


Now for how it works
--------------------
- Everything is controlled though a securityfs interface which consists
  of three files: "stage", "policy", and "control".

- Policies are created by writing lines to the stage file and then
  writing the "commit" command to the control file. "Committing" the
  policy merges the staged policy into the actual policy (the "policy"
  file). Note that privileges can only be dropped during a commit, and
  afterwards there is no way to get them back.

- Policies are effective for the process which created them and are also
  copied to all it's child processes.

- For example, the following commands will set the root filesystem
  read-only with the exception of allowing execute permission in /bin/
  and write permission in /tmp/. (note that directory permission are
  uppercase and file permissions are lowercase, both are recursive)

  $ echo r--R-X /    > /sys/kernel/security/dpriv/stage
  $ echo r-xR-X /bin > /sys/kernel/security/dpriv/stage
  $ echo rw-RWX /tmp > /sys/kernel/security/dpriv/stage
  $ echo commit      > /sys/kernel/security/dpriv/control


And some technical details
--------------------------
(subject to change)

- Dpriv stores a list of active and staged permissions for each process
  in current->cred->security. Each permissions line consists of a mode
  mask and the inode that it is effective for.

- When determining access rights for an inode: if the inode is in the
  list of active permissions, use that mask, else recursively fetch (and
  merge) the permissions from the inodes parents

  (to do this, all the parents for hard links/mounts will need to be
  known and stored in the inode somehow, possibly using extended
  attributes, I haven't completely worked this out yet)

- Permissions for things other than files could be implemented as well,
  but I haven't started working on those either.


Let me know what you think

[-- Attachment #2: Type: application/pgp-signature, Size: 198 bytes --]

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [RFC] Privilege dropping security module
  2009-09-23  0:56 [RFC] Privilege dropping security module Andy Spencer
@ 2009-09-23 20:46 ` Casey Schaufler
  2009-09-23 22:31   ` Andy Spencer
  2009-09-23 21:31 ` [RFC][PATCH] " Andy Spencer
  2009-09-29  7:10 ` [RFC][PATCH] Permission masking security module (was dpriv) Andy Spencer
  2 siblings, 1 reply; 24+ messages in thread
From: Casey Schaufler @ 2009-09-23 20:46 UTC (permalink / raw)
  To: andy753421; +Cc: linux-security-module, linux-kernel

Andy Spencer wrote:
> I started work on a Linux Security Module called dpriv a few days ago
> and would like to get some feedback. It's by no means ready to be
> included in the kernel, but I'm sure there are many things that I could
> have done better so suggestions are welcome.
>   

Hi Andy. Git is a wonderful tool, but if you want people to review
your work you need to post patches.

> The code is available from gitweb and the dpriv branch of my tree:
>
>   git://lug.rose-hulman.edu/~spenceal/linux-dev dpriv
>
>   http://lug.rose-hulman.edu/git/?p=~spenceal/linux-dev;f=security/dpriv;hb=refs/heads/dpriv
>
>
> I'll start off with a quick FAQ
> -------------------------------
> Q: Why another LSM, don't we have enough already?
> A: As far as I know there are several rather unique things about dpriv
>    when compared to other LSMs. (it's also been a good way for me to
>    familiarize myself with the kernel)
>
> Q: So what's unique about dpriv?
> A: - Dpriv can be used by any user, not just by root
>    - The "policy" is created at runtime instead of fixed beforehand
>    - It does *not* implement Mandatory Access Control
>   

A good thing, or at least, a partial answer to #1.

>
> Now for how it works
> --------------------
> - Everything is controlled though a securityfs interface which consists
>   of three files: "stage", "policy", and "control".
>
> - Policies are created by writing lines to the stage file and then
>   writing the "commit" command to the control file. "Committing" the
>   policy merges the staged policy into the actual policy (the "policy"
>   file). Note that privileges can only be dropped during a commit, and
>   afterwards there is no way to get them back.
>
> - Policies are effective for the process which created them and are also
>   copied to all it's child processes.
>
> - For example, the following commands will set the root filesystem
>   read-only with the exception of allowing execute permission in /bin/
>   and write permission in /tmp/. (note that directory permission are
>   uppercase and file permissions are lowercase, both are recursive)
>
>   $ echo r--R-X /    > /sys/kernel/security/dpriv/stage
>   $ echo r-xR-X /bin > /sys/kernel/security/dpriv/stage
>   $ echo rw-RWX /tmp > /sys/kernel/security/dpriv/stage
>   $ echo commit      > /sys/kernel/security/dpriv/control
>
>   

And what do you propose as an interesting use case for dpriv?

> And some technical details
> --------------------------
> (subject to change)
>
> - Dpriv stores a list of active and staged permissions for each process
>   in current->cred->security. Each permissions line consists of a mode
>   mask and the inode that it is effective for.
>
> - When determining access rights for an inode: if the inode is in the
>   list of active permissions, use that mask, else recursively fetch (and
>   merge) the permissions from the inodes parents
>
>   (to do this, all the parents for hard links/mounts will need to be
>   known and stored in the inode somehow, possibly using extended
>   attributes, I haven't completely worked this out yet)
>
> - Permissions for things other than files could be implemented as well,
>   but I haven't started working on those either.
>
>
> Let me know what you think
>   


^ permalink raw reply	[flat|nested] 24+ messages in thread

* [RFC][PATCH] Privilege dropping security module
  2009-09-23  0:56 [RFC] Privilege dropping security module Andy Spencer
  2009-09-23 20:46 ` Casey Schaufler
@ 2009-09-23 21:31 ` Andy Spencer
  2009-09-24 16:25   ` Casey Schaufler
  2009-09-29  7:10 ` [RFC][PATCH] Permission masking security module (was dpriv) Andy Spencer
  2 siblings, 1 reply; 24+ messages in thread
From: Andy Spencer @ 2009-09-23 21:31 UTC (permalink / raw)
  To: linux-security-module, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 30864 bytes --]

policy.h and policy.c contain the core data types use for storing and accessing
permission associated with tasks.

dpriv.c contains the struct security_operations hooks for dpriv.

fs.c contains the securityfs interface that is uses to configure and access
policies.

readme.txt contains some notes and will eventually be deleted or moved to the
Documentation folder.

The rest is used for configuring and building dpriv

Signed-off-by: Andy Spencer <andy753421@gmail.com>

 security/Kconfig          |    1 +
 security/Makefile         |    2 +
 security/dpriv/Kconfig    |    8 ++
 security/dpriv/Makefile   |    1 +
 security/dpriv/dpriv.c    |  124 +++++++++++++++++++++++
 security/dpriv/fs.c       |  243 +++++++++++++++++++++++++++++++++++++++++++++
 security/dpriv/policy.c   |  235 +++++++++++++++++++++++++++++++++++++++++++
 security/dpriv/policy.h   |  228 ++++++++++++++++++++++++++++++++++++++++++
 security/dpriv/readme.txt |  102 +++++++++++++++++++
 9 files changed, 944 insertions(+), 0 deletions(-)

diff --git a/security/Kconfig b/security/Kconfig
index fb363cd..b2e310e 100644
--- a/security/Kconfig
+++ b/security/Kconfig
@@ -159,6 +159,7 @@ config LSM_MMAP_MIN_ADDR
 	  this low address space will need the permission specific to the
 	  systems running LSM.
 
+source security/dpriv/Kconfig
 source security/selinux/Kconfig
 source security/smack/Kconfig
 source security/tomoyo/Kconfig
diff --git a/security/Makefile b/security/Makefile
index 95ecc06..2ca4d14 100644
--- a/security/Makefile
+++ b/security/Makefile
@@ -3,6 +3,7 @@
 #
 
 obj-$(CONFIG_KEYS)			+= keys/
+subdir-$(CONFIG_SECURITY_DPRIV)         += dpriv
 subdir-$(CONFIG_SECURITY_SELINUX)	+= selinux
 subdir-$(CONFIG_SECURITY_SMACK)		+= smack
 subdir-$(CONFIG_SECURITY_TOMOYO)        += tomoyo
@@ -14,6 +15,7 @@ obj-y		+= commoncap.o min_addr.o
 obj-$(CONFIG_SECURITY)			+= security.o capability.o
 obj-$(CONFIG_SECURITYFS)		+= inode.o
 # Must precede capability.o in order to stack properly.
+obj-$(CONFIG_SECURITY_DPRIV)		+= dpriv/built-in.o
 obj-$(CONFIG_SECURITY_SELINUX)		+= selinux/built-in.o
 obj-$(CONFIG_SECURITY_SMACK)		+= smack/built-in.o
 obj-$(CONFIG_AUDIT)			+= lsm_audit.o
diff --git a/security/dpriv/Kconfig b/security/dpriv/Kconfig
new file mode 100644
index 0000000..17bf66a
--- /dev/null
+++ b/security/dpriv/Kconfig
@@ -0,0 +1,8 @@
+config SECURITY_DPRIV
+	bool "Privilege dropping"
+	depends on SECURITY
+	select SECURITYFS
+	default n
+	help
+	  This enabled the DPriv privilege dropping mechanism.
+	  If you are unsure how to answer this question, answer N.
diff --git a/security/dpriv/Makefile b/security/dpriv/Makefile
new file mode 100644
index 0000000..0ff3b05
--- /dev/null
+++ b/security/dpriv/Makefile
@@ -0,0 +1 @@
+obj-y = dpriv.o policy.o fs.o
diff --git a/security/dpriv/dpriv.c b/security/dpriv/dpriv.c
new file mode 100644
index 0000000..263c5d0
--- /dev/null
+++ b/security/dpriv/dpriv.c
@@ -0,0 +1,124 @@
+/**
+ * dpriv/dpriv.c -- Linux Security Module interface for privilege dropping
+ *
+ * Copyright (C) 2009 Andy Spencer <spenceal@rose-hulman.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, either version 2 of the License, or (at your option)
+ * any later version.
+ * 
+ * This program 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
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/kernel.h>
+#include <linux/security.h>
+#include <linux/sched.h>
+
+#include "policy.h"
+
+/* Credentials */
+static void dpriv_cred_free(struct cred *cred)
+{
+	kfree(cred->security);
+	cred->security = NULL;
+}
+
+static int dpriv_cred_prepare(struct cred *new, const struct cred *old,
+		gfp_t gfp)
+{
+	new->security = dpriv_task_dup(old->security);
+	return 0;
+}
+
+static int dpriv_dentry_open(struct file *file, const struct cred *cred)
+{
+	u16 perm, need;
+
+	/* Set parent link */
+	if (file->f_dentry->d_sb->s_root != file->f_dentry &&
+			file->f_dentry->d_parent)
+		file->f_inode->i_security = file->f_dentry->d_parent->d_inode;
+	else
+		file->f_inode->i_security = NULL;
+
+
+	/* Check privs */
+	perm = dpriv_policy_get_perm(dpriv_cur_policy, file->f_inode);
+	need = flags_to_mode(file->f_flags);
+	need = imode_to_perm(need, file->f_inode);
+	if (deny(perm, need)) {
+		char path_buf[4096];
+		char *path = d_path(&file->f_path, path_buf, sizeof(path_buf));
+		pr_debug("denied perm=%o:%o path=%s\n", perm, need, path);
+		return -EACCES;
+	}
+	return 0;
+}
+
+/* Mostly for directory walking */
+static int dpriv_inode_permission(struct inode *inode, int mask)
+{
+	u16 perm = dpriv_policy_get_perm(dpriv_cur_policy, inode);
+	u16 need = imode_to_perm(mask, inode);
+	if (deny(perm, need)) {
+		pr_debug("denied perm=%o:%o:%o inode=%p\n",
+				perm, need, mask, inode);
+		return -EACCES;
+	}
+	return 0;
+}
+
+/* TODO: Use these to store the multiple pointers? */
+/*
+static int dpriv_inode_alloc_security(struct inode *inode)
+{
+	return 0;
+}
+static int dpriv_inode_init_security(struct inode *inode, struct inode *dir,
+		char **name, void **value, size_t *len)
+{
+	return 0;
+}
+static void dpriv_inode_free_security(struct inode *inode)
+{
+}
+*/
+
+/* Registration */
+static struct security_operations dpriv_security_ops = {
+	.name                 = "dpriv",
+	.cred_prepare         = dpriv_cred_prepare,
+	.cred_free            = dpriv_cred_free,
+	.dentry_open          = dpriv_dentry_open,
+	.inode_permission     = dpriv_inode_permission,
+	//.inode_alloc_security = dpriv_inode_alloc_security,
+	//.inode_init_security  = dpriv_inode_init_security,
+	//.inode_free_security  = dpriv_inode_free_security,
+	/* TODO: add path operations and update the policies when the
+	 * filesystem layout changes */
+};
+
+static int __init dpriv_init(void)
+{
+	struct cred *cred = (struct cred *)current_cred();
+
+	if (!security_module_enable(&dpriv_security_ops))
+		return 0;
+	if (register_security(&dpriv_security_ops))
+		panic("Failure registering DPriv");
+	cred->security = dpriv_task_new();
+	pr_info("DPriv initialized\n");
+	return 0;
+}
+
+security_initcall(dpriv_init);
diff --git a/security/dpriv/fs.c b/security/dpriv/fs.c
new file mode 100644
index 0000000..c0af74d
--- /dev/null
+++ b/security/dpriv/fs.c
@@ -0,0 +1,243 @@
+/**
+ * dpriv/fs.c -- Security FS interface for privilege dropping
+ *
+ * Copyright (C) 2009 Andy Spencer <spenceal@rose-hulman.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, either version 2 of the License, or (at your option)
+ * any later version.
+ * 
+ * This program 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
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/kernel.h>
+#include <linux/security.h>
+#include <linux/fs.h>
+#include <linux/seq_file.h>
+#include <linux/ctype.h>
+#include <asm/uaccess.h>
+
+#include "policy.h"
+
+/* Arbitrary maximum lengths */
+#define DPRIV_COMMAND_MAX 32
+#define DPRIV_PATH_MAX    4000
+
+/***************************
+ * Generic policy iterator *
+ ***************************/
+/* Use this for reading form any policy file */
+static void *generic_seq_start(struct seq_file *sf, loff_t *pos)
+{
+	struct dpriv_policy *policy = sf->private;
+	down_read(&policy->privs_lock);
+	return seq_list_start(&policy->privs, *pos);
+}
+
+static void *generic_seq_next(struct seq_file *sf, void *seq, loff_t *pos)
+{
+	struct dpriv_policy *policy = sf->private;
+	return seq_list_next(seq, &policy->privs, pos);
+}
+
+static void generic_seq_stop(struct seq_file *sf, void *seq)
+{
+	struct dpriv_policy *policy = sf->private;
+	up_read(&policy->privs_lock);
+}
+
+static int generic_seq_show(struct seq_file *sf, void *seq)
+{
+	struct dpriv_line *line = list_entry(seq, struct dpriv_line, list);
+	char perm_str[DPRIV_PERM_BITS+1];
+	perm_to_str(line->perm, perm_str);
+	seq_printf(sf, "%*s %s\n", DPRIV_PERM_BITS, perm_str, line->path);
+	return 0;
+}
+
+static struct seq_operations generic_seq_ops = {
+	.start = generic_seq_start,
+	.next  = generic_seq_next,
+	.stop  = generic_seq_stop,
+	.show  = generic_seq_show,
+};
+
+static int generic_seq_open(struct file *file, struct dpriv_policy *policy)
+{
+	/* From __seq_open_private
+	 * Not sure if this is correct way to store private data */
+	struct seq_file *sf;
+       	if (seq_open(file, &generic_seq_ops) < 0)
+		return -ENOMEM;
+ 	sf = file->private_data;
+	sf->private = policy;
+	return 0;
+};
+
+
+
+/**************
+ * Stage file *
+ **************/
+static int stage_open(struct inode *inode, struct file *file)
+{
+	return generic_seq_open(file, dpriv_cur_stage);
+};
+
+/* Move a char * forward until it reaches non-whitespace */
+#define strfwd(str) ({                 \
+	while (*str && isspace(*str))  \
+		str++;                 \
+	str;                           \
+})
+
+/* Move a char * forward until it reaches whitespace */
+#define strwfwd(str) ({                \
+	while (*str && !isspace(*str)) \
+		str++;                 \
+	str;                           \
+})
+
+/**
+ * Parse policy lines one at a time.
+ * Format: /\s*([rwxsguRWXSGU\-]*)\s*(.*)(\s*)?/
+ *   \1: See str_to_perm() for discussion
+ *   \2: A file path, \3 trailing whitespace is optional
+ */
+static ssize_t stage_write(struct file *filp, const char *buffer,
+		size_t length, loff_t* off)
+{
+	/* TODO: read multiple lines */
+	int perm;
+	struct file *file;
+	const int write_max = DPRIV_PERM_BITS+DPRIV_PATH_MAX+10; /* spaces */
+	char _buf[write_max+1] = {}, *bufp = _buf, *perm_str, *path_str;
+
+	if (length > write_max)
+		length = write_max;
+	if (copy_from_user(bufp, buffer, length))
+		return -EFAULT;
+
+	/* This parsing is kind of ugly, but should avoid buffer overflows */
+	/* Parse the perm */
+	perm_str = strfwd(bufp); /* save ptr */
+	if (!strwfwd(bufp)[0])   /* make sure we have file */
+		return -EINVAL;
+	bufp++[0] = '\0';        /* terminate mdoe_str */
+	if ((perm = str_to_perm(perm_str)) < 0)
+		return -EINVAL;
+
+	/* Parse the file path */
+	bufp = strfwd(bufp);     /* to path */
+	if (bufp[0] == '\0')
+		return -EINVAL;
+	if (IS_ERR(file = filp_open(bufp, 0, 0))) {
+		/* file not found, try trimming spaces */
+		strstrip(bufp);
+		if (bufp[0] == '\0')
+			return -EINVAL;
+		if (IS_ERR(file = filp_open(bufp, 0, 0)))
+			return -ENOENT;
+	}
+	path_str = kstrdup(bufp, GFP_KERNEL);
+
+	dpriv_policy_set_perm(dpriv_cur_stage, file->f_inode, path_str, perm);
+
+	pr_debug("dpriv_task=%p pid=%d perm=%o[%s] path=%p[%s]\n",
+		dpriv_cur_task, current->pid, perm, perm_str, file, path_str);
+
+	return length;
+}
+
+static const struct file_operations dpriv_stage_fops = {
+	.open    = stage_open,
+	.write   = stage_write,
+	.read    = seq_read,
+	.llseek  = seq_lseek,
+	.release = seq_release,
+};
+
+
+
+/***************
+ * Policy file *
+ ***************/
+static int policy_open(struct inode *inode, struct file *file)
+{
+	return generic_seq_open(file, dpriv_cur_policy);
+};
+
+static const struct file_operations dpriv_policy_fops = {
+	.open    = policy_open,
+	.read    = seq_read,
+	.llseek  = seq_lseek,
+	.release = seq_release,
+};
+
+
+
+/****************
+ * Control file *
+ ****************/
+/**
+ * Read various commands from the user
+ * Format: /(\w+).* /
+ * Commands:
+ *   commit: copy stage to the policy and reset stage
+ */
+static ssize_t control_write(struct file *filp, const char *buffer,
+		size_t length, loff_t* off)
+{
+	char command[DPRIV_COMMAND_MAX+1] = {};
+
+	if (length > DPRIV_COMMAND_MAX)
+		length = DPRIV_COMMAND_MAX;
+
+	if (copy_from_user(command, buffer, length))
+		return -EFAULT;
+
+	strstrip(command);
+
+	if (!strcmp("commit", command)) {
+		pr_debug("committing stage for pid=%d\n", current->pid);
+		dpriv_policy_commit(dpriv_cur_stage, dpriv_cur_policy);
+		dpriv_policy_reset(dpriv_cur_stage);
+	} else {
+		pr_debug("unimplemented control coomand `%s'\n", command);
+	}
+
+	return length;
+}
+
+static const struct file_operations dpriv_control_fops = {
+	.write = control_write,
+};
+
+
+
+/****************
+ * Registration *
+ ****************/
+static int __init dpriv_fs_init(void)
+{
+	struct dentry *dpriv_dir = securityfs_create_dir("dpriv", NULL);
+	securityfs_create_file("stage",
+			0666, dpriv_dir, NULL, &dpriv_stage_fops);
+	securityfs_create_file("policy",
+			0444, dpriv_dir, NULL, &dpriv_policy_fops);
+	securityfs_create_file("control",
+			0222, dpriv_dir, NULL, &dpriv_control_fops);
+	pr_info("DPriv FS initialized\n");
+	return 0;
+}
+
+fs_initcall(dpriv_fs_init);
diff --git a/security/dpriv/policy.c b/security/dpriv/policy.c
new file mode 100644
index 0000000..14823a0
--- /dev/null
+++ b/security/dpriv/policy.c
@@ -0,0 +1,235 @@
+/**
+ * dpriv/policy.c -- Privilege dropping core functionality
+ *
+ * Copyright (C) 2009 Andy Spencer <spenceal@rose-hulman.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, either version 2 of the License, or (at your option)
+ * any later version.
+ * 
+ * This program 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
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/kernel.h>
+#include <linux/security.h>
+#include <linux/sched.h>
+#include <linux/ctype.h>
+#include <linux/fs.h>
+
+#include "policy.h"
+
+/*******************
+ * Permission bits *
+ *******************/
+static char perm_bit_list[] = "rwxsguRWXSGU";
+static u16  perm_bit_table['z'] = {
+	['x'] DPRIV_EXEC,
+	['w'] DPRIV_WRITE,
+	['r'] DPRIV_READ,
+	['s'] DPRIV_KEEPSWP,
+	['g'] DPRIV_SETGID,
+	['u'] DPRIV_SETUID,
+	['X'] DPRIV_WALK,
+	['W'] DPRIV_CREATE,
+	['R'] DPRIV_LIST,
+	['S'] DPRIV_STICKY,
+	['G'] DPRIV_PASSGID,
+	['U'] DPRIV_PASSUID,
+	['-'] 0,
+}; /* plus 0.25k .. */
+
+u16 flags_to_mode(unsigned int flags)
+{
+	u16 mode = 0;
+	if (flags & FMODE_READ ) mode |= DPRIV_READ;
+	if (flags & FMODE_WRITE) mode |= DPRIV_WRITE;
+	if (flags & FMODE_EXEC ) mode |= DPRIV_EXEC;
+	if (flags & O_CREAT    ) mode |= DPRIV_CREATE;
+	return mode;
+}
+
+int str_to_perm(const char *str)
+{
+	int perm = 0;
+	for (; *str; str++) {
+		if ((!isalpha(*str) || !perm_bit_table[(int)*str]) &&
+				*str != '-')
+			return -1;
+		perm |= perm_bit_table[(int)*str];
+	}
+	return perm;
+}
+
+void perm_to_str(u16 perm, char *str)
+{
+	char *c = perm_bit_list;
+	for (; *c; c++,str++)
+		*str = (perm & perm_bit_table[(int)*c]) ?  *c : '-';
+	*str = '\0';
+}
+
+
+
+/**************
+ * DPriv Line *
+ **************/
+struct dpriv_line *dpriv_line_new(const struct inode *inode,
+		const char *path, u16 perm)
+{
+	struct dpriv_line *line;
+	line = kzalloc(sizeof(struct dpriv_line), GFP_KERNEL);
+	line->inode = inode;
+	line->path  = path;
+	line->perm  = perm;
+	return line;
+}
+
+
+
+/****************
+ * DPriv Policy *
+ ****************/
+void dpriv_policy_init(struct dpriv_policy *policy)
+{
+	INIT_LIST_HEAD(&policy->privs);
+	init_rwsem(&policy->privs_lock);
+}
+
+void dpriv_policy_reset(struct dpriv_policy *policy)
+{
+	struct list_head *pos, *n;
+	struct dpriv_line *line;
+	list_for_each_safe(pos, n, &policy->privs){
+		 line = list_entry(pos, struct dpriv_line, list);
+		 list_del(pos);
+		 kfree(line);
+	}
+}
+
+struct dpriv_line *dpriv_policy_get_line(const struct dpriv_policy *policy,
+		const struct inode *inode)
+{
+	struct dpriv_line *line;
+	list_for_each_entry(line, &policy->privs, list)
+		if (line->inode == inode)
+			return line;
+	return NULL;
+}
+
+u16 dpriv_policy_get_perm(const struct dpriv_policy *policy,
+		const struct inode *inode)
+{
+	/* Stop if a permissions is found for current node */
+	struct dpriv_line *line = dpriv_policy_get_line(policy, inode);
+	if (line)
+		return line->perm;
+
+	/* Allow everything if we've reach the root without finding perms */
+	/* TODO: recurse to parent filesystems */
+	if (inode->i_security == NULL)
+		return USHORT_MAX;
+
+	/* Check parents for recursive permissions */
+	/* TODO: Check for multiple parents */
+	return dpriv_policy_get_perm(policy, inode->i_security);
+	// perm = USHORT_MAX;
+	// foreach parent:
+	// 	perm &= dpriv_policy_get_perm(policy, inode->d_parent);
+	// return perm;
+}
+
+/* We need the inode and path so we can create the line if it doesn't exist */
+void dpriv_policy_set_perm(struct dpriv_policy *policy,
+		const struct inode *inode, const char *path, u16 perm)
+{
+	struct dpriv_line *line = dpriv_policy_get_line(policy, inode);
+	if (line) {
+		line->perm = perm;
+	} else {
+		line = dpriv_line_new(inode, path, perm);
+		list_add_tail(&line->list, &policy->privs);
+	}
+}
+
+/* Do a semi-deep copy, that is, copy enough that the policies are distinct,
+ * but without duplicating conostant data such as paths and dentries */
+void dpriv_policy_append(struct dpriv_policy *from, struct dpriv_policy *to)
+{
+	struct dpriv_line *fl, *tl;
+	list_for_each_entry(fl, &from->privs, list) {
+		tl = dpriv_line_new(fl->inode, fl->path, fl->perm);
+		list_add_tail(&tl->list, &to->privs);
+	}
+}
+
+void dpriv_policy_commit(struct dpriv_policy *from, struct dpriv_policy *to)
+{
+	u16 perm;
+	struct dpriv_line *line, *n;
+	struct dpriv_policy merge;
+	dpriv_policy_init(&merge);
+
+	/* Merge paths from @to into merge */
+	list_for_each_entry(line, &to->privs, list) {
+		perm  = line->perm;
+		perm &= dpriv_policy_get_perm(from, line->inode);
+		dpriv_policy_set_perm(&merge, line->inode, line->path, perm);
+	}
+
+	/* Merge paths from @from into merge */
+	list_for_each_entry(line, &from->privs, list) {
+		perm  = line->perm;
+		perm &= dpriv_policy_get_perm(to, line->inode);
+		dpriv_policy_set_perm(&merge, line->inode, line->path, perm);
+	}
+
+	/* Free old entries */
+	dpriv_policy_reset(to);
+	list_for_each_entry_safe(line, n, &merge.privs, list)
+		list_move_tail(&line->list, &to->privs);
+}
+
+
+
+/**************
+ * DPriv Task *
+ **************/
+struct dpriv_task *dpriv_task_new(void)
+{
+	struct dpriv_task *task;
+	task = kzalloc(sizeof(struct dpriv_task), GFP_KERNEL);
+
+	dpriv_policy_init(&task->stage);
+	dpriv_policy_init(&task->policy);
+
+	INIT_LIST_HEAD(&task->fds);
+	init_rwsem(&task->fds_lock);
+
+	return task;
+}
+
+struct dpriv_task *dpriv_task_dup(struct dpriv_task *task)
+{
+	struct dpriv_task *copy = dpriv_task_new();
+	struct dpriv_line *tl, *cl;
+
+	/* Copy policies */
+	dpriv_policy_append(&task->stage,  &copy->stage);
+	dpriv_policy_append(&task->policy, &copy->policy);
+
+	/* Copy file descriptors */
+	list_for_each_entry(tl, &task->fds, list) {
+		cl = dpriv_line_new(tl->inode, tl->path, tl->perm);
+		list_add_tail(&cl->list, &copy->fds);
+	}
+	return copy;
+}
diff --git a/security/dpriv/policy.h b/security/dpriv/policy.h
new file mode 100644
index 0000000..70c0cbb
--- /dev/null
+++ b/security/dpriv/policy.h
@@ -0,0 +1,228 @@
+/**
+ * dpriv/policy.h -- Privilege dropping core functionality
+ *
+ * Copyright (C) 2009 Andy Spencer <spenceal@rose-hulman.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, either version 2 of the License, or (at your option)
+ * any later version.
+ * 
+ * This program 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
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __DPRIV_POLICY_H__
+#define __DPRIV_POLICY_H__
+
+#define f_inode f_dentry->d_inode
+
+/**
+ * Terminology
+ *   mode   = `Unix' mode (u16 use for filesyste mode bits)
+ *   perm   = DPriv permission bits (see below)
+ *   privs  = List of files and associated perm
+ *   policy = Privs + whatever else
+ */
+
+#define dpriv_cur_task   ((struct dpriv_task *)current_security())
+#define dpriv_cur_stage  ((struct dpriv_policy *)&dpriv_cur_task->stage)
+#define dpriv_cur_policy ((struct dpriv_policy *)&dpriv_cur_task->policy)
+
+
+/*******************
+ * Permission bits *
+ *******************/
+/* File bits */
+#define DPRIV_EXEC    (1u<<0 ) /* x */
+#define DPRIV_WRITE   (1u<<1 ) /* w */
+#define DPRIV_READ    (1u<<2 ) /* r */
+#define DPRIV_KEEPSWP (1u<<3 ) /* s (ignored) */
+#define DPRIV_SETGID  (1u<<4 ) /* g */
+#define DPRIV_SETUID  (1u<<5 ) /* u */
+
+/* Directory bits */
+#define DPRIV_WALK    (1u<<6 ) /* X */
+#define DPRIV_CREATE  (1u<<7 ) /* W */
+#define DPRIV_LIST    (1u<<8 ) /* R */
+#define DPRIV_STICKY  (1u<<9 ) /* S */
+#define DPRIV_PASSGID (1u<<10) /* G */
+#define DPRIV_PASSUID (1u<<11) /* U (ignored) */
+
+/* Meta bits/masks */
+#define DPRIV_PERM_BITS 12
+#define DPRIV_MASK      0b111111111111
+#define DPRIV_FILE_MASK 0b000000111111
+#define DPRIV_DIR_MASK  0b111111000000
+
+/* Mode conversion functions */
+#define deny(perm, request) \
+	unlikely(perm >= 0 && ~perm & request)
+
+/* Convert from a unix directory mode to a perm */
+#define dmode_to_perm(mode) \
+	((mode<<6))
+
+/* Convert from a unix file mode to a perm */
+#define fmode_to_perm(mode) \
+	(mode)
+
+/* Convert from a unix perm to a mode based on inode type */
+#define imode_to_perm(mode, inode)     \
+	(S_ISDIR(inode->i_mode) ?      \
+		 dmode_to_perm(mode) : \
+		 fmode_to_perm(mode))
+
+/**
+ * Convert struct file->f_flags to a Unix mode
+ * <x>mode_to_perm should probably be called on the resulting mode
+ */
+u16 flags_to_mode(unsigned int flags);
+
+/**
+ * Parse a permission string into a perm
+ * @str:
+ *  - Format is "rwxsguRWXSGU" (see Permission bits)
+ *  - Order does not matter
+ *  - '-' is ignored, any other character is invalid
+ *  - return -1 on invalid str
+ */
+int str_to_perm(const char *str);
+
+/**
+ * Convert a perm to a string for printing
+ */
+void perm_to_str(u16 perm, char *str);
+
+
+
+/**************
+ * DPriv Line *
+ **************/
+/**
+ * An entry in the policy
+ *
+ * Example:
+ *   /var/tmp (rw-)
+ *
+ * @list:  list_head for stroing in policy or fds
+ * @inode: Some point in the filesystem, topically an inode
+ * @path:  Path given when the line was created, debugging only
+ * @perm:  Permissions given to location and it's kids
+ */
+struct dpriv_line {
+	struct list_head    list;
+	const struct inode *inode;
+	const char         *path;
+	u16                 perm;
+};
+
+/**
+ * Allocate and initalize a new dpriv_line
+ * @indoe, @path, @perm: fileds to store en line
+ */
+struct dpriv_line *dpriv_line_new(const struct inode *inode,
+		const char *path, u16 perm);
+
+
+
+/****************
+ * DPriv Policy *
+ ****************/
+/**
+ * Contains permisisons and operations allowed for given security policy
+ *
+ * @privs:      List of dpriv_lines for filesystem privilages
+ * @privs_lock: Used for printing (maybe other?)
+ *
+ * Example:
+ *   privs: 
+ *     /         (r--)
+ *     /bin/     (r-x)
+ *     /tmp/     (rw-)
+ */
+struct dpriv_policy {
+	struct list_head privs;
+	struct rw_semaphore privs_lock;
+	/* TODO: add other security things */
+};
+
+/* Initialize a blank @policy */
+void dpriv_policy_init(struct dpriv_policy *policy);
+
+/* Clear/free data from @policy */
+void dpriv_policy_reset(struct dpriv_policy *policy);
+
+/* Return the line from @policy->privs that matches @inode */
+struct dpriv_line *dpriv_policy_get_line(const struct dpriv_policy *policy,
+		const struct inode *inode);
+
+/* Recursivly lookup perm for @inode in @policy */
+u16 dpriv_policy_get_perm(const struct dpriv_policy *policy,
+		const struct inode *inode);
+
+/* Set perm for @inode in @policy to @perm, create new line if necessasiary */
+void dpriv_policy_set_perm(struct dpriv_policy *policy,
+		const struct inode *inode, const char *path, u16 perm);
+
+/* Copy lines from @from to @to making sure that no additional oeratoins are
+ * allowed in @to after the commit is performed */
+void dpriv_policy_commit(struct dpriv_policy *from, struct dpriv_policy *to);
+
+
+
+/**************
+ * DPriv Task *
+ **************/
+/**
+ * Contains information for a given task, including the security policy, stage,
+ * and cache information.
+ *
+ * @stage:
+ *   The modifialbe policy, privilages can be allowed or denied in the stage
+ * @policy:
+ *   The effective policy, used to determines whether an action is allowed
+ * @fds:
+ *   Open file descriptors and their implied permissions based on @policy
+ *
+ * @policy can only be modified by commiting @stage to @policy. When this is
+ * done, it is insured that no additional operations will be allowed by @policy
+ * after the commit.
+ *
+ * @fds is used to cache permissions on open file descriptors. This allows
+ * permissions to be quickly passed down form parents when opening directory
+ * entries. Note that when opening a file descriptor with multiple parents
+ * (e.g. a hard link) the permissions from the parent are incomplete and the
+ * permissions form the alternate parents must be determined as well.
+ *
+ * NOTE: @fds is currently not used, we recursivly iterate up to parents
+ * whenever it's needed. This might be faster anyway?
+ *
+ * Example:
+ *   stage:  (see dpriv_policy)
+ *   policy: (see dpriv_policy)
+ *   fds:
+ *     /foo     ~(r--)
+ *     /bin/foo ~(r-x)
+ *     /tmp/foo ~(rw-)
+ */
+struct dpriv_task {
+	struct dpriv_policy stage;
+	struct dpriv_policy policy;
+
+	struct list_head fds;
+	struct rw_semaphore fds_lock;
+};
+
+/* Allocate a blank task */
+struct dpriv_task *dpriv_task_new(void);
+
+/* Create a semi-deep copy of @task, suitable for passing to a child on exec */
+struct dpriv_task *dpriv_task_dup(struct dpriv_task *task);
+
+#endif
diff --git a/security/dpriv/readme.txt b/security/dpriv/readme.txt
new file mode 100644
index 0000000..073d15d
--- /dev/null
+++ b/security/dpriv/readme.txt
@@ -0,0 +1,102 @@
+Source code
+-----------
+  policy.[ch] - policy datatypes
+  dpriv.c     - security/credentials hooks
+  fs.c        - securityfs hooks
+
+
+TODO
+----
+  - Check for race conditions
+
+
+Overview
+--------
+1. Each process keeps a list of inode -> priv mappings:
+   - i.e. the security policy
+
+2. Caching possibilities (todo?)
+   - Processes keeps a list of open fds to prevent recursing up the FS tree?
+   - Store the most recent processes access in each inode? 
+
+Privs: 
+  - read/write/exec/sticky/setuid/setgui
+  - All permissions are recursive
+  - Permissions for dirs and file are separate
+    - This prevents recursion problems
+    - e.g. you can set noexec for files without smashing directories
+  - Notation
+     (rwx) = specified permission (inode in policy)
+    ~(rwx) = implied permission (parent(s) in policy)
+
+Things to do when:
+  1. Setting privs
+     - Add policy line(s) for given path?
+     - Update privs on open inodes that are children of policy line?
+  2. Loading inode
+     - Cache privs from parent(s)?
+  3. Namespace modification (mv,ln,bind,etc)
+     - OR
+       - Keep policy for inode the same        (policy = old      )
+       - Merge policy for both locations       (policy = old & new)
+       - Change policy to reflect new location (policy =       new)
+     - If mv, and including old implied policy:
+       - need to write new (combined) policy line
+
+
+Security FS
+-----------
+files:
+  -rw-rw-rw- /securityfs/dpriv/stage
+  -r--r--r-- /securityfs/dpriv/policy
+  --w--w--w- /securityfs/dpriv/control
+
+stage:
+  read:  print staged policy
+  write: set inode in staged policy to given perms OR
+         add inode to staged policy with given perms
+         > staged[inode] = perms
+
+  In the stage, order does not matter, adding a line simply writes or
+  overwrites the location with no regard to the rest of the policy.
+
+policy:
+  read: print active policy
+ 
+control:
+  write:
+    "commit" - merge staged policy into policy
+      > for (inode in policy, staged):
+      >     new[inode] =
+      >         implied_privs(policy, inode) &
+      >         implied_privs(staged, inode) 
+      > clear(staged)
+
+    When committing, privilages can only be revoked.
+   
+
+Examples
+--------
+Example 1:
+  set /src/     (rw-)
+  set /dst/     (r-x)
+  
+  $ mv /src/foo /dst
+  
+  get /src/     (rw-)
+  get /dst/     (r-x)
+  OR:
+    get /dst/foo  (rw-)
+    get /dst/foo ~(r-x)
+    get /dst/foo  (rw-) & ~(r-x) = (r--)
+
+Example 2:
+  $ ln /src/foo /dst
+  
+  set /src/     (rw-)
+  set /dst/     (rwx)
+  
+  get /src/     (rw-)
+  get /dst/     (rwx)
+  get /src/foo ~(rw-) & ~(rwx) = ~(rw-)
+  get /dst/foo ~(rw-) & ~(rwx) = ~(rw-)

[-- Attachment #2: Type: application/pgp-signature, Size: 198 bytes --]

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* Re: [RFC] Privilege dropping security module
  2009-09-23 20:46 ` Casey Schaufler
@ 2009-09-23 22:31   ` Andy Spencer
  2009-09-23 23:03     ` Tetsuo Handa
                       ` (2 more replies)
  0 siblings, 3 replies; 24+ messages in thread
From: Andy Spencer @ 2009-09-23 22:31 UTC (permalink / raw)
  To: Casey Schaufler; +Cc: linux-security-module, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 1296 bytes --]

> Hi Andy. Git is a wonderful tool, but if you want people to review
> your work you need to post patches.

Thanks for letting me know, I've posted a separate message with patch.


> And what do you propose as an interesting use case for dpriv?

I think the two most important things about dpriv is that it can be used
by ordinary users and that is can create policies programmatically.

Being able to use dpriv as a non root user is pretty strait forward. For
example, a user of a multi-user system may want to try some untrusted
code without risking access to the rest of the system:

  $ cd ~/my_project
  $ echo rxRX   /                > /sys/kernel/security/dpriv/stage
  $ echo X      $HOME            > /sys/kernel/security/dpriv/stage
  $ echo rwxRWX $HOME/my_project > /sys/kernel/security/dpriv/stage
  $ echo commit                  > /sys/kernel/security/dpriv/control
  $ patch < untrusted.patch
  $ make && ./src/some_exe

The above example also demonstrates how dpriv can be used
programmatically. That is, a policy for allowing read-write-exec access
to build and test tools in ~/my_project didn't have to exist ahead of
time.

A more realistic example might be for a virtual hosting web server where
you want apache to only have access to the files for the current virtual
host.

[-- Attachment #2: Type: application/pgp-signature, Size: 198 bytes --]

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [RFC] Privilege dropping security module
  2009-09-23 22:31   ` Andy Spencer
@ 2009-09-23 23:03     ` Tetsuo Handa
  2009-09-24 16:37     ` David Wagner
  2009-10-01  7:38     ` Pavel Machek
  2 siblings, 0 replies; 24+ messages in thread
From: Tetsuo Handa @ 2009-09-23 23:03 UTC (permalink / raw)
  To: andy753421; +Cc: linux-security-module, linux-kernel

Andy Spencer wrote:
> > And what do you propose as an interesting use case for dpriv?
> 
> I think the two most important things about dpriv is that it can be used
> by ordinary users and that is can create policies programmatically.
> 
> Being able to use dpriv as a non root user is pretty strait forward. For
> example, a user of a multi-user system may want to try some untrusted
> code without risking access to the rest of the system:
You need to add some quota, or malicious/careless users can consume
all kernel memory and crash the system...

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [RFC][PATCH] Privilege dropping security module
  2009-09-23 21:31 ` [RFC][PATCH] " Andy Spencer
@ 2009-09-24 16:25   ` Casey Schaufler
  2009-09-25 10:06     ` Andy Spencer
  0 siblings, 1 reply; 24+ messages in thread
From: Casey Schaufler @ 2009-09-24 16:25 UTC (permalink / raw)
  To: andy753421; +Cc: linux-security-module, linux-kernel

Andy Spencer wrote:
> policy.h and policy.c contain the core data types use for storing and accessing
> permission associated with tasks.
>
> dpriv.c contains the struct security_operations hooks for dpriv.
>   
Lots of mechanical comments in line.

The term "privilege" is generally applied to a broader scope
than discretionary access controls. You might want to consider
using a name that is more precisely descriptive of the feature.
It probably isn't that important, but I for one would be happier.

You're not dropping privilege, that would imply restricting
root and/or capability access. You're masking file permissions.

> fs.c contains the securityfs interface that is uses to configure and access
> policies.
>
> readme.txt contains some notes and will eventually be deleted or moved to the
> Documentation folder.
>
> The rest is used for configuring and building dpriv
>
> Signed-off-by: Andy Spencer <andy753421@gmail.com>
>
>  security/Kconfig          |    1 +
>  security/Makefile         |    2 +
>  security/dpriv/Kconfig    |    8 ++
>  security/dpriv/Makefile   |    1 +
>  security/dpriv/dpriv.c    |  124 +++++++++++++++++++++++
>  security/dpriv/fs.c       |  243 +++++++++++++++++++++++++++++++++++++++++++++
>  security/dpriv/policy.c   |  235 +++++++++++++++++++++++++++++++++++++++++++
>  security/dpriv/policy.h   |  228 ++++++++++++++++++++++++++++++++++++++++++
>  security/dpriv/readme.txt |  102 +++++++++++++++++++
>  9 files changed, 944 insertions(+), 0 deletions(-)
>
> diff --git a/security/Kconfig b/security/Kconfig
> index fb363cd..b2e310e 100644
> --- a/security/Kconfig
> +++ b/security/Kconfig
> @@ -159,6 +159,7 @@ config LSM_MMAP_MIN_ADDR
>  	  this low address space will need the permission specific to the
>  	  systems running LSM.
>  
> +source security/dpriv/Kconfig
>  source security/selinux/Kconfig
>  source security/smack/Kconfig
>  source security/tomoyo/Kconfig
> diff --git a/security/Makefile b/security/Makefile
> index 95ecc06..2ca4d14 100644
> --- a/security/Makefile
> +++ b/security/Makefile
> @@ -3,6 +3,7 @@
>  #
>  
>  obj-$(CONFIG_KEYS)			+= keys/
> +subdir-$(CONFIG_SECURITY_DPRIV)         += dpriv
>  subdir-$(CONFIG_SECURITY_SELINUX)	+= selinux
>  subdir-$(CONFIG_SECURITY_SMACK)		+= smack
>  subdir-$(CONFIG_SECURITY_TOMOYO)        += tomoyo
> @@ -14,6 +15,7 @@ obj-y		+= commoncap.o min_addr.o
>  obj-$(CONFIG_SECURITY)			+= security.o capability.o
>  obj-$(CONFIG_SECURITYFS)		+= inode.o
>  # Must precede capability.o in order to stack properly.
> +obj-$(CONFIG_SECURITY_DPRIV)		+= dpriv/built-in.o
>  obj-$(CONFIG_SECURITY_SELINUX)		+= selinux/built-in.o
>  obj-$(CONFIG_SECURITY_SMACK)		+= smack/built-in.o
>  obj-$(CONFIG_AUDIT)			+= lsm_audit.o
> diff --git a/security/dpriv/Kconfig b/security/dpriv/Kconfig
> new file mode 100644
> index 0000000..17bf66a
> --- /dev/null
> +++ b/security/dpriv/Kconfig
> @@ -0,0 +1,8 @@
> +config SECURITY_DPRIV
> +	bool "Privilege dropping"
> +	depends on SECURITY
> +	select SECURITYFS
> +	default n
> +	help
> +	  This enabled the DPriv privilege dropping mechanism.
> +	  If you are unsure how to answer this question, answer N.
> diff --git a/security/dpriv/Makefile b/security/dpriv/Makefile
> new file mode 100644
> index 0000000..0ff3b05
> --- /dev/null
> +++ b/security/dpriv/Makefile
> @@ -0,0 +1 @@
> +obj-y = dpriv.o policy.o fs.o
> diff --git a/security/dpriv/dpriv.c b/security/dpriv/dpriv.c
> new file mode 100644
> index 0000000..263c5d0
> --- /dev/null
> +++ b/security/dpriv/dpriv.c
> @@ -0,0 +1,124 @@
> +/**
> + * dpriv/dpriv.c -- Linux Security Module interface for privilege dropping
> + *
> + * Copyright (C) 2009 Andy Spencer <spenceal@rose-hulman.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, either version 2 of the License, or (at your option)
> + * any later version.
> + * 
> + * This program 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
> + * this program.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +
> +#define pr_fmt(fmt) "%s: " fmt, __func__
>   

You have defined this in multiple places, which isn't good,
and it's a bit of code that obfuscates what's going on. It
may seem like a good idea, but you'll be better off if you
go ahead and put the code in where you're using it.

> +
> +#include <linux/kernel.h>
> +#include <linux/security.h>
> +#include <linux/sched.h>
> +
> +#include "policy.h"
> +
> +/* Credentials */
> +static void dpriv_cred_free(struct cred *cred)
> +{
> +	kfree(cred->security);
> +	cred->security = NULL;
> +}
> +
> +static int dpriv_cred_prepare(struct cred *new, const struct cred *old,
> +		gfp_t gfp)
> +{
> +	new->security = dpriv_task_dup(old->security);
> +	return 0;
> +}
> +
> +static int dpriv_dentry_open(struct file *file, const struct cred *cred)
> +{
> +	u16 perm, need;
> +
> +	/* Set parent link */
> +	if (file->f_dentry->d_sb->s_root != file->f_dentry &&
> +			file->f_dentry->d_parent)
> +		file->f_inode->i_security = file->f_dentry->d_parent->d_inode;
> +	else
> +		file->f_inode->i_security = NULL;
> +
> +
> +	/* Check privs */
> +	perm = dpriv_policy_get_perm(dpriv_cur_policy, file->f_inode);
> +	need = flags_to_mode(file->f_flags);
> +	need = imode_to_perm(need, file->f_inode);
> +	if (deny(perm, need)) {
> +		char path_buf[4096];
>   

I think I saw this mentioned elsewhere, but you can't put a
4k buffer on the stack.

> +		char *path = d_path(&file->f_path, path_buf, sizeof(path_buf));
> +		pr_debug("denied perm=%o:%o path=%s\n", perm, need, path);
> +		return -EACCES;
> +	}
> +	return 0;
> +}
> +
> +/* Mostly for directory walking */
> +static int dpriv_inode_permission(struct inode *inode, int mask)
> +{
> +	u16 perm = dpriv_policy_get_perm(dpriv_cur_policy, inode);
> +	u16 need = imode_to_perm(mask, inode);
> +	if (deny(perm, need)) {
> +		pr_debug("denied perm=%o:%o:%o inode=%p\n",
> +				perm, need, mask, inode);
> +		return -EACCES;
> +	}
> +	return 0;
> +}
> +
> +/* TODO: Use these to store the multiple pointers? */
> +/*
> +static int dpriv_inode_alloc_security(struct inode *inode)
> +{
> +	return 0;
> +}
> +static int dpriv_inode_init_security(struct inode *inode, struct inode *dir,
> +		char **name, void **value, size_t *len)
> +{
> +	return 0;
> +}
> +static void dpriv_inode_free_security(struct inode *inode)
> +{
> +}
> +*/
> +
> +/* Registration */
> +static struct security_operations dpriv_security_ops = {
> +	.name                 = "dpriv",
> +	.cred_prepare         = dpriv_cred_prepare,
> +	.cred_free            = dpriv_cred_free,
> +	.dentry_open          = dpriv_dentry_open,
> +	.inode_permission     = dpriv_inode_permission,
> +	//.inode_alloc_security = dpriv_inode_alloc_security,
> +	//.inode_init_security  = dpriv_inode_init_security,
> +	//.inode_free_security  = dpriv_inode_free_security,
>   

"//" comments are not used in the kernel

> +	/* TODO: add path operations and update the policies when the
> +	 * filesystem layout changes */
> +};
> +
> +static int __init dpriv_init(void)
> +{
> +	struct cred *cred = (struct cred *)current_cred();
> +
> +	if (!security_module_enable(&dpriv_security_ops))
> +		return 0;
> +	if (register_security(&dpriv_security_ops))
> +		panic("Failure registering DPriv");
> +	cred->security = dpriv_task_new();
> +	pr_info("DPriv initialized\n");
> +	return 0;
> +}
> +
> +security_initcall(dpriv_init);
> diff --git a/security/dpriv/fs.c b/security/dpriv/fs.c
> new file mode 100644
> index 0000000..c0af74d
> --- /dev/null
> +++ b/security/dpriv/fs.c
> @@ -0,0 +1,243 @@
> +/**
> + * dpriv/fs.c -- Security FS interface for privilege dropping
> + *
> + * Copyright (C) 2009 Andy Spencer <spenceal@rose-hulman.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, either version 2 of the License, or (at your option)
> + * any later version.
> + * 
> + * This program 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
> + * this program.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#define pr_fmt(fmt) "%s: " fmt, __func__
> +
> +#include <linux/kernel.h>
> +#include <linux/security.h>
> +#include <linux/fs.h>
> +#include <linux/seq_file.h>
> +#include <linux/ctype.h>
> +#include <asm/uaccess.h>
> +
> +#include "policy.h"
> +
> +/* Arbitrary maximum lengths */
> +#define DPRIV_COMMAND_MAX 32
> +#define DPRIV_PATH_MAX    4000
>   

Why use a value other than PATH_MAX? If it's arbitrary,
go with the "standard" arbitrary.

> +
> +/***************************
> + * Generic policy iterator *
> + ***************************/
> +/* Use this for reading form any policy file */
> +static void *generic_seq_start(struct seq_file *sf, loff_t *pos)
> +{
> +	struct dpriv_policy *policy = sf->private;
> +	down_read(&policy->privs_lock);
> +	return seq_list_start(&policy->privs, *pos);
> +}
> +
> +static void *generic_seq_next(struct seq_file *sf, void *seq, loff_t *pos)
> +{
> +	struct dpriv_policy *policy = sf->private;
> +	return seq_list_next(seq, &policy->privs, pos);
> +}
> +
> +static void generic_seq_stop(struct seq_file *sf, void *seq)
> +{
> +	struct dpriv_policy *policy = sf->private;
> +	up_read(&policy->privs_lock);
> +}
> +
> +static int generic_seq_show(struct seq_file *sf, void *seq)
> +{
> +	struct dpriv_line *line = list_entry(seq, struct dpriv_line, list);
> +	char perm_str[DPRIV_PERM_BITS+1];
> +	perm_to_str(line->perm, perm_str);
> +	seq_printf(sf, "%*s %s\n", DPRIV_PERM_BITS, perm_str, line->path);
> +	return 0;
> +}
> +
> +static struct seq_operations generic_seq_ops = {
> +	.start = generic_seq_start,
> +	.next  = generic_seq_next,
> +	.stop  = generic_seq_stop,
> +	.show  = generic_seq_show,
> +};
> +
> +static int generic_seq_open(struct file *file, struct dpriv_policy *policy)
> +{
> +	/* From __seq_open_private
> +	 * Not sure if this is correct way to store private data */
> +	struct seq_file *sf;
> +       	if (seq_open(file, &generic_seq_ops) < 0)
> +		return -ENOMEM;
> + 	sf = file->private_data;
> +	sf->private = policy;
> +	return 0;
> +};
> +
> +
> +
> +/**************
> + * Stage file *
> + **************/
> +static int stage_open(struct inode *inode, struct file *file)
> +{
> +	return generic_seq_open(file, dpriv_cur_stage);
> +};
> +
> +/* Move a char * forward until it reaches non-whitespace */
> +#define strfwd(str) ({                 \
> +	while (*str && isspace(*str))  \
> +		str++;                 \
> +	str;                           \
> +})
> +
> +/* Move a char * forward until it reaches whitespace */
> +#define strwfwd(str) ({                \
> +	while (*str && !isspace(*str)) \
> +		str++;                 \
> +	str;                           \
> +})
>   

Function macros are discouraged. If you really want code duplication
use static inline functions. And stick with your namespace, use
dpriv_strfwd() instead of strfwd().

> +
> +/**
> + * Parse policy lines one at a time.
>   

String parsing in the kernel is considered harmful.
Simplify this.

> + * Format: /\s*([rwxsguRWXSGU\-]*)\s*(.*)(\s*)?/
> + *   \1: See str_to_perm() for discussion
> + *   \2: A file path, \3 trailing whitespace is optional
> + */
> +static ssize_t stage_write(struct file *filp, const char *buffer,
> +		size_t length, loff_t* off)
> +{
> +	/* TODO: read multiple lines */
> +	int perm;
> +	struct file *file;
> +	const int write_max = DPRIV_PERM_BITS+DPRIV_PATH_MAX+10; /* spaces */
> +	char _buf[write_max+1] = {}, *bufp = _buf, *perm_str, *path_str;
> +
> +	if (length > write_max)
> +		length = write_max;
> +	if (copy_from_user(bufp, buffer, length))
> +		return -EFAULT;
> +
> +	/* This parsing is kind of ugly, but should avoid buffer overflows */
> +	/* Parse the perm */
> +	perm_str = strfwd(bufp); /* save ptr */
> +	if (!strwfwd(bufp)[0])   /* make sure we have file */
> +		return -EINVAL;
> +	bufp++[0] = '\0';        /* terminate mdoe_str */
> +	if ((perm = str_to_perm(perm_str)) < 0)
> +		return -EINVAL;
> +
> +	/* Parse the file path */
> +	bufp = strfwd(bufp);     /* to path */
> +	if (bufp[0] == '\0')
> +		return -EINVAL;
> +	if (IS_ERR(file = filp_open(bufp, 0, 0))) {
> +		/* file not found, try trimming spaces */
> +		strstrip(bufp);
> +		if (bufp[0] == '\0')
> +			return -EINVAL;
> +		if (IS_ERR(file = filp_open(bufp, 0, 0)))
> +			return -ENOENT;
> +	}
> +	path_str = kstrdup(bufp, GFP_KERNEL);
> +
> +	dpriv_policy_set_perm(dpriv_cur_stage, file->f_inode, path_str, perm);
> +
> +	pr_debug("dpriv_task=%p pid=%d perm=%o[%s] path=%p[%s]\n",
> +		dpriv_cur_task, current->pid, perm, perm_str, file, path_str);
> +
> +	return length;
> +}
> +
> +static const struct file_operations dpriv_stage_fops = {
> +	.open    = stage_open,
> +	.write   = stage_write,
> +	.read    = seq_read,
> +	.llseek  = seq_lseek,
> +	.release = seq_release,
> +};
> +
> +
> +
> +/***************
> + * Policy file *
> + ***************/
> +static int policy_open(struct inode *inode, struct file *file)
>   

Stick with your namespace.

> +{
> +	return generic_seq_open(file, dpriv_cur_policy);
> +};
> +
> +static const struct file_operations dpriv_policy_fops = {
> +	.open    = policy_open,
> +	.read    = seq_read,
> +	.llseek  = seq_lseek,
> +	.release = seq_release,
> +};
> +
> +
> +
> +/****************
> + * Control file *
> + ****************/
> +/**
> + * Read various commands from the user
> + * Format: /(\w+).* /
> + * Commands:
> + *   commit: copy stage to the policy and reset stage
> + */
> +static ssize_t control_write(struct file *filp, const char *buffer,
>   

Namespace.

> +		size_t length, loff_t* off)
> +{
> +	char command[DPRIV_COMMAND_MAX+1] = {};
> +
> +	if (length > DPRIV_COMMAND_MAX)
> +		length = DPRIV_COMMAND_MAX;
> +
> +	if (copy_from_user(command, buffer, length))
> +		return -EFAULT;
> +
> +	strstrip(command);
> +
> +	if (!strcmp("commit", command)) {
> +		pr_debug("committing stage for pid=%d\n", current->pid);
> +		dpriv_policy_commit(dpriv_cur_stage, dpriv_cur_policy);
> +		dpriv_policy_reset(dpriv_cur_stage);
> +	} else {
> +		pr_debug("unimplemented control coomand `%s'\n", command);
> +	}
> +
> +	return length;
> +}
> +
> +static const struct file_operations dpriv_control_fops = {
> +	.write = control_write,
> +};
> +
> +
> +
> +/****************
> + * Registration *
> + ****************/
> +static int __init dpriv_fs_init(void)
> +{
> +	struct dentry *dpriv_dir = securityfs_create_dir("dpriv", NULL);
> +	securityfs_create_file("stage",
> +			0666, dpriv_dir, NULL, &dpriv_stage_fops);
> +	securityfs_create_file("policy",
> +			0444, dpriv_dir, NULL, &dpriv_policy_fops);
> +	securityfs_create_file("control",
> +			0222, dpriv_dir, NULL, &dpriv_control_fops);
> +	pr_info("DPriv FS initialized\n");
> +	return 0;
> +}
> +
> +fs_initcall(dpriv_fs_init);
> diff --git a/security/dpriv/policy.c b/security/dpriv/policy.c
> new file mode 100644
> index 0000000..14823a0
> --- /dev/null
> +++ b/security/dpriv/policy.c
> @@ -0,0 +1,235 @@
> +/**
> + * dpriv/policy.c -- Privilege dropping core functionality
> + *
> + * Copyright (C) 2009 Andy Spencer <spenceal@rose-hulman.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, either version 2 of the License, or (at your option)
> + * any later version.
> + * 
> + * This program 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
> + * this program.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#define pr_fmt(fmt) "%s: " fmt, __func__
> +
> +#include <linux/kernel.h>
> +#include <linux/security.h>
> +#include <linux/sched.h>
> +#include <linux/ctype.h>
> +#include <linux/fs.h>
> +
> +#include "policy.h"
> +
> +/*******************
> + * Permission bits *
> + *******************/
> +static char perm_bit_list[] = "rwxsguRWXSGU";
> +static u16  perm_bit_table['z'] = {
>   

Namespace.

> +	['x'] DPRIV_EXEC,
> +	['w'] DPRIV_WRITE,
> +	['r'] DPRIV_READ,
> +	['s'] DPRIV_KEEPSWP,
> +	['g'] DPRIV_SETGID,
> +	['u'] DPRIV_SETUID,
> +	['X'] DPRIV_WALK,
> +	['W'] DPRIV_CREATE,
> +	['R'] DPRIV_LIST,
> +	['S'] DPRIV_STICKY,
> +	['G'] DPRIV_PASSGID,
> +	['U'] DPRIV_PASSUID,
> +	['-'] 0,
> +}; /* plus 0.25k .. */
> +
> +u16 flags_to_mode(unsigned int flags)
>   

Definitely namespace. Could this be static?

> +{
> +	u16 mode = 0;
> +	if (flags & FMODE_READ ) mode |= DPRIV_READ;
> +	if (flags & FMODE_WRITE) mode |= DPRIV_WRITE;
> +	if (flags & FMODE_EXEC ) mode |= DPRIV_EXEC;
> +	if (flags & O_CREAT    ) mode |= DPRIV_CREATE;
> +	return mode;
> +}
> +
> +int str_to_perm(const char *str)
>   

Definitely namespace. Could this be static?

> +{
> +	int perm = 0;
> +	for (; *str; str++) {
> +		if ((!isalpha(*str) || !perm_bit_table[(int)*str]) &&
> +				*str != '-')
> +			return -1;
> +		perm |= perm_bit_table[(int)*str];
> +	}
> +	return perm;
> +}
> +
> +void perm_to_str(u16 perm, char *str)
>   

Definitely namespace. Could this be static?

> +{
> +	char *c = perm_bit_list;
> +	for (; *c; c++,str++)
> +		*str = (perm & perm_bit_table[(int)*c]) ?  *c : '-';
> +	*str = '\0';
> +}
> +
> +
> +
> +/**************
> + * DPriv Line *
> + **************/
> +struct dpriv_line *dpriv_line_new(const struct inode *inode,
> +		const char *path, u16 perm)
> +{
> +	struct dpriv_line *line;
> +	line = kzalloc(sizeof(struct dpriv_line), GFP_KERNEL);
> +	line->inode = inode;
> +	line->path  = path;
> +	line->perm  = perm;
> +	return line;
> +}
> +
> +
> +
> +/****************
> + * DPriv Policy *
> + ****************/
> +void dpriv_policy_init(struct dpriv_policy *policy)
> +{
> +	INIT_LIST_HEAD(&policy->privs);
> +	init_rwsem(&policy->privs_lock);
> +}
> +
> +void dpriv_policy_reset(struct dpriv_policy *policy)
> +{
> +	struct list_head *pos, *n;
> +	struct dpriv_line *line;
> +	list_for_each_safe(pos, n, &policy->privs){
> +		 line = list_entry(pos, struct dpriv_line, list);
> +		 list_del(pos);
> +		 kfree(line);
> +	}
> +}
> +
> +struct dpriv_line *dpriv_policy_get_line(const struct dpriv_policy *policy,
> +		const struct inode *inode)
> +{
> +	struct dpriv_line *line;
> +	list_for_each_entry(line, &policy->privs, list)
> +		if (line->inode == inode)
> +			return line;
> +	return NULL;
> +}
> +
> +u16 dpriv_policy_get_perm(const struct dpriv_policy *policy,
> +		const struct inode *inode)
> +{
> +	/* Stop if a permissions is found for current node */
> +	struct dpriv_line *line = dpriv_policy_get_line(policy, inode);
> +	if (line)
> +		return line->perm;
> +
> +	/* Allow everything if we've reach the root without finding perms */
> +	/* TODO: recurse to parent filesystems */
> +	if (inode->i_security == NULL)
> +		return USHORT_MAX;
> +
> +	/* Check parents for recursive permissions */
> +	/* TODO: Check for multiple parents */
> +	return dpriv_policy_get_perm(policy, inode->i_security);
> +	// perm = USHORT_MAX;
> +	// foreach parent:
> +	// 	perm &= dpriv_policy_get_perm(policy, inode->d_parent);
> +	// return perm;
>   

Again on the "//" comments.

> +}
> +
> +/* We need the inode and path so we can create the line if it doesn't exist */
> +void dpriv_policy_set_perm(struct dpriv_policy *policy,
> +		const struct inode *inode, const char *path, u16 perm)
> +{
> +	struct dpriv_line *line = dpriv_policy_get_line(policy, inode);
> +	if (line) {
> +		line->perm = perm;
> +	} else {
> +		line = dpriv_line_new(inode, path, perm);
> +		list_add_tail(&line->list, &policy->privs);
> +	}
> +}
> +
> +/* Do a semi-deep copy, that is, copy enough that the policies are distinct,
> + * but without duplicating conostant data such as paths and dentries */
> +void dpriv_policy_append(struct dpriv_policy *from, struct dpriv_policy *to)
> +{
> +	struct dpriv_line *fl, *tl;
> +	list_for_each_entry(fl, &from->privs, list) {
> +		tl = dpriv_line_new(fl->inode, fl->path, fl->perm);
> +		list_add_tail(&tl->list, &to->privs);
> +	}
> +}
> +
> +void dpriv_policy_commit(struct dpriv_policy *from, struct dpriv_policy *to)
> +{
> +	u16 perm;
> +	struct dpriv_line *line, *n;
> +	struct dpriv_policy merge;
> +	dpriv_policy_init(&merge);
> +
> +	/* Merge paths from @to into merge */
> +	list_for_each_entry(line, &to->privs, list) {
> +		perm  = line->perm;
> +		perm &= dpriv_policy_get_perm(from, line->inode);
> +		dpriv_policy_set_perm(&merge, line->inode, line->path, perm);
> +	}
> +
> +	/* Merge paths from @from into merge */
> +	list_for_each_entry(line, &from->privs, list) {
> +		perm  = line->perm;
> +		perm &= dpriv_policy_get_perm(to, line->inode);
> +		dpriv_policy_set_perm(&merge, line->inode, line->path, perm);
> +	}
> +
> +	/* Free old entries */
> +	dpriv_policy_reset(to);
> +	list_for_each_entry_safe(line, n, &merge.privs, list)
> +		list_move_tail(&line->list, &to->privs);
> +}
> +
> +
> +
> +/**************
> + * DPriv Task *
> + **************/
> +struct dpriv_task *dpriv_task_new(void)
> +{
> +	struct dpriv_task *task;
> +	task = kzalloc(sizeof(struct dpriv_task), GFP_KERNEL);
> +
> +	dpriv_policy_init(&task->stage);
> +	dpriv_policy_init(&task->policy);
> +
> +	INIT_LIST_HEAD(&task->fds);
> +	init_rwsem(&task->fds_lock);
> +
> +	return task;
> +}
> +
> +struct dpriv_task *dpriv_task_dup(struct dpriv_task *task)
> +{
> +	struct dpriv_task *copy = dpriv_task_new();
> +	struct dpriv_line *tl, *cl;
> +
> +	/* Copy policies */
> +	dpriv_policy_append(&task->stage,  &copy->stage);
> +	dpriv_policy_append(&task->policy, &copy->policy);
> +
> +	/* Copy file descriptors */
> +	list_for_each_entry(tl, &task->fds, list) {
> +		cl = dpriv_line_new(tl->inode, tl->path, tl->perm);
> +		list_add_tail(&cl->list, &copy->fds);
> +	}
> +	return copy;
> +}
> diff --git a/security/dpriv/policy.h b/security/dpriv/policy.h
> new file mode 100644
> index 0000000..70c0cbb
> --- /dev/null
> +++ b/security/dpriv/policy.h
> @@ -0,0 +1,228 @@
> +/**
> + * dpriv/policy.h -- Privilege dropping core functionality
> + *
> + * Copyright (C) 2009 Andy Spencer <spenceal@rose-hulman.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, either version 2 of the License, or (at your option)
> + * any later version.
> + * 
> + * This program 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
> + * this program.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#ifndef __DPRIV_POLICY_H__
> +#define __DPRIV_POLICY_H__
> +
> +#define f_inode f_dentry->d_inode
> +
> +/**
> + * Terminology
> + *   mode   = `Unix' mode (u16 use for filesyste mode bits)
> + *   perm   = DPriv permission bits (see below)
> + *   privs  = List of files and associated perm
> + *   policy = Privs + whatever else
> + */
> +
> +#define dpriv_cur_task   ((struct dpriv_task *)current_security())
> +#define dpriv_cur_stage  ((struct dpriv_policy *)&dpriv_cur_task->stage)
> +#define dpriv_cur_policy ((struct dpriv_policy *)&dpriv_cur_task->policy)
>   

You may get other feedback, but I think that using macros
to hide indirections make code harder to understand.

> +
> +
> +/*******************
> + * Permission bits *
> + *******************/
> +/* File bits */
> +#define DPRIV_EXEC    (1u<<0 ) /* x */
> +#define DPRIV_WRITE   (1u<<1 ) /* w */
> +#define DPRIV_READ    (1u<<2 ) /* r */
> +#define DPRIV_KEEPSWP (1u<<3 ) /* s (ignored) */
> +#define DPRIV_SETGID  (1u<<4 ) /* g */
> +#define DPRIV_SETUID  (1u<<5 ) /* u */
> +
> +/* Directory bits */
> +#define DPRIV_WALK    (1u<<6 ) /* X */
> +#define DPRIV_CREATE  (1u<<7 ) /* W */
> +#define DPRIV_LIST    (1u<<8 ) /* R */
> +#define DPRIV_STICKY  (1u<<9 ) /* S */
> +#define DPRIV_PASSGID (1u<<10) /* G */
> +#define DPRIV_PASSUID (1u<<11) /* U (ignored) */
> +
> +/* Meta bits/masks */
> +#define DPRIV_PERM_BITS 12
> +#define DPRIV_MASK      0b111111111111
> +#define DPRIV_FILE_MASK 0b000000111111
> +#define DPRIV_DIR_MASK  0b111111000000
> +
> +/* Mode conversion functions */
> +#define deny(perm, request) \
> +	unlikely(perm >= 0 && ~perm & request)
>   

Keep to your namespace and use static inline functions.

> +
> +/* Convert from a unix directory mode to a perm */
> +#define dmode_to_perm(mode) \
> +	((mode<<6))
>   

Keep to your namespace and use static inline functions.

> +
> +/* Convert from a unix file mode to a perm */
> +#define fmode_to_perm(mode) \
> +	(mode)
>   

Keep to your namespace and use static inline functions.

> +
> +/* Convert from a unix perm to a mode based on inode type */
> +#define imode_to_perm(mode, inode)     \
> +	(S_ISDIR(inode->i_mode) ?      \
> +		 dmode_to_perm(mode) : \
> +		 fmode_to_perm(mode))
> +
>   

Keep to your namespace and use static inline functions.

> +/**
> + * Convert struct file->f_flags to a Unix mode
> + * <x>mode_to_perm should probably be called on the resulting mode
> + */
> +u16 flags_to_mode(unsigned int flags);
>   

Keep to your namespace.
> +
> +/**
> + * Parse a permission string into a perm
> + * @str:
> + *  - Format is "rwxsguRWXSGU" (see Permission bits)
> + *  - Order does not matter
> + *  - '-' is ignored, any other character is invalid
> + *  - return -1 on invalid str
> + */
> +int str_to_perm(const char *str);
>   

Keep to your namespace.

> +
> +/**
> + * Convert a perm to a string for printing
> + */
> +void perm_to_str(u16 perm, char *str);
>   

Keep to your namespace.

> +
> +
> +
> +/**************
> + * DPriv Line *
> + **************/
> +/**
> + * An entry in the policy
> + *
> + * Example:
> + *   /var/tmp (rw-)
> + *
> + * @list:  list_head for stroing in policy or fds
> + * @inode: Some point in the filesystem, topically an inode
> + * @path:  Path given when the line was created, debugging only
> + * @perm:  Permissions given to location and it's kids
> + */
> +struct dpriv_line {
> +	struct list_head    list;
> +	const struct inode *inode;
> +	const char         *path;
> +	u16                 perm;
> +};
> +
> +/**
> + * Allocate and initalize a new dpriv_line
> + * @indoe, @path, @perm: fileds to store en line
> + */
> +struct dpriv_line *dpriv_line_new(const struct inode *inode,
> +		const char *path, u16 perm);
> +
> +
> +
> +/****************
> + * DPriv Policy *
> + ****************/
> +/**
> + * Contains permisisons and operations allowed for given security policy
> + *
> + * @privs:      List of dpriv_lines for filesystem privilages
> + * @privs_lock: Used for printing (maybe other?)
> + *
> + * Example:
> + *   privs: 
> + *     /         (r--)
> + *     /bin/     (r-x)
> + *     /tmp/     (rw-)
> + */
> +struct dpriv_policy {
> +	struct list_head privs;
> +	struct rw_semaphore privs_lock;
> +	/* TODO: add other security things */
> +};
> +
> +/* Initialize a blank @policy */
> +void dpriv_policy_init(struct dpriv_policy *policy);
> +
> +/* Clear/free data from @policy */
> +void dpriv_policy_reset(struct dpriv_policy *policy);
> +
> +/* Return the line from @policy->privs that matches @inode */
> +struct dpriv_line *dpriv_policy_get_line(const struct dpriv_policy *policy,
> +		const struct inode *inode);
> +
> +/* Recursivly lookup perm for @inode in @policy */
> +u16 dpriv_policy_get_perm(const struct dpriv_policy *policy,
> +		const struct inode *inode);
> +
> +/* Set perm for @inode in @policy to @perm, create new line if necessasiary */
> +void dpriv_policy_set_perm(struct dpriv_policy *policy,
> +		const struct inode *inode, const char *path, u16 perm);
> +
> +/* Copy lines from @from to @to making sure that no additional oeratoins are
> + * allowed in @to after the commit is performed */
> +void dpriv_policy_commit(struct dpriv_policy *from, struct dpriv_policy *to);
> +
> +
> +
> +/**************
> + * DPriv Task *
> + **************/
> +/**
> + * Contains information for a given task, including the security policy, stage,
> + * and cache information.
> + *
> + * @stage:
> + *   The modifialbe policy, privilages can be allowed or denied in the stage
> + * @policy:
> + *   The effective policy, used to determines whether an action is allowed
> + * @fds:
> + *   Open file descriptors and their implied permissions based on @policy
> + *
> + * @policy can only be modified by commiting @stage to @policy. When this is
> + * done, it is insured that no additional operations will be allowed by @policy
> + * after the commit.
> + *
> + * @fds is used to cache permissions on open file descriptors. This allows
> + * permissions to be quickly passed down form parents when opening directory
> + * entries. Note that when opening a file descriptor with multiple parents
> + * (e.g. a hard link) the permissions from the parent are incomplete and the
> + * permissions form the alternate parents must be determined as well.
> + *
> + * NOTE: @fds is currently not used, we recursivly iterate up to parents
> + * whenever it's needed. This might be faster anyway?
> + *
> + * Example:
> + *   stage:  (see dpriv_policy)
> + *   policy: (see dpriv_policy)
> + *   fds:
> + *     /foo     ~(r--)
> + *     /bin/foo ~(r-x)
> + *     /tmp/foo ~(rw-)
> + */
> +struct dpriv_task {
> +	struct dpriv_policy stage;
> +	struct dpriv_policy policy;
> +
> +	struct list_head fds;
> +	struct rw_semaphore fds_lock;
> +};
> +
> +/* Allocate a blank task */
> +struct dpriv_task *dpriv_task_new(void);
> +
> +/* Create a semi-deep copy of @task, suitable for passing to a child on exec */
> +struct dpriv_task *dpriv_task_dup(struct dpriv_task *task);
> +
> +#endif
> diff --git a/security/dpriv/readme.txt b/security/dpriv/readme.txt
> new file mode 100644
> index 0000000..073d15d
> --- /dev/null
> +++ b/security/dpriv/readme.txt
> @@ -0,0 +1,102 @@
> +Source code
> +-----------
> +  policy.[ch] - policy datatypes
> +  dpriv.c     - security/credentials hooks
> +  fs.c        - securityfs hooks
> +
> +
> +TODO
> +----
> +  - Check for race conditions
> +
> +
> +Overview
> +--------
> +1. Each process keeps a list of inode -> priv mappings:
> +   - i.e. the security policy
> +
> +2. Caching possibilities (todo?)
> +   - Processes keeps a list of open fds to prevent recursing up the FS tree?
> +   - Store the most recent processes access in each inode? 
> +
> +Privs: 
> +  - read/write/exec/sticky/setuid/setgui
> +  - All permissions are recursive
> +  - Permissions for dirs and file are separate
> +    - This prevents recursion problems
> +    - e.g. you can set noexec for files without smashing directories
> +  - Notation
> +     (rwx) = specified permission (inode in policy)
> +    ~(rwx) = implied permission (parent(s) in policy)
> +
> +Things to do when:
> +  1. Setting privs
> +     - Add policy line(s) for given path?
> +     - Update privs on open inodes that are children of policy line?
> +  2. Loading inode
> +     - Cache privs from parent(s)?
> +  3. Namespace modification (mv,ln,bind,etc)
> +     - OR
> +       - Keep policy for inode the same        (policy = old      )
> +       - Merge policy for both locations       (policy = old & new)
> +       - Change policy to reflect new location (policy =       new)
> +     - If mv, and including old implied policy:
> +       - need to write new (combined) policy line
> +
> +
> +Security FS
> +-----------
> +files:
> +  -rw-rw-rw- /securityfs/dpriv/stage
> +  -r--r--r-- /securityfs/dpriv/policy
> +  --w--w--w- /securityfs/dpriv/control
> +
> +stage:
> +  read:  print staged policy
> +  write: set inode in staged policy to given perms OR
> +         add inode to staged policy with given perms
> +         > staged[inode] = perms
> +
> +  In the stage, order does not matter, adding a line simply writes or
> +  overwrites the location with no regard to the rest of the policy.
> +
> +policy:
> +  read: print active policy
> + 
> +control:
> +  write:
> +    "commit" - merge staged policy into policy
> +      > for (inode in policy, staged):
> +      >     new[inode] =
> +      >         implied_privs(policy, inode) &
> +      >         implied_privs(staged, inode) 
> +      > clear(staged)
> +
> +    When committing, privilages can only be revoked.
> +   
> +
> +Examples
> +--------
> +Example 1:
> +  set /src/     (rw-)
> +  set /dst/     (r-x)
> +  
> +  $ mv /src/foo /dst
> +  
> +  get /src/     (rw-)
> +  get /dst/     (r-x)
> +  OR:
> +    get /dst/foo  (rw-)
> +    get /dst/foo ~(r-x)
> +    get /dst/foo  (rw-) & ~(r-x) = (r--)
> +
> +Example 2:
> +  $ ln /src/foo /dst
> +  
> +  set /src/     (rw-)
> +  set /dst/     (rwx)
> +  
> +  get /src/     (rw-)
> +  get /dst/     (rwx)
> +  get /src/foo ~(rw-) & ~(rwx) = ~(rw-)
> +  get /dst/foo ~(rw-) & ~(rwx) = ~(rw-)
>   


^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [RFC] Privilege dropping security module
  2009-09-23 22:31   ` Andy Spencer
  2009-09-23 23:03     ` Tetsuo Handa
@ 2009-09-24 16:37     ` David Wagner
  2009-09-25  7:22       ` Andy Spencer
  2009-10-01  7:38     ` Pavel Machek
  2 siblings, 1 reply; 24+ messages in thread
From: David Wagner @ 2009-09-24 16:37 UTC (permalink / raw)
  To: linux-kernel

Andy Spencer  wrote:
>Being able to use dpriv as a non root user is pretty strait forward. For
>example, a user of a multi-user system may want to try some untrusted
>code without risking access to the rest of the system:
>
>  $ cd ~/my_project
>  $ echo rxRX   /                > /sys/kernel/security/dpriv/stage
>  $ echo X      $HOME            > /sys/kernel/security/dpriv/stage
>  $ echo rwxRWX $HOME/my_project > /sys/kernel/security/dpriv/stage
>  $ echo commit                  > /sys/kernel/security/dpriv/control
>  $ patch < untrusted.patch
>  $ make && ./src/some_exe

If I understand correctly, this isn't sufficient to run untrusted code,
because it only restricts access to the filesystem.  You gotta restrict
access to the network, interaction with other processes, and so on.
(For instance, does dpriv let the untrusted process take over another of
your processes using ptrace?)

There's a tremendous amount of research literature on building secure
sandboxes.  You should study it, if you're not familiar with it.

I suspect making all permissions recursive is going to lead to overly
permissive policies.  Suppose I want to allow read access to everything
under /lib and /usr/lib, read-execute access to everything under /bin
and /usr/bin, and read-write access to everything under /tmp.  (But I
do not want to allow any access to any other directories.)  How do I
do it?

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [RFC] Privilege dropping security module
  2009-09-24 16:37     ` David Wagner
@ 2009-09-25  7:22       ` Andy Spencer
  2009-09-25 20:48         ` David Wagner
  0 siblings, 1 reply; 24+ messages in thread
From: Andy Spencer @ 2009-09-25  7:22 UTC (permalink / raw)
  To: David Wagner; +Cc: linux-kernel

[-- Attachment #1: Type: text/plain, Size: 1747 bytes --]

> If I understand correctly, this isn't sufficient to run untrusted code,
> because it only restricts access to the filesystem.  You gotta restrict
> access to the network, interaction with other processes, and so on.
> (For instance, does dpriv let the untrusted process take over another of
> your processes using ptrace?)

Yes, currently dpriv allows a process to take over another process using
ptrace. However, I plan on supporting limits on ptrace (and other
things) in the future.


> I suspect making all permissions recursive is going to lead to overly
> permissive policies.  Suppose I want to allow read access to everything
> under /lib and /usr/lib, read-execute access to everything under /bin
> and /usr/bin, and read-write access to everything under /tmp.  (But I
> do not want to allow any access to any other directories.)  How do I
> do it?

The important thing is that limits can be overridden while they are
still in the stage. This allows you to deny access to a directory, but
still allow access to specific subdirectories.

  $ echo -----X /        > /sys/kernel/security/dpriv/stage
  $ echo r--R-X /lib     > /sys/kernel/security/dpriv/stage
  $ echo r--R-X /usr/lib > /sys/kernel/security/dpriv/stage
  $ echo r-xR-X /bin     > /sys/kernel/security/dpriv/stage
  $ echo r-xR-X /usr/bin > /sys/kernel/security/dpriv/stage
  $ echo rw-RWX /tmp     > /sys/kernel/security/dpriv/stage
  $ echo commit          > /sys/kernel/security/dpriv/control

Does this answer your question? It should be noted that you can still
walk to any directory because X is allowed on /. To prevent this you
would have to allow X on / and then specifically deny X on /*/, but
expanding * is something that I would rather have done in userspace.

[-- Attachment #2: Type: application/pgp-signature, Size: 198 bytes --]

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [RFC][PATCH] Privilege dropping security module
  2009-09-24 16:25   ` Casey Schaufler
@ 2009-09-25 10:06     ` Andy Spencer
  2009-09-25 16:23       ` Casey Schaufler
  2009-09-25 21:00       ` David Wagner
  0 siblings, 2 replies; 24+ messages in thread
From: Andy Spencer @ 2009-09-25 10:06 UTC (permalink / raw)
  To: Casey Schaufler; +Cc: linux-security-module, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 5599 bytes --]

> I think I saw this mentioned elsewhere, but you can't put a
> 4k buffer on the stack.
> ...
> "//" comments are not used in the kernel
> ...
> Why use a value other than PATH_MAX? If it's arbitrary,
> go with the "standard" arbitrary.
> ... 
> Stick with your namespace.

Fixed as suggested, thanks.


> The term "privilege" is generally applied to a broader scope
> than discretionary access controls. You might want to consider
> using a name that is more precisely descriptive of the feature.
> It probably isn't that important, but I for one would be happier.
> 
> You're not dropping privilege, that would imply restricting
> root and/or capability access. You're masking file permissions.

I have no objections to changing the name if you have a better
suggestion. However, I would like to avoid the term "discretionary
access control" since DACs deal with passing permissions between
subjects, while in dpriv, the only subject involved is the current
subject (well, and its children). Also, as pointed out in another
thread, dpriv will eventually need to support more than just file
permissions if it wants to be secure.


> > +#define pr_fmt(fmt) "%s: " fmt, __func__
> 
> You have defined this in multiple places, which isn't good,
> and it's a bit of code that obfuscates what's going on. It
> may seem like a good idea, but you'll be better off if you
> go ahead and put the code in where you're using it.

Defining pr_fmt seems to be fairly common in the rest of the kernel, a
quick grep shows 98 occurrences. If these are all deprecated, I don't
mind changing it, but it seem worth the bit of obfuscation to me.


> > +u16 flags_to_mode(unsigned int flags)
> > +int str_to_perm(const char *str)
> > +void perm_to_str(u16 perm, char *str)
> 
> Definitely namespace. Could this be static?

I added the namespace, but they're used in a couple other files so they
can't be static. There are a couple other function in policy.[ch] that
could be static but I'm leaving them in the header for now in case they
end up being needed later.


> > +#define dpriv_cur_task   ((struct dpriv_task *)current_security())
> > +#define dpriv_cur_stage  ((struct dpriv_policy *)&dpriv_cur_task->stage)
> > +#define dpriv_cur_policy ((struct dpriv_policy *)&dpriv_cur_task->policy)
> 
> You may get other feedback, but I think that using macros
> to hide indirections make code harder to understand.

I think I'd like to keep these as well, although it might be good to
change them to either `dpriv_current_*()' or `current_dpriv_*()' to
follow the conventions used in <linux/cred.h>.


> > +/* Mode conversion functions */
> > +#define deny(perm, request) \
> > +	unlikely(perm >= 0 && ~perm & request)
> 
> Keep to your namespace and use static inline functions.

I changed these to static inline functions, but the one thing I don't
like about doing so is that I don't think the compiler can't optimize
the `unlikely()' macro as well. For the time being, I moved the unlikely
macros to where deny (now dpriv_denied) gets called.


> Function macros are discouraged. If you really want code duplication
> use static inline functions. And stick with your namespace, use
> dpriv_strfwd() instead of strfwd().
> ...
> String parsing in the kernel is considered harmful.
> Simplify this.

I reworked much of the string parsing. I think /some/ parsing is going
to be necessary no matter what, but hopefully the new way of doing it is
easier to understand and less likely to contains bugs. As a result those
macros are no longer needed at all. (Note that this depends on a
separate patch to sscanf). I've included the new dpriv_stage_write()
below.

(I can include another full diff against either the previous patch or
the mainline kernel if anyone would like it.)

 
static ssize_t dpriv_stage_write(struct file *filp, const char *ubuffer,
		size_t length, loff_t *off)
{
	struct file *file;
	int err, rval, perm;
	char *kbuffer, *perm_str, *path_str;
	int perm_start, perm_end, path_start;

	if (!(kbuffer = kzalloc(length+1, GFP_KERNEL)))
		return -ENOMEM;

	if (copy_from_user(kbuffer, ubuffer, length))
		goto fail_fault;

	/* Parse input */
	path_start = -1;
	sscanf(kbuffer, " %n%*s%n %n", &perm_start, &perm_end, &path_start);
	if (path_start == -1)
		goto fail_inval;
	perm_str = kbuffer+perm_start;
	kbuffer[perm_end] = '\0';
	path_str = kbuffer+path_start;

	/* Check and convert perm */
	if (perm_str[0] == '\0')
		goto fail_inval;
	if ((perm = dpriv_str_to_perm(perm_str)) < 0)
		goto fail_inval;

	/* Check and open path */
	if (path_str[0] == '\0')
		goto fail_inval;
	if (IS_ERR(file = filp_open(path_str, 0, 0))) {
		/* file not found, try trimming trailing spaces */
		strstrip(path_str);
		if (path_str[0] == '\0')
			goto fail_inval;
		if (IS_ERR(file = filp_open(path_str, 0, 0)))
			goto fail_noent;
	}

	path_str = kstrdup(path_str, GFP_KERNEL);
	if (!path_str)
		goto fail_nomem;

	if ((err = dpriv_policy_set_perm(dpriv_cur_stage,
			file->f_dentry->d_inode, path_str, perm))) {
		kfree(path_str);
		rval = err;
		goto out;
	}

	pr_debug("dpriv_task=%p pid=%d perm=%o[%s] path=%p[%s]\n",
		dpriv_cur_task, current->pid, perm, perm_str, file, path_str);

	rval = length;
	goto out; /* Success */

fail_inval: rval = -EINVAL; goto out;
fail_nomem: rval = -ENOMEM; goto out;
fail_fault: rval = -EFAULT; goto out;
fail_noent: rval = -ENOENT; goto out;
out:
	kfree(kbuffer);
	/* if (rval < 0) abort task ? */
	return rval;
}

[-- Attachment #2: Type: application/pgp-signature, Size: 198 bytes --]

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [RFC][PATCH] Privilege dropping security module
  2009-09-25 10:06     ` Andy Spencer
@ 2009-09-25 16:23       ` Casey Schaufler
  2009-09-26 21:35         ` Andy Spencer
  2009-09-25 21:00       ` David Wagner
  1 sibling, 1 reply; 24+ messages in thread
From: Casey Schaufler @ 2009-09-25 16:23 UTC (permalink / raw)
  To: Andy Spencer; +Cc: linux-security-module, linux-kernel

Andy Spencer wrote:
>> I think I saw this mentioned elsewhere, but you can't put a
>> 4k buffer on the stack.
>> ...
>> "//" comments are not used in the kernel
>> ...
>> Why use a value other than PATH_MAX? If it's arbitrary,
>> go with the "standard" arbitrary.
>> ... 
>> Stick with your namespace.
>>     
>
> Fixed as suggested, thanks.
>   

It's amazing who much of this stuff there is to attend to.
If you haven't, run checkpatch.py on your patches. You'll
need to pass that eventually.

>> The term "privilege" is generally applied to a broader scope
>> than discretionary access controls. You might want to consider
>> using a name that is more precisely descriptive of the feature.
>> It probably isn't that important, but I for one would be happier.
>>
>> You're not dropping privilege, that would imply restricting
>> root and/or capability access. You're masking file permissions.
>>     
>
> I have no objections to changing the name if you have a better
> suggestion. However, I would like to avoid the term "discretionary
> access control" since DACs deal with passing permissions between
> subjects, while in dpriv, the only subject involved is the current
> subject (well, and its children). Also, as pointed out in another
> thread, dpriv will eventually need to support more than just file
> permissions if it wants to be secure.
>   

Hmm. You are working with the Linux DAC mechanism, even if only within
a process tree. You're not dropping privilege, you're applying a mask to
the file permission bits, currently for file system objects, and with
other objects (sysvipc at least) in the future. Hmm. modemask? Something
derived from "restricted process tree?"

>>> +#define pr_fmt(fmt) "%s: " fmt, __func__
>>>       
>> You have defined this in multiple places, which isn't good,
>> and it's a bit of code that obfuscates what's going on. It
>> may seem like a good idea, but you'll be better off if you
>> go ahead and put the code in where you're using it.
>>     
>
> Defining pr_fmt seems to be fairly common in the rest of the kernel, a
> quick grep shows 98 occurrences. If these are all deprecated, I don't
> mind changing it, but it seem worth the bit of obfuscation to me.
>   

Sigh. 98? I guess I'm wrong. Sorry 'bout that.

>>> +u16 flags_to_mode(unsigned int flags)
>>> +int str_to_perm(const char *str)
>>> +void perm_to_str(u16 perm, char *str)
>>>       
>> Definitely namespace. Could this be static?
>>     
>
> I added the namespace, but they're used in a couple other files so they
> can't be static. There are a couple other function in policy.[ch] that
> could be static but I'm leaving them in the header for now in case they
> end up being needed later.
>   

You will need to change that if you want the code upstream. There are
people lurking out there, looking for things that could be static be
aren't and pouncing on unwary developers.

>   
>>> +#define dpriv_cur_task   ((struct dpriv_task *)current_security())
>>> +#define dpriv_cur_stage  ((struct dpriv_policy *)&dpriv_cur_task->stage)
>>> +#define dpriv_cur_policy ((struct dpriv_policy *)&dpriv_cur_task->policy)
>>>       
>> You may get other feedback, but I think that using macros
>> to hide indirections make code harder to understand.
>>     
>
> I think I'd like to keep these as well, although it might be good to
> change them to either `dpriv_current_*()' or `current_dpriv_*()' to
> follow the conventions used in <linux/cred.h>.
>   

Ok. If I get confused in later reviews, don't say I didn't warn you.

>>> +/* Mode conversion functions */
>>> +#define deny(perm, request) \
>>> +	unlikely(perm >= 0 && ~perm & request)
>>>       
>> Keep to your namespace and use static inline functions.
>>     
>
> I changed these to static inline functions, but the one thing I don't
> like about doing so is that I don't think the compiler can't optimize
> the `unlikely()' macro as well. For the time being, I moved the unlikely
> macros to where deny (now dpriv_denied) gets called.
>   

My understanding is that hand optimization of things like this
almost always results in worse results than you get if you let
the compiler work its will, but that's up to you.

>   
>> Function macros are discouraged. If you really want code duplication
>> use static inline functions. And stick with your namespace, use
>> dpriv_strfwd() instead of strfwd().
>> ...
>> String parsing in the kernel is considered harmful.
>> Simplify this.
>>     
>
> I reworked much of the string parsing. I think /some/ parsing is going
> to be necessary no matter what, but hopefully the new way of doing it is
> easier to understand and less likely to contains bugs. As a result those
> macros are no longer needed at all. (Note that this depends on a
> separate patch to sscanf). I've included the new dpriv_stage_write()
> below.
>
> (I can include another full diff against either the previous patch or
> the mainline kernel if anyone would like it.)
>   

Please repost against the mainline.
I will look at the semantics of the code next time around.

Thank you.


^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [RFC] Privilege dropping security module
  2009-09-25  7:22       ` Andy Spencer
@ 2009-09-25 20:48         ` David Wagner
  2009-09-26 21:09           ` Andy Spencer
  0 siblings, 1 reply; 24+ messages in thread
From: David Wagner @ 2009-09-25 20:48 UTC (permalink / raw)
  To: linux-kernel

Andy Spencer  wrote:
>> If I understand correctly, this isn't sufficient to run untrusted code,
>> because it only restricts access to the filesystem.  You gotta restrict
>> access to the network, interaction with other processes, and so on.
>> (For instance, does dpriv let the untrusted process take over another of
>> your processes using ptrace?)
>
>Yes, currently dpriv allows a process to take over another process using
>ptrace. However, I plan on supporting limits on ptrace (and other
>things) in the future.

Ptrace is just one example: there is a non-trivial list of non-filesystem
resources that must be controlled, to run untrusted code safely.

>> I suspect making all permissions recursive is going to lead to overly
>> permissive policies.  Suppose I want to allow read access to everything
>> under /lib and /usr/lib, read-execute access to everything under /bin
>> and /usr/bin, and read-write access to everything under /tmp.  (But I
>> do not want to allow any access to any other directories.)  How do I
>> do it?
>
>The important thing is that limits can be overridden while they are
>still in the stage. This allows you to deny access to a directory, but
>still allow access to specific subdirectories.
>
>  $ echo -----X /        > /sys/kernel/security/dpriv/stage
>  $ echo r--R-X /lib     > /sys/kernel/security/dpriv/stage
>  $ echo r--R-X /usr/lib > /sys/kernel/security/dpriv/stage
>  $ echo r-xR-X /bin     > /sys/kernel/security/dpriv/stage
>  $ echo r-xR-X /usr/bin > /sys/kernel/security/dpriv/stage
>  $ echo rw-RWX /tmp     > /sys/kernel/security/dpriv/stage
>  $ echo commit          > /sys/kernel/security/dpriv/control
>
>Does this answer your question?

Unfortunately it does not, because as you note:

>It should be noted that you can still
>walk to any directory because X is allowed on /.

Which contradicts the goal of not allowing access to any other
directories.  In other words, the implementation above of my desired
policy is overly permissive.  This illustrates my more general point:
the dpriv policy specification language seems likely to lead to overly
permissive policies -- policies that are more permissive than what was
really desired.

>To prevent this you
>would have to allow X on / and then specifically deny X on /*/, but
>expanding * is something that I would rather have done in userspace.

In principle a user of dpriv could do that, to be sure, but in practice
this becomes unwieldy, so I suspect few will.  As a result, in practice
this interface to dpriv probably means that most implemented policies
will be more permissive than intended/desired.  I consider that a defect
in the design of the specification language.  It seems like it would
be preferable to have a specification language that better facilitates
secure use of dpriv.

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [RFC][PATCH] Privilege dropping security module
  2009-09-25 10:06     ` Andy Spencer
  2009-09-25 16:23       ` Casey Schaufler
@ 2009-09-25 21:00       ` David Wagner
  2009-09-29  7:36         ` Andy Spencer
  1 sibling, 1 reply; 24+ messages in thread
From: David Wagner @ 2009-09-25 21:00 UTC (permalink / raw)
  To: linux-kernel

Andy Spencer  wrote:
>static ssize_t dpriv_stage_write(struct file *filp, const char *ubuffer,
>		size_t length, loff_t *off)
>{
>	struct file *file;
>	int err, rval, perm;
>	char *kbuffer, *perm_str, *path_str;
>	int perm_start, perm_end, path_start;
>
>	if (!(kbuffer = kzalloc(length+1, GFP_KERNEL)))
>		return -ENOMEM;
>
>	if (copy_from_user(kbuffer, ubuffer, length))
>		goto fail_fault;

Can 'length+1' overflow?
(Can the caller arrange to pass MAX_SIZE_T as the length parameter?
If yes, that's a vulnerability.)
I haven't checked how dpriv_stage_write() is called, to see whether
this is possible.

>	/* Parse input */
>	path_start = -1;
>	sscanf(kbuffer, " %n%*s%n %n", &perm_start, &perm_end, &path_start);
>	if (path_start == -1)
>		goto fail_inval;
>	perm_str = kbuffer+perm_start;
>	kbuffer[perm_end] = '\0';
>	path_str = kbuffer+path_start;

What if kbuffer isn't '\0'-terminated?  Won't this read past the end
of kbuffer?

Are you certain that perm_end and path_start will be within bounds?
If the user supplies a sufficiently large string (more than MAX_INT
characters long), could perm_end or path_start be negative?

>	rval = length;

Converts size_t to ssize_t.

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [RFC] Privilege dropping security module
  2009-09-25 20:48         ` David Wagner
@ 2009-09-26 21:09           ` Andy Spencer
  2009-09-27  0:28             ` David Wagner
  0 siblings, 1 reply; 24+ messages in thread
From: Andy Spencer @ 2009-09-26 21:09 UTC (permalink / raw)
  To: David Wagner; +Cc: linux-kernel

[-- Attachment #1: Type: text/plain, Size: 797 bytes --]

> As a result, in practice this interface to dpriv probably means that
> most implemented policies will be more permissive than
> intended/desired.  I consider that a defect in the design of the
> specification language.  It seems like it would be preferable to have
> a specification language that better facilitates secure use of dpriv.

What would you suggest as a better specification language? Would it be
sufficient to have recursive and non recursive variants for masking
permissions?

There's an implementation problem with using recursive permissions and
expanding * in userspace as well. If the user allows access to `foo' and
denies access to `foo/*', and later creates new entry of `foo/bar', the
new entry would have access allowed, which would probably not reflect
the users intent.

[-- Attachment #2: Type: application/pgp-signature, Size: 198 bytes --]

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [RFC][PATCH] Privilege dropping security module
  2009-09-25 16:23       ` Casey Schaufler
@ 2009-09-26 21:35         ` Andy Spencer
  2009-09-28  5:38           ` Rob Meijer
  0 siblings, 1 reply; 24+ messages in thread
From: Andy Spencer @ 2009-09-26 21:35 UTC (permalink / raw)
  To: Casey Schaufler; +Cc: linux-security-module, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 1188 bytes --]

> It's amazing who much of this stuff there is to attend to.  If you
> haven't, run checkpatch.py on your patches. You'll need to pass that
> eventually.

I've fixed the remaining things that checkpatch.pl suggests as well as a
few others and will include those checks for future patches.


> Hmm. You are working with the Linux DAC mechanism, even if only within
> a process tree. You're not dropping privilege, you're applying a mask
> to the file permission bits, currently for file system objects, and
> with other objects (sysvipc at least) in the future. Hmm. modemask?
> Something derived from "restricted process tree?"

`Access Control Masking' or `Policy Masking' perhaps?


> You will need to change that if you want the code upstream. There are
> people lurking out there, looking for things that could be static be
> aren't and pouncing on unwary developers.

I noticed that `make namespacecheck' complained about that as well, so I
went ahead and make those static.


> Please repost against the mainline.  I will look at the semantics of
> the code next time around.

I'll repost in a couple days once I've worked in a few more suggestions.
(and hopefully with a new name)

[-- Attachment #2: Type: application/pgp-signature, Size: 198 bytes --]

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [RFC] Privilege dropping security module
  2009-09-26 21:09           ` Andy Spencer
@ 2009-09-27  0:28             ` David Wagner
  0 siblings, 0 replies; 24+ messages in thread
From: David Wagner @ 2009-09-27  0:28 UTC (permalink / raw)
  To: linux-kernel

Andy Spencer  wrote:
>What would you suggest as a better specification language? Would it be
>sufficient to have recursive and non recursive variants for masking
>permissions?

I haven't given it enough thought to recommend a better specification
language.  Having both recursive and non-recursive variants might do
the trick.

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [RFC][PATCH] Privilege dropping security module
  2009-09-26 21:35         ` Andy Spencer
@ 2009-09-28  5:38           ` Rob Meijer
  0 siblings, 0 replies; 24+ messages in thread
From: Rob Meijer @ 2009-09-28  5:38 UTC (permalink / raw)
  To: Andy Spencer; +Cc: Casey Schaufler, linux-security-module, linux-kernel

On Sat, September 26, 2009 23:35, Andy Spencer wrote:
>> It's amazing who much of this stuff there is to attend to.  If you
>> haven't, run checkpatch.py on your patches. You'll need to pass that
>> eventually.
>
> I've fixed the remaining things that checkpatch.pl suggests as well as a
> few others and will include those checks for future patches.
>
>
>> Hmm. You are working with the Linux DAC mechanism, even if only within
>> a process tree. You're not dropping privilege, you're applying a mask
>> to the file permission bits, currently for file system objects, and
>> with other objects (sysvipc at least) in the future. Hmm. modemask?
>> Something derived from "restricted process tree?"
>
> `Access Control Masking' or `Policy Masking' perhaps?
>

Or  'Permission Attenuation' ?


^ permalink raw reply	[flat|nested] 24+ messages in thread

* [RFC][PATCH] Permission masking security module (was dpriv)
  2009-09-23  0:56 [RFC] Privilege dropping security module Andy Spencer
  2009-09-23 20:46 ` Casey Schaufler
  2009-09-23 21:31 ` [RFC][PATCH] " Andy Spencer
@ 2009-09-29  7:10 ` Andy Spencer
  2009-09-29 17:44   ` Greg KH
  2009-10-01  2:33   ` Casey Schaufler
  2 siblings, 2 replies; 24+ messages in thread
From: Andy Spencer @ 2009-09-29  7:10 UTC (permalink / raw)
  To: linux-security-module, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 36189 bytes --]

Changes since the previous patch (dpriv-p0):

- Change the name to pmask (Permission Masking)

- Use recursive and non-recursive variants for setting masks 
  $ echo self $perm $path > $stage # Set perms for $path only
  $ echo kids $perm $path > $stage # Set perms for children of $path
  $ echo both $perm $path > $stage # Set perms for $path and children

- Add a quota of 500 permission lines per policy

- Improve securityfs parsing.

- Change '-' to '.' in permissions string to avoid confusion with
  `chmod -rwx foo'

- Many syntax/naming/etc bug fixes

Signed-off-by: Andy Spencer <andy753421@gmail.com>
---
 Documentation/pmask.txt |  102 ++++++++++++++
 lib/vsprintf.c          |    2 +-
 security/Kconfig        |    1 +
 security/Makefile       |    2 +
 security/pmask/Kconfig  |    8 +
 security/pmask/Makefile |    1 +
 security/pmask/fs.c     |  299 +++++++++++++++++++++++++++++++++++++++++
 security/pmask/pmask.c  |  144 ++++++++++++++++++++
 security/pmask/policy.c |  337 +++++++++++++++++++++++++++++++++++++++++++++++
 security/pmask/policy.h |  230 ++++++++++++++++++++++++++++++++
 10 files changed, 1125 insertions(+), 1 deletions(-)
 create mode 100644 Documentation/pmask.txt
 create mode 100644 security/pmask/Kconfig
 create mode 100644 security/pmask/Makefile
 create mode 100644 security/pmask/fs.c
 create mode 100644 security/pmask/pmask.c
 create mode 100644 security/pmask/policy.c
 create mode 100644 security/pmask/policy.h

diff --git a/Documentation/pmask.txt b/Documentation/pmask.txt
new file mode 100644
index 0000000..522cb30
--- /dev/null
+++ b/Documentation/pmask.txt
@@ -0,0 +1,102 @@
+Source code
+-----------
+  policy.[ch] - policy datatypes
+  pmask.c     - security/credentials hooks
+  fs.c        - securityfs hooks
+
+
+TODO
+----
+  - Check for race conditions
+
+
+Overview
+--------
+1. Each process keeps a list of inode -> priv mappings:
+   - i.e. the security policy
+
+2. Caching possibilities (todo?)
+   - Processes keeps a list of open fds to prevent recursing up the FS tree?
+   - Store the most recent processes access in each inode?
+
+Privs:
+  - read/write/exec/sticky/setuid/setgui
+  - All permissions are recursive
+  - Permissions for dirs and file are separate
+    - This prevents recursion problems
+    - e.g. you can set noexec for files without smashing directories
+  - Notation
+     (rwx) = specified permission (inode in policy)
+    ~(rwx) = implied permission (parent(s) in policy)
+
+Things to do when:
+  1. Setting privs
+     - Add policy line(s) for given path?
+     - Update privs on open inodes that are children of policy line?
+  2. Loading inode
+     - Cache privs from parent(s)?
+  3. Namespace modification (mv,ln,bind,etc)
+     - OR
+       - Keep policy for inode the same        (policy = old      )
+       - Merge policy for both locations       (policy = old & new)
+       - Change policy to reflect new location (policy =       new)
+     - If mv, and including old implied policy:
+       - need to write new (combined) policy line
+
+
+Security FS
+-----------
+files:
+  -rw-rw-rw- /securityfs/pmask/stage
+  -r--r--r-- /securityfs/pmask/policy
+  --w--w--w- /securityfs/pmask/control
+
+stage:
+  read:  print staged policy
+  write: set inode in staged policy to given perms OR
+         add inode to staged policy with given perms
+         > staged[inode] = perms
+
+  In the stage, order does not matter, adding a line simply writes or
+  overwrites the location with no regard to the rest of the policy.
+
+policy:
+  read: print active policy
+
+control:
+  write:
+    "commit" - merge staged policy into policy
+      > for (inode in policy, staged):
+      >     new[inode] =
+      >         implied_privs(policy, inode) &
+      >         implied_privs(staged, inode)
+      > clear(staged)
+
+    When committing, privilages can only be revoked.
+
+
+Examples
+--------
+Example 1:
+  set /src/     (rw-)
+  set /dst/     (r-x)
+
+  $ mv /src/foo /dst
+
+  get /src/     (rw-)
+  get /dst/     (r-x)
+  OR:
+    get /dst/foo  (rw-)
+    get /dst/foo ~(r-x)
+    get /dst/foo  (rw-) & ~(r-x) = (r--)
+
+Example 2:
+  $ ln /src/foo /dst
+
+  set /src/     (rw-)
+  set /dst/     (rwx)
+
+  get /src/     (rw-)
+  get /dst/     (rwx)
+  get /src/foo ~(rw-) & ~(rwx) = ~(rw-)
+  get /dst/foo ~(rw-) & ~(rwx) = ~(rw-)
diff --git a/lib/vsprintf.c b/lib/vsprintf.c
index b91839e..33bed5e 100644
--- a/lib/vsprintf.c
+++ b/lib/vsprintf.c
@@ -1771,7 +1771,7 @@ int vsscanf(const char * buf, const char * fmt, va_list args)
 		 * advance both strings to next white space
 		 */
 		if (*fmt == '*') {
-			while (!isspace(*fmt) && *fmt)
+			while (!isspace(*fmt) && *fmt != '%' && *fmt)
 				fmt++;
 			while (!isspace(*str) && *str)
 				str++;
diff --git a/security/Kconfig b/security/Kconfig
index fb363cd..d4521b5 100644
--- a/security/Kconfig
+++ b/security/Kconfig
@@ -159,6 +159,7 @@ config LSM_MMAP_MIN_ADDR
 	  this low address space will need the permission specific to the
 	  systems running LSM.
 
+source security/pmask/Kconfig
 source security/selinux/Kconfig
 source security/smack/Kconfig
 source security/tomoyo/Kconfig
diff --git a/security/Makefile b/security/Makefile
index 95ecc06..f8c5b26 100644
--- a/security/Makefile
+++ b/security/Makefile
@@ -3,6 +3,7 @@
 #
 
 obj-$(CONFIG_KEYS)			+= keys/
+subdir-$(CONFIG_SECURITY_PERM_MASKING)  += pmask
 subdir-$(CONFIG_SECURITY_SELINUX)	+= selinux
 subdir-$(CONFIG_SECURITY_SMACK)		+= smack
 subdir-$(CONFIG_SECURITY_TOMOYO)        += tomoyo
@@ -14,6 +15,7 @@ obj-y		+= commoncap.o min_addr.o
 obj-$(CONFIG_SECURITY)			+= security.o capability.o
 obj-$(CONFIG_SECURITYFS)		+= inode.o
 # Must precede capability.o in order to stack properly.
+obj-$(CONFIG_SECURITY_PERM_MASKING)	+= pmask/built-in.o
 obj-$(CONFIG_SECURITY_SELINUX)		+= selinux/built-in.o
 obj-$(CONFIG_SECURITY_SMACK)		+= smack/built-in.o
 obj-$(CONFIG_AUDIT)			+= lsm_audit.o
diff --git a/security/pmask/Kconfig b/security/pmask/Kconfig
new file mode 100644
index 0000000..a8ca887
--- /dev/null
+++ b/security/pmask/Kconfig
@@ -0,0 +1,8 @@
+config SECURITY_PERM_MASKING
+	bool "Permission masking"
+	depends on SECURITY
+	select SECURITYFS
+	default n
+	help
+	  This enabled support for masking filesystem permissions.
+	  If you are unsure how to answer this question, answer N.
diff --git a/security/pmask/Makefile b/security/pmask/Makefile
new file mode 100644
index 0000000..39d5b27
--- /dev/null
+++ b/security/pmask/Makefile
@@ -0,0 +1 @@
+obj-y = pmask.o policy.o fs.o
diff --git a/security/pmask/fs.c b/security/pmask/fs.c
new file mode 100644
index 0000000..84a6515
--- /dev/null
+++ b/security/pmask/fs.c
@@ -0,0 +1,299 @@
+/**
+ * pmask/fs.c -- Security FS interface for privilege dropping
+ *
+ * Copyright (C) 2009 Andy Spencer <spenceal@rose-hulman.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, either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program 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
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/kernel.h>
+#include <linux/security.h>
+#include <linux/fs.h>
+#include <linux/seq_file.h>
+#include <linux/ctype.h>
+#include <linux/uaccess.h>
+
+#include "policy.h"
+
+/***************************
+ * Generic policy iterator *
+ ***************************/
+/* Use this for reading form any policy file */
+static void *pmask_seq_start(struct seq_file *sf, loff_t *pos)
+{
+	struct pmask_policy *policy = sf->private;
+	down_read(&policy->privs_lock);
+	return seq_list_start(&policy->privs, *pos);
+}
+
+static void *pmask_seq_next(struct seq_file *sf, void *seq, loff_t *pos)
+{
+	struct pmask_policy *policy = sf->private;
+	return seq_list_next(seq, &policy->privs, pos);
+}
+
+static void pmask_seq_stop(struct seq_file *sf, void *seq)
+{
+	struct pmask_policy *policy = sf->private;
+	up_read(&policy->privs_lock);
+}
+
+static int pmask_seq_show(struct seq_file *sf, void *seq)
+{
+	struct pmask_line *line = list_entry(seq, struct pmask_line, list);
+	char perm_str[PMASK_PERM_BITS+1] = {};
+	if (pmask_isset(line->self_perm) &&
+			line->self_perm == line->kids_perm) {
+		pmask_perm_to_str(line->self_perm, perm_str);
+		seq_printf(sf, "both %s %s\n", perm_str, line->path);
+	} else {
+		if (pmask_isset(line->self_perm)) {
+			pmask_perm_to_str(line->self_perm, perm_str);
+			seq_printf(sf, "self %s %s\n", perm_str, line->path);
+		}
+		if (pmask_isset(line->kids_perm)) {
+			pmask_perm_to_str(line->kids_perm, perm_str);
+			seq_printf(sf, "kids %s %s\n", perm_str, line->path);
+		}
+	}
+	return 0;
+}
+
+static const struct seq_operations pmask_seq_ops = {
+	.start = pmask_seq_start,
+	.next  = pmask_seq_next,
+	.stop  = pmask_seq_stop,
+	.show  = pmask_seq_show,
+};
+
+static int pmask_seq_open(struct file *file, struct pmask_policy *policy)
+{
+	/* From __seq_open_private
+	 * Not sure if this is correct way to store private data */
+	struct seq_file *sf;
+	if (seq_open(file, &pmask_seq_ops) < 0) {
+		pr_warning("Out of memory opening PMask sequence\n");
+		return -ENOMEM;
+	}
+	sf = file->private_data;
+	sf->private = policy;
+	return 0;
+};
+
+
+
+/**************
+ * Stage file *
+ **************/
+static int pmask_stage_open(struct inode *inode, struct file *file)
+{
+	return pmask_seq_open(file, pmask_cur_stage);
+};
+
+/**
+ * Parse policy lines one at a time.
+ * Format: /\s*([rwxsguRWXSGU\-]*)\s*(.*)(\s*)?/
+ *   \1: See pmask_str_to_perm() for discussion
+ *   \2: A file path, \3 trailing whitespace is optional
+ */
+static ssize_t pmask_stage_write(struct file *filp, const char *ubuffer,
+		size_t length, loff_t *off)
+{
+	struct file *file;
+	int err, rval, perm, scope;
+	char *kbuffer, *cmd_str, *perm_str, *path_str;
+	int cmd_start, cmd_end, perm_start, perm_end, path_start;
+
+	if (length > (size_t)~0LL)
+		return -EINVAL;;
+	kbuffer = kmalloc(length+1, GFP_KERNEL);
+	if (!kbuffer)
+		return -ENOMEM;
+	kbuffer[length] = '\0';
+
+	if (copy_from_user(kbuffer, ubuffer, length))
+		goto fail_fault;
+
+	/* Parse input */
+	path_start = -1;
+	sscanf(kbuffer, " %n%*s%n %n%*s%n %n", &cmd_start, &cmd_end,
+			&perm_start, &perm_end, &path_start);
+	if (path_start == -1)
+		goto fail_inval;
+	cmd_str  = kbuffer+cmd_start;  kbuffer[cmd_end]  = '\0';
+	perm_str = kbuffer+perm_start; kbuffer[perm_end] = '\0';
+	path_str = kbuffer+path_start;
+
+	/* Check and convert cmd/scope */
+	if (!strcmp(cmd_str, "self"))
+		scope = PMASK_SELF;
+	else if (!strcmp(cmd_str, "kids"))
+		scope = PMASK_KIDS;
+	else if (!strcmp(cmd_str, "both"))
+		scope = PMASK_BOTH;
+	else
+		goto fail_inval;
+
+	/* Check and convert perm */
+	if (perm_str[0] == '\0')
+		goto fail_inval;
+	perm = pmask_str_to_perm(perm_str);
+	if (perm < 0)
+		goto fail_inval;
+
+	/* Check and open path */
+	if (path_str[0] == '\0')
+		goto fail_inval;
+	file = filp_open(path_str, 0, 0);
+	if (IS_ERR(file)) {
+		/* file not found, try trimming trailing spaces */
+		strstrip(path_str);
+		if (path_str[0] == '\0')
+			goto fail_inval;
+		file = filp_open(path_str, 0, 0);
+		if (IS_ERR(file))
+			goto fail_noent;
+	}
+
+	path_str = kstrdup(path_str, GFP_KERNEL);
+	if (!path_str)
+		goto fail_nomem;
+
+	err = pmask_policy_set_perm(pmask_cur_stage,
+			file->f_dentry->d_inode, path_str, perm, scope);
+	if (err) {
+		kfree(path_str);
+		rval = err;
+		goto out;
+	}
+
+	pr_debug("pmask_task=%p pid=%d perm=%o[%s] path=%p[%s]\n",
+		pmask_cur_task, current->pid, perm, perm_str, file, path_str);
+
+	rval = length;
+	goto out; /* Success */
+
+fail_inval: rval = -EINVAL; goto out;
+fail_nomem: rval = -ENOMEM; goto out;
+fail_fault: rval = -EFAULT; goto out;
+fail_noent: rval = -ENOENT; goto out;
+out:
+	kfree(kbuffer);
+	/* if (rval < 0) abort task ? */
+	return rval;
+}
+
+static const struct file_operations pmask_stage_fops = {
+	.open    = pmask_stage_open,
+	.write   = pmask_stage_write,
+	.read    = seq_read,
+	.llseek  = seq_lseek,
+	.release = seq_release,
+};
+
+
+
+/***************
+ * Policy file *
+ ***************/
+static int pmask_policy_open(struct inode *inode, struct file *file)
+{
+	return pmask_seq_open(file, pmask_cur_policy);
+};
+
+static const struct file_operations pmask_policy_fops = {
+	.open    = pmask_policy_open,
+	.read    = seq_read,
+	.llseek  = seq_lseek,
+	.release = seq_release,
+};
+
+
+
+/****************
+ * Control file *
+ ****************/
+/**
+ * Read various commands from the user
+ * Format: /(\w+).* /
+ * Commands:
+ *   commit: copy stage to the policy and reset stage
+ */
+static ssize_t pmask_control_write(struct file *filp, const char *buffer,
+		size_t length, loff_t *off)
+{
+	int rval, err;
+	char *command;
+
+	command = kzalloc(length+1, GFP_KERNEL);
+	if (!command)
+		return -ENOMEM;
+
+	if (copy_from_user(command, buffer, length)) {
+		rval = -EFAULT;
+		goto out;
+	}
+
+	strstrip(command);
+
+	if (!strcmp("commit", command)) {
+		pr_debug("committing stage for pid=%d\n", current->pid);
+		err = pmask_policy_commit(pmask_cur_stage, pmask_cur_policy);
+		if (err) {
+			rval = err;
+			goto out;
+		}
+		pmask_policy_clear(pmask_cur_stage);
+	} else {
+		pr_debug("unimplemented control coomand `%s'\n", command);
+		rval = -EINVAL;
+		goto out;
+	}
+
+	rval = length; /* success */
+	goto out;
+
+out:
+	kfree(command);
+	return rval;
+}
+
+static const struct file_operations pmask_control_fops = {
+	.write = pmask_control_write,
+};
+
+
+
+/****************
+ * Registration *
+ ****************/
+static int __init pmask_fs_init(void)
+{
+	struct dentry *pmask_dir;
+	if (!pmask_loaded)
+		return 0;
+	pmask_dir = securityfs_create_dir("pmask", NULL);
+	securityfs_create_file("stage",
+			0666, pmask_dir, NULL, &pmask_stage_fops);
+	securityfs_create_file("policy",
+			0444, pmask_dir, NULL, &pmask_policy_fops);
+	securityfs_create_file("control",
+			0222, pmask_dir, NULL, &pmask_control_fops);
+	pr_info("PMask FS initialized\n");
+	return 0;
+}
+
+fs_initcall(pmask_fs_init);
diff --git a/security/pmask/pmask.c b/security/pmask/pmask.c
new file mode 100644
index 0000000..c64e4bc
--- /dev/null
+++ b/security/pmask/pmask.c
@@ -0,0 +1,144 @@
+/**
+ * pmask/pmask.c -- Linux Security Module interface for privilege dropping
+ *
+ * Copyright (C) 2009 Andy Spencer <spenceal@rose-hulman.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, either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program 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
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/kernel.h>
+#include <linux/security.h>
+#include <linux/sched.h>
+
+#include "policy.h"
+
+int pmask_loaded;
+
+/* Credentials */
+static void pmask_cred_free(struct cred *cred)
+{
+	pmask_task_free(cred->security);
+	cred->security = NULL;
+}
+
+static int pmask_cred_prepare(struct cred *new, const struct cred *old,
+		gfp_t gfp)
+{
+	new->security = pmask_task_dup(old->security, gfp);
+	if (!new->security) {
+		pr_warning("Out of memory while preparing PMask task\n");
+		return -ENOMEM;
+	}
+	return 0;
+}
+
+static int pmask_dentry_open(struct file *file, const struct cred *cred)
+{
+	u16 perm, need;
+
+	/* Set parent link */
+	if (!IS_ROOT(file->f_dentry))
+		file->f_dentry->d_inode->i_security =
+				file->f_dentry->d_parent->d_inode;
+	else
+		file->f_dentry->d_inode->i_security = NULL;
+
+
+	/* Check privs */
+	perm = pmask_policy_get_perm(pmask_cur_policy,
+			file->f_dentry->d_inode);
+	need = pmask_flags_to_mode(file->f_flags);
+	need = pmask_imode_to_perm(need, file->f_dentry->d_inode);
+	if (unlikely(pmask_denied(perm, need))) {
+		char *path = kzalloc(PATH_MAX, GFP_KERNEL);
+		if (path) {
+			path = d_path(&file->f_path, path, sizeof(path));
+			pr_debug("denied perm=%o:%o path=%s\n",
+					perm, need, path);
+			kfree(path);
+		} else {
+			pr_warning("Out of memory getting path\n");
+			pr_debug("denied perm=%o:%o path=%s\n",
+					perm, need, "-ENOMEM");
+		}
+		return -EACCES;
+	}
+	return 0;
+}
+
+/* Mostly for directory walking */
+static int pmask_inode_permission(struct inode *inode, int mask)
+{
+	u16 perm = pmask_policy_get_perm(pmask_cur_policy, inode);
+	u16 need = pmask_imode_to_perm(mask, inode);
+	if (unlikely(pmask_denied(perm, need))) {
+		pr_debug("denied perm=%o:%o:%o inode=%p\n",
+				perm, need, mask, inode);
+		return -EACCES;
+	}
+	return 0;
+}
+
+/* TODO: Use these to store the multiple pointers? */
+/*
+static int pmask_inode_alloc_security(struct inode *inode)
+{
+	return 0;
+}
+static int pmask_inode_init_security(struct inode *inode, struct inode *dir,
+		char **name, void **value, size_t *len)
+{
+	return 0;
+}
+static void pmask_inode_free_security(struct inode *inode)
+{
+}
+*/
+
+/* Registration */
+static struct security_operations pmask_security_ops = {
+	.name                 = "pmask",
+	.cred_prepare         = pmask_cred_prepare,
+	.cred_free            = pmask_cred_free,
+	.dentry_open          = pmask_dentry_open,
+	.inode_permission     = pmask_inode_permission,
+	/*
+	.inode_alloc_security = pmask_inode_alloc_security,
+	.inode_init_security  = pmask_inode_init_security,
+	.inode_free_security  = pmask_inode_free_security,
+	*/
+	/* TODO: add path operations and update the policies when the
+	 * filesystem layout changes */
+};
+
+static int __init pmask_init(void)
+{
+	struct cred *cred = (struct cred *)current_cred();
+
+	if (!security_module_enable(&pmask_security_ops))
+		return 0;
+	if (register_security(&pmask_security_ops))
+		panic("Failure registering PMask");
+	cred->security = pmask_task_new(GFP_KERNEL);
+	if (!cred->security)
+		panic("Out of memory while initializing pmask");
+	pr_info("PMask initialized\n");
+	pmask_loaded = 1;
+	return 0;
+}
+
+security_initcall(pmask_init);
diff --git a/security/pmask/policy.c b/security/pmask/policy.c
new file mode 100644
index 0000000..90d0606
--- /dev/null
+++ b/security/pmask/policy.c
@@ -0,0 +1,337 @@
+/**
+ * pmask/policy.c -- Privilege dropping core functionality
+ *
+ * Copyright (C) 2009 Andy Spencer <spenceal@rose-hulman.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, either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program 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
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/kernel.h>
+#include <linux/security.h>
+#include <linux/sched.h>
+#include <linux/ctype.h>
+#include <linux/fs.h>
+
+#include "policy.h"
+
+/*******************
+ * Permission bits *
+ *******************/
+static char pmask_perm_bit_list[] = "rwxsguRWXSGU";
+static u16  pmask_perm_bit_table['z'] = {
+	['x'] PMASK_EXEC,
+	['w'] PMASK_WRITE,
+	['r'] PMASK_READ,
+	['s'] PMASK_KEEPSWP,
+	['g'] PMASK_SETGID,
+	['u'] PMASK_SETUID,
+	['X'] PMASK_WALK,
+	['W'] PMASK_CREATE,
+	['R'] PMASK_LIST,
+	['S'] PMASK_STICKY,
+	['G'] PMASK_PASSGID,
+	['U'] PMASK_PASSUID,
+	['.'] 0,
+}; /* plus 0.25k .. */
+
+u16 pmask_flags_to_mode(unsigned int flags)
+{
+	u16 mode = 0;
+	if (flags & FMODE_READ)  mode |= PMASK_READ;
+	if (flags & FMODE_WRITE) mode |= PMASK_WRITE;
+	if (flags & FMODE_EXEC)  mode |= PMASK_EXEC;
+	if (flags & O_CREAT)     mode |= PMASK_CREATE;
+	return mode;
+}
+
+int pmask_str_to_perm(const char *str)
+{
+	int perm = 0;
+	for (; *str; str++) {
+		if ((!isalpha(*str) || !pmask_perm_bit_table[(int)*str]) &&
+				*str != '.')
+			return -1;
+		perm |= pmask_perm_bit_table[(int)*str];
+	}
+	return perm;
+}
+
+void pmask_perm_to_str(u16 perm, char *str)
+{
+	char *c = pmask_perm_bit_list;
+	for (; *c; c++, str++)
+		*str = (perm & pmask_perm_bit_table[(int)*c]) ?  *c : '.';
+	*str = '\0';
+}
+
+
+
+/**************
+ * PMask Line *
+ **************/
+/**
+ * Allocate and initalize a new pmask_line
+ * @indoe, @path, @perm: fileds to store en line
+ */
+static struct pmask_line *pmask_line_new(const struct inode *inode,
+		const char *path, gfp_t gfp)
+{
+	struct pmask_line *line;
+	line = kzalloc(sizeof(struct pmask_line), gfp);
+	if (!line)
+		return NULL;
+	line->inode = inode;
+	line->path  = path;
+	line->self_perm = PMASK_IGNORE;
+	line->kids_perm = PMASK_IGNORE;
+	return line;
+}
+
+
+
+/****************
+ * PMask Policy *
+ ****************/
+/* Return the line from @policy->privs that matches @inode */
+static struct pmask_line *pmask_policy_get_line(
+		const struct pmask_policy *policy, const struct inode *inode)
+{
+	struct pmask_line *line;
+	list_for_each_entry(line, &policy->privs, list)
+		if (line->inode == inode)
+			return line;
+	return NULL;
+}
+
+/* Create and add a line to to @policy while checking for errors and updating
+ * the quota */
+static struct pmask_line *pmask_policy_add_line(struct pmask_policy *policy,
+		const struct inode *inode, const char *path, gfp_t gfp)
+{
+	struct pmask_line *line;
+	if (atomic_read(&policy->privs_count) >= PMASK_MAX_LINES)
+		return NULL;
+	atomic_inc(&policy->privs_count);
+	line = pmask_line_new(inode, path, GFP_KERNEL);
+	if (!line)
+		return NULL;
+	list_add_tail(&line->list, &policy->privs);
+	return line;
+}
+
+/* Create and add a line to to @policy while checking for errors and updating
+ * the quota */
+static struct pmask_line *pmask_policy_ensure_line(struct pmask_policy *policy,
+		const struct inode *inode, const char *path, gfp_t gfp)
+{
+	struct pmask_line *line = pmask_policy_get_line(policy, inode);
+	if (!line)
+		line = pmask_policy_add_line(policy, inode, path, gfp);
+	return line;
+}
+
+
+/* Delete a line form @policy and update the quota */
+static void pmask_policy_del_line(struct pmask_policy *policy,
+		struct pmask_line *line)
+{
+	list_del(&line->list);
+	kfree(line);
+	atomic_dec(&policy->privs_count);
+}
+
+/* Do a semi-deep copy, that is, copy enough that the policies are distinct,
+ * but without duplicating conostant data such as paths and dentries */
+static int pmask_policy_append(struct pmask_policy *from,
+		struct pmask_policy *to, gfp_t gfp)
+{
+	struct pmask_line *old_line, *new_line;
+	list_for_each_entry(old_line, &from->privs, list) {
+		new_line = pmask_policy_add_line(to,
+				old_line->inode, old_line->path, gfp);
+		if (!new_line)
+			return -ENOMEM;
+		new_line->self_perm = old_line->self_perm;
+		new_line->kids_perm = old_line->kids_perm;
+	}
+	return 0;
+}
+
+/* Initialize a blank @policy */
+static void pmask_policy_init(struct pmask_policy *policy)
+{
+	INIT_LIST_HEAD(&policy->privs);
+	init_rwsem(&policy->privs_lock);
+	atomic_set(&policy->privs_count, 0);
+}
+
+void pmask_policy_clear(struct pmask_policy *policy)
+{
+	struct pmask_line *line, *n;
+	list_for_each_entry_safe(line, n, &policy->privs, list)
+		pmask_policy_del_line(policy, line);
+}
+
+
+static u16 pmask_policy_get_perm_rec(const struct pmask_policy *policy,
+		const struct inode *inode)
+{
+	struct pmask_line *line;
+
+	/* Allow everything if we've reach the root without finding perms */
+	/* TODO: recurse to parent filesystems */
+	if (inode == NULL)
+		return USHORT_MAX;
+
+	line = pmask_policy_get_line(policy, inode);
+
+	if (line && pmask_isset(line->kids_perm))
+		return line->kids_perm;
+
+	/* Check parents for recursive permissions */
+	/* TODO: Check for multiple parents */
+	return pmask_policy_get_perm_rec(policy, inode->i_security);
+	/*
+	 * perm = USHORT_MAX;
+	 * foreach parent:
+	 * 	perm &= pmask_policy_get_perm(policy, inode->d_parent);
+	 * return perm;
+	 */
+}
+
+u16 pmask_policy_get_perm(const struct pmask_policy *policy,
+		const struct inode *inode)
+{
+	/* Stop if a permissions is found for current node */
+	struct pmask_line *line = pmask_policy_get_line(policy, inode);
+	if (line && pmask_isset(line->self_perm))
+		return line->self_perm;
+	return pmask_policy_get_perm_rec(policy, inode->i_security);
+}
+
+/* We need the inode and path so we can create the line if it doesn't exist */
+int pmask_policy_set_perm(struct pmask_policy *policy,
+		const struct inode *inode, const char *path,
+		u16 perm, int scope)
+{
+	struct pmask_line *line = pmask_policy_ensure_line(policy,
+			inode, path, GFP_KERNEL);
+	if (!line)
+		return -ENOMEM;
+	if (scope == PMASK_BOTH || scope == PMASK_SELF)
+		line->self_perm = perm;
+	if (scope == PMASK_BOTH || scope == PMASK_KIDS)
+		line->kids_perm = perm;
+	return 0;
+}
+
+static void pmask_policy_merge_line(const struct inode *inode,
+		struct pmask_policy *pl, struct pmask_policy *pr,
+		struct pmask_line *to)
+{
+	struct pmask_line *ll = pmask_policy_get_line(pl, inode);
+	struct pmask_line *lr = pmask_policy_get_line(pr, inode);
+	if ((ll && pmask_isset(ll->self_perm)) ||
+	    (lr && pmask_isset(lr->self_perm))) {
+		to->self_perm  = pmask_policy_get_perm(pl, inode);
+		to->self_perm &= pmask_policy_get_perm(pr, inode);
+	}
+	if ((ll && pmask_isset(ll->kids_perm)) ||
+	    (lr && pmask_isset(lr->kids_perm))) {
+		to->kids_perm  = pmask_policy_get_perm_rec(pl, inode);
+		to->kids_perm &= pmask_policy_get_perm_rec(pr, inode);
+	}
+}
+
+int pmask_policy_commit(struct pmask_policy *from, struct pmask_policy *to)
+{
+	struct pmask_line *line, *merge_line, *n;
+	struct pmask_policy merge;
+	pmask_policy_init(&merge);
+
+	/* Merge paths from @to into merge */
+	list_for_each_entry(line, &to->privs, list) {
+		merge_line = pmask_policy_ensure_line(&merge,
+				line->inode, line->path, GFP_KERNEL);
+		if (!merge_line)
+			goto fail;
+		pmask_policy_merge_line(line->inode, from, to, merge_line);
+	}
+
+	/* Merge paths from @from into merge */
+	list_for_each_entry(line, &from->privs, list) {
+		merge_line = pmask_policy_ensure_line(&merge,
+				line->inode, line->path, GFP_KERNEL);
+		if (!merge_line)
+			goto fail;
+		pmask_policy_merge_line(line->inode, from, to, merge_line);
+	}
+
+	/* Free old entries, and move to ones to @to */
+	pmask_policy_clear(to);
+	list_for_each_entry_safe(line, n, &merge.privs, list)
+		list_move_tail(&line->list, &to->privs);
+
+	return 0;
+
+fail:
+	pmask_policy_clear(&merge);
+	return -ENOMEM;
+}
+
+
+
+/**************
+ * PMask Task *
+ **************/
+struct pmask_task *pmask_task_new(gfp_t gfp)
+{
+	struct pmask_task *task;
+	task = kzalloc(sizeof(struct pmask_task), gfp);
+	if (!task)
+		return NULL;
+
+	pmask_policy_init(&task->stage);
+	pmask_policy_init(&task->policy);
+
+	return task;
+}
+
+void pmask_task_free(struct pmask_task *task)
+{
+	pmask_policy_clear(&task->stage);
+	pmask_policy_clear(&task->policy);
+	kfree(task);
+}
+
+struct pmask_task *pmask_task_dup(struct pmask_task *task, gfp_t gfp)
+{
+	struct pmask_task *copy = pmask_task_new(gfp);
+	if (!copy)
+		return NULL;
+
+	/* Copy policies */
+	if (pmask_policy_append(&task->stage,  &copy->stage,  gfp))
+		goto fail;
+	if (pmask_policy_append(&task->policy, &copy->policy, gfp))
+		goto fail;
+
+	return copy;
+
+fail:
+	pmask_task_free(copy);
+	return NULL;
+}
diff --git a/security/pmask/policy.h b/security/pmask/policy.h
new file mode 100644
index 0000000..bc2d526
--- /dev/null
+++ b/security/pmask/policy.h
@@ -0,0 +1,230 @@
+/**
+ * pmask/policy.h -- Privilege dropping core functionality
+ *
+ * Copyright (C) 2009 Andy Spencer <spenceal@rose-hulman.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, either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program 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
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __PMASK_POLICY_H__
+#define __PMASK_POLICY_H__
+
+/* Set to 1 when pmask is initialized */
+extern int pmask_loaded;
+
+/**
+ * Terminology
+ *   mode   = `Unix' mode (u16 use for filesyste mode bits)
+ *   perm   = PMask permission bits (see below)
+ *   privs  = List of files and associated perm
+ *   policy = Privs + whatever else
+ */
+
+#define pmask_cur_task   ((struct pmask_task *)current_security())
+#define pmask_cur_stage  ((struct pmask_policy *)&pmask_cur_task->stage)
+#define pmask_cur_policy ((struct pmask_policy *)&pmask_cur_task->policy)
+
+
+/*******************
+ * Permission bits *
+ *******************/
+/* File bits */
+#define PMASK_EXEC    (1u<<0)  /* x */
+#define PMASK_WRITE   (1u<<1)  /* w */
+#define PMASK_READ    (1u<<2)  /* r */
+#define PMASK_KEEPSWP (1u<<3)  /* s (ignored) */
+#define PMASK_SETGID  (1u<<4)  /* g */
+#define PMASK_SETUID  (1u<<5)  /* u */
+
+/* Directory bits */
+#define PMASK_WALK    (1u<<6)  /* X */
+#define PMASK_CREATE  (1u<<7)  /* W */
+#define PMASK_LIST    (1u<<8)  /* R */
+#define PMASK_STICKY  (1u<<9)  /* S */
+#define PMASK_PASSGID (1u<<10) /* G */
+#define PMASK_PASSUID (1u<<11) /* U (ignored) */
+
+/* Special bits */
+#define PMASK_IGNORE  (1u<<12) /* Permissions unset */
+
+/* Meta bits/masks */
+#define PMASK_PERM_BITS 12
+#define PMASK_MASK      0b111111111111
+#define PMASK_FILE_MASK 0b000000111111
+#define PMASK_DIR_MASK  0b111111000000
+
+/* Scope of permission */
+enum {
+	PMASK_SELF, /* Permissions only affect the inode */
+	PMASK_KIDS, /* Permissions only affect the inodes children */
+	PMASK_BOTH, /* Permissions affect inode and children */
+};
+
+/* Determine if a permission is set or ignored */
+static inline bool pmask_isset(u16 perm)
+{
+	return !(perm & PMASK_IGNORE);
+}
+
+/* Mode conversion functions */
+static inline bool pmask_denied(u16 perm, u16 request)
+{
+	return perm >= 0 && ~perm & request;
+}
+
+/* Convert from a unix directory mode to a perm */
+static inline u16 pmask_dmode_to_perm(u16 mode)
+{
+	return mode << 6;
+}
+
+/* Convert from a unix file mode to a perm */
+static inline u16 pmask_fmode_to_perm(u16 mode)
+{
+	return mode;
+}
+
+/* Convert from a unix perm to a mode based on inode type */
+static inline u16 pmask_imode_to_perm(u16 mode, struct inode *inode)
+{
+	return S_ISDIR(inode->i_mode) ?
+		 pmask_dmode_to_perm(mode) :
+		 pmask_fmode_to_perm(mode);
+}
+
+/**
+ * Convert struct file->f_flags to a Unix mode
+ * <x>mode_to_perm should probably be called on the resulting mode
+ */
+u16 pmask_flags_to_mode(unsigned int flags);
+
+/**
+ * Parse a permission string into a perm
+ * @str:
+ *  - Format is "rwxsguRWXSGU" (see Permission bits)
+ *  - Order does not matter
+ *  - '.' is ignored, any other character is invalid
+ *  - return -1 on invalid str
+ */
+int pmask_str_to_perm(const char *str);
+
+/**
+ * Convert a perm to a string for printing
+ */
+void pmask_perm_to_str(u16 perm, char *str);
+
+
+
+/**************
+ * PMask Line *
+ **************/
+/**
+ * An entry in the policy
+ *
+ * Example:
+ *   /var/tmp (rw-)
+ *
+ * @list:      list_head for stroing in policy
+ * @inode:     Some point in the filesystem, topically an inode
+ * @path:      Path given when the line was created, debugging only
+ * @self_perm: Permissions given to the location
+ * @kids_perm: Permissions given to the location's kids
+ */
+struct pmask_line {
+	struct list_head    list;
+	const struct inode *inode;
+	const char         *path;
+	u16                 self_perm;
+	u16                 kids_perm;
+};
+
+
+
+/****************
+ * PMask Policy *
+ ****************/
+#define PMASK_MAX_LINES 500
+
+/**
+ * Contains permisisons and operations allowed for given security policy
+ *
+ * @privs:      List of pmask_lines for filesystem privilages
+ * @privs_lock: Used for printing (maybe other?)
+ *
+ * Example:
+ *   privs:
+ *     /         (r--)
+ *     /bin/     (r-x)
+ *     /tmp/     (rw-)
+ */
+struct pmask_policy {
+	struct list_head privs;
+	struct rw_semaphore privs_lock;
+	atomic_t privs_count;
+	/* TODO: add other security things */
+};
+
+/* Clear/free data from @policy */
+void pmask_policy_clear(struct pmask_policy *policy);
+
+/* Recursivly lookup perm for @inode in @policy */
+u16 pmask_policy_get_perm(const struct pmask_policy *policy,
+		const struct inode *inode);
+
+/* Set perm for @inode in @policy to @perm, create new line if necessasiary */
+int pmask_policy_set_perm(struct pmask_policy *policy,
+		const struct inode *inode, const char *path,
+		u16 perm, int scope);
+
+/* Copy lines from @from to @to making sure that no additional oeratoins are
+ * allowed in @to after the commit is performed */
+int pmask_policy_commit(struct pmask_policy *from, struct pmask_policy *to);
+
+
+
+/**************
+ * PMask Task *
+ **************/
+/**
+ * Contains information for a given task, including the security policy, stage,
+ * and cache information.
+ *
+ * @stage:
+ *   The modifialbe policy, privilages can be allowed or denied in the stage
+ * @policy:
+ *   The effective policy, used to determines whether an action is allowed
+ *
+ * @policy can only be modified by commiting @stage to @policy. When this is
+ * done, it is insured that no additional operations will be allowed by @policy
+ * after the commit.
+ *
+ * Example:
+ *   stage:  (see pmask_policy)
+ *   policy: (see pmask_policy)
+ */
+struct pmask_task {
+	struct pmask_policy stage;
+	struct pmask_policy policy;
+};
+
+/* Allocate a blank task */
+struct pmask_task *pmask_task_new(gfp_t gfp);
+
+/* Free a task and data associated with it */
+void pmask_task_free(struct pmask_task *task);
+
+/* Create a semi-deep copy of @task, suitable for passing to a child on exec */
+struct pmask_task *pmask_task_dup(struct pmask_task *task, gfp_t gfp);
+
+#endif

[-- Attachment #2: Type: application/pgp-signature, Size: 198 bytes --]

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* Re: [RFC][PATCH] Privilege dropping security module
  2009-09-25 21:00       ` David Wagner
@ 2009-09-29  7:36         ` Andy Spencer
  0 siblings, 0 replies; 24+ messages in thread
From: Andy Spencer @ 2009-09-29  7:36 UTC (permalink / raw)
  To: David Wagner; +Cc: linux-kernel

[-- Attachment #1: Type: text/plain, Size: 1174 bytes --]

> Can 'length+1' overflow?
> (Can the caller arrange to pass MAX_SIZE_T as the length parameter?
> If yes, that's a vulnerability.)
> I haven't checked how dpriv_stage_write() is called, to see whether
> this is possible.

It's called by vfs_write so it could be a problem. I couldn't find any
standard macros, I added a check to ensure that

  (length < (ssize_t)(~0ULL))
  
which should fix this and ensure that length will fit in the returned
value.


> What if kbuffer isn't '\0'-terminated?  Won't this read past the end
> of kbuffer?

Allocating kbuffer to length+1 zeros should ensure that it's '\0'
terminated, but it changed it to use kmalloc and explicitly set the
terminator instead.


> Are you certain that perm_end and path_start will be within bounds?
> If the user supplies a sufficiently large string (more than MAX_INT
> characters long), could perm_end or path_start be negative?

Limiting length should fix this as well. I also change the markers to
size_t.


Unfortunately I only noticed a few of these things after posting the
second patch against the mainline, so not all of these changes are
included in that post..

[-- Attachment #2: Type: application/pgp-signature, Size: 198 bytes --]

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [RFC][PATCH] Permission masking security module (was dpriv)
  2009-09-29  7:10 ` [RFC][PATCH] Permission masking security module (was dpriv) Andy Spencer
@ 2009-09-29 17:44   ` Greg KH
  2009-09-30  0:18     ` Andy Spencer
  2009-10-01  2:33   ` Casey Schaufler
  1 sibling, 1 reply; 24+ messages in thread
From: Greg KH @ 2009-09-29 17:44 UTC (permalink / raw)
  To: Andy Spencer; +Cc: linux-security-module, linux-kernel

On Tue, Sep 29, 2009 at 07:10:10AM +0000, Andy Spencer wrote:
> --- a/lib/vsprintf.c
> +++ b/lib/vsprintf.c
> @@ -1771,7 +1771,7 @@ int vsscanf(const char * buf, const char * fmt, va_list args)
>  		 * advance both strings to next white space
>  		 */
>  		if (*fmt == '*') {
> -			while (!isspace(*fmt) && *fmt)
> +			while (!isspace(*fmt) && *fmt != '%' && *fmt)
>  				fmt++;
>  			while (!isspace(*str) && *str)
>  				str++;

What is this patch for?  Care to break it out separate with a different
changelog comment and push it upstream now if it is needed?

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [RFC][PATCH] Permission masking security module (was dpriv)
  2009-09-29 17:44   ` Greg KH
@ 2009-09-30  0:18     ` Andy Spencer
  0 siblings, 0 replies; 24+ messages in thread
From: Andy Spencer @ 2009-09-30  0:18 UTC (permalink / raw)
  To: Greg KH; +Cc: linux-security-module, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 373 bytes --]

> What is this patch for?  Care to break it out separate with a
> different changelog comment and push it upstream now if it is needed?

It fixes a bug in sscanf when using, for example, `%*s%n'.

I posted a separate patch for this a few days ago, but it hasn't been
merged upstream so I included that change here as well.

For reference: http://lkml.org/lkml/2009/9/25/31

[-- Attachment #2: Type: application/pgp-signature, Size: 198 bytes --]

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [RFC][PATCH] Permission masking security module (was dpriv)
  2009-09-29  7:10 ` [RFC][PATCH] Permission masking security module (was dpriv) Andy Spencer
  2009-09-29 17:44   ` Greg KH
@ 2009-10-01  2:33   ` Casey Schaufler
  1 sibling, 0 replies; 24+ messages in thread
From: Casey Schaufler @ 2009-10-01  2:33 UTC (permalink / raw)
  To: Andy Spencer; +Cc: linux-security-module, linux-kernel, Casey Schaufler

Andy Spencer wrote:
> Changes since the previous patch (dpriv-p0):
>
> - Change the name to pmask (Permission Masking)
>
> - Use recursive and non-recursive variants for setting masks 
>   $ echo self $perm $path > $stage # Set perms for $path only
>   $ echo kids $perm $path > $stage # Set perms for children of $path
>   $ echo both $perm $path > $stage # Set perms for $path and children
>
> - Add a quota of 500 permission lines per policy
>
> - Improve securityfs parsing.
>
> - Change '-' to '.' in permissions string to avoid confusion with
>   `chmod -rwx foo'
>
> - Many syntax/naming/etc bug fixes
>   

I have some comments below. Nothing that should be a big surprise.
I would really like to see more and bigger comments, especially describing
the inner workings of the hooks.

So what is the use for this? It's sort of intuitive, but not completely.
Is there a particular problem it solves, or is it a general "it feels like
it ought to help" sort of thing?

What's your intent regarding paths that follow symlinks, go through
mount points,
that kind of thing.


> Signed-off-by: Andy Spencer <andy753421@gmail.com>
> ---
>  Documentation/pmask.txt |  102 ++++++++++++++
>  lib/vsprintf.c          |    2 +-
>  security/Kconfig        |    1 +
>  security/Makefile       |    2 +
>  security/pmask/Kconfig  |    8 +
>  security/pmask/Makefile |    1 +
>  security/pmask/fs.c     |  299 +++++++++++++++++++++++++++++++++++++++++
>  security/pmask/pmask.c  |  144 ++++++++++++++++++++
>  security/pmask/policy.c |  337 +++++++++++++++++++++++++++++++++++++++++++++++
>  security/pmask/policy.h |  230 ++++++++++++++++++++++++++++++++
>  10 files changed, 1125 insertions(+), 1 deletions(-)
>  create mode 100644 Documentation/pmask.txt
>  create mode 100644 security/pmask/Kconfig
>  create mode 100644 security/pmask/Makefile
>  create mode 100644 security/pmask/fs.c
>  create mode 100644 security/pmask/pmask.c
>  create mode 100644 security/pmask/policy.c
>  create mode 100644 security/pmask/policy.h
>
> diff --git a/Documentation/pmask.txt b/Documentation/pmask.txt
> new file mode 100644
> index 0000000..522cb30
> --- /dev/null
> +++ b/Documentation/pmask.txt
> @@ -0,0 +1,102 @@
> +Source code
> +-----------
> +  policy.[ch] - policy datatypes
> +  pmask.c     - security/credentials hooks
> +  fs.c        - securityfs hooks
> +
> +
> +TODO
> +----
> +  - Check for race conditions
> +
> +
> +Overview
> +--------
> +1. Each process keeps a list of inode -> priv mappings:
> +   - i.e. the security policy
> +
> +2. Caching possibilities (todo?)
> +   - Processes keeps a list of open fds to prevent recursing up the FS tree?
> +   - Store the most recent processes access in each inode?
> +
> +Privs:
> +  - read/write/exec/sticky/setuid/setgui
> +  - All permissions are recursive
> +  - Permissions for dirs and file are separate
> +    - This prevents recursion problems
> +    - e.g. you can set noexec for files without smashing directories
> +  - Notation
> +     (rwx) = specified permission (inode in policy)
> +    ~(rwx) = implied permission (parent(s) in policy)
> +
> +Things to do when:
> +  1. Setting privs
> +     - Add policy line(s) for given path?
> +     - Update privs on open inodes that are children of policy line?
> +  2. Loading inode
> +     - Cache privs from parent(s)?
> +  3. Namespace modification (mv,ln,bind,etc)
> +     - OR
> +       - Keep policy for inode the same        (policy = old      )
> +       - Merge policy for both locations       (policy = old & new)
> +       - Change policy to reflect new location (policy =       new)
> +     - If mv, and including old implied policy:
> +       - need to write new (combined) policy line
> +
> +
> +Security FS
> +-----------
> +files:
> +  -rw-rw-rw- /securityfs/pmask/stage
> +  -r--r--r-- /securityfs/pmask/policy
> +  --w--w--w- /securityfs/pmask/control
> +
> +stage:
> +  read:  print staged policy
> +  write: set inode in staged policy to given perms OR
> +         add inode to staged policy with given perms
> +         > staged[inode] = perms
> +
> +  In the stage, order does not matter, adding a line simply writes or
> +  overwrites the location with no regard to the rest of the policy.
> +
> +policy:
> +  read: print active policy
> +
> +control:
> +  write:
> +    "commit" - merge staged policy into policy
> +      > for (inode in policy, staged):
> +      >     new[inode] =
> +      >         implied_privs(policy, inode) &
> +      >         implied_privs(staged, inode)
> +      > clear(staged)
> +
> +    When committing, privilages can only be revoked.
> +
> +
> +Examples
> +--------
> +Example 1:
> +  set /src/     (rw-)
> +  set /dst/     (r-x)
> +
> +  $ mv /src/foo /dst
> +
> +  get /src/     (rw-)
> +  get /dst/     (r-x)
> +  OR:
> +    get /dst/foo  (rw-)
> +    get /dst/foo ~(r-x)
> +    get /dst/foo  (rw-) & ~(r-x) = (r--)
> +
> +Example 2:
> +  $ ln /src/foo /dst
> +
> +  set /src/     (rw-)
> +  set /dst/     (rwx)
> +
> +  get /src/     (rw-)
> +  get /dst/     (rwx)
> +  get /src/foo ~(rw-) & ~(rwx) = ~(rw-)
> +  get /dst/foo ~(rw-) & ~(rwx) = ~(rw-)
> diff --git a/lib/vsprintf.c b/lib/vsprintf.c
> index b91839e..33bed5e 100644
> --- a/lib/vsprintf.c
> +++ b/lib/vsprintf.c
> @@ -1771,7 +1771,7 @@ int vsscanf(const char * buf, const char * fmt, va_list args)
>  		 * advance both strings to next white space
>  		 */
>  		if (*fmt == '*') {
> -			while (!isspace(*fmt) && *fmt)
> +			while (!isspace(*fmt) && *fmt != '%' && *fmt)
>  				fmt++;
>  			while (!isspace(*str) && *str)
>  				str++;
>   

Since this isn't core to the task at hand you shouldn't include it here.
I understand you're pursuing this in another thread, so you should not
include it here, as was discussed elsewhere.


> diff --git a/security/Kconfig b/security/Kconfig
> index fb363cd..d4521b5 100644
> --- a/security/Kconfig
> +++ b/security/Kconfig
> @@ -159,6 +159,7 @@ config LSM_MMAP_MIN_ADDR
>  	  this low address space will need the permission specific to the
>  	  systems running LSM.
>  
> +source security/pmask/Kconfig
>  source security/selinux/Kconfig
>  source security/smack/Kconfig
>  source security/tomoyo/Kconfig
> diff --git a/security/Makefile b/security/Makefile
> index 95ecc06..f8c5b26 100644
> --- a/security/Makefile
> +++ b/security/Makefile
> @@ -3,6 +3,7 @@
>  #
>  
>  obj-$(CONFIG_KEYS)			+= keys/
> +subdir-$(CONFIG_SECURITY_PERM_MASKING)  += pmask
>  subdir-$(CONFIG_SECURITY_SELINUX)	+= selinux
>  subdir-$(CONFIG_SECURITY_SMACK)		+= smack
>  subdir-$(CONFIG_SECURITY_TOMOYO)        += tomoyo
> @@ -14,6 +15,7 @@ obj-y		+= commoncap.o min_addr.o
>  obj-$(CONFIG_SECURITY)			+= security.o capability.o
>  obj-$(CONFIG_SECURITYFS)		+= inode.o
>  # Must precede capability.o in order to stack properly.
> +obj-$(CONFIG_SECURITY_PERM_MASKING)	+= pmask/built-in.o
>  obj-$(CONFIG_SECURITY_SELINUX)		+= selinux/built-in.o
>  obj-$(CONFIG_SECURITY_SMACK)		+= smack/built-in.o
>  obj-$(CONFIG_AUDIT)			+= lsm_audit.o
> diff --git a/security/pmask/Kconfig b/security/pmask/Kconfig
> new file mode 100644
> index 0000000..a8ca887
> --- /dev/null
> +++ b/security/pmask/Kconfig
> @@ -0,0 +1,8 @@
> +config SECURITY_PERM_MASKING
> +	bool "Permission masking"
> +	depends on SECURITY
> +	select SECURITYFS
> +	default n
> +	help
> +	  This enabled support for masking filesystem permissions.
> +	  If you are unsure how to answer this question, answer N.
> diff --git a/security/pmask/Makefile b/security/pmask/Makefile
> new file mode 100644
> index 0000000..39d5b27
> --- /dev/null
> +++ b/security/pmask/Makefile
> @@ -0,0 +1 @@
> +obj-y = pmask.o policy.o fs.o
> diff --git a/security/pmask/fs.c b/security/pmask/fs.c
> new file mode 100644
> index 0000000..84a6515
> --- /dev/null
> +++ b/security/pmask/fs.c
> @@ -0,0 +1,299 @@
> +/**
> + * pmask/fs.c -- Security FS interface for privilege dropping
> + *
> + * Copyright (C) 2009 Andy Spencer <spenceal@rose-hulman.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, either version 2 of the License, or (at your option)
> + * any later version.
> + *
> + * This program 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
> + * this program.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#define pr_fmt(fmt) "%s: " fmt, __func__
> +
> +#include <linux/kernel.h>
> +#include <linux/security.h>
> +#include <linux/fs.h>
> +#include <linux/seq_file.h>
> +#include <linux/ctype.h>
> +#include <linux/uaccess.h>
> +
> +#include "policy.h"
> +
> +/***************************
> + * Generic policy iterator *
> + ***************************/
> +/* Use this for reading form any policy file */
>   

I'll pick the nit. s/form/from/

> +static void *pmask_seq_start(struct seq_file *sf, loff_t *pos)
> +{
> +	struct pmask_policy *policy = sf->private;
> +	down_read(&policy->privs_lock);
> +	return seq_list_start(&policy->privs, *pos);
> +}
> +
> +static void *pmask_seq_next(struct seq_file *sf, void *seq, loff_t *pos)
> +{
> +	struct pmask_policy *policy = sf->private;
> +	return seq_list_next(seq, &policy->privs, pos);
> +}
> +
> +static void pmask_seq_stop(struct seq_file *sf, void *seq)
> +{
> +	struct pmask_policy *policy = sf->private;
> +	up_read(&policy->privs_lock);
> +}
> +
> +static int pmask_seq_show(struct seq_file *sf, void *seq)
> +{
> +	struct pmask_line *line = list_entry(seq, struct pmask_line, list);
> +	char perm_str[PMASK_PERM_BITS+1] = {};
> +	if (pmask_isset(line->self_perm) &&
> +			line->self_perm == line->kids_perm) {
> +		pmask_perm_to_str(line->self_perm, perm_str);
> +		seq_printf(sf, "both %s %s\n", perm_str, line->path);
> +	} else {
> +		if (pmask_isset(line->self_perm)) {
> +			pmask_perm_to_str(line->self_perm, perm_str);
> +			seq_printf(sf, "self %s %s\n", perm_str, line->path);
> +		}
> +		if (pmask_isset(line->kids_perm)) {
> +			pmask_perm_to_str(line->kids_perm, perm_str);
> +			seq_printf(sf, "kids %s %s\n", perm_str, line->path);
> +		}
> +	}
> +	return 0;
> +}
> +
> +static const struct seq_operations pmask_seq_ops = {
> +	.start = pmask_seq_start,
> +	.next  = pmask_seq_next,
> +	.stop  = pmask_seq_stop,
> +	.show  = pmask_seq_show,
> +};
> +
> +static int pmask_seq_open(struct file *file, struct pmask_policy *policy)
> +{
> +	/* From __seq_open_private
> +	 * Not sure if this is correct way to store private data */
> +	struct seq_file *sf;
> +	if (seq_open(file, &pmask_seq_ops) < 0) {
> +		pr_warning("Out of memory opening PMask sequence\n");
> +		return -ENOMEM;
> +	}
> +	sf = file->private_data;
> +	sf->private = policy;
> +	return 0;
> +};
> +
> +
> +
> +/**************
> + * Stage file *
> + **************/
> +static int pmask_stage_open(struct inode *inode, struct file *file)
> +{
> +	return pmask_seq_open(file, pmask_cur_stage);
> +};
> +
> +/**
> + * Parse policy lines one at a time.
> + * Format: /\s*([rwxsguRWXSGU\-]*)\s*(.*)(\s*)?/
> + *   \1: See pmask_str_to_perm() for discussion
> + *   \2: A file path, \3 trailing whitespace is optional
> + */
> +static ssize_t pmask_stage_write(struct file *filp, const char *ubuffer,
> +		size_t length, loff_t *off)
> +{
> +	struct file *file;
> +	int err, rval, perm, scope;
> +	char *kbuffer, *cmd_str, *perm_str, *path_str;
> +	int cmd_start, cmd_end, perm_start, perm_end, path_start;
> +
> +	if (length > (size_t)~0LL)
> +		return -EINVAL;;
> +	kbuffer = kmalloc(length+1, GFP_KERNEL);
> +	if (!kbuffer)
> +		return -ENOMEM;
> +	kbuffer[length] = '\0';
> +
> +	if (copy_from_user(kbuffer, ubuffer, length))
> +		goto fail_fault;
> +
> +	/* Parse input */
> +	path_start = -1;
> +	sscanf(kbuffer, " %n%*s%n %n%*s%n %n", &cmd_start, &cmd_end,
> +			&perm_start, &perm_end, &path_start);
> +	if (path_start == -1)
> +		goto fail_inval;
> +	cmd_str  = kbuffer+cmd_start;  kbuffer[cmd_end]  = '\0';
> +	perm_str = kbuffer+perm_start; kbuffer[perm_end] = '\0';
> +	path_str = kbuffer+path_start;
> +
> +	/* Check and convert cmd/scope */
> +	if (!strcmp(cmd_str, "self"))
> +		scope = PMASK_SELF;
> +	else if (!strcmp(cmd_str, "kids"))
> +		scope = PMASK_KIDS;
> +	else if (!strcmp(cmd_str, "both"))
> +		scope = PMASK_BOTH;
> +	else
> +		goto fail_inval;
> +
> +	/* Check and convert perm */
> +	if (perm_str[0] == '\0')
> +		goto fail_inval;
> +	perm = pmask_str_to_perm(perm_str);
> +	if (perm < 0)
> +		goto fail_inval;
> +
> +	/* Check and open path */
> +	if (path_str[0] == '\0')
> +		goto fail_inval;
> +	file = filp_open(path_str, 0, 0);
> +	if (IS_ERR(file)) {
> +		/* file not found, try trimming trailing spaces */
> +		strstrip(path_str);
> +		if (path_str[0] == '\0')
> +			goto fail_inval;
> +		file = filp_open(path_str, 0, 0);
> +		if (IS_ERR(file))
> +			goto fail_noent;
> +	}
> +
> +	path_str = kstrdup(path_str, GFP_KERNEL);
> +	if (!path_str)
> +		goto fail_nomem;
> +
> +	err = pmask_policy_set_perm(pmask_cur_stage,
> +			file->f_dentry->d_inode, path_str, perm, scope);
> +	if (err) {
> +		kfree(path_str);
> +		rval = err;
> +		goto out;
> +	}
> +
> +	pr_debug("pmask_task=%p pid=%d perm=%o[%s] path=%p[%s]\n",
> +		pmask_cur_task, current->pid, perm, perm_str, file, path_str);
> +
> +	rval = length;
> +	goto out; /* Success */
> +
> +fail_inval: rval = -EINVAL; goto out;
> +fail_nomem: rval = -ENOMEM; goto out;
> +fail_fault: rval = -EFAULT; goto out;
> +fail_noent: rval = -ENOENT; goto out;
> +out:
>   

This is why goto's got a bad reputation. Set rval in the code above and
"goto out:". Your "fail_noent" is especially painful as you have a goto
to the next line.

> +	kfree(kbuffer);
> +	/* if (rval < 0) abort task ? */
> +	return rval;
> +}
> +
> +static const struct file_operations pmask_stage_fops = {
> +	.open    = pmask_stage_open,
> +	.write   = pmask_stage_write,
> +	.read    = seq_read,
> +	.llseek  = seq_lseek,
> +	.release = seq_release,
> +};
> +
> +
> +
> +/***************
> + * Policy file *
> + ***************/
> +static int pmask_policy_open(struct inode *inode, struct file *file)
> +{
> +	return pmask_seq_open(file, pmask_cur_policy);
> +};
> +
> +static const struct file_operations pmask_policy_fops = {
> +	.open    = pmask_policy_open,
> +	.read    = seq_read,
> +	.llseek  = seq_lseek,
> +	.release = seq_release,
> +};
> +
> +
> +
> +/****************
> + * Control file *
> + ****************/
> +/**
> + * Read various commands from the user
> + * Format: /(\w+).* /
> + * Commands:
> + *   commit: copy stage to the policy and reset stage
> + */
> +static ssize_t pmask_control_write(struct file *filp, const char *buffer,
> +		size_t length, loff_t *off)
> +{
> +	int rval, err;
> +	char *command;
> +
> +	command = kzalloc(length+1, GFP_KERNEL);
> +	if (!command)
> +		return -ENOMEM;
> +
> +	if (copy_from_user(command, buffer, length)) {
> +		rval = -EFAULT;
> +		goto out;
> +	}
> +
> +	strstrip(command);
> +
> +	if (!strcmp("commit", command)) {
> +		pr_debug("committing stage for pid=%d\n", current->pid);
> +		err = pmask_policy_commit(pmask_cur_stage, pmask_cur_policy);
> +		if (err) {
> +			rval = err;
> +			goto out;
> +		}
> +		pmask_policy_clear(pmask_cur_stage);
> +	} else {
> +		pr_debug("unimplemented control coomand `%s'\n", command);
> +		rval = -EINVAL;
> +		goto out;
> +	}
> +
> +	rval = length; /* success */
> +	goto out;
> +
> +out:
> +	kfree(command);
> +	return rval;
> +}
> +
> +static const struct file_operations pmask_control_fops = {
> +	.write = pmask_control_write,
> +};
> +
> +
> +
> +/****************
> + * Registration *
> + ****************/
> +static int __init pmask_fs_init(void)
> +{
> +	struct dentry *pmask_dir;
> +	if (!pmask_loaded)
> +		return 0;
> +	pmask_dir = securityfs_create_dir("pmask", NULL);
> +	securityfs_create_file("stage",
> +			0666, pmask_dir, NULL, &pmask_stage_fops);
> +	securityfs_create_file("policy",
> +			0444, pmask_dir, NULL, &pmask_policy_fops);
> +	securityfs_create_file("control",
> +			0222, pmask_dir, NULL, &pmask_control_fops);
> +	pr_info("PMask FS initialized\n");
> +	return 0;
> +}
> +
> +fs_initcall(pmask_fs_init);
> diff --git a/security/pmask/pmask.c b/security/pmask/pmask.c
> new file mode 100644
> index 0000000..c64e4bc
> --- /dev/null
> +++ b/security/pmask/pmask.c
> @@ -0,0 +1,144 @@
> +/**
> + * pmask/pmask.c -- Linux Security Module interface for privilege dropping
> + *
> + * Copyright (C) 2009 Andy Spencer <spenceal@rose-hulman.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, either version 2 of the License, or (at your option)
> + * any later version.
> + *
> + * This program 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
> + * this program.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +
> +#define pr_fmt(fmt) "%s: " fmt, __func__
> +
> +#include <linux/kernel.h>
> +#include <linux/security.h>
> +#include <linux/sched.h>
> +
> +#include "policy.h"
> +
> +int pmask_loaded;
> +
> +/* Credentials */
> +static void pmask_cred_free(struct cred *cred)
> +{
> +	pmask_task_free(cred->security);
> +	cred->security = NULL;
> +}
> +
> +static int pmask_cred_prepare(struct cred *new, const struct cred *old,
> +		gfp_t gfp)
> +{
> +	new->security = pmask_task_dup(old->security, gfp);
> +	if (!new->security) {
> +		pr_warning("Out of memory while preparing PMask task\n");
> +		return -ENOMEM;
> +	}
> +	return 0;
> +}
> +
> +static int pmask_dentry_open(struct file *file, const struct cred *cred)
> +{
> +	u16 perm, need;
> +
> +	/* Set parent link */
> +	if (!IS_ROOT(file->f_dentry))
> +		file->f_dentry->d_inode->i_security =
> +				file->f_dentry->d_parent->d_inode;
> +	else
> +		file->f_dentry->d_inode->i_security = NULL;
> +
> +
> +	/* Check privs */
> +	perm = pmask_policy_get_perm(pmask_cur_policy,
> +			file->f_dentry->d_inode);
> +	need = pmask_flags_to_mode(file->f_flags);
> +	need = pmask_imode_to_perm(need, file->f_dentry->d_inode);
> +	if (unlikely(pmask_denied(perm, need))) {
> +		char *path = kzalloc(PATH_MAX, GFP_KERNEL);
> +		if (path) {
> +			path = d_path(&file->f_path, path, sizeof(path));
> +			pr_debug("denied perm=%o:%o path=%s\n",
> +					perm, need, path);
> +			kfree(path);
> +		} else {
> +			pr_warning("Out of memory getting path\n");
> +			pr_debug("denied perm=%o:%o path=%s\n",
> +					perm, need, "-ENOMEM");
> +		}
> +		return -EACCES;
> +	}
> +	return 0;
> +}
> +
> +/* Mostly for directory walking */
> +static int pmask_inode_permission(struct inode *inode, int mask)
> +{
> +	u16 perm = pmask_policy_get_perm(pmask_cur_policy, inode);
> +	u16 need = pmask_imode_to_perm(mask, inode);
> +	if (unlikely(pmask_denied(perm, need))) {
> +		pr_debug("denied perm=%o:%o:%o inode=%p\n",
> +				perm, need, mask, inode);
> +		return -EACCES;
> +	}
> +	return 0;
> +}
> +
> +/* TODO: Use these to store the multiple pointers? */
> +/*
> +static int pmask_inode_alloc_security(struct inode *inode)
> +{
> +	return 0;
> +}
> +static int pmask_inode_init_security(struct inode *inode, struct inode *dir,
> +		char **name, void **value, size_t *len)
> +{
> +	return 0;
> +}
> +static void pmask_inode_free_security(struct inode *inode)
> +{
> +}
> +*/
> +
> +/* Registration */
> +static struct security_operations pmask_security_ops = {
> +	.name                 = "pmask",
> +	.cred_prepare         = pmask_cred_prepare,
> +	.cred_free            = pmask_cred_free,
> +	.dentry_open          = pmask_dentry_open,
> +	.inode_permission     = pmask_inode_permission,
> +	/*
> +	.inode_alloc_security = pmask_inode_alloc_security,
> +	.inode_init_security  = pmask_inode_init_security,
> +	.inode_free_security  = pmask_inode_free_security,
> +	*/
> +	/* TODO: add path operations and update the policies when the
> +	 * filesystem layout changes */
> +};
> +
> +static int __init pmask_init(void)
> +{
> +	struct cred *cred = (struct cred *)current_cred();
> +
> +	if (!security_module_enable(&pmask_security_ops))
> +		return 0;
> +	if (register_security(&pmask_security_ops))
> +		panic("Failure registering PMask");
> +	cred->security = pmask_task_new(GFP_KERNEL);
> +	if (!cred->security)
> +		panic("Out of memory while initializing pmask");
> +	pr_info("PMask initialized\n");
> +	pmask_loaded = 1;
> +	return 0;
> +}
> +
> +security_initcall(pmask_init);
> diff --git a/security/pmask/policy.c b/security/pmask/policy.c
> new file mode 100644
> index 0000000..90d0606
> --- /dev/null
> +++ b/security/pmask/policy.c
> @@ -0,0 +1,337 @@
> +/**
> + * pmask/policy.c -- Privilege dropping core functionality
> + *
> + * Copyright (C) 2009 Andy Spencer <spenceal@rose-hulman.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, either version 2 of the License, or (at your option)
> + * any later version.
> + *
> + * This program 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
> + * this program.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#define pr_fmt(fmt) "%s: " fmt, __func__
> +
> +#include <linux/kernel.h>
> +#include <linux/security.h>
> +#include <linux/sched.h>
> +#include <linux/ctype.h>
> +#include <linux/fs.h>
> +
> +#include "policy.h"
> +
> +/*******************
> + * Permission bits *
> + *******************/
> +static char pmask_perm_bit_list[] = "rwxsguRWXSGU";
> +static u16  pmask_perm_bit_table['z'] = {
> +	['x'] PMASK_EXEC,
> +	['w'] PMASK_WRITE,
> +	['r'] PMASK_READ,
> +	['s'] PMASK_KEEPSWP,
> +	['g'] PMASK_SETGID,
> +	['u'] PMASK_SETUID,
> +	['X'] PMASK_WALK,
> +	['W'] PMASK_CREATE,
> +	['R'] PMASK_LIST,
> +	['S'] PMASK_STICKY,
> +	['G'] PMASK_PASSGID,
> +	['U'] PMASK_PASSUID,
> +	['.'] 0,
> +}; /* plus 0.25k .. */
> +
> +u16 pmask_flags_to_mode(unsigned int flags)
> +{
> +	u16 mode = 0;
> +	if (flags & FMODE_READ)  mode |= PMASK_READ;
> +	if (flags & FMODE_WRITE) mode |= PMASK_WRITE;
> +	if (flags & FMODE_EXEC)  mode |= PMASK_EXEC;
> +	if (flags & O_CREAT)     mode |= PMASK_CREATE;
> +	return mode;
> +}
> +
> +int pmask_str_to_perm(const char *str)
> +{
> +	int perm = 0;
> +	for (; *str; str++) {
> +		if ((!isalpha(*str) || !pmask_perm_bit_table[(int)*str]) &&
> +				*str != '.')
> +			return -1;
> +		perm |= pmask_perm_bit_table[(int)*str];
> +	}
> +	return perm;
> +}
> +
> +void pmask_perm_to_str(u16 perm, char *str)
> +{
> +	char *c = pmask_perm_bit_list;
> +	for (; *c; c++, str++)
> +		*str = (perm & pmask_perm_bit_table[(int)*c]) ?  *c : '.';
> +	*str = '\0';
> +}
> +
> +
> +
> +/**************
> + * PMask Line *
> + **************/
> +/**
> + * Allocate and initalize a new pmask_line
> + * @indoe, @path, @perm: fileds to store en line
> + */
> +static struct pmask_line *pmask_line_new(const struct inode *inode,
> +		const char *path, gfp_t gfp)
> +{
> +	struct pmask_line *line;
> +	line = kzalloc(sizeof(struct pmask_line), gfp);
> +	if (!line)
> +		return NULL;
> +	line->inode = inode;
> +	line->path  = path;
> +	line->self_perm = PMASK_IGNORE;
> +	line->kids_perm = PMASK_IGNORE;
> +	return line;
> +}
> +
> +
> +
> +/****************
> + * PMask Policy *
> + ****************/
> +/* Return the line from @policy->privs that matches @inode */
> +static struct pmask_line *pmask_policy_get_line(
> +		const struct pmask_policy *policy, const struct inode *inode)
> +{
> +	struct pmask_line *line;
> +	list_for_each_entry(line, &policy->privs, list)
> +		if (line->inode == inode)
> +			return line;
> +	return NULL;
> +}
> +
> +/* Create and add a line to to @policy while checking for errors and updating
> + * the quota */
> +static struct pmask_line *pmask_policy_add_line(struct pmask_policy *policy,
> +		const struct inode *inode, const char *path, gfp_t gfp)
> +{
> +	struct pmask_line *line;
> +	if (atomic_read(&policy->privs_count) >= PMASK_MAX_LINES)
> +		return NULL;
> +	atomic_inc(&policy->privs_count);
> +	line = pmask_line_new(inode, path, GFP_KERNEL);
> +	if (!line)
> +		return NULL;
> +	list_add_tail(&line->list, &policy->privs);
> +	return line;
> +}
> +
> +/* Create and add a line to to @policy while checking for errors and updating
> + * the quota */
> +static struct pmask_line *pmask_policy_ensure_line(struct pmask_policy *policy,
> +		const struct inode *inode, const char *path, gfp_t gfp)
> +{
> +	struct pmask_line *line = pmask_policy_get_line(policy, inode);
> +	if (!line)
> +		line = pmask_policy_add_line(policy, inode, path, gfp);
> +	return line;
> +}
> +
> +
> +/* Delete a line form @policy and update the quota */
> +static void pmask_policy_del_line(struct pmask_policy *policy,
> +		struct pmask_line *line)
> +{
> +	list_del(&line->list);
> +	kfree(line);
> +	atomic_dec(&policy->privs_count);
> +}
> +
> +/* Do a semi-deep copy, that is, copy enough that the policies are distinct,
> + * but without duplicating conostant data such as paths and dentries */
> +static int pmask_policy_append(struct pmask_policy *from,
> +		struct pmask_policy *to, gfp_t gfp)
> +{
> +	struct pmask_line *old_line, *new_line;
> +	list_for_each_entry(old_line, &from->privs, list) {
> +		new_line = pmask_policy_add_line(to,
> +				old_line->inode, old_line->path, gfp);
> +		if (!new_line)
> +			return -ENOMEM;
> +		new_line->self_perm = old_line->self_perm;
> +		new_line->kids_perm = old_line->kids_perm;
> +	}
> +	return 0;
> +}
> +
> +/* Initialize a blank @policy */
> +static void pmask_policy_init(struct pmask_policy *policy)
> +{
> +	INIT_LIST_HEAD(&policy->privs);
> +	init_rwsem(&policy->privs_lock);
> +	atomic_set(&policy->privs_count, 0);
> +}
> +
> +void pmask_policy_clear(struct pmask_policy *policy)
> +{
> +	struct pmask_line *line, *n;
> +	list_for_each_entry_safe(line, n, &policy->privs, list)
> +		pmask_policy_del_line(policy, line);
> +}
> +
> +
> +static u16 pmask_policy_get_perm_rec(const struct pmask_policy *policy,
> +		const struct inode *inode)
> +{
> +	struct pmask_line *line;
> +
> +	/* Allow everything if we've reach the root without finding perms */
> +	/* TODO: recurse to parent filesystems */
> +	if (inode == NULL)
> +		return USHORT_MAX;
> +
> +	line = pmask_policy_get_line(policy, inode);
> +
> +	if (line && pmask_isset(line->kids_perm))
> +		return line->kids_perm;
> +
> +	/* Check parents for recursive permissions */
> +	/* TODO: Check for multiple parents */
> +	return pmask_policy_get_perm_rec(policy, inode->i_security);
> +	/*
> +	 * perm = USHORT_MAX;
> +	 * foreach parent:
> +	 * 	perm &= pmask_policy_get_perm(policy, inode->d_parent);
> +	 * return perm;
> +	 */
> +}
> +
> +u16 pmask_policy_get_perm(const struct pmask_policy *policy,
> +		const struct inode *inode)
> +{
> +	/* Stop if a permissions is found for current node */
> +	struct pmask_line *line = pmask_policy_get_line(policy, inode);
> +	if (line && pmask_isset(line->self_perm))
> +		return line->self_perm;
> +	return pmask_policy_get_perm_rec(policy, inode->i_security);
> +}
> +
> +/* We need the inode and path so we can create the line if it doesn't exist */
> +int pmask_policy_set_perm(struct pmask_policy *policy,
> +		const struct inode *inode, const char *path,
> +		u16 perm, int scope)
> +{
> +	struct pmask_line *line = pmask_policy_ensure_line(policy,
> +			inode, path, GFP_KERNEL);
> +	if (!line)
> +		return -ENOMEM;
> +	if (scope == PMASK_BOTH || scope == PMASK_SELF)
> +		line->self_perm = perm;
> +	if (scope == PMASK_BOTH || scope == PMASK_KIDS)
> +		line->kids_perm = perm;
> +	return 0;
> +}
> +
> +static void pmask_policy_merge_line(const struct inode *inode,
> +		struct pmask_policy *pl, struct pmask_policy *pr,
> +		struct pmask_line *to)
> +{
> +	struct pmask_line *ll = pmask_policy_get_line(pl, inode);
> +	struct pmask_line *lr = pmask_policy_get_line(pr, inode);
> +	if ((ll && pmask_isset(ll->self_perm)) ||
> +	    (lr && pmask_isset(lr->self_perm))) {
> +		to->self_perm  = pmask_policy_get_perm(pl, inode);
> +		to->self_perm &= pmask_policy_get_perm(pr, inode);
> +	}
> +	if ((ll && pmask_isset(ll->kids_perm)) ||
> +	    (lr && pmask_isset(lr->kids_perm))) {
> +		to->kids_perm  = pmask_policy_get_perm_rec(pl, inode);
> +		to->kids_perm &= pmask_policy_get_perm_rec(pr, inode);
> +	}
> +}
> +
> +int pmask_policy_commit(struct pmask_policy *from, struct pmask_policy *to)
> +{
> +	struct pmask_line *line, *merge_line, *n;
> +	struct pmask_policy merge;
> +	pmask_policy_init(&merge);
> +
> +	/* Merge paths from @to into merge */
> +	list_for_each_entry(line, &to->privs, list) {
> +		merge_line = pmask_policy_ensure_line(&merge,
> +				line->inode, line->path, GFP_KERNEL);
> +		if (!merge_line)
> +			goto fail;
> +		pmask_policy_merge_line(line->inode, from, to, merge_line);
> +	}
> +
> +	/* Merge paths from @from into merge */
> +	list_for_each_entry(line, &from->privs, list) {
> +		merge_line = pmask_policy_ensure_line(&merge,
> +				line->inode, line->path, GFP_KERNEL);
> +		if (!merge_line)
> +			goto fail;
> +		pmask_policy_merge_line(line->inode, from, to, merge_line);
> +	}
> +
> +	/* Free old entries, and move to ones to @to */
> +	pmask_policy_clear(to);
> +	list_for_each_entry_safe(line, n, &merge.privs, list)
> +		list_move_tail(&line->list, &to->privs);
> +
> +	return 0;
> +
> +fail:
> +	pmask_policy_clear(&merge);
> +	return -ENOMEM;
> +}
> +
> +
> +
> +/**************
> + * PMask Task *
> + **************/
> +struct pmask_task *pmask_task_new(gfp_t gfp)
> +{
> +	struct pmask_task *task;
> +	task = kzalloc(sizeof(struct pmask_task), gfp);
> +	if (!task)
> +		return NULL;
> +
> +	pmask_policy_init(&task->stage);
> +	pmask_policy_init(&task->policy);
> +
> +	return task;
> +}
> +
> +void pmask_task_free(struct pmask_task *task)
> +{
> +	pmask_policy_clear(&task->stage);
> +	pmask_policy_clear(&task->policy);
> +	kfree(task);
> +}
> +
> +struct pmask_task *pmask_task_dup(struct pmask_task *task, gfp_t gfp)
> +{
> +	struct pmask_task *copy = pmask_task_new(gfp);
> +	if (!copy)
> +		return NULL;
> +
> +	/* Copy policies */
> +	if (pmask_policy_append(&task->stage,  &copy->stage,  gfp))
> +		goto fail;
> +	if (pmask_policy_append(&task->policy, &copy->policy, gfp))
> +		goto fail;
> +
> +	return copy;
> +
> +fail:
> +	pmask_task_free(copy);
> +	return NULL;
> +}
> diff --git a/security/pmask/policy.h b/security/pmask/policy.h
> new file mode 100644
> index 0000000..bc2d526
> --- /dev/null
> +++ b/security/pmask/policy.h
> @@ -0,0 +1,230 @@
> +/**
> + * pmask/policy.h -- Privilege dropping core functionality
> + *
> + * Copyright (C) 2009 Andy Spencer <spenceal@rose-hulman.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, either version 2 of the License, or (at your option)
> + * any later version.
> + *
> + * This program 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
> + * this program.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#ifndef __PMASK_POLICY_H__
> +#define __PMASK_POLICY_H__
> +
> +/* Set to 1 when pmask is initialized */
> +extern int pmask_loaded;
> +
> +/**
> + * Terminology
> + *   mode   = `Unix' mode (u16 use for filesyste mode bits)
> + *   perm   = PMask permission bits (see below)
> + *   privs  = List of files and associated perm
> + *   policy = Privs + whatever else
> + */
> +
> +#define pmask_cur_task   ((struct pmask_task *)current_security())
> +#define pmask_cur_stage  ((struct pmask_policy *)&pmask_cur_task->stage)
> +#define pmask_cur_policy ((struct pmask_policy *)&pmask_cur_task->policy)
> +
> +
> +/*******************
> + * Permission bits *
> + *******************/
> +/* File bits */
> +#define PMASK_EXEC    (1u<<0)  /* x */
> +#define PMASK_WRITE   (1u<<1)  /* w */
> +#define PMASK_READ    (1u<<2)  /* r */
> +#define PMASK_KEEPSWP (1u<<3)  /* s (ignored) */
> +#define PMASK_SETGID  (1u<<4)  /* g */
> +#define PMASK_SETUID  (1u<<5)  /* u */
> +
> +/* Directory bits */
> +#define PMASK_WALK    (1u<<6)  /* X */
> +#define PMASK_CREATE  (1u<<7)  /* W */
> +#define PMASK_LIST    (1u<<8)  /* R */
> +#define PMASK_STICKY  (1u<<9)  /* S */
> +#define PMASK_PASSGID (1u<<10) /* G */
> +#define PMASK_PASSUID (1u<<11) /* U (ignored) */
> +
> +/* Special bits */
> +#define PMASK_IGNORE  (1u<<12) /* Permissions unset */
> +
> +/* Meta bits/masks */
> +#define PMASK_PERM_BITS 12
> +#define PMASK_MASK      0b111111111111
> +#define PMASK_FILE_MASK 0b000000111111
> +#define PMASK_DIR_MASK  0b111111000000
> +
> +/* Scope of permission */
> +enum {
> +	PMASK_SELF, /* Permissions only affect the inode */
> +	PMASK_KIDS, /* Permissions only affect the inodes children */
> +	PMASK_BOTH, /* Permissions affect inode and children */
> +};
> +
> +/* Determine if a permission is set or ignored */
> +static inline bool pmask_isset(u16 perm)
> +{
> +	return !(perm & PMASK_IGNORE);
> +}
> +
> +/* Mode conversion functions */
> +static inline bool pmask_denied(u16 perm, u16 request)
> +{
> +	return perm >= 0 && ~perm & request;
> +}
> +
> +/* Convert from a unix directory mode to a perm */
> +static inline u16 pmask_dmode_to_perm(u16 mode)
> +{
> +	return mode << 6;
> +}
> +
> +/* Convert from a unix file mode to a perm */
> +static inline u16 pmask_fmode_to_perm(u16 mode)
> +{
> +	return mode;
> +}
> +
> +/* Convert from a unix perm to a mode based on inode type */
> +static inline u16 pmask_imode_to_perm(u16 mode, struct inode *inode)
> +{
> +	return S_ISDIR(inode->i_mode) ?
> +		 pmask_dmode_to_perm(mode) :
> +		 pmask_fmode_to_perm(mode);
> +}
> +
> +/**
> + * Convert struct file->f_flags to a Unix mode
> + * <x>mode_to_perm should probably be called on the resulting mode
> + */
> +u16 pmask_flags_to_mode(unsigned int flags);
> +
> +/**
> + * Parse a permission string into a perm
> + * @str:
> + *  - Format is "rwxsguRWXSGU" (see Permission bits)
> + *  - Order does not matter
> + *  - '.' is ignored, any other character is invalid
> + *  - return -1 on invalid str
> + */
> +int pmask_str_to_perm(const char *str);
> +
> +/**
> + * Convert a perm to a string for printing
> + */
> +void pmask_perm_to_str(u16 perm, char *str);
> +
> +
> +
> +/**************
> + * PMask Line *
> + **************/
> +/**
> + * An entry in the policy
> + *
> + * Example:
> + *   /var/tmp (rw-)
> + *
> + * @list:      list_head for stroing in policy
> + * @inode:     Some point in the filesystem, topically an inode
> + * @path:      Path given when the line was created, debugging only
> + * @self_perm: Permissions given to the location
> + * @kids_perm: Permissions given to the location's kids
> + */
> +struct pmask_line {
> +	struct list_head    list;
> +	const struct inode *inode;
> +	const char         *path;
> +	u16                 self_perm;
> +	u16                 kids_perm;
> +};
> +
> +
> +
> +/****************
> + * PMask Policy *
> + ****************/
> +#define PMASK_MAX_LINES 500
> +
> +/**
> + * Contains permisisons and operations allowed for given security policy
> + *
> + * @privs:      List of pmask_lines for filesystem privilages
> + * @privs_lock: Used for printing (maybe other?)
> + *
> + * Example:
> + *   privs:
> + *     /         (r--)
> + *     /bin/     (r-x)
> + *     /tmp/     (rw-)
> + */
> +struct pmask_policy {
> +	struct list_head privs;
> +	struct rw_semaphore privs_lock;
> +	atomic_t privs_count;
> +	/* TODO: add other security things */
> +};
> +
> +/* Clear/free data from @policy */
> +void pmask_policy_clear(struct pmask_policy *policy);
> +
> +/* Recursivly lookup perm for @inode in @policy */
> +u16 pmask_policy_get_perm(const struct pmask_policy *policy,
> +		const struct inode *inode);
> +
> +/* Set perm for @inode in @policy to @perm, create new line if necessasiary */
> +int pmask_policy_set_perm(struct pmask_policy *policy,
> +		const struct inode *inode, const char *path,
> +		u16 perm, int scope);
> +
> +/* Copy lines from @from to @to making sure that no additional oeratoins are
> + * allowed in @to after the commit is performed */
> +int pmask_policy_commit(struct pmask_policy *from, struct pmask_policy *to);
> +
> +
> +
> +/**************
> + * PMask Task *
> + **************/
> +/**
> + * Contains information for a given task, including the security policy, stage,
> + * and cache information.
> + *
> + * @stage:
> + *   The modifialbe policy, privilages can be allowed or denied in the stage
> + * @policy:
> + *   The effective policy, used to determines whether an action is allowed
> + *
> + * @policy can only be modified by commiting @stage to @policy. When this is
> + * done, it is insured that no additional operations will be allowed by @policy
> + * after the commit.
> + *
> + * Example:
> + *   stage:  (see pmask_policy)
> + *   policy: (see pmask_policy)
> + */
> +struct pmask_task {
> +	struct pmask_policy stage;
> +	struct pmask_policy policy;
> +};
> +
> +/* Allocate a blank task */
> +struct pmask_task *pmask_task_new(gfp_t gfp);
> +
> +/* Free a task and data associated with it */
> +void pmask_task_free(struct pmask_task *task);
> +
> +/* Create a semi-deep copy of @task, suitable for passing to a child on exec */
> +struct pmask_task *pmask_task_dup(struct pmask_task *task, gfp_t gfp);
> +
> +#endif
>   


^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [RFC] Privilege dropping security module
  2009-09-23 22:31   ` Andy Spencer
  2009-09-23 23:03     ` Tetsuo Handa
  2009-09-24 16:37     ` David Wagner
@ 2009-10-01  7:38     ` Pavel Machek
  2009-10-01  9:15       ` Andy Spencer
  2 siblings, 1 reply; 24+ messages in thread
From: Pavel Machek @ 2009-10-01  7:38 UTC (permalink / raw)
  To: Andy Spencer; +Cc: Casey Schaufler, linux-security-module, linux-kernel

On Wed 2009-09-23 22:31:10, Andy Spencer wrote:
> > Hi Andy. Git is a wonderful tool, but if you want people to review
> > your work you need to post patches.
> 
> Thanks for letting me know, I've posted a separate message with patch.
> 
> 
> > And what do you propose as an interesting use case for dpriv?
> 
> I think the two most important things about dpriv is that it can be used
> by ordinary users and that is can create policies programmatically.
> 
> Being able to use dpriv as a non root user is pretty strait forward. For
> example, a user of a multi-user system may want to try some untrusted
> code without risking access to the rest of the system:
> 
>   $ cd ~/my_project
>   $ echo rxRX   /                > /sys/kernel/security/dpriv/stage
>   $ echo X      $HOME            > /sys/kernel/security/dpriv/stage
>   $ echo rwxRWX $HOME/my_project > /sys/kernel/security/dpriv/stage
>   $ echo commit                  > /sys/kernel/security/dpriv/control
>   $ patch < untrusted.patch
>   $ make && ./src/some_exe

Yeah, and now your ~/.ssh/identity is being uploaded to remote server.

I believe people are already sandboxing apps with selinux...

...and subterfugue certainly does what you want, using
ptrace... no kernel mods needed and should already be secure.

								Pavel 

-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [RFC] Privilege dropping security module
  2009-10-01  7:38     ` Pavel Machek
@ 2009-10-01  9:15       ` Andy Spencer
  2009-10-01 10:42         ` Pavel Machek
  0 siblings, 1 reply; 24+ messages in thread
From: Andy Spencer @ 2009-10-01  9:15 UTC (permalink / raw)
  To: Pavel Machek; +Cc: linux-security-module, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 798 bytes --]

> Yeah, and now your ~/.ssh/identity is being uploaded to remote server.

The given policy sets the home directory (including ~/.ssh/) to `X'
which does not include read access, so ~/.ssh/identity should be safe.
There are some other problems with this particular policy though, /tmp/
is still readable for example.


> I believe people are already sandboxing apps with selinux...

Yes, some people (including myself) are already using selinux, tomoyo,
smack, etc, for sandboxing. However, I think those have some
disadvantages that I'm trying to address.


> ...and subterfugue certainly does what you want, using ptrace... no
> kernel mods needed and should already be secure.

subterfugue does look interesting, but it seems like it would be pretty
slow and hasn't been unmaintained since 2001.

[-- Attachment #2: Type: application/pgp-signature, Size: 198 bytes --]

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [RFC] Privilege dropping security module
  2009-10-01  9:15       ` Andy Spencer
@ 2009-10-01 10:42         ` Pavel Machek
  0 siblings, 0 replies; 24+ messages in thread
From: Pavel Machek @ 2009-10-01 10:42 UTC (permalink / raw)
  To: Andy Spencer; +Cc: linux-security-module, linux-kernel

On Thu 2009-10-01 09:15:37, Andy Spencer wrote:
> > Yeah, and now your ~/.ssh/identity is being uploaded to remote server.
> 
> The given policy sets the home directory (including ~/.ssh/) to `X'
> which does not include read access, so ~/.ssh/identity should be safe.
> There are some other problems with this particular policy though, /tmp/
> is still readable for example.
> 
> 
> > I believe people are already sandboxing apps with selinux...
> 
> Yes, some people (including myself) are already using selinux, tomoyo,
> smack, etc, for sandboxing. However, I think those have some
> disadvantages that I'm trying to address.

Ok, I guess advantages over selinux (etc) are probably worth it for
changelog on submission...

> > ...and subterfugue certainly does what you want, using ptrace... no
> > kernel mods needed and should already be secure.
> 
> subterfugue does look interesting, but it seems like it would be pretty
> slow and hasn't been unmaintained since 2001.

It is userland code, it should not have rotted that much.
									Pavel


-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

^ permalink raw reply	[flat|nested] 24+ messages in thread

end of thread, other threads:[~2009-10-01 10:42 UTC | newest]

Thread overview: 24+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2009-09-23  0:56 [RFC] Privilege dropping security module Andy Spencer
2009-09-23 20:46 ` Casey Schaufler
2009-09-23 22:31   ` Andy Spencer
2009-09-23 23:03     ` Tetsuo Handa
2009-09-24 16:37     ` David Wagner
2009-09-25  7:22       ` Andy Spencer
2009-09-25 20:48         ` David Wagner
2009-09-26 21:09           ` Andy Spencer
2009-09-27  0:28             ` David Wagner
2009-10-01  7:38     ` Pavel Machek
2009-10-01  9:15       ` Andy Spencer
2009-10-01 10:42         ` Pavel Machek
2009-09-23 21:31 ` [RFC][PATCH] " Andy Spencer
2009-09-24 16:25   ` Casey Schaufler
2009-09-25 10:06     ` Andy Spencer
2009-09-25 16:23       ` Casey Schaufler
2009-09-26 21:35         ` Andy Spencer
2009-09-28  5:38           ` Rob Meijer
2009-09-25 21:00       ` David Wagner
2009-09-29  7:36         ` Andy Spencer
2009-09-29  7:10 ` [RFC][PATCH] Permission masking security module (was dpriv) Andy Spencer
2009-09-29 17:44   ` Greg KH
2009-09-30  0:18     ` Andy Spencer
2009-10-01  2:33   ` Casey Schaufler

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.