lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Date:	Thu, 24 Sep 2009 09:25:17 -0700
From:	Casey Schaufler <casey@...aufler-ca.com>
To:	andy753421@...il.com
CC:	linux-security-module@...r.kernel.org, linux-kernel@...r.kernel.org
Subject: Re: [RFC][PATCH] Privilege dropping security module

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@...il.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@...e-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@...e-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@...e-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@...e-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-)
>   

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ