2004-11-04 21:55:23

by Serge E. Hallyn

[permalink] [raw]
Subject: [RFC] [PATCH] [0/6] LSM Stacking

The following set of patches add support required to stack most
LSMs. The most important patch is the first, which provides a
method for more than one LSM to annotate information to kernel
objects. LSM's known to use the LSM fields include selinux, bsdjail,
seclvl, and digsig. Without this patch (or something like it),
none of these modules can be used together.

The rest of the patches add stacking support to the existing LSMs.
Another set of three patches will be sent containing a stackable
version of bsdjail.

I have run some performance tests on a Fedora Core Devel system
under three configurations:

1. A 2.6.10-rc1-bk10 system with capabilities and SELinux compiled
into the kernel as it was in the default Fedora kernel.

2. A 2.6.10-rc1-bk10 system with the stacking patches, and capabilities
and SELinux compiled into the kernel under the stacker LSM. Other
than stacker being compiled in and the size of the LSM void* array
being set to 4, the exact same .config was used.

3. The same kernel as in (2), but with bsdjail and seclvl also stacked.

On each of these configurations, I ran unixbench twice, and compiled
a kernel twice (with the same .config, and all files in the cached
each time).

The kernel compilation results are as follows:

No stacking (1) Stacking (2) More Stacking (3)
Run 1 real 9m51.647s real 9m48.045s real 9m53.292s
user 8m28.637s user 8m29.108s user 8m33.319s
sys 1m13.900s sys 1m14.993s sys 1m15.377s

Run 2 real 9m48.154s real 9m53.369s real 9m53.292s
user 8m28.983s user 8m29.101s user 8m34.407s
sys 1m13.981s sys 1m15.307s sys 1m15.611s

Run 3 real 9m51.105s real 9m51.840s real 9m58.840s
user 8m28.894s user 8m29.192s user 8m33.538s
sys 1m14.183s sys 1m15.345s sys 1m16.146s

Unixbench summaries are as follows. (I can send the full output if
anyone asks)

No stacking (1) Stacking (2) More Stacking (3)
Run 1 651.5 647.1 634.3
Run 2 648.2 642.8 632.7

-serge



2004-11-04 21:59:45

by Serge E. Hallyn

[permalink] [raw]
Subject: Re: [RFC] [PATCH] [1/6] LSM Stacking: Replace LSM void* with arrays

The attached patch replaced the LSM security fields on kernel
objects with an array of pointers, so that more than 1 LSM
can annotate information on kernel objects.

-serge

Signed-off-by: Serge E. Hallyn <[email protected]>

Index: linux-2.6.10-rc1-bk12-stack/fs/exec.c
===================================================================
--- linux-2.6.10-rc1-bk12-stack.orig/fs/exec.c 2004-11-02
19:56:00.004087472 -0600
+++ linux-2.6.10-rc1-bk12-stack/fs/exec.c 2004-11-02 19:57:14.722728512
-0600
@@ -1164,8 +1164,7 @@
__free_page(page);
}

- if (bprm->security)
- security_bprm_free(bprm);
+ security_bprm_free(bprm);

out_mm:
if (bprm->mm)
Index: linux-2.6.10-rc1-bk12-stack/fs/inode.c
===================================================================
--- linux-2.6.10-rc1-bk12-stack.orig/fs/inode.c 2004-11-02
19:55:49.602668728 -0600
+++ linux-2.6.10-rc1-bk12-stack/fs/inode.c 2004-11-02 19:57:14.723728360
-0600
@@ -113,6 +113,7 @@

if (inode) {
struct address_space * const mapping = &inode->i_data;
+ int i;

inode->i_sb = sb;
inode->i_blkbits = sb->s_blocksize_bits;
@@ -134,7 +135,8 @@
inode->i_bdev = NULL;
inode->i_cdev = NULL;
inode->i_rdev = 0;
- inode->i_security = NULL;
+ for (i=0; i<CONFIG_NUM_LSMS; i++)
+ inode->i_security[i] = NULL;
inode->dirtied_when = 0;
if (security_inode_alloc(inode)) {
if (inode->i_sb->s_op->destroy_inode)
Index: linux-2.6.10-rc1-bk12-stack/include/linux/binfmts.h
===================================================================
--- linux-2.6.10-rc1-bk12-stack.orig/include/linux/binfmts.h 2004-10-18
16:54:30.000000000 -0500
+++ linux-2.6.10-rc1-bk12-stack/include/linux/binfmts.h 2004-11-02
19:57:14.737726232 -0600
@@ -29,7 +29,7 @@
struct file * file;
int e_uid, e_gid;
kernel_cap_t cap_inheritable, cap_permitted, cap_effective;
- void *security;
+ void *security[CONFIG_NUM_LSMS];
int argc, envc;
char * filename; /* Name of binary as seen by procps */
char * interp; /* Name of the binary really executed. Most
Index: linux-2.6.10-rc1-bk12-stack/include/linux/fs.h
===================================================================
--- linux-2.6.10-rc1-bk12-stack.orig/include/linux/fs.h 2004-11-02
19:56:00.579999920 -0600
+++ linux-2.6.10-rc1-bk12-stack/include/linux/fs.h 2004-11-02
19:57:14.739725928 -0600
@@ -479,7 +479,7 @@
unsigned int i_flags;

atomic_t i_writecount;
- void *i_security;
+ void *i_security[CONFIG_NUM_LSMS];
union {
void *generic_ip;
} u;
@@ -553,7 +553,7 @@
rwlock_t lock; /* protects pid, uid, euid fields */
int pid; /* pid or -pgrp where SIGIO should be sent */
uid_t uid, euid; /* uid/euid of process setting the owner */
- void *security;
+ void *security[CONFIG_NUM_LSMS];
int signum; /* posix.1b rt signal to be delivered on IO */
};

@@ -589,7 +589,7 @@
struct file_ra_state f_ra;

unsigned long f_version;
- void *f_security;
+ void *f_security[CONFIG_NUM_LSMS];

/* needed for tty driver, and maybe others */
void *private_data;
@@ -773,7 +773,7 @@
int s_syncing;
int s_need_sync_fs;
atomic_t s_active;
- void *s_security;
+ void *s_security[CONFIG_NUM_LSMS];
struct xattr_handler **s_xattr;

struct list_head s_dirty; /* dirty inodes */
Index: linux-2.6.10-rc1-bk12-stack/include/linux/ipc.h
===================================================================
--- linux-2.6.10-rc1-bk12-stack.orig/include/linux/ipc.h 2004-10-18
16:53:05.000000000 -0500
+++ linux-2.6.10-rc1-bk12-stack/include/linux/ipc.h 2004-11-02
19:57:14.745725016 -0600
@@ -65,7 +65,7 @@
gid_t cgid;
mode_t mode;
unsigned long seq;
- void *security;
+ void *security[CONFIG_NUM_LSMS];
};

#endif /* __KERNEL__ */
Index: linux-2.6.10-rc1-bk12-stack/include/linux/msg.h
===================================================================
--- linux-2.6.10-rc1-bk12-stack.orig/include/linux/msg.h 2004-10-18
16:54:31.000000000 -0500
+++ linux-2.6.10-rc1-bk12-stack/include/linux/msg.h 2004-11-02
19:57:14.745725016 -0600
@@ -70,7 +70,7 @@
long m_type;
int m_ts; /* message text size */
struct msg_msgseg* next;
- void *security;
+ void *security[CONFIG_NUM_LSMS];
/* the actual message follows immediately */
};

Index: linux-2.6.10-rc1-bk12-stack/include/linux/sched.h
===================================================================
--- linux-2.6.10-rc1-bk12-stack.orig/include/linux/sched.h 2004-11-02
19:56:00.667986544 -0600
+++ linux-2.6.10-rc1-bk12-stack/include/linux/sched.h 2004-11-02
19:57:14.746724864 -0600
@@ -627,7 +627,7 @@
void *notifier_data;
sigset_t *notifier_mask;

- void *security;
+ void *security[CONFIG_NUM_LSMS];
struct audit_context *audit_context;

/* Thread group tracking */
Index: linux-2.6.10-rc1-bk12-stack/include/net/sock.h
===================================================================
--- linux-2.6.10-rc1-bk12-stack.orig/include/net/sock.h 2004-11-02
19:56:00.764971800 -0600
+++ linux-2.6.10-rc1-bk12-stack/include/net/sock.h 2004-11-02
19:57:14.747724712 -0600
@@ -254,7 +254,7 @@
__u32 sk_sndmsg_off;
struct sk_buff *sk_send_head;
int sk_write_pending;
- void *sk_security;
+ void *sk_security[CONFIG_NUM_LSMS];
__u8 sk_queue_shrunk;
/* three bytes hole, try to pack */
void (*sk_state_change)(struct sock *sk);
Index: linux-2.6.10-rc1-bk12-stack/ipc/msg.c
===================================================================
--- linux-2.6.10-rc1-bk12-stack.orig/ipc/msg.c 2004-11-02
19:56:00.805965568 -0600
+++ linux-2.6.10-rc1-bk12-stack/ipc/msg.c 2004-11-02 19:57:14.747724712
-0600
@@ -90,6 +90,7 @@
int id;
int retval;
struct msg_queue *msq;
+ int i;

msq = ipc_rcu_alloc(sizeof(*msq));
if (!msq)
@@ -98,7 +99,9 @@
msq->q_perm.mode = (msgflg & S_IRWXUGO);
msq->q_perm.key = key;

- msq->q_perm.security = NULL;
+ for (i=0; i<CONFIG_NUM_LSMS; i++)
+ msq->q_perm.security[i] = NULL;
+
retval = security_msg_queue_alloc(msq);
if (retval) {
ipc_rcu_putref(msq);
Index: linux-2.6.10-rc1-bk12-stack/ipc/msgutil.c
===================================================================
--- linux-2.6.10-rc1-bk12-stack.orig/ipc/msgutil.c 2004-10-18
16:54:07.000000000 -0500
+++ linux-2.6.10-rc1-bk12-stack/ipc/msgutil.c 2004-11-02
19:57:14.757723192 -0600
@@ -31,6 +31,7 @@
struct msg_msgseg **pseg;
int err;
int alen;
+ int i;

alen = len;
if (alen > DATALEN_MSG)
@@ -41,7 +42,8 @@
return ERR_PTR(-ENOMEM);

msg->next = NULL;
- msg->security = NULL;
+ for (i=0; i<CONFIG_NUM_LSMS; i++)
+ msg->security[i] = NULL;

if (copy_from_user(msg + 1, src, alen)) {
err = -EFAULT;
Index: linux-2.6.10-rc1-bk12-stack/ipc/sem.c
===================================================================
--- linux-2.6.10-rc1-bk12-stack.orig/ipc/sem.c 2004-11-02
19:56:00.805965568 -0600
+++ linux-2.6.10-rc1-bk12-stack/ipc/sem.c 2004-11-02 19:57:14.758723040
-0600
@@ -161,6 +161,7 @@
int retval;
struct sem_array *sma;
int size;
+ int i;

if (!nsems)
return -EINVAL;
@@ -177,7 +178,9 @@
sma->sem_perm.mode = (semflg & S_IRWXUGO);
sma->sem_perm.key = key;

- sma->sem_perm.security = NULL;
+ for (i=0; i<CONFIG_NUM_LSMS; i++)
+ sma->sem_perm.security[i] = NULL;
+
retval = security_sem_alloc(sma);
if (retval) {
ipc_rcu_putref(sma);
Index: linux-2.6.10-rc1-bk12-stack/ipc/shm.c
===================================================================
--- linux-2.6.10-rc1-bk12-stack.orig/ipc/shm.c 2004-11-02
19:55:51.451387680 -0600
+++ linux-2.6.10-rc1-bk12-stack/ipc/shm.c 2004-11-02 19:57:14.758723040
-0600
@@ -181,6 +181,7 @@
struct file * file;
char name[13];
int id;
+ int i;

if (size < SHMMIN || size > shm_ctlmax)
return -EINVAL;
@@ -196,7 +197,9 @@
shp->shm_flags = (shmflg & S_IRWXUGO);
shp->mlock_user = NULL;

- shp->shm_perm.security = NULL;
+ for (i=0; i<CONFIG_NUM_LSMS; i++)
+ shp->shm_perm.security[i] = NULL;
+
error = security_shm_alloc(shp);
if (error) {
ipc_rcu_putref(shp);
Index: linux-2.6.10-rc1-bk12-stack/kernel/fork.c
===================================================================
--- linux-2.6.10-rc1-bk12-stack.orig/kernel/fork.c 2004-11-02
19:56:00.815964048 -0600
+++ linux-2.6.10-rc1-bk12-stack/kernel/fork.c 2004-11-02
19:57:14.759722888 -0600
@@ -789,6 +789,7 @@
{
int retval;
struct task_struct *p = NULL;
+ int i;

if ((clone_flags & (CLONE_NEWNS|CLONE_FS)) == (CLONE_NEWNS|CLONE_FS))
return ERR_PTR(-EINVAL);
@@ -871,7 +872,8 @@
p->utime = p->stime = 0;
p->lock_depth = -1; /* -1 = no lock */
do_posix_clock_monotonic_gettime(&p->start_time);
- p->security = NULL;
+ for (i=0; i<CONFIG_NUM_LSMS; i++)
+ p->security[i] = NULL;
p->io_context = NULL;
p->io_wait = NULL;
p->audit_context = NULL;
Index: linux-2.6.10-rc1-bk12-stack/security/Kconfig
===================================================================
--- linux-2.6.10-rc1-bk12-stack.orig/security/Kconfig 2004-11-02
19:55:53.582063768 -0600
+++ linux-2.6.10-rc1-bk12-stack/security/Kconfig 2004-11-02
19:57:14.759722888 -0600
@@ -44,6 +44,17 @@

If you are unsure how to answer this question, answer N.

+config NUM_LSMS
+ int "Number of security pointers to allocate"
+ depends on SECURITY
+ default "4"
+ help
+ Each LSM which annotates information with kernel objects
+ must have its own annotation entry. This option specifies
+ the number of required entries. It is safe to enter the
+ number of LSM's which you will load.
+
+
config SECURITY_NETWORK
bool "Socket and Networking Security Hooks"
depends on SECURITY


2004-11-04 22:13:48

by Serge E. Hallyn

[permalink] [raw]
Subject: Re: [RFC] [PATCH] [3/6] LSM Stacking: capability LSM stacking support

This patch adds stacking support to the POSIX Capability LSM.

Signed-off-by: Serge E. Hallyn <[email protected]>

Index: linux-2.6.10-rc1-bk12-stack/security/capability.c
===================================================================
--- linux-2.6.10-rc1-bk12-stack.orig/security/capability.c 2004-11-02
19:55:53.583063616 -0600
+++ linux-2.6.10-rc1-bk12-stack/security/capability.c 2004-11-02
20:10:16.850826968 -0600
@@ -60,6 +60,8 @@

static int __init capability_init (void)
{
+ int rc;
+
if (capability_disable) {
printk(KERN_INFO "Capabilities disabled at initialization\n");
return 0;
@@ -67,7 +69,8 @@
/* register ourselves with the security framework */
if (register_security (&capability_ops)) {
/* try registering with primary module */
- if (mod_reg_security (MY_NAME, &capability_ops)) {
+ rc = mod_reg_security (MY_NAME, &capability_ops);
+ if (rc < 0) {
printk (KERN_INFO "Failure registering capabilities "
"with primary security module.\n");
return -EINVAL;


2004-11-04 22:13:49

by Serge E. Hallyn

[permalink] [raw]
Subject: Re: [RFC] [PATCH] [2/6] LSM Stacking: Add stacker LSM

This patch adds the stacker module, still required in order to
stack several LSMs. In order to use this with SELinux, it must be
compiled into the kernel. Otherwise, it can be loaded as a module.

Signed-off-by: Serge E. Hallyn <[email protected]>

Index: linux-2.6.10-rc1-bk12-stack/security/Kconfig
===================================================================
--- linux-2.6.10-rc1-bk12-stack.orig/security/Kconfig 2004-11-02
19:57:14.759722888 -0600
+++ linux-2.6.10-rc1-bk12-stack/security/Kconfig 2004-11-02
20:05:25.515116728 -0600
@@ -97,5 +97,11 @@

source security/selinux/Kconfig

+config SECURITY_STACKER
+ boolean "LSM Stacking"
+ depends on SECURITY
+ help
+ Stack multiple LSMs.
+
endmenu

Index: linux-2.6.10-rc1-bk12-stack/security/Makefile
===================================================================
--- linux-2.6.10-rc1-bk12-stack.orig/security/Makefile 2004-11-02
19:55:53.582063768 -0600
+++ linux-2.6.10-rc1-bk12-stack/security/Makefile 2004-11-02
20:05:25.514116880 -0600
@@ -2,6 +2,7 @@
# Makefile for the kernel security code
#

+obj-$(CONFIG_SECURITY_STACKER) += stacker.o
obj-$(CONFIG_KEYS) += keys/
subdir-$(CONFIG_SECURITY_SELINUX) += selinux

Index: linux-2.6.10-rc1-bk12-stack/security/stacker.c
===================================================================
--- linux-2.6.10-rc1-bk12-stack.orig/security/stacker.c 2004-11-02
13:46:30.056668176 -0600
+++ linux-2.6.10-rc1-bk12-stack/security/stacker.c 2004-11-02
20:05:41.007761488 -0600
@@ -0,0 +1,1605 @@
+/* "Stacker" Linux security module (LSM).
+ *
+ * New version, being hacked by Serge Hallyn. Serge, describe the
+ * changes. (hooks update, sysfs, security blob)
+ *
+ * version 2002-09-04.
+ * Load this module first as the primary LSM module,
+ * and you can then stack (load) multiple additional LSM modules.
+ *
+ * Copyright (C) 2002 David A. Wheeler <[email protected]>.
+ * 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.
+ *
+ * Below is administrator documentation, LSM module authoring
documentation,
+ * design documentation, and the code itself.
+ */
+
+/*
+ * Several of the ->security fields have no alloc/free pairs. Huh?
+ * ipc and fown, in particular.
+ */
+
+#include "stacker.h"
+
+short sysfsfiles_registered;
+
+int num_stacked_modules;
+
+#ifdef CONSERVATIVE_STACKER_LOCKING
+
+/* Turn on "CONSERVATIVE_STACKER_LOCKING" to use an extremely
conservative,
+ * safe, and slow locking technique in the stacker.
+ *
+ * You MUST use this technique if you are dealing with a
+ * (nasty and rare!) computer architecture where aligned pointer writes
+ * are not atomic.
+ *
+ * This approach uses a multiple reader - one writer semaphor;
+ * whenever using/changing stacker state (in particular the linked list
+ * of modules, etc.), grab this lock. Note that writing is VERY rare.
+ * This could switch to big-reader locks (brlocks), but using them
requires
+ * an allocation of an id. Besides, if you want speed, the default
+ * approach is even faster. In the future an RCU lock might be
appropriate,
+ * if they're ever added to Linux, but again, why bother since the
+ * default implementation is faster still. For more info on Linux
locking,
+ * see: http://www.linuxjournal.com/article.php?sid=5833
+ *
+ */
+struct rwlock_t stacker_rwsem = RW_LOCK_UNLOCKED;
+
+#define INIT_STACKER_LOCKING
+#define LOCK_STACKER_FOR_READING read_lock(&stacker_rwsem)
+#define UNLOCK_STACKER_FOR_READING read_unlock(&stacker_rwsem)
+#define LOCK_STACKER_FOR_WRITING write_lock(&stacker_rwsem)
+#define UNLOCK_STACKER_FOR_WRITING write_unlock(&stacker_rwsem)
+
+#else /* ! CONSERVATIVE_STACKER_LOCKING */
+
+/* The default: non-conservative stacker locking.
+ * This approach uses aligned pointer writes (presuming they are
atomic)
+ * to completely avoid the need for read locks.
+ */
+
+struct semaphore stacker_sem;
+
+/* TODO: I'm using semaphore and down_interruptable. Are there
+ any hooks that can be called inside an interrupt where it is NOT
+ possible to sleep on a semaphore?
+*/
+
+#define INIT_STACKER_LOCKING sema_init(&stacker_sem, 1)
+#define LOCK_STACKER_FOR_READING
+#define UNLOCK_STACKER_FOR_READING
+#define LOCK_STACKER_FOR_WRITING down_interruptible(&stacker_sem)
+#define UNLOCK_STACKER_FOR_WRITING up(&stacker_sem)
+#endif /* CONSERVATIVE_STACKER_LOCKING */
+
+
+/* Flag to keep track of how we were registered */
+static int short_circuit_restrictive; /* = 0; if 1, short-circuit
restrictive
+ hooks */
+static int short_circuit_capable; /* = 0; if 1, short-circuit
capable() */
+
+static int forbid_stacker_register; /* = 0; if 1, can't register */
+
+
+/* The set of LSM modules stacked by this "stacker" module
+ is stored as a singly linked list of module_entries whose head is
+ pointed to by stacked_modules. It's initially NULL (an empty list).
*/
+struct module_entry *stacked_modules;
+
+
+/* penultimate_stacked_module points to the next-to-last
+ * entry in the stacked list if there are 2+ entries, else it's NULL;
+ * this pointer is used to make modifying the end a constant time
operation.
+ *
+ * Here are a few invariants that may help you understand this:
+ * len(list)==0: stacked_modules==penultimate_stacked_module==NULL.
+ * len(list)==1: stacked_modules==x, x->next == NULL,
+ * penultimate_stacked_module==NULL.
+ * len(list)==2: stacked_modules==penultimate_stacked_module==x,
+ * x->next == y, y->next == NULL.
+ * len(list)==3: stacked_modules==x, x->next == y,
+ * y->next == z, z->next == NULL,
+ * penultimate_stacked_module==y.
+ * */
+static struct module_entry *penultimate_stacked_module;
+
+
+/* Maximum number of characters in a stacked LSM module name */
+#define MAX_MODULE_NAME_LEN 128
+
+/*
+ * find_lsm_module_by_name:
+ * Find a module by name. Used only by getprocattr and setprocattr
+ */
+struct module_entry *find_lsm_module_by_name(char *s, char *e)
+{
+ struct module_entry *m;
+
+ for (m=stacked_modules; m; m = m->next) {
+ if (m->namelen == e-s && strncmp(m->module_name, s, e-s)==0)
+ break;
+ }
+ return m;
+}
+
+/* Walk through the linked list of modules in stacked_modules
+ * and ask each (in turn) for their results, then return the
+ * results. If more than one module reports an error, return
+ * the FIRST error code. Note that this ALWAYS calls ALL modules,
since
+ * some modules may change state when called.
+ * This is wrapped in do { .. } while(0), see
+ * http://www.kernelnewbies.org/faq/index.php3#dowhile for
+ * why this is a good idea. */
+
+#define RETURN_ERROR_IF_ANY_ERROR(BASE_FUNC,FUNC_WITH_ARGS) do { \
+ int final_result = 0; \
+ int result; \
+ struct module_entry *m; \
+ LOCK_STACKER_FOR_READING; \
+ for (m = stacked_modules; m; m = m->next) { \
+ if (!m->module_operations.BASE_FUNC) \
+ continue; \
+ result = m->module_operations.FUNC_WITH_ARGS; \
+ if (result && !final_result) { \
+ final_result = result; \
+ if (short_circuit_restrictive) break; \
+ } \
+ } \
+ UNLOCK_STACKER_FOR_READING; \
+ return final_result; } while (0)
+
+
+
+/* Call all modules in stacked_modules' routine */
+#define CALL_ALL(BASE_FUNC,FUNC_WITH_ARGS) do { \
+ struct module_entry *m; \
+ LOCK_STACKER_FOR_READING; \
+ for (m = stacked_modules; m; m = m->next) { \
+ if (m->module_operations.BASE_FUNC) \
+ m->module_operations.FUNC_WITH_ARGS; \
+ } \
+ UNLOCK_STACKER_FOR_READING; } while (0)
+
+
+static void add_module_entry(struct module_entry *new_module_entry)
+{
+ printk(KERN_INFO "add_module_entry: starting\n");
+ if (!stacked_modules)
+ /* no module loaded yet */
+ stacked_modules = new_module_entry;
+ else {
+ if (!penultimate_stacked_module)
+ /* EXACTLY 1 */
+ penultimate_stacked_module = stacked_modules;
+ else
+ /* > 1 module already loaded */
+ penultimate_stacked_module = penultimate_stacked_module->next;
+
+ penultimate_stacked_module->next = new_module_entry;
+ }
+ printk(KERN_INFO "add_module_entry: done\n");
+}
+
+
+static int stacker_ptrace (struct task_struct *parent, struct
task_struct *child)
+{
+ RETURN_ERROR_IF_ANY_ERROR(ptrace,ptrace(parent, child));
+}
+
+static int stacker_capget (struct task_struct *target, kernel_cap_t *
effective,
+ kernel_cap_t * inheritable, kernel_cap_t * permitted)
+{
+ RETURN_ERROR_IF_ANY_ERROR(capget,capget(target, effective,
inheritable, permitted));
+}
+
+static int stacker_capset_check (struct task_struct *target,
+ kernel_cap_t * effective,
+ kernel_cap_t * inheritable,
+ kernel_cap_t * permitted)
+{
+ RETURN_ERROR_IF_ANY_ERROR(capset_check,capset_check(target, effective,
inheritable, permitted));
+}
+
+static void stacker_capset_set (struct task_struct *target,
+ kernel_cap_t * effective,
+ kernel_cap_t * inheritable,
+ kernel_cap_t * permitted)
+{
+ CALL_ALL(capset_set,capset_set(target, effective, inheritable,
permitted));
+}
+
+static int stacker_acct (struct file *file)
+{
+ RETURN_ERROR_IF_ANY_ERROR(acct,acct(file));
+}
+
+static int stacker_capable (struct task_struct *tsk, int cap)
+{
+ /* This is an AUTHORITATIVE hook, so it needs to be
+ * handled differently than the normal "restrictive" hooks.
+ * Instead of returning a failure if any module fails,
+ * we need to return a success if ANY module succeeds (returns 0).
+ *
+ * Unless short_circuit_capable is true, we'll call all of the
+ * modules (even if an earlier one replies with a success).
+ *
+ * Returns 0 if allowed, -EPERM if not.
+ * If a stacked module returns -EPIPE, _no_ later stacked modules
+ * are consulted and -EPERM is returned.
+ * If a stacked module returns -EACCES, it undoes any previous
+ * permission (and resets the interim return value to be -EPERM);
+ * only a still-later module can return 0.
+ */
+
+ int final_result = -EPERM;
+ int result;
+ struct module_entry *m;
+
+/* If no modules are stacked, then we want to return 0 if uid==0 */
+ if (!stacked_modules) {
+ if (cap_is_fs_cap (cap) ? tsk->fsuid == 0 : tsk->euid == 0)
+ /* capability granted */
+ return 0;
+
+ /* capability denied */
+ return -EPERM;
+ }
+
+ LOCK_STACKER_FOR_READING;
+ for (m = stacked_modules; m; m = m->next) {
+ result = (m->module_operations).capable(tsk,cap);
+ if (result == -EPIPE) {
+ final_result = -EPERM;
+ break;
+ } else if (result == -EACCES) {
+ final_result = -EPERM;
+ } else if (!result) {
+ final_result = 0;
+ if (short_circuit_capable) break;
+ }
+ }
+ UNLOCK_STACKER_FOR_READING;
+
+ return final_result;
+}
+
+
+static int stacker_sysctl (struct ctl_table * table, int op)
+{
+ RETURN_ERROR_IF_ANY_ERROR(sysctl,sysctl(table, op));
+}
+
+static int stacker_quotactl (int cmds, int type, int id, struct
super_block *sb)
+{
+ RETURN_ERROR_IF_ANY_ERROR(quotactl,quotactl(cmds,type,id,sb));
+}
+
+static int stacker_quota_on (struct file *f)
+{
+ RETURN_ERROR_IF_ANY_ERROR(quota_on,quota_on(f));
+}
+
+static int stacker_syslog (int type)
+{
+ RETURN_ERROR_IF_ANY_ERROR(syslog,syslog(type));
+}
+
+static int stacker_vm_enough_memory(long pages)
+{
+ RETURN_ERROR_IF_ANY_ERROR(vm_enough_memory,vm_enough_memory(pages));
+}
+
+static int stacker_netlink_send (struct sock *sk, struct sk_buff *skb)
+{
+ /* NOTE: The dummy module does this:
+ if (current->euid == 0)
+ cap_raise (NETLINK_CB (skb).eff_cap, CAP_NET_ADMIN);
+ else
+ NETLINK_CB (skb).eff_cap = 0;
+ * if this would be a problem with your module, then tell
+ * your administrators what to do. */
+
+ RETURN_ERROR_IF_ANY_ERROR(netlink_send,netlink_send(sk,skb));
+}
+
+
+static int stacker_netlink_recv (struct sk_buff *skb)
+{
+ RETURN_ERROR_IF_ANY_ERROR(netlink_recv,netlink_recv(skb));
+}
+
+static int stacker_bprm_alloc_security (struct linux_binprm *bprm)
+{
+ RETURN_ERROR_IF_ANY_ERROR(bprm_alloc_security,bprm_alloc_security(bprm));
+}
+
+static void stacker_bprm_free_security (struct linux_binprm *bprm)
+{
+ CALL_ALL(bprm_free_security,bprm_free_security(bprm));
+}
+
+static void stacker_bprm_apply_creds (struct linux_binprm *bprm, int
unsafe)
+{
+ CALL_ALL(bprm_apply_creds,bprm_apply_creds(bprm, unsafe));
+}
+
+static int stacker_bprm_set_security (struct linux_binprm *bprm)
+{
+ RETURN_ERROR_IF_ANY_ERROR(bprm_set_security,bprm_set_security(bprm));
+}
+
+static int stacker_bprm_check_security (struct linux_binprm *bprm)
+{
+ RETURN_ERROR_IF_ANY_ERROR(bprm_check_security,bprm_check_security(bprm));
+}
+
+static int stacker_bprm_secureexec (struct linux_binprm *bprm)
+{
+ RETURN_ERROR_IF_ANY_ERROR(bprm_secureexec,bprm_secureexec(bprm));
+}
+
+static int stacker_sb_alloc_security (struct super_block *sb)
+{
+ RETURN_ERROR_IF_ANY_ERROR(sb_alloc_security,sb_alloc_security(sb));
+}
+
+static void stacker_sb_free_security (struct super_block *sb)
+{
+ CALL_ALL(sb_free_security,sb_free_security(sb));
+}
+
+static int stacker_sb_copy_data (struct file_system_type *type,
+ void *orig, void *copy)
+{
+ RETURN_ERROR_IF_ANY_ERROR(sb_copy_data,sb_copy_data(type,orig,copy));
+}
+
+static int stacker_sb_kern_mount (struct super_block *sb, void *data)
+{
+ RETURN_ERROR_IF_ANY_ERROR(sb_kern_mount,sb_kern_mount(sb, data));
+}
+
+static int stacker_sb_statfs (struct super_block *sb)
+{
+ RETURN_ERROR_IF_ANY_ERROR(sb_statfs,sb_statfs(sb));
+}
+
+static int stacker_mount (char *dev_name, struct nameidata *nd, char
*type,
+ unsigned long flags, void *data)
+{
+ RETURN_ERROR_IF_ANY_ERROR(sb_mount,sb_mount(dev_name, nd, type, flags,
data));
+}
+
+static int stacker_check_sb (struct vfsmount *mnt, struct nameidata
*nd)
+{
+ RETURN_ERROR_IF_ANY_ERROR(sb_check_sb,sb_check_sb(mnt, nd));
+}
+
+static int stacker_umount (struct vfsmount *mnt, int flags)
+{
+ RETURN_ERROR_IF_ANY_ERROR(sb_umount,sb_umount(mnt, flags));
+}
+
+static void stacker_umount_close (struct vfsmount *mnt)
+{
+ CALL_ALL(sb_umount_close,sb_umount_close(mnt));
+}
+
+static void stacker_umount_busy (struct vfsmount *mnt)
+{
+ CALL_ALL(sb_umount_busy,sb_umount_busy(mnt));
+}
+
+static void stacker_post_remount (struct vfsmount *mnt, unsigned long
flags,
+ void *data)
+{
+ CALL_ALL(sb_post_remount,sb_post_remount(mnt, flags, data));
+}
+
+
+static void stacker_post_mountroot (void)
+{
+ CALL_ALL(sb_post_mountroot,sb_post_mountroot());
+}
+
+static void stacker_post_addmount (struct vfsmount *mnt, struct
nameidata *nd)
+{
+ CALL_ALL(sb_post_addmount,sb_post_addmount(mnt, nd));
+}
+
+static int stacker_pivotroot (struct nameidata *old_nd, struct
nameidata *new_nd)
+{
+ RETURN_ERROR_IF_ANY_ERROR(sb_pivotroot,sb_pivotroot(old_nd, new_nd));
+}
+
+static void stacker_post_pivotroot (struct nameidata *old_nd, struct
nameidata *new_nd)
+{
+ CALL_ALL(sb_post_pivotroot,sb_post_pivotroot(old_nd, new_nd));
+}
+
+static int stacker_inode_alloc_security (struct inode *inode)
+{
+ RETURN_ERROR_IF_ANY_ERROR(inode_alloc_security,inode_alloc_security(inode));
+}
+
+static void stacker_inode_free_security (struct inode *inode)
+{
+ CALL_ALL(inode_free_security,inode_free_security(inode));
+}
+
+static int stacker_inode_create (struct inode *inode, struct dentry
*dentry,
+ int mask)
+{
+ RETURN_ERROR_IF_ANY_ERROR(inode_create,inode_create(inode, dentry,
mask));
+}
+
+static void stacker_inode_post_create (struct inode *inode,
+ struct dentry *dentry, int mask)
+{
+ CALL_ALL(inode_post_create,inode_post_create(inode, dentry, mask));
+}
+
+static int stacker_inode_link (struct dentry *old_dentry, struct inode
*inode,
+ struct dentry *new_dentry)
+{
+ RETURN_ERROR_IF_ANY_ERROR(inode_link,inode_link(old_dentry, inode,
new_dentry));
+}
+
+static void stacker_inode_post_link (struct dentry *old_dentry,
+ struct inode *inode,
+ struct dentry *new_dentry)
+{
+ CALL_ALL(inode_post_link,inode_post_link(old_dentry, inode,
new_dentry));
+}
+
+static int stacker_inode_unlink (struct inode *inode, struct dentry
*dentry)
+{
+ RETURN_ERROR_IF_ANY_ERROR(inode_unlink,inode_unlink(inode, dentry));
+}
+
+static int stacker_inode_symlink (struct inode *inode, struct dentry
*dentry,
+ const char *name)
+{
+ RETURN_ERROR_IF_ANY_ERROR(inode_symlink,inode_symlink(inode, dentry,
name));
+}
+
+static void stacker_inode_post_symlink (struct inode *inode,
+ struct dentry *dentry, const char *name)
+{
+ CALL_ALL(inode_post_symlink,inode_post_symlink(inode, dentry, name));
+}
+
+static int stacker_inode_mkdir (struct inode *inode, struct dentry
*dentry,
+ int mask)
+{
+ RETURN_ERROR_IF_ANY_ERROR(inode_mkdir,inode_mkdir(inode, dentry,
mask));
+}
+
+static void stacker_inode_post_mkdir (struct inode *inode,
+ struct dentry *dentry, int mask)
+{
+ CALL_ALL(inode_post_mkdir,inode_post_mkdir(inode, dentry, mask));
+}
+
+static int stacker_inode_rmdir (struct inode *inode, struct dentry
*dentry)
+{
+ RETURN_ERROR_IF_ANY_ERROR(inode_rmdir,inode_rmdir(inode, dentry));
+}
+
+static int stacker_inode_mknod (struct inode *inode, struct dentry
*dentry,
+ int major, dev_t minor)
+{
+ RETURN_ERROR_IF_ANY_ERROR(inode_mknod,inode_mknod(inode, dentry,
major, minor));
+}
+
+static void stacker_inode_post_mknod (struct inode *inode,
+ struct dentry *dentry, int major, dev_t minor)
+{
+ CALL_ALL(inode_post_mknod,inode_post_mknod(inode, dentry, major,
minor));
+}
+
+static int stacker_inode_rename (struct inode *old_inode,
+ struct dentry *old_dentry,
+ struct inode *new_inode,
+ struct dentry *new_dentry)
+{
+ RETURN_ERROR_IF_ANY_ERROR(inode_rename,inode_rename(old_inode,
old_dentry,
+ new_inode, new_dentry));
+}
+
+static void stacker_inode_post_rename (struct inode *old_inode,
+ struct dentry *old_dentry,
+ struct inode *new_inode,
+ struct dentry *new_dentry)
+{
+ CALL_ALL(inode_post_rename,inode_post_rename(old_inode, old_dentry,
+ new_inode, new_dentry));
+}
+
+static int stacker_inode_readlink (struct dentry *dentry)
+{
+ RETURN_ERROR_IF_ANY_ERROR(inode_readlink,inode_readlink(dentry));
+}
+
+static int stacker_inode_follow_link (struct dentry *dentry,
+ struct nameidata *nameidata)
+{
+ RETURN_ERROR_IF_ANY_ERROR(inode_follow_link,inode_follow_link(dentry,
nameidata));
+}
+
+static int stacker_inode_permission (struct inode *inode, int mask,
+ struct nameidata *nd)
+{
+ RETURN_ERROR_IF_ANY_ERROR(inode_permission,inode_permission(inode,
mask, nd));
+}
+
+static int stacker_inode_setattr (struct dentry *dentry, struct iattr
*iattr)
+{
+ RETURN_ERROR_IF_ANY_ERROR(inode_setattr,inode_setattr(dentry, iattr));
+}
+
+static int stacker_inode_getattr (struct vfsmount *mnt, struct dentry
*dentry)
+{
+ RETURN_ERROR_IF_ANY_ERROR(inode_getattr,inode_getattr(mnt,dentry));
+}
+
+static void stacker_inode_delete (struct inode *ino)
+{
+ CALL_ALL(inode_delete,inode_delete(ino));
+}
+
+static int stacker_inode_setxattr (struct dentry *dentry, char *name,
+ void *value, size_t size, int flags)
+{
+ RETURN_ERROR_IF_ANY_ERROR(inode_setxattr,inode_setxattr(dentry,name,value,size,flags));
+}
+
+static void stacker_inode_post_setxattr (struct dentry *dentry, char
*name, void *value,
+ size_t size, int flags)
+{
+ CALL_ALL(inode_post_setxattr,inode_post_setxattr(dentry,name,value,size,flags));
+}
+
+static int stacker_inode_getxattr (struct dentry *dentry, char *name)
+{
+ RETURN_ERROR_IF_ANY_ERROR(inode_getxattr,inode_getxattr(dentry,name));
+}
+
+static int stacker_inode_listxattr (struct dentry *dentry)
+{
+ RETURN_ERROR_IF_ANY_ERROR(inode_listxattr,inode_listxattr(dentry));
+}
+
+static int stacker_inode_removexattr (struct dentry *dentry, char
*name)
+{
+ RETURN_ERROR_IF_ANY_ERROR(inode_removexattr,inode_removexattr(dentry,name));
+}
+
+static int stacker_inode_getsecurity(struct inode *inode, const char
*name, void *buffer, size_t size)
+{
+ RETURN_ERROR_IF_ANY_ERROR(inode_getsecurity,inode_getsecurity(inode,name,buffer,size));
+}
+
+static int stacker_inode_setsecurity(struct inode *inode, const char
*name, const void *value, size_t size, int flags)
+{
+ RETURN_ERROR_IF_ANY_ERROR(inode_setsecurity,inode_setsecurity(inode,name,value,size,flags));
+}
+
+static int stacker_inode_listsecurity(struct inode *inode, char
*buffer, size_t buffer_size)
+{
+ RETURN_ERROR_IF_ANY_ERROR(inode_listsecurity,inode_listsecurity(inode,buffer,buffer_size));
+}
+
+static int stacker_file_permission (struct file *file, int mask)
+{
+ RETURN_ERROR_IF_ANY_ERROR(file_permission,file_permission(file,mask));
+}
+
+static int stacker_file_alloc_security (struct file *file)
+{
+ RETURN_ERROR_IF_ANY_ERROR(file_alloc_security,file_alloc_security(file));
+}
+
+static void stacker_file_free_security (struct file *file)
+{
+ CALL_ALL(file_free_security,file_free_security(file));
+}
+
+static int stacker_file_ioctl (struct file *file, unsigned int command,
+ unsigned long arg)
+{
+ RETURN_ERROR_IF_ANY_ERROR(file_ioctl,file_ioctl(file,command,arg));
+}
+
+static int stacker_file_mmap (struct file *file, unsigned long prot,
+ unsigned long flags)
+{
+ RETURN_ERROR_IF_ANY_ERROR(file_mmap,file_mmap(file, prot, flags));
+}
+
+static int stacker_file_mprotect (struct vm_area_struct *vma,
+ unsigned long prot)
+{
+ RETURN_ERROR_IF_ANY_ERROR(file_mprotect,file_mprotect(vma,prot));
+}
+
+static int stacker_file_lock (struct file *file, unsigned int cmd)
+{
+ RETURN_ERROR_IF_ANY_ERROR(file_lock,file_lock(file,cmd));
+}
+
+static int stacker_file_fcntl (struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ RETURN_ERROR_IF_ANY_ERROR(file_fcntl,file_fcntl(file,cmd,arg));
+}
+
+/* say - this is completely unused */
+/* If that changes, we'll need to set file->security? */
+static int stacker_file_set_fowner (struct file *file)
+{
+ RETURN_ERROR_IF_ANY_ERROR(file_set_fowner,file_set_fowner(file));
+}
+
+static int stacker_file_send_sigiotask (struct task_struct *tsk,
+ struct fown_struct *fown,
+ int sig)
+{
+ RETURN_ERROR_IF_ANY_ERROR(file_send_sigiotask,file_send_sigiotask(tsk,fown,sig));
+}
+
+static int stacker_file_receive (struct file *file)
+{
+ RETURN_ERROR_IF_ANY_ERROR(file_receive,file_receive(file));
+}
+
+static int stacker_task_create (unsigned long clone_flags)
+{
+ RETURN_ERROR_IF_ANY_ERROR(task_create,task_create(clone_flags));
+}
+
+static int stacker_task_alloc_security (struct task_struct *p)
+{
+ RETURN_ERROR_IF_ANY_ERROR(task_alloc_security,task_alloc_security(p));
+}
+
+static void stacker_task_free_security (struct task_struct *p)
+{
+ CALL_ALL(task_free_security,task_free_security(p));
+}
+
+static int stacker_task_setuid (uid_t id0, uid_t id1, uid_t id2, int
flags)
+{
+ RETURN_ERROR_IF_ANY_ERROR(task_setuid,task_setuid(id0,id1,id2,flags));
+}
+
+static int stacker_task_post_setuid (uid_t id0, uid_t id1, uid_t id2,
int flags)
+{
+ RETURN_ERROR_IF_ANY_ERROR(task_post_setuid,task_post_setuid(id0,id1,id2,flags));
+}
+
+static int stacker_task_setgid (gid_t id0, gid_t id1, gid_t id2, int
flags)
+{
+ RETURN_ERROR_IF_ANY_ERROR(task_setgid,task_setgid(id0,id1,id2,flags));
+}
+
+static int stacker_task_setpgid (struct task_struct *p, pid_t pgid)
+{
+ RETURN_ERROR_IF_ANY_ERROR(task_setpgid,task_setpgid(p,pgid));
+}
+
+static int stacker_task_getpgid (struct task_struct *p)
+{
+ RETURN_ERROR_IF_ANY_ERROR(task_getpgid,task_getpgid(p));
+}
+
+static int stacker_task_getsid (struct task_struct *p)
+{
+ RETURN_ERROR_IF_ANY_ERROR(task_getsid,task_getsid(p));
+}
+
+static int stacker_task_setgroups (struct group_info *group_info)
+{
+ RETURN_ERROR_IF_ANY_ERROR(task_setgroups,task_setgroups(group_info));
+}
+
+static int stacker_task_setnice (struct task_struct *p, int nice)
+{
+ RETURN_ERROR_IF_ANY_ERROR(task_setnice,task_setnice(p,nice));
+}
+
+static int stacker_task_setrlimit (unsigned int resource, struct rlimit
*new_rlim)
+{
+ RETURN_ERROR_IF_ANY_ERROR(task_setrlimit,task_setrlimit(resource,new_rlim));
+}
+
+static int stacker_task_setscheduler (struct task_struct *p, int
policy,
+ struct sched_param *lp)
+{
+ RETURN_ERROR_IF_ANY_ERROR(task_setscheduler,task_setscheduler(p,policy,lp));
+}
+
+static int stacker_task_getscheduler (struct task_struct *p)
+{
+ RETURN_ERROR_IF_ANY_ERROR(task_getscheduler,task_getscheduler(p));
+}
+
+static int stacker_task_wait (struct task_struct *p)
+{
+ RETURN_ERROR_IF_ANY_ERROR(task_wait,task_wait(p));
+}
+
+static int stacker_task_kill (struct task_struct *p, struct siginfo
*info,
+ int sig)
+{
+ RETURN_ERROR_IF_ANY_ERROR(task_kill,task_kill(p,info,sig));
+}
+
+static int stacker_task_prctl (int option, unsigned long arg2, unsigned
long arg3,
+ unsigned long arg4, unsigned long arg5)
+{
+ RETURN_ERROR_IF_ANY_ERROR(task_prctl,task_prctl(option,arg2,arg3,arg4,arg5));
+}
+
+static void stacker_task_reparent_to_init (struct task_struct *p)
+{
+ /* Note that the dummy version of this hook would call:
+ * p->euid = p->fsuid = 0; */
+
+ CALL_ALL(task_reparent_to_init,task_reparent_to_init(p));
+}
+
+static void stacker_task_to_inode(struct task_struct *p, struct inode
*inode)
+{
+ CALL_ALL(task_to_inode,task_to_inode(p, inode));
+}
+
+#ifdef CONFIG_SECURITY_NETWORK
+static int stacker_socket_create (int family, int type, int protocol,
int kern)
+{
+ RETURN_ERROR_IF_ANY_ERROR(socket_create,socket_create(family,type,protocol,kern));
+}
+
+static void stacker_socket_post_create (struct socket *sock, int
family,
+ int type, int protocol, int kern)
+{
+ CALL_ALL(socket_post_create,socket_post_create(sock,family,type,protocol,kern));
+}
+
+static int stacker_socket_bind (struct socket *sock, struct sockaddr
*address,
+ int addrlen)
+{
+ RETURN_ERROR_IF_ANY_ERROR(socket_bind,socket_bind(sock,address,addrlen));
+}
+
+static int stacker_socket_connect (struct socket *sock,
+ struct sockaddr *address, int addrlen)
+{
+ RETURN_ERROR_IF_ANY_ERROR(socket_connect,socket_connect(sock,address,addrlen));
+}
+
+static int stacker_socket_listen (struct socket *sock, int backlog)
+{
+ RETURN_ERROR_IF_ANY_ERROR(socket_listen,socket_listen(sock,backlog));
+}
+
+static int stacker_socket_accept (struct socket *sock, struct socket
*newsock)
+{
+ RETURN_ERROR_IF_ANY_ERROR(socket_accept,socket_accept(sock,newsock));
+}
+
+static void stacker_socket_post_accept (struct socket *sock,
+ struct socket *newsock)
+{
+ CALL_ALL(socket_post_accept,socket_post_accept(sock,newsock));
+}
+
+static int stacker_socket_sendmsg (struct socket *sock, struct msghdr
*msg,
+ int size)
+{
+ RETURN_ERROR_IF_ANY_ERROR(socket_sendmsg,socket_sendmsg(sock,msg,size));
+}
+
+static int stacker_socket_recvmsg (struct socket *sock, struct msghdr
*msg,
+ int size, int flags)
+{
+ RETURN_ERROR_IF_ANY_ERROR(socket_recvmsg,socket_recvmsg(sock,msg,size,flags));
+}
+
+static int stacker_socket_getsockname (struct socket *sock)
+{
+ RETURN_ERROR_IF_ANY_ERROR(socket_getsockname,socket_getsockname(sock));
+}
+
+static int stacker_socket_getpeername (struct socket *sock)
+{
+ RETURN_ERROR_IF_ANY_ERROR(socket_getpeername,socket_getpeername(sock));
+}
+
+static int stacker_socket_setsockopt (struct socket *sock, int level,
int optname)
+{
+ RETURN_ERROR_IF_ANY_ERROR(socket_setsockopt,socket_setsockopt(sock,level,optname));
+}
+
+static int stacker_socket_getsockopt (struct socket *sock, int level,
int optname)
+{
+ RETURN_ERROR_IF_ANY_ERROR(socket_getsockopt,socket_getsockopt(sock,level,optname));
+}
+
+static int stacker_socket_shutdown (struct socket *sock, int how)
+{
+ RETURN_ERROR_IF_ANY_ERROR(socket_shutdown,socket_shutdown(sock,how));
+}
+
+static int stacker_socket_sock_rcv_skb (struct sock *sk, struct sk_buff
*skb)
+{
+ RETURN_ERROR_IF_ANY_ERROR(socket_sock_rcv_skb,socket_sock_rcv_skb(sk,skb));
+}
+
+static int stacker_unix_stream_connect (struct socket *sock,
+ struct socket *other, struct sock *newsk)
+{
+ RETURN_ERROR_IF_ANY_ERROR(unix_stream_connect,unix_stream_connect(sock,other,newsk));
+}
+
+static int stacker_unix_may_send (struct socket *sock,
+ struct socket *other)
+{
+ RETURN_ERROR_IF_ANY_ERROR(unix_may_send,unix_may_send(sock,other));
+}
+
+static int stacker_socket_getpeersec(struct socket *sock,
+ char __user *optval, int __user *optlen, unsigned len)
+{
+ RETURN_ERROR_IF_ANY_ERROR(socket_getpeersec,socket_getpeersec(sock,optval,optlen,len));
+}
+
+static int stacker_sk_alloc_security(struct sock *sk, int family,
+ int priority)
+{
+ RETURN_ERROR_IF_ANY_ERROR(sk_alloc_security,sk_alloc_security(sk,family,priority));
+}
+
+static void stacker_sk_free_security (struct sock *sk)
+{
+ CALL_ALL(sk_free_security,sk_free_security(sk));
+}
+
+#endif
+
+static int stacker_ipc_permission (struct kern_ipc_perm *ipcp, short
flag)
+{
+ RETURN_ERROR_IF_ANY_ERROR(ipc_permission,ipc_permission(ipcp,flag));
+}
+
+static int stacker_msg_msg_alloc_security (struct msg_msg *msg)
+{
+ RETURN_ERROR_IF_ANY_ERROR(msg_msg_alloc_security,msg_msg_alloc_security(msg));
+}
+
+static void stacker_msg_msg_free_security (struct msg_msg *msg)
+{
+ CALL_ALL(msg_msg_free_security,msg_msg_free_security(msg));
+}
+
+static int stacker_msg_queue_alloc_security (struct msg_queue *msq)
+{
+ RETURN_ERROR_IF_ANY_ERROR(msg_queue_alloc_security,msg_queue_alloc_security(msq));
+}
+
+static void stacker_msg_queue_free_security (struct msg_queue *msq)
+{
+ CALL_ALL(msg_queue_free_security,msg_queue_free_security(msq));
+}
+
+static int stacker_msg_queue_associate (struct msg_queue *msq, int
msqflg)
+{
+ RETURN_ERROR_IF_ANY_ERROR(msg_queue_associate,msg_queue_associate(msq,msqflg));
+}
+
+static int stacker_msg_queue_msgctl (struct msg_queue *msq, int cmd)
+{
+ RETURN_ERROR_IF_ANY_ERROR(msg_queue_msgctl,msg_queue_msgctl(msq,cmd));
+}
+
+static int stacker_msg_queue_msgsnd (struct msg_queue *msq, struct
msg_msg *msg,
+ int msgflg)
+{
+ RETURN_ERROR_IF_ANY_ERROR(msg_queue_msgsnd,msg_queue_msgsnd(msq,msg,msgflg));
+}
+
+static int stacker_msg_queue_msgrcv (struct msg_queue *msq, struct
msg_msg *msg,
+ struct task_struct *target, long type,
+ int mode)
+{
+ RETURN_ERROR_IF_ANY_ERROR(msg_queue_msgrcv,msg_queue_msgrcv(msq,msg,target,type,mode));
+}
+
+static int stacker_shm_alloc_security (struct shmid_kernel *shp)
+{
+ RETURN_ERROR_IF_ANY_ERROR(shm_alloc_security,shm_alloc_security(shp));
+}
+
+static void stacker_shm_free_security (struct shmid_kernel *shp)
+{
+ CALL_ALL(shm_free_security,shm_free_security(shp));
+}
+
+static int stacker_shm_associate (struct shmid_kernel *shp, int shmflg)
+{
+ RETURN_ERROR_IF_ANY_ERROR(shm_associate,shm_associate(shp,shmflg));
+}
+
+static int stacker_shm_shmctl (struct shmid_kernel *shp, int cmd)
+{
+ RETURN_ERROR_IF_ANY_ERROR(shm_shmctl,shm_shmctl(shp,cmd));
+}
+
+static int stacker_shm_shmat (struct shmid_kernel *shp, char *shmaddr,
+ int shmflg)
+{
+ RETURN_ERROR_IF_ANY_ERROR(shm_shmat,shm_shmat(shp,shmaddr,shmflg));
+}
+
+static int stacker_sem_alloc_security (struct sem_array *sma)
+{
+ RETURN_ERROR_IF_ANY_ERROR(sem_alloc_security,sem_alloc_security(sma));
+}
+
+static void stacker_sem_free_security (struct sem_array *sma)
+{
+ CALL_ALL(sem_free_security,sem_free_security(sma));
+}
+
+static int stacker_sem_associate (struct sem_array *sma, int semflg)
+{
+ RETURN_ERROR_IF_ANY_ERROR(sem_associate,sem_associate(sma,semflg));
+}
+
+static int stacker_sem_semctl (struct sem_array *sma, int cmd)
+{
+ RETURN_ERROR_IF_ANY_ERROR(sem_semctl,sem_semctl(sma,cmd));
+}
+
+static int stacker_sem_semop (struct sem_array *sma,
+ struct sembuf *sops, unsigned nsops, int alter)
+{
+ RETURN_ERROR_IF_ANY_ERROR(sem_semop,sem_semop(sma,sops,nsops,alter));
+}
+
+static void stacker_d_instantiate (struct dentry *dentry, struct inode
*inode)
+{
+ CALL_ALL(d_instantiate,d_instantiate(dentry,inode));
+}
+
+static int
+stacker_getprocattr(struct task_struct *p, char *name, void *value,
size_t size)
+{
+ int ret = 0;
+ struct module_entry *m;
+
+ memset(value, 0, size);
+ for (m=stacked_modules; m && ret<size; m = m->next) {
+ if (m->module_operations.getprocattr) {
+ if (m->namelen+2+ret >= size)
+ continue;
+ ret += sprintf(value+ret, "%s:", m->module_name);
+ ret += m->module_operations.getprocattr(p,name,
+ value+ret, size-ret);
+ if (ret+1<size)
+ ((char *)value)[ret++]='\n';
+ }
+ }
+ return ret;
+}
+
+static int stacker_setprocattr(struct task_struct *p, char *name, void
*value, size_t size)
+{
+ char *s, *e;
+ int len, ret;
+ struct module_entry *m;
+ char modname[50];
+
+ s = value;
+ e = strnchr(s, size, ':');
+ if (!e) {
+ printk(KERN_INFO "%s: couln't find module name end\n",
+ __FUNCTION__);
+ return -EINVAL;
+ }
+ m = find_lsm_module_by_name(s,e);
+ if (!m) {
+ strncpy(modname, s, 50);
+ modname[49] = '\0';
+ printk(KERN_INFO "%s: Bad module name, %s\n",
+ __FUNCTION__, modname);
+ return -EINVAL;
+ }
+ s = e+1;
+ while ((void *)s < value+size && *s == ' ')
+ s++;
+ if (s == value+size) {
+ printk(KERN_INFO "%s: no data after module name\n", __FUNCTION__);
+ return -EINVAL;
+ }
+ len = size - (int)((void *)s - value);
+ printk(KERN_INFO "%s: sending to module\n", __FUNCTION__);
+ ret = m->module_operations.setprocattr(p,name,s,len);
+ printk(KERN_INFO "%s: ret was %d, returning %d\n", __FUNCTION__, ret,
ret+(size-len));
+ if (ret < 0)
+ return ret;
+ return ret + (size-len);
+}
+
+static int stacker_register (const char *name, struct
security_operations *ops)
+{
+ /* This function is the primary reason for the stacker module.
+ Add the stacked module (as specified by name and ops)
+ according to the current ordering policy. */
+
+ char *new_module_name;
+ struct module_entry *new_module_entry;
+ int namelen;
+
+ num_stacked_modules++;
+
+ LOCK_STACKER_FOR_WRITING;
+ if (forbid_stacker_register) {
+ printk(KERN_INFO "%s: module stacking is forbidding.\n",
+ __FUNCTION__);
+ UNLOCK_STACKER_FOR_WRITING;
+ return -EINVAL;
+ }
+ /* TODO: What should I check on re: security? Should I check
+ for euid == 0? Has that already been checked? (dwheeler)
+ Serge's comments: XXX
+ 1. We could force stacked modules to be
+ added through the sysfs interface instead of insmod. That
+ way a stacked module (dte, selinux) could, once stacked,
+ control additional module stacking.
+ 2. We could lobby for the reintroduction of the module
+ add/delete hooks.
+ */
+
+ /* Allocate memory */
+ namelen = strnlen(name, MAX_MODULE_NAME_LEN);
+ new_module_name = kmalloc(namelen+1, GFP_KERNEL);
+ new_module_entry = kmalloc(sizeof(struct module_entry), GFP_KERNEL);
+ if (!new_module_name || !new_module_entry) {
+ UNLOCK_STACKER_FOR_WRITING;
+ printk (KERN_INFO
+ "%s: Failure registering module - out of memory\n",
+ __FUNCTION__);
+ return -ENOMEM;
+ }
+
+ memset(new_module_entry, 0, sizeof(struct module_entry));
+
+ /* Copy the data into the allocated memory. */
+ strncpy(new_module_name, name, namelen);
+ new_module_name[namelen] = '\0';
+
+ /*new_module_entry->module_operations = *ops;*/
+ memcpy(&new_module_entry->module_operations, ops,
+ sizeof(struct security_operations));
+
+ new_module_entry->module_name = new_module_name;
+ new_module_entry->namelen = namelen;
+
+ add_module_entry(new_module_entry);
+
+ /* One more write barrier; this one is to _ensure_ that the
+ * inactive list is valid before releasing the locking. */
+ wmb();
+ UNLOCK_STACKER_FOR_WRITING;
+ printk(KERN_INFO "%s: registered %s module\n", __FUNCTION__,
+ new_module_entry->module_name);
+ return num_stacked_modules-1;
+}
+
+/*
+ * Unregister a registered LSM.
+ * It would be nice if we could veto this, but we can't...
+ */
+static int stacker_unregister (const char *name, struct
security_operations *ops)
+{
+ struct module_entry *m, *b=NULL, *bb=NULL;
+ int len = strnlen(name, MAX_MODULE_NAME_LEN);
+ int ret = 0;
+
+ LOCK_STACKER_FOR_WRITING;
+ m = stacked_modules;
+ while (m) {
+ if (m->namelen == len && strncmp(m->module_name, name, len)==0)
+ break;
+ bb = b;
+ b = m;
+ m = m->next;
+ }
+ if (!m) {
+ printk(KERN_INFO "%s: could not find module %s.\n",
+ __FUNCTION__, name);
+ ret = -ENOENT;
+ goto out;
+ }
+ if (b)
+ b->next = m->next;
+ else
+ stacked_modules = m->next;
+
+ if (penultimate_stacked_module) {
+ if (penultimate_stacked_module == m)
+ penultimate_stacked_module = b;
+ else if (penultimate_stacked_module == b)
+ penultimate_stacked_module = bb;
+ }
+ num_stacked_modules--;
+ kfree(m->module_name);
+ kfree(m);
+out:
+ UNLOCK_STACKER_FOR_WRITING;
+
+ return ret;
+}
+
+
+#if 1
+struct security_operations stacker_ops = {
+ .ptrace = stacker_ptrace,
+ .capget = stacker_capget,
+ .capset_check = stacker_capset_check,
+ .capset_set = stacker_capset_set,
+ .acct = stacker_acct,
+ .sysctl = stacker_sysctl,
+ .capable = stacker_capable,
+ .quotactl = stacker_quotactl,
+ .quota_on = stacker_quota_on,
+ .syslog = stacker_syslog,
+ .vm_enough_memory = stacker_vm_enough_memory,
+
+ .bprm_alloc_security = stacker_bprm_alloc_security,
+ .bprm_free_security = stacker_bprm_free_security,
+ .bprm_apply_creds = stacker_bprm_apply_creds,
+ .bprm_set_security = stacker_bprm_set_security,
+ .bprm_check_security = stacker_bprm_check_security,
+ .bprm_secureexec = stacker_bprm_secureexec,
+
+ .sb_alloc_security = stacker_sb_alloc_security,
+ .sb_free_security = stacker_sb_free_security,
+ .sb_copy_data = stacker_sb_copy_data,
+ .sb_kern_mount = stacker_sb_kern_mount,
+ .sb_statfs = stacker_sb_statfs,
+ .sb_mount = stacker_mount,
+ .sb_check_sb = stacker_check_sb,
+ .sb_umount = stacker_umount,
+ .sb_umount_close = stacker_umount_close,
+ .sb_umount_busy = stacker_umount_busy,
+ .sb_post_remount = stacker_post_remount,
+ .sb_post_mountroot = stacker_post_mountroot,
+ .sb_post_addmount = stacker_post_addmount,
+ .sb_pivotroot = stacker_pivotroot,
+ .sb_post_pivotroot = stacker_post_pivotroot,
+
+ .inode_alloc_security = stacker_inode_alloc_security,
+ .inode_free_security = stacker_inode_free_security,
+ .inode_create = stacker_inode_create,
+ .inode_post_create = stacker_inode_post_create,
+ .inode_link = stacker_inode_link,
+ .inode_post_link = stacker_inode_post_link,
+ .inode_unlink = stacker_inode_unlink,
+ .inode_symlink = stacker_inode_symlink,
+ .inode_post_symlink = stacker_inode_post_symlink,
+ .inode_mkdir = stacker_inode_mkdir,
+ .inode_post_mkdir = stacker_inode_post_mkdir,
+ .inode_rmdir = stacker_inode_rmdir,
+ .inode_mknod = stacker_inode_mknod,
+ .inode_post_mknod = stacker_inode_post_mknod,
+ .inode_rename = stacker_inode_rename,
+ .inode_post_rename = stacker_inode_post_rename,
+ .inode_readlink = stacker_inode_readlink,
+ .inode_follow_link = stacker_inode_follow_link,
+ .inode_permission = stacker_inode_permission,
+
+ .inode_setattr = stacker_inode_setattr,
+ .inode_getattr = stacker_inode_getattr,
+ .inode_delete = stacker_inode_delete,
+ .inode_setxattr = stacker_inode_setxattr,
+ .inode_post_setxattr = stacker_inode_post_setxattr,
+ .inode_getxattr = stacker_inode_getxattr,
+ .inode_listxattr = stacker_inode_listxattr,
+ .inode_removexattr = stacker_inode_removexattr,
+ .inode_getsecurity = stacker_inode_getsecurity,
+ .inode_setsecurity = stacker_inode_setsecurity,
+ .inode_listsecurity = stacker_inode_listsecurity,
+
+ .file_permission = stacker_file_permission,
+ .file_alloc_security = stacker_file_alloc_security,
+ .file_free_security = stacker_file_free_security,
+ .file_ioctl = stacker_file_ioctl,
+ .file_mmap = stacker_file_mmap,
+ .file_mprotect = stacker_file_mprotect,
+ .file_lock = stacker_file_lock,
+ .file_fcntl = stacker_file_fcntl,
+ .file_set_fowner = stacker_file_set_fowner,
+ .file_send_sigiotask = stacker_file_send_sigiotask,
+ .file_receive = stacker_file_receive,
+
+ .task_create = stacker_task_create,
+ .task_alloc_security = stacker_task_alloc_security,
+ .task_free_security = stacker_task_free_security,
+ .task_setuid = stacker_task_setuid,
+ .task_post_setuid = stacker_task_post_setuid,
+ .task_setgid = stacker_task_setgid,
+ .task_setpgid = stacker_task_setpgid,
+ .task_getpgid = stacker_task_getpgid,
+ .task_getsid = stacker_task_getsid,
+ .task_setgroups = stacker_task_setgroups,
+ .task_setnice = stacker_task_setnice,
+ .task_setrlimit = stacker_task_setrlimit,
+ .task_setscheduler = stacker_task_setscheduler,
+ .task_getscheduler = stacker_task_getscheduler,
+ .task_kill = stacker_task_kill,
+ .task_wait = stacker_task_wait,
+ .task_prctl = stacker_task_prctl,
+ .task_reparent_to_init = stacker_task_reparent_to_init,
+ .task_to_inode = stacker_task_to_inode,
+
+ .ipc_permission = stacker_ipc_permission,
+
+ .msg_msg_alloc_security = stacker_msg_msg_alloc_security,
+ .msg_msg_free_security = stacker_msg_msg_free_security,
+ .msg_queue_alloc_security = stacker_msg_queue_alloc_security,
+ .msg_queue_free_security = stacker_msg_queue_free_security,
+ .msg_queue_associate = stacker_msg_queue_associate,
+ .msg_queue_msgctl = stacker_msg_queue_msgctl,
+ .msg_queue_msgsnd = stacker_msg_queue_msgsnd,
+ .msg_queue_msgrcv = stacker_msg_queue_msgrcv,
+ .shm_alloc_security = stacker_shm_alloc_security,
+ .shm_free_security = stacker_shm_free_security,
+ .shm_associate = stacker_shm_associate,
+ .shm_shmctl = stacker_shm_shmctl,
+ .shm_shmat = stacker_shm_shmat,
+
+ .sem_alloc_security = stacker_sem_alloc_security,
+ .sem_free_security = stacker_sem_free_security,
+ .sem_associate = stacker_sem_associate,
+ .sem_semctl = stacker_sem_semctl,
+ .sem_semop = stacker_sem_semop,
+
+ .netlink_send = stacker_netlink_send,
+ .netlink_recv = stacker_netlink_recv,
+
+ .register_security = stacker_register,
+ .unregister_security = stacker_unregister,
+
+ .d_instantiate = stacker_d_instantiate,
+ .getprocattr = stacker_getprocattr,
+ .setprocattr = stacker_setprocattr,
+
+#ifdef CONFIG_SECURITY_NETWORK
+ .unix_stream_connect = stacker_unix_stream_connect,
+ .unix_may_send = stacker_unix_may_send,
+ .socket_create = stacker_socket_create,
+ .socket_post_create = stacker_socket_post_create,
+ .socket_bind = stacker_socket_bind,
+ .socket_connect = stacker_socket_connect,
+ .socket_listen = stacker_socket_listen,
+ .socket_accept = stacker_socket_accept,
+ .socket_post_accept = stacker_socket_post_accept,
+ .socket_sendmsg = stacker_socket_sendmsg,
+ .socket_recvmsg = stacker_socket_recvmsg,
+ .socket_getsockname = stacker_socket_getsockname,
+ .socket_getpeername = stacker_socket_getpeername,
+ .socket_getsockopt = stacker_socket_getsockopt,
+ .socket_setsockopt = stacker_socket_setsockopt,
+ .socket_shutdown = stacker_socket_shutdown,
+ .socket_sock_rcv_skb = stacker_socket_sock_rcv_skb,
+ .socket_getpeersec = stacker_socket_getpeersec,
+ .sk_alloc_security = stacker_sk_alloc_security,
+ .sk_free_security = stacker_sk_free_security,
+#endif
+
+};
+#else
+struct security_operations stacker_ops = {
+ .register_security = stacker_register,
+};
+#endif
+
+
+/*
+ * Functions to provide the sysfs interface
+ */
+
+/* A structure to pass into sysfs through kobjects */
+struct stacker_kobj {
+ struct list_head slot_list;
+ struct kobject kobj;
+};
+
+struct stacker_attribute {
+ struct attribute attr;
+ ssize_t (*show)(struct stacker_kobj *, char *);
+ ssize_t (*store)(struct stacker_kobj *, const char *, size_t);
+};
+
+/* variables to hold kobject/sysfs data */
+struct subsystem stacker_subsys;
+
+void unregister_sysfs_files(void);
+
+ssize_t stacker_attr_store(struct kobject *kobj,
+ struct attribute *attr, const char *buf, size_t len)
+{
+ struct stacker_kobj *obj = container_of(kobj,
+ struct stacker_kobj, kobj);
+ struct stacker_attribute *attribute = container_of(attr,
+ struct stacker_attribute, attr);
+
+ return attribute->store ? attribute->store(obj, buf, len) : 0;
+}
+
+static ssize_t stacker_attr_show(struct kobject *kobj,
+ struct attribute *attr, char *buf)
+{
+ struct stacker_kobj *obj = container_of(kobj,
+ struct stacker_kobj, kobj);
+ struct stacker_attribute *attribute = container_of(attr,
+ struct stacker_attribute, attr);
+
+ return attribute->show ? attribute->show(obj, buf) : 0;
+}
+
+struct sysfs_ops stacker_sysfs_ops = {
+ .show = stacker_attr_show,
+ .store = stacker_attr_store,
+};
+
+static struct kobj_type stacker_ktype = {
+ .sysfs_ops = &stacker_sysfs_ops
+};
+
+decl_subsys(stacker, &stacker_ktype, NULL);
+
+/* Set lockdown */
+static ssize_t lockdown_read (struct stacker_kobj *obj, char *buff)
+{
+ ssize_t len;
+
+ if (forbid_stacker_register)
+ len = sprintf(buff, "Locked down\n");
+ else
+ len = sprintf(buff, "Unlocked\n");
+
+ return len;
+}
+
+static ssize_t lockdown_write (struct stacker_kobj *obj, const char
*buff, size_t count)
+{
+ if (count>0)
+ forbid_stacker_register = 1;
+
+ return count;
+}
+
+static struct stacker_attribute stacker_attr_lockdown = {
+ .attr = {.name = "lockdown", .mode = S_IFREG | S_IRUGO | S_IWUSR},
+ .show = lockdown_read,
+ .store = lockdown_write
+};
+
+/* list modules */
+static ssize_t listmodules_read (struct stacker_kobj *obj, char *buff)
+{
+ ssize_t len = 0;
+ struct module_entry *m;
+
+ LOCK_STACKER_FOR_READING;
+ for (m = stacked_modules; m; m = m->next)
+ len += snprintf(buff+len, PAGE_SIZE - len, "%s\n", m->module_name);
+ UNLOCK_STACKER_FOR_READING;
+
+ return len;
+}
+
+static ssize_t listmodules_write (struct stacker_kobj *obj, const char
*buff, size_t count)
+{
+ return count;
+}
+
+static struct stacker_attribute stacker_attr_listmodules = {
+ .attr = {.name = "list_modules", .mode = S_IFREG | S_IRUGO | S_IWUSR},
+ .show = listmodules_read,
+ .store = listmodules_write
+};
+
+/* stop responding to sysfs */
+static ssize_t stop_responding_read (struct stacker_kobj *obj, char
*buff)
+{
+ ssize_t len;
+
+ len = sprintf(buff, "Obviously not\n");
+
+ return len;
+}
+
+static ssize_t stop_responding_write (struct stacker_kobj *obj, const
char *buff, size_t count)
+{
+ if (count>0) {
+ unregister_sysfs_files();
+ }
+
+ return count;
+}
+
+static struct stacker_attribute stacker_attr_stop_responding = {
+ .attr = {.name = "stop_responding", .mode = S_IFREG | S_IRUGO |
S_IWUSR},
+ .show = stop_responding_read,
+ .store = stop_responding_write
+};
+
+/* short-circuit restrictive */
+static ssize_t shortcircuit_restrictive_read (struct stacker_kobj *obj,
char *buff)
+{
+ ssize_t len;
+
+ if (short_circuit_restrictive)
+ len = sprintf(buff, "Restrictive hooks are being
short-circuited.\n");
+ else
+ len = sprintf(buff, "Restrictive hooks are not being
short-circuited.\n");
+
+ return len;
+}
+
+static ssize_t shortcircuit_restrictive_write (struct stacker_kobj
*obj, const char *buff, size_t count)
+{
+ if (count==0)
+ short_circuit_restrictive = 0;
+ else
+ short_circuit_restrictive = 1;
+
+ return count;
+}
+
+static struct stacker_attribute stacker_attr_shortcircuit_restr = {
+ .attr = {.name = "shortcircuit_restrictive", .mode = S_IFREG | S_IRUGO
| S_IWUSR},
+ .show = shortcircuit_restrictive_read,
+ .store = shortcircuit_restrictive_write
+};
+
+/* short-circuit capable */
+static ssize_t shortcircuit_capable_read (struct stacker_kobj *obj,
char *buff)
+{
+ ssize_t len;
+
+ if (short_circuit_capable)
+ len = sprintf(buff, "Capable is being short-circuited.\n");
+ else
+ len = sprintf(buff, "Capable is not being short-circuited.\n");
+
+ return len;
+}
+
+static ssize_t shortcircuit_capable_write (struct stacker_kobj *obj,
const char *buff, size_t count)
+{
+ if (count==0)
+ short_circuit_capable = 0;
+ else
+ short_circuit_capable = 1;
+
+ return count;
+}
+
+static struct stacker_attribute stacker_attr_shortcircuit_cap = {
+ .attr = {.name = "shortcircuit_capable", .mode = S_IFREG | S_IRUGO |
S_IWUSR},
+ .show = shortcircuit_capable_read,
+ .store = shortcircuit_capable_write
+};
+
+void unregister_sysfs_files(void)
+{
+ struct kobject *kobj;
+
+ if (!sysfsfiles_registered)
+ return;
+
+ kobj = &stacker_subsys.kset.kobj;
+ sysfs_remove_file(kobj, &stacker_attr_lockdown.attr);
+ sysfs_remove_file(kobj, &stacker_attr_listmodules.attr);
+ sysfs_remove_file(kobj, &stacker_attr_stop_responding.attr);
+ sysfs_remove_file(kobj, &stacker_attr_shortcircuit_restr.attr);
+ sysfs_remove_file(kobj, &stacker_attr_shortcircuit_cap.attr);
+
+ sysfsfiles_registered = 0;
+}
+
+static int register_sysfs_files(void)
+{
+ int result;
+
+ result = subsystem_register(&stacker_subsys);
+ if (result) {
+ printk(KERN_NOTICE "Error (%d) registering stacker sysfssubsystem\n",
+ result);
+ return result;
+ }
+
+ sysfs_create_file(&stacker_subsys.kset.kobj,
+ &stacker_attr_lockdown.attr);
+ sysfs_create_file(&stacker_subsys.kset.kobj,
+ &stacker_attr_listmodules.attr);
+ sysfs_create_file(&stacker_subsys.kset.kobj,
+ &stacker_attr_stop_responding.attr);
+ sysfs_create_file(&stacker_subsys.kset.kobj,
+ &stacker_attr_shortcircuit_restr.attr);
+ sysfs_create_file(&stacker_subsys.kset.kobj,
+ &stacker_attr_shortcircuit_cap.attr);
+ sysfsfiles_registered = 1;
+ printk(KERN_NOTICE "sysfs files registered\n");
+ return 0;
+}
+
+module_init(register_sysfs_files);
+
+/*
+ * Structure sent back to security modules for getting and
+ * setting security blobs
+ */
+
+#if defined(CONFIG_SECURITY_stacker_MODULE)
+# define MY_NAME THIS_MODULE->name
+#else
+# define MY_NAME "stacker"
+#endif
+
+static int __init stacker_init (void)
+{
+ stacked_modules = NULL;
+ forbid_stacker_register = 0;
+ short_circuit_capable = 0;
+ short_circuit_restrictive = 0;
+ penultimate_stacked_module = NULL;
+ sysfsfiles_registered = 0;
+ num_stacked_modules = 0;
+
+ INIT_STACKER_LOCKING;
+
+ if (register_security (&stacker_ops)) {
+ /*
+ * stacking stacker is just a stupid idea, so don't ask
+ * the current module to load us.
+ */
+ printk (KERN_INFO "Failure registering stacker module "
+ "with primary security module.\n");
+ return -EINVAL;
+ }
+ printk(KERN_INFO "Stacker LSM initialized\n");
+
+ return 0;
+}
+
+static void __exit stacker_exit (void)
+{
+ /*
+ * Since we have no return value, we can't just say no.
+ * Should probably force all child modules to exit somehow...
+ */
+
+ unregister_sysfs_files();
+ if (unregister_security (&stacker_ops)) {
+ printk (KERN_INFO
+ "Failure unregistering stacker module with the kernel\n");
+ }
+}
+
+security_initcall (stacker_init);
+module_exit (stacker_exit);
+
+MODULE_DESCRIPTION("LSM Stacker - supports multiple simultaneous LSM
modules");
+MODULE_AUTHOR("David A. Wheeler, Serge Hallyn");
+MODULE_LICENSE("GPL");
Index: linux-2.6.10-rc1-bk12-stack/security/stacker.h
===================================================================
--- linux-2.6.10-rc1-bk12-stack.orig/security/stacker.h 2004-11-02
13:46:30.056668176 -0600
+++ linux-2.6.10-rc1-bk12-stack/security/stacker.h 2004-11-02
19:58:22.848371840 -0600
@@ -0,0 +1,33 @@
+#ifndef __STACKER_H
+#define __STACKER_H
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/security.h>
+#include <linux/skbuff.h>
+#include <linux/netlink.h>
+#include <linux/capability.h>
+#include <linux/rwsem.h>
+#include <asm/semaphore.h>
+#include <asm/system.h>
+#include <linux/kobject.h>
+#include <linux/sysfs.h>
+#include <linux/spinlock.h>
+#include <linux/netdevice.h>
+#include <linux/string.h>
+
+/* A "module entry" keeps track of one of the stacked modules
+ * Note that module_operations is aggregated instead of being pointed
to -
+ * it's one less allocation and one less pointer to follow. */
+
+struct module_entry {
+ struct module_entry *next; /* MUST BE FIRST for alignment */
+ struct module_entry *inactive_next; /* USE THIS for inactive list */
+ char *module_name;
+ int namelen;
+ struct security_operations module_operations;
+ int offset; /* mainly used when we control obj->security structs */
+};
+#endif


2004-11-04 22:17:33

by Serge E. Hallyn

[permalink] [raw]
Subject: Re: [RFC] [PATCH] [4/6] LSM Stacking: seclvl LSM stacking support

This patch adds stacking support to the BSD Secure Level LSM.

Signed-off-by: Serge E. Hallyn <[email protected]>

Index: linux-2.6.10-rc1-bk12-stack/security/seclvl.c
===================================================================
--- linux-2.6.10-rc1-bk12-stack.orig/security/seclvl.c 2004-11-02
20:28:47.000000000 -0600
+++ linux-2.6.10-rc1-bk12-stack/security/seclvl.c 2004-11-03
23:01:08.478130544 -0600
@@ -58,6 +58,8 @@
MODULE_PARM_DESC(verbosity, "Initial verbosity level (0 or 1; defaults
to "
"0, which is Quiet)");

+static int seclvl_idx;
+
/**
* Optional password which can be passed in to bring seclvl to 0
* (i.e., for halt/reboot). Defaults to NULL (the passwd attribute
@@ -498,7 +500,7 @@
return -EPERM;
}
/* claimed, mark it to release on close */
- inode->i_security = current;
+ inode->i_security[seclvl_idx] = current;
}
return 0;
}
@@ -506,12 +508,12 @@
/* release the blockdev if you claimed it */
static void seclvl_bd_release(struct inode *inode)
{
- if (inode && S_ISBLK(inode->i_mode) && inode->i_security == current) {
+ if (inode && S_ISBLK(inode->i_mode) && inode->i_security[seclvl_idx]
== current) {
struct block_device *bdev = inode->i_bdev;
if (bdev) {
bd_release(bdev);
blkdev_put(bdev);
- inode->i_security = NULL;
+ inode->i_security[seclvl_idx] = NULL;
}
}
}
@@ -697,11 +699,12 @@
"seclvl: Failure registering with the "
"kernel.\n");
/* try registering with primary module */
- rc = mod_reg_security(MY_NAME, &seclvl_ops);
- if (rc) {
+ seclvl_idx = mod_reg_security(MY_NAME, &seclvl_ops);
+ if (seclvl_idx < 0) {
seclvl_printk(0, KERN_ERR, "seclvl: Failure "
"registering with primary security "
"module.\n");
+ rc = seclvl_idx;
goto exit;
} /* if primary module registered */
secondary = 1;


2004-11-04 22:23:27

by Serge E. Hallyn

[permalink] [raw]
Subject: Re: [RFC] [PATCH] [5/6] LSM Stacking: SELinux LSM stacking support

This patch adds stacking support to the SELinux LSM.

Signed-off-by: Serge E. Hallyn <[email protected]>

Index: linux-2.6.10-rc1-bk12-stack/security/selinux/hooks.c
===================================================================
--- linux-2.6.10-rc1-bk12-stack.orig/security/selinux/hooks.c 2004-11-02
19:56:04.309432960 -0600
+++ linux-2.6.10-rc1-bk12-stack/security/selinux/hooks.c 2004-11-02
20:08:19.593652768 -0600
@@ -76,6 +76,9 @@
extern int policydb_loaded_version;
extern int selinux_nlmsg_lookup(u16 sclass, u16 nlmsg_type, u32 *perm);

+int selinux_idx;
+static int secondary;
+
#ifdef CONFIG_SECURITY_SELINUX_DEVELOP
int selinux_enforcing = 0;

@@ -98,15 +101,6 @@
__setup("selinux=", selinux_enabled_setup);
#endif

-/* Original (dummy) security module. */
-static struct security_operations *original_ops = NULL;
-
-/* Minimal support for a secondary security module,
- just to allow the use of the dummy or capability modules.
- The owlsm module can alternatively be used as a secondary
- module as long as CONFIG_OWLSM_FD is not enabled. */
-static struct security_operations *secondary_ops = NULL;
-
/* Lists of inode and superblock security structures initialized
before the policy was loaded. */
static LIST_HEAD(superblock_security_head);
@@ -126,25 +120,25 @@
tsec->magic = SELINUX_MAGIC;
tsec->task = task;
tsec->osid = tsec->sid = tsec->ptrace_sid = SECINITSID_UNLABELED;
- task->security = tsec;
+ task->security[selinux_idx] = tsec;

return 0;
}

static void task_free_security(struct task_struct *task)
{
- struct task_security_struct *tsec = task->security;
+ struct task_security_struct *tsec = task->security[selinux_idx];

if (!tsec || tsec->magic != SELINUX_MAGIC)
return;

- task->security = NULL;
+ task->security[selinux_idx] = NULL;
kfree(tsec);
}

static int inode_alloc_security(struct inode *inode)
{
- struct task_security_struct *tsec = current->security;
+ struct task_security_struct *tsec = current->security[selinux_idx];
struct inode_security_struct *isec;

isec = kmalloc(sizeof(struct inode_security_struct), GFP_KERNEL);
@@ -162,15 +156,15 @@
isec->task_sid = tsec->sid;
else
isec->task_sid = SECINITSID_UNLABELED;
- inode->i_security = isec;
+ inode->i_security[selinux_idx] = isec;

return 0;
}

static void inode_free_security(struct inode *inode)
{
- struct inode_security_struct *isec = inode->i_security;
- struct superblock_security_struct *sbsec = inode->i_sb->s_security;
+ struct inode_security_struct *isec = inode->i_security[selinux_idx];
+ struct superblock_security_struct *sbsec =
inode->i_sb->s_security[selinux_idx];

if (!isec || isec->magic != SELINUX_MAGIC)
return;
@@ -180,13 +174,13 @@
list_del_init(&isec->list);
spin_unlock(&sbsec->isec_lock);

- inode->i_security = NULL;
+ inode->i_security[selinux_idx] = NULL;
kfree(isec);
}

static int file_alloc_security(struct file *file)
{
- struct task_security_struct *tsec = current->security;
+ struct task_security_struct *tsec = current->security[selinux_idx];
struct file_security_struct *fsec;

fsec = kmalloc(sizeof(struct file_security_struct), GFP_ATOMIC);
@@ -203,19 +197,19 @@
fsec->sid = SECINITSID_UNLABELED;
fsec->fown_sid = SECINITSID_UNLABELED;
}
- file->f_security = fsec;
+ file->f_security[selinux_idx] = fsec;

return 0;
}

static void file_free_security(struct file *file)
{
- struct file_security_struct *fsec = file->f_security;
+ struct file_security_struct *fsec = file->f_security[selinux_idx];

if (!fsec || fsec->magic != SELINUX_MAGIC)
return;

- file->f_security = NULL;
+ file->f_security[selinux_idx] = NULL;
kfree(fsec);
}

@@ -236,14 +230,14 @@
sbsec->sb = sb;
sbsec->sid = SECINITSID_UNLABELED;
sbsec->def_sid = SECINITSID_FILE;
- sb->s_security = sbsec;
+ sb->s_security[selinux_idx] = sbsec;

return 0;
}

static void superblock_free_security(struct super_block *sb)
{
- struct superblock_security_struct *sbsec = sb->s_security;
+ struct superblock_security_struct *sbsec =
sb->s_security[selinux_idx];

if (!sbsec || sbsec->magic != SELINUX_MAGIC)
return;
@@ -253,7 +247,7 @@
list_del_init(&sbsec->list);
spin_unlock(&sb_security_lock);

- sb->s_security = NULL;
+ sb->s_security[selinux_idx] = NULL;
kfree(sbsec);
}

@@ -273,19 +267,19 @@
ssec->magic = SELINUX_MAGIC;
ssec->sk = sk;
ssec->peer_sid = SECINITSID_UNLABELED;
- sk->sk_security = ssec;
+ sk->sk_security[selinux_idx] = ssec;

return 0;
}

static void sk_free_security(struct sock *sk)
{
- struct sk_security_struct *ssec = sk->sk_security;
+ struct sk_security_struct *ssec = sk->sk_security[selinux_idx];

if (sk->sk_family != PF_UNIX || ssec->magic != SELINUX_MAGIC)
return;

- sk->sk_security = NULL;
+ sk->sk_security[selinux_idx] = NULL;
kfree(ssec);
}
#endif /* CONFIG_SECURITY_NETWORK */
@@ -332,8 +326,8 @@
const char *name;
u32 sid;
int alloc = 0, rc = 0, seen = 0;
- struct task_security_struct *tsec = current->security;
- struct superblock_security_struct *sbsec = sb->s_security;
+ struct task_security_struct *tsec = current->security[selinux_idx];
+ struct superblock_security_struct *sbsec =
sb->s_security[selinux_idx];

if (!data)
goto out;
@@ -499,7 +493,7 @@

static int superblock_doinit(struct super_block *sb, void *data)
{
- struct superblock_security_struct *sbsec = sb->s_security;
+ struct superblock_security_struct *sbsec =
sb->s_security[selinux_idx];
struct dentry *root = sb->s_root;
struct inode *inode = root->d_inode;
int rc = 0;
@@ -722,7 +716,7 @@
static int inode_doinit_with_dentry(struct inode *inode, struct dentry
*opt_dentry)
{
struct superblock_security_struct *sbsec = NULL;
- struct inode_security_struct *isec = inode->i_security;
+ struct inode_security_struct *isec = inode->i_security[selinux_idx];
u32 sid;
struct dentry *dentry;
#define INITCONTEXTLEN 255
@@ -739,7 +733,7 @@
if (isec->initialized)
goto out;

- sbsec = inode->i_sb->s_security;
+ sbsec = inode->i_sb->s_security[selinux_idx];
if (!sbsec->initialized) {
/* Defer initialization until selinux_complete_init,
after the initial policy is loaded and the security
@@ -921,8 +915,8 @@
{
struct task_security_struct *tsec1, *tsec2;

- tsec1 = tsk1->security;
- tsec2 = tsk2->security;
+ tsec1 = tsk1->security[selinux_idx];
+ tsec2 = tsk2->security[selinux_idx];
return avc_has_perm(tsec1->sid, tsec2->sid,
SECCLASS_PROCESS, perms, &tsec2->avcr, NULL);
}
@@ -934,7 +928,7 @@
struct task_security_struct *tsec;
struct avc_audit_data ad;

- tsec = tsk->security;
+ tsec = tsk->security[selinux_idx];

AVC_AUDIT_DATA_INIT(&ad,CAP);
ad.tsk = tsk;
@@ -950,7 +944,7 @@
{
struct task_security_struct *tsec;

- tsec = tsk->security;
+ tsec = tsk->security[selinux_idx];

return avc_has_perm(tsec->sid, SECINITSID_KERNEL,
SECCLASS_SYSTEM, perms, NULL, NULL);
@@ -971,8 +965,8 @@
struct inode_security_struct *isec;
struct avc_audit_data ad;

- tsec = tsk->security;
- isec = inode->i_security;
+ tsec = tsk->security[selinux_idx];
+ isec = inode->i_security[selinux_idx];

if (!adp) {
adp = &ad;
@@ -1012,8 +1006,8 @@
struct file *file,
u32 av)
{
- struct task_security_struct *tsec = tsk->security;
- struct file_security_struct *fsec = file->f_security;
+ struct task_security_struct *tsec = tsk->security[selinux_idx];
+ struct file_security_struct *fsec = file->f_security[selinux_idx];
struct vfsmount *mnt = file->f_vfsmnt;
struct dentry *dentry = file->f_dentry;
struct inode *inode = dentry->d_inode;
@@ -1052,9 +1046,9 @@
struct avc_audit_data ad;
int rc;

- tsec = current->security;
- dsec = dir->i_security;
- sbsec = dir->i_sb->s_security;
+ tsec = current->security[selinux_idx];
+ dsec = dir->i_security[selinux_idx];
+ sbsec = dir->i_sb->s_security[selinux_idx];

AVC_AUDIT_DATA_INIT(&ad, FS);
ad.u.fs.dentry = dentry;
@@ -1099,9 +1093,9 @@
u32 av;
int rc;

- tsec = current->security;
- dsec = dir->i_security;
- isec = dentry->d_inode->i_security;
+ tsec = current->security[selinux_idx];
+ dsec = dir->i_security[selinux_idx];
+ isec = dentry->d_inode->i_security[selinux_idx];

AVC_AUDIT_DATA_INIT(&ad, FS);
ad.u.fs.dentry = dentry;
@@ -1145,11 +1139,11 @@
int old_is_dir, new_is_dir;
int rc;

- tsec = current->security;
- old_dsec = old_dir->i_security;
- old_isec = old_dentry->d_inode->i_security;
+ tsec = current->security[selinux_idx];
+ old_dsec = old_dir->i_security[selinux_idx];
+ old_isec = old_dentry->d_inode->i_security[selinux_idx];
old_is_dir = S_ISDIR(old_dentry->d_inode->i_mode);
- new_dsec = new_dir->i_security;
+ new_dsec = new_dir->i_security[selinux_idx];

AVC_AUDIT_DATA_INIT(&ad, FS);

@@ -1183,7 +1177,7 @@
if (rc)
return rc;
if (new_dentry->d_inode) {
- new_isec = new_dentry->d_inode->i_security;
+ new_isec = new_dentry->d_inode->i_security[selinux_idx];
new_is_dir = S_ISDIR(new_dentry->d_inode->i_mode);
rc = avc_has_perm(tsec->sid, new_isec->sid,
new_isec->sclass,
@@ -1205,8 +1199,8 @@
struct task_security_struct *tsec;
struct superblock_security_struct *sbsec;

- tsec = tsk->security;
- sbsec = sb->s_security;
+ tsec = tsk->security[selinux_idx];
+ sbsec = sb->s_security[selinux_idx];
return avc_has_perm(tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM,
perms, NULL, ad);
}
@@ -1259,8 +1253,8 @@
/* Set an inode's SID to a specified value. */
int inode_security_set_sid(struct inode *inode, u32 sid)
{
- struct inode_security_struct *isec = inode->i_security;
- struct superblock_security_struct *sbsec = inode->i_sb->s_security;
+ struct inode_security_struct *isec = inode->i_security[selinux_idx];
+ struct superblock_security_struct *sbsec =
inode->i_sb->s_security[selinux_idx];

if (!sbsec->initialized) {
/* Defer initialization to selinux_complete_init. */
@@ -1289,9 +1283,9 @@
unsigned int len;
int rc;

- tsec = current->security;
- dsec = dir->i_security;
- sbsec = dir->i_sb->s_security;
+ tsec = current->security[selinux_idx];
+ dsec = dir->i_security[selinux_idx];
+ sbsec = dir->i_sb->s_security[selinux_idx];

inode = dentry->d_inode;
if (!inode) {
@@ -1358,14 +1352,10 @@

static int selinux_ptrace(struct task_struct *parent, struct
task_struct *child)
{
- struct task_security_struct *psec = parent->security;
- struct task_security_struct *csec = child->security;
+ struct task_security_struct *psec = parent->security[selinux_idx];
+ struct task_security_struct *csec = child->security[selinux_idx];
int rc;

- rc = secondary_ops->ptrace(parent,child);
- if (rc)
- return rc;
-
rc = task_has_perm(parent, child, PROCESS__PTRACE);
/* Save the SID of the tracing process for later use in apply_creds.
*/
if (!rc)
@@ -1376,47 +1366,17 @@
static int selinux_capget(struct task_struct *target, kernel_cap_t
*effective,
kernel_cap_t *inheritable, kernel_cap_t
*permitted)
{
- int error;
-
- error = task_has_perm(current, target, PROCESS__GETCAP);
- if (error)
- return error;
-
- return secondary_ops->capget(target, effective, inheritable,
permitted);
+ return task_has_perm(current, target, PROCESS__GETCAP);
}

static int selinux_capset_check(struct task_struct *target,
kernel_cap_t *effective,
kernel_cap_t *inheritable, kernel_cap_t
*permitted)
{
- int error;
-
- error = secondary_ops->capset_check(target, effective, inheritable,
permitted);
- if (error)
- return error;
-
return task_has_perm(current, target, PROCESS__SETCAP);
}

-static void selinux_capset_set(struct task_struct *target, kernel_cap_t
*effective,
- kernel_cap_t *inheritable, kernel_cap_t
*permitted)
-{
- int error;
-
- error = task_has_perm(current, target, PROCESS__SETCAP);
- if (error)
- return;
-
- secondary_ops->capset_set(target, effective, inheritable, permitted);
-}
-
static int selinux_capable(struct task_struct *tsk, int cap)
{
- int rc;
-
- rc = secondary_ops->capable(tsk, cap);
- if (rc)
- return rc;
-
return task_has_capability(tsk,cap);
}

@@ -1428,11 +1388,7 @@
u32 tsid;
int rc;

- rc = secondary_ops->sysctl(table, op);
- if (rc)
- return rc;
-
- tsec = current->security;
+ tsec = current->security[selinux_idx];

rc = selinux_proc_get_sid(table->de, (op == 001) ?
SECCLASS_DIR : SECCLASS_FILE, &tsid);
@@ -1500,10 +1456,6 @@
{
int rc;

- rc = secondary_ops->syslog(type);
- if (rc)
- return rc;
-
switch (type) {
case 3: /* Read last kernel messages */
case 10: /* Return size of the log buffer */
@@ -1541,7 +1493,7 @@
{
unsigned long free, allowed;
int rc;
- struct task_security_struct *tsec = current->security;
+ struct task_security_struct *tsec = current->security[selinux_idx];

vm_acct_memory(pages);

@@ -1569,13 +1521,10 @@
* Don't audit the check, as it is applied to all processes
* that allocate mappings.
*/
- rc = secondary_ops->capable(current, CAP_SYS_ADMIN);
- if (!rc) {
- rc = avc_has_perm_noaudit(tsec->sid, tsec->sid,
- SECCLASS_CAPABILITY,
- CAP_TO_MASK(CAP_SYS_ADMIN),
- NULL, NULL);
- }
+ rc = avc_has_perm_noaudit(tsec->sid, tsec->sid,
+ SECCLASS_CAPABILITY,
+ CAP_TO_MASK(CAP_SYS_ADMIN),
+ NULL, NULL);
if (rc)
free -= free / 32;

@@ -1613,7 +1562,7 @@
bsec->sid = SECINITSID_UNLABELED;
bsec->set = 0;

- bprm->security = bsec;
+ bprm->security[selinux_idx] = bsec;
return 0;
}

@@ -1627,17 +1576,13 @@
struct avc_audit_data ad;
int rc;

- rc = secondary_ops->bprm_set_security(bprm);
- if (rc)
- return rc;
-
- bsec = bprm->security;
+ bsec = bprm->security[selinux_idx];

if (bsec->set)
return 0;

- tsec = current->security;
- isec = inode->i_security;
+ tsec = current->security[selinux_idx];
+ isec = inode->i_security[selinux_idx];

/* Default to the current task SID. */
bsec->sid = tsec->sid;
@@ -1696,33 +1641,26 @@
return 0;
}

-static int selinux_bprm_check_security (struct linux_binprm *bprm)
-{
- return secondary_ops->bprm_check_security(bprm);
-}
-
-
static int selinux_bprm_secureexec (struct linux_binprm *bprm)
{
- struct task_security_struct *tsec = current->security;
- int atsecure = 0;
+ struct task_security_struct *tsec = current->security[selinux_idx];

if (tsec->osid != tsec->sid) {
/* Enable secure mode for SIDs transitions unless
the noatsecure permission is granted between
the two SIDs, i.e. ahp returns 0. */
- atsecure = avc_has_perm(tsec->osid, tsec->sid,
+ return avc_has_perm(tsec->osid, tsec->sid,
SECCLASS_PROCESS,
PROCESS__NOATSECURE, NULL, NULL);
}

- return (atsecure || secondary_ops->bprm_secureexec(bprm));
+ return 0;
}

static void selinux_bprm_free_security(struct linux_binprm *bprm)
{
- struct bprm_security_struct *bsec = bprm->security;
- bprm->security = NULL;
+ struct bprm_security_struct *bsec = bprm->security[selinux_idx];
+ bprm->security[selinux_idx] = NULL;
kfree(bsec);
}

@@ -1822,11 +1760,9 @@
struct rlimit *rlim, *initrlim;
int rc, i;

- secondary_ops->bprm_apply_creds(bprm, unsafe);
+ tsec = current->security[selinux_idx];

- tsec = current->security;
-
- bsec = bprm->security;
+ bsec = bprm->security[selinux_idx];
sid = bsec->sid;

tsec->osid = tsec->sid;
@@ -2030,12 +1966,6 @@
unsigned long flags,
void * data)
{
- int rc;
-
- rc = secondary_ops->sb_mount(dev_name, nd, type, flags, data);
- if (rc)
- return rc;
-
if (flags & MS_REMOUNT)
return superblock_has_perm(current, nd->mnt->mnt_sb,
FILESYSTEM__REMOUNT, NULL);
@@ -2046,12 +1976,6 @@

static int selinux_umount(struct vfsmount *mnt, int flags)
{
- int rc;
-
- rc = secondary_ops->sb_umount(mnt, flags);
- if (rc)
- return rc;
-
return superblock_has_perm(current,mnt->mnt_sb,
FILESYSTEM__UNMOUNT,NULL);
}
@@ -2080,11 +2004,6 @@

static int selinux_inode_link(struct dentry *old_dentry, struct inode
*dir, struct dentry *new_dentry)
{
- int rc;
-
- rc = secondary_ops->inode_link(old_dentry,dir,new_dentry);
- if (rc)
- return rc;
return may_link(dir, old_dentry, MAY_LINK);
}

@@ -2095,11 +2014,6 @@

static int selinux_inode_unlink(struct inode *dir, struct dentry
*dentry)
{
- int rc;
-
- rc = secondary_ops->inode_unlink(dir, dentry);
- if (rc)
- return rc;
return may_link(dir, dentry, MAY_UNLINK);
}

@@ -2130,12 +2044,6 @@

static int selinux_inode_mknod(struct inode *dir, struct dentry
*dentry, int mode, dev_t dev)
{
- int rc;
-
- rc = secondary_ops->inode_mknod(dir, dentry, mode, dev);
- if (rc)
- return rc;
-
return may_create(dir, dentry, inode_mode_to_security_class(mode));
}

@@ -2163,23 +2071,12 @@

static int selinux_inode_follow_link(struct dentry *dentry, struct
nameidata *nameidata)
{
- int rc;
-
- rc = secondary_ops->inode_follow_link(dentry,nameidata);
- if (rc)
- return rc;
return dentry_has_perm(current, NULL, dentry, FILE__READ);
}

static int selinux_inode_permission(struct inode *inode, int mask,
struct nameidata *nd)
{
- int rc;
-
- rc = secondary_ops->inode_permission(inode, mask, nd);
- if (rc)
- return rc;
-
if (!mask) {
/* No permission to check. Existence test. */
return 0;
@@ -2191,12 +2088,6 @@

static int selinux_inode_setattr(struct dentry *dentry, struct iattr
*iattr)
{
- int rc;
-
- rc = secondary_ops->inode_setattr(dentry, iattr);
- if (rc)
- return rc;
-
if (iattr->ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID |
ATTR_ATIME_SET | ATTR_MTIME_SET))
return dentry_has_perm(current, NULL, dentry, FILE__SETATTR);
@@ -2211,9 +2102,9 @@

static int selinux_inode_setxattr(struct dentry *dentry, char *name,
void *value, size_t size, int flags)
{
- struct task_security_struct *tsec = current->security;
+ struct task_security_struct *tsec = current->security[selinux_idx];
struct inode *inode = dentry->d_inode;
- struct inode_security_struct *isec = inode->i_security;
+ struct inode_security_struct *isec = inode->i_security[selinux_idx];
struct superblock_security_struct *sbsec;
struct avc_audit_data ad;
u32 newsid;
@@ -2233,7 +2124,7 @@
return dentry_has_perm(current, NULL, dentry, FILE__SETATTR);
}

- sbsec = inode->i_sb->s_security;
+ sbsec = inode->i_sb->s_security[selinux_idx];
if (sbsec->behavior == SECURITY_FS_USE_MNTPOINT)
return -EOPNOTSUPP;

@@ -2270,7 +2161,7 @@
void *value, size_t size, int
flags)
{
struct inode *inode = dentry->d_inode;
- struct inode_security_struct *isec = inode->i_security;
+ struct inode_security_struct *isec = inode->i_security[selinux_idx];
u32 newsid;
int rc;

@@ -2293,7 +2184,7 @@
static int selinux_inode_getxattr (struct dentry *dentry, char *name)
{
struct inode *inode = dentry->d_inode;
- struct superblock_security_struct *sbsec = inode->i_sb->s_security;
+ struct superblock_security_struct *sbsec =
inode->i_sb->s_security[selinux_idx];

if (sbsec->behavior == SECURITY_FS_USE_MNTPOINT)
return -EOPNOTSUPP;
@@ -2330,7 +2221,7 @@

static int selinux_inode_getsecurity(struct inode *inode, const char
*name, void *buffer, size_t size)
{
- struct inode_security_struct *isec = inode->i_security;
+ struct inode_security_struct *isec = inode->i_security[selinux_idx];
char *context;
unsigned len;
int rc;
@@ -2360,7 +2251,7 @@
static int selinux_inode_setsecurity(struct inode *inode, const char
*name,
const void *value, size_t size,
int flags)
{
- struct inode_security_struct *isec = inode->i_security;
+ struct inode_security_struct *isec = inode->i_security[selinux_idx];
u32 newsid;
int rc;

@@ -2481,12 +2372,6 @@

static int selinux_file_mmap(struct file *file, unsigned long prot,
unsigned long flags)
{
- int rc;
-
- rc = secondary_ops->file_mmap(file, prot, flags);
- if (rc)
- return rc;
-
return file_map_prot_check(file, prot,
(flags & MAP_TYPE) == MAP_SHARED);
}
@@ -2494,12 +2379,6 @@
static int selinux_file_mprotect(struct vm_area_struct *vma,
unsigned long prot)
{
- int rc;
-
- rc = secondary_ops->file_mprotect(vma, prot);
- if (rc)
- return rc;
-
return file_map_prot_check(vma->vm_file, prot,
vma->vm_flags&VM_SHARED);
}

@@ -2557,8 +2436,8 @@
struct task_security_struct *tsec;
struct file_security_struct *fsec;

- tsec = current->security;
- fsec = file->f_security;
+ tsec = current->security[selinux_idx];
+ fsec = file->f_security[selinux_idx];
fsec->fown_sid = tsec->sid;

return 0;
@@ -2575,8 +2454,8 @@
/* struct fown_struct is never outside the context of a struct file */
file = (struct file *)((long)fown - offsetof(struct
file,f_owner));

- tsec = tsk->security;
- fsec = file->f_security;
+ tsec = tsk->security[selinux_idx];
+ fsec = file->f_security[selinux_idx];

if (!signum)
perm = signal_to_av(SIGIO); /* as per send_sigio_to_task */
@@ -2596,12 +2475,6 @@

static int selinux_task_create(unsigned long clone_flags)
{
- int rc;
-
- rc = secondary_ops->task_create(clone_flags);
- if (rc)
- return rc;
-
return task_has_perm(current, current, PROCESS__FORK);
}

@@ -2610,12 +2483,12 @@
struct task_security_struct *tsec1, *tsec2;
int rc;

- tsec1 = current->security;
+ tsec1 = current->security[selinux_idx];

rc = task_alloc_security(tsk);
if (rc)
return rc;
- tsec2 = tsk->security;
+ tsec2 = tsk->security[selinux_idx];

tsec2->osid = tsec1->osid;
tsec2->sid = tsec1->sid;
@@ -2648,11 +2521,6 @@
return 0;
}

-static int selinux_task_post_setuid(uid_t id0, uid_t id1, uid_t id2,
int flags)
-{
- return secondary_ops->task_post_setuid(id0,id1,id2,flags);
-}
-
static int selinux_task_setgid(gid_t id0, gid_t id1, gid_t id2, int
flags)
{
/* See the comment for setuid above. */
@@ -2682,23 +2550,12 @@

static int selinux_task_setnice(struct task_struct *p, int nice)
{
- int rc;
-
- rc = secondary_ops->task_setnice(p, nice);
- if (rc)
- return rc;
-
return task_has_perm(current,p, PROCESS__SETSCHED);
}

static int selinux_task_setrlimit(unsigned int resource, struct rlimit
*new_rlim)
{
struct rlimit *old_rlim = current->signal->rlim + resource;
- int rc;
-
- rc = secondary_ops->task_setrlimit(resource, new_rlim);
- if (rc)
- return rc;

/* Control the ability to change the hard limit (whether
lowering or raising it), so that the hard limit can
@@ -2714,8 +2571,8 @@
{
struct task_security_struct *tsec1, *tsec2;

- tsec1 = current->security;
- tsec2 = p->security;
+ tsec1 = current->security[selinux_idx];
+ tsec2 = p->security[selinux_idx];

/* No auditing from the setscheduler hook, since the runqueue lock
is held and the system will deadlock if we try to log an audit
@@ -2733,11 +2590,6 @@
static int selinux_task_kill(struct task_struct *p, struct siginfo
*info, int sig)
{
u32 perm;
- int rc;
-
- rc = secondary_ops->task_kill(p, info, sig);
- if (rc)
- return rc;

if (info && ((unsigned long)info == 1 ||
(unsigned long)info == 2 || SI_FROMKERNEL(info)))
@@ -2776,9 +2628,7 @@
{
struct task_security_struct *tsec;

- secondary_ops->task_reparent_to_init(p);
-
- tsec = p->security;
+ tsec = p->security[selinux_idx];
tsec->osid = tsec->sid;
tsec->sid = SECINITSID_KERNEL;
return;
@@ -2787,8 +2637,8 @@
static void selinux_task_to_inode(struct task_struct *p,
struct inode *inode)
{
- struct task_security_struct *tsec = p->security;
- struct inode_security_struct *isec = inode->i_security;
+ struct task_security_struct *tsec = p->security[selinux_idx];
+ struct inode_security_struct *isec = inode->i_security[selinux_idx];

isec->sid = tsec->sid;
isec->initialized = 1;
@@ -2957,8 +2807,8 @@
struct avc_audit_data ad;
int err = 0;

- tsec = task->security;
- isec = SOCK_INODE(sock)->i_security;
+ tsec = task->security[selinux_idx];
+ isec = SOCK_INODE(sock)->i_security[selinux_idx];

if (isec->sid == SECINITSID_KERNEL)
goto out;
@@ -2981,7 +2831,7 @@
if (kern)
goto out;

- tsec = current->security;
+ tsec = current->security[selinux_idx];
err = avc_has_perm(tsec->sid, tsec->sid,
socket_type_to_security_class(family, type,
protocol), SOCKET__CREATE, NULL, NULL);
@@ -3000,9 +2850,9 @@
err = inode_doinit(SOCK_INODE(sock));
if (err < 0)
return;
- isec = SOCK_INODE(sock)->i_security;
+ isec = SOCK_INODE(sock)->i_security[selinux_idx];

- tsec = current->security;
+ tsec = current->security[selinux_idx];
isec->sclass = socket_type_to_security_class(family, type, protocol);
isec->sid = kern ? SECINITSID_KERNEL : tsec->sid;

@@ -3039,8 +2889,8 @@
struct sock *sk = sock->sk;
u32 sid, node_perm, addrlen;

- tsec = current->security;
- isec = SOCK_INODE(sock)->i_security;
+ tsec = current->security[selinux_idx];
+ isec = SOCK_INODE(sock)->i_security[selinux_idx];

if (family == PF_INET) {
addr4 = (struct sockaddr_in *)address;
@@ -3129,9 +2979,9 @@
err = inode_doinit(SOCK_INODE(newsock));
if (err < 0)
return err;
- newisec = SOCK_INODE(newsock)->i_security;
+ newisec = SOCK_INODE(newsock)->i_security[selinux_idx];

- isec = SOCK_INODE(sock)->i_security;
+ isec = SOCK_INODE(sock)->i_security[selinux_idx];
newisec->sclass = isec->sclass;
newisec->sid = isec->sid;

@@ -3186,12 +3036,8 @@
struct avc_audit_data ad;
int err;

- err = secondary_ops->unix_stream_connect(sock, other, newsk);
- if (err)
- return err;
-
- isec = SOCK_INODE(sock)->i_security;
- other_isec = SOCK_INODE(other)->i_security;
+ isec = SOCK_INODE(sock)->i_security[selinux_idx];
+ other_isec = SOCK_INODE(other)->i_security[selinux_idx];

AVC_AUDIT_DATA_INIT(&ad,NET);
ad.u.net.sk = other->sk;
@@ -3204,11 +3050,11 @@
return err;

/* connecting socket */
- ssec = sock->sk->sk_security;
+ ssec = sock->sk->sk_security[selinux_idx];
ssec->peer_sid = other_isec->sid;

/* server child socket */
- ssec = newsk->sk_security;
+ ssec = newsk->sk_security[selinux_idx];
ssec->peer_sid = isec->sid;

return 0;
@@ -3222,8 +3068,8 @@
struct avc_audit_data ad;
int err;

- isec = SOCK_INODE(sock)->i_security;
- other_isec = SOCK_INODE(other)->i_security;
+ isec = SOCK_INODE(sock)->i_security[selinux_idx];
+ other_isec = SOCK_INODE(other)->i_security[selinux_idx];

AVC_AUDIT_DATA_INIT(&ad,NET);
ad.u.net.sk = other->sk;
@@ -3265,7 +3111,7 @@
inode = SOCK_INODE(sock);
if (inode) {
struct inode_security_struct *isec;
- isec = inode->i_security;
+ isec = inode->i_security[selinux_idx];
sock_sid = isec->sid;
sock_class = isec->sclass;
}
@@ -3349,13 +3195,13 @@
struct sk_security_struct *ssec;
struct inode_security_struct *isec;

- isec = SOCK_INODE(sock)->i_security;
+ isec = SOCK_INODE(sock)->i_security[selinux_idx];
if (isec->sclass != SECCLASS_UNIX_STREAM_SOCKET) {
err = -ENOPROTOOPT;
goto out;
}

- ssec = sock->sk->sk_security;
+ ssec = sock->sk->sk_security[selinux_idx];

err = security_sid_to_context(ssec->peer_sid, &scontext,
&scontext_len);
if (err)
@@ -3394,7 +3240,7 @@
u32 perm;
struct nlmsghdr *nlh;
struct socket *sock = sk->sk_socket;
- struct inode_security_struct *isec = SOCK_INODE(sock)->i_security;
+ struct inode_security_struct *isec =
SOCK_INODE(sock)->i_security[selinux_idx];

if (skb->len < NLMSG_SPACE(0)) {
err = -EINVAL;
@@ -3451,7 +3297,7 @@
if (err)
goto out;

- isec = inode->i_security;
+ isec = inode->i_security[selinux_idx];

switch (isec->sclass) {
case SECCLASS_UDP_SOCKET:
@@ -3577,7 +3423,7 @@
struct kern_ipc_perm *perm,
u16 sclass)
{
- struct task_security_struct *tsec = task->security;
+ struct task_security_struct *tsec = task->security[selinux_idx];
struct ipc_security_struct *isec;

isec = kmalloc(sizeof(struct ipc_security_struct), GFP_KERNEL);
@@ -3593,18 +3439,18 @@
} else {
isec->sid = SECINITSID_UNLABELED;
}
- perm->security = isec;
+ perm->security[selinux_idx] = isec;

return 0;
}

static void ipc_free_security(struct kern_ipc_perm *perm)
{
- struct ipc_security_struct *isec = perm->security;
+ struct ipc_security_struct *isec = perm->security[selinux_idx];
if (!isec || isec->magic != SELINUX_MAGIC)
return;

- perm->security = NULL;
+ perm->security[selinux_idx] = NULL;
kfree(isec);
}

@@ -3620,18 +3466,18 @@
msec->magic = SELINUX_MAGIC;
msec->msg = msg;
msec->sid = SECINITSID_UNLABELED;
- msg->security = msec;
+ msg->security[selinux_idx] = msec;

return 0;
}

static void msg_msg_free_security(struct msg_msg *msg)
{
- struct msg_security_struct *msec = msg->security;
+ struct msg_security_struct *msec = msg->security[selinux_idx];
if (!msec || msec->magic != SELINUX_MAGIC)
return;

- msg->security = NULL;
+ msg->security[selinux_idx] = NULL;
kfree(msec);
}

@@ -3642,8 +3488,8 @@
struct ipc_security_struct *isec;
struct avc_audit_data ad;

- tsec = current->security;
- isec = ipc_perms->security;
+ tsec = current->security[selinux_idx];
+ isec = ipc_perms->security[selinux_idx];

AVC_AUDIT_DATA_INIT(&ad, IPC);
ad.u.ipc_id = ipc_perms->key;
@@ -3674,8 +3520,8 @@
if (rc)
return rc;

- tsec = current->security;
- isec = msq->q_perm.security;
+ tsec = current->security[selinux_idx];
+ isec = msq->q_perm.security[selinux_idx];

AVC_AUDIT_DATA_INIT(&ad, IPC);
ad.u.ipc_id = msq->q_perm.key;
@@ -3700,8 +3546,8 @@
struct ipc_security_struct *isec;
struct avc_audit_data ad;

- tsec = current->security;
- isec = msq->q_perm.security;
+ tsec = current->security[selinux_idx];
+ isec = msq->q_perm.security[selinux_idx];

AVC_AUDIT_DATA_INIT(&ad, IPC);
ad.u.ipc_id = msq->q_perm.key;
@@ -3746,9 +3592,9 @@
struct avc_audit_data ad;
int rc;

- tsec = current->security;
- isec = msq->q_perm.security;
- msec = msg->security;
+ tsec = current->security[selinux_idx];
+ isec = msq->q_perm.security[selinux_idx];
+ msec = msg->security[selinux_idx];

/*
* First time through, need to assign label to the message
@@ -3796,9 +3642,9 @@
struct avc_audit_data ad;
int rc;

- tsec = target->security;
- isec = msq->q_perm.security;
- msec = msg->security;
+ tsec = target->security[selinux_idx];
+ isec = msq->q_perm.security[selinux_idx];
+ msec = msg->security[selinux_idx];

AVC_AUDIT_DATA_INIT(&ad, IPC);
ad.u.ipc_id = msq->q_perm.key;
@@ -3825,8 +3671,8 @@
if (rc)
return rc;

- tsec = current->security;
- isec = shp->shm_perm.security;
+ tsec = current->security[selinux_idx];
+ isec = shp->shm_perm.security[selinux_idx];

AVC_AUDIT_DATA_INIT(&ad, IPC);
ad.u.ipc_id = shp->shm_perm.key;
@@ -3851,8 +3697,8 @@
struct ipc_security_struct *isec;
struct avc_audit_data ad;

- tsec = current->security;
- isec = shp->shm_perm.security;
+ tsec = current->security[selinux_idx];
+ isec = shp->shm_perm.security[selinux_idx];

AVC_AUDIT_DATA_INIT(&ad, IPC);
ad.u.ipc_id = shp->shm_perm.key;
@@ -3898,11 +3744,6 @@
char __user *shmaddr, int shmflg)
{
u32 perms;
- int rc;
-
- rc = secondary_ops->shm_shmat(shp, shmaddr, shmflg);
- if (rc)
- return rc;

if (shmflg & SHM_RDONLY)
perms = SHM__READ;
@@ -3924,8 +3765,8 @@
if (rc)
return rc;

- tsec = current->security;
- isec = sma->sem_perm.security;
+ tsec = current->security[selinux_idx];
+ isec = sma->sem_perm.security[selinux_idx];

AVC_AUDIT_DATA_INIT(&ad, IPC);
ad.u.ipc_id = sma->sem_perm.key;
@@ -3950,8 +3791,8 @@
struct ipc_security_struct *isec;
struct avc_audit_data ad;

- tsec = current->security;
- isec = sma->sem_perm.security;
+ tsec = current->security[selinux_idx];
+ isec = sma->sem_perm.security[selinux_idx];

AVC_AUDIT_DATA_INIT(&ad, IPC);
ad.u.ipc_id = sma->sem_perm.key;
@@ -4017,7 +3858,7 @@

static int selinux_ipc_permission(struct kern_ipc_perm *ipcp, short
flag)
{
- struct ipc_security_struct *isec = ipcp->security;
+ struct ipc_security_struct *isec = ipcp->security[selinux_idx];
u16 sclass = SECCLASS_IPC;
u32 av = 0;

@@ -4036,37 +3877,6 @@
return ipc_has_perm(ipcp, sclass, av);
}

-/* module stacking operations */
-int selinux_register_security (const char *name, struct
security_operations *ops)
-{
- if (secondary_ops != original_ops) {
- printk(KERN_INFO "%s: There is already a secondary security "
- "module registered.\n", __FUNCTION__);
- return -EINVAL;
- }
-
- secondary_ops = ops;
-
- printk(KERN_INFO "%s: Registering secondary module %s\n",
- __FUNCTION__,
- name);
-
- return 0;
-}
-
-int selinux_unregister_security (const char *name, struct
security_operations *ops)
-{
- if (ops != secondary_ops) {
- printk (KERN_INFO "%s: trying to unregister a security module "
- "that is not registered.\n", __FUNCTION__);
- return -EINVAL;
- }
-
- secondary_ops = original_ops;
-
- return 0;
-}
-
static void selinux_d_instantiate (struct dentry *dentry, struct inode
*inode)
{
if (inode)
@@ -4090,7 +3900,7 @@
if (!size)
return -ERANGE;

- tsec = p->security;
+ tsec = p->security[selinux_idx];

if (!strcmp(name, "current"))
sid = tsec->sid;
@@ -4160,7 +3970,7 @@
operation. See selinux_bprm_set_security for the execve
checks and may_create for the file creation checks. The
operation will then fail if the context is not permitted. */
- tsec = p->security;
+ tsec = p->security[selinux_idx];
if (!strcmp(name, "exec"))
tsec->exec_sid = sid;
else if (!strcmp(name, "fscreate"))
@@ -4175,7 +3985,6 @@
.ptrace = selinux_ptrace,
.capget = selinux_capget,
.capset_check = selinux_capset_check,
- .capset_set = selinux_capset_set,
.sysctl = selinux_sysctl,
.capable = selinux_capable,
.quotactl = selinux_quotactl,
@@ -4190,7 +3999,6 @@
.bprm_free_security = selinux_bprm_free_security,
.bprm_apply_creds = selinux_bprm_apply_creds,
.bprm_set_security = selinux_bprm_set_security,
- .bprm_check_security = selinux_bprm_check_security,
.bprm_secureexec = selinux_bprm_secureexec,

.sb_alloc_security = selinux_sb_alloc_security,
@@ -4247,7 +4055,6 @@
.task_alloc_security = selinux_task_alloc_security,
.task_free_security = selinux_task_free_security,
.task_setuid = selinux_task_setuid,
- .task_post_setuid = selinux_task_post_setuid,
.task_setgid = selinux_task_setgid,
.task_setpgid = selinux_task_setpgid,
.task_getpgid = selinux_task_getpgid,
@@ -4287,9 +4094,6 @@
.sem_semctl = selinux_sem_semctl,
.sem_semop = selinux_sem_semop,

- .register_security = selinux_register_security,
- .unregister_security = selinux_unregister_security,
-
.d_instantiate = selinux_d_instantiate,

.getprocattr = selinux_getprocattr,
@@ -4319,10 +4123,13 @@
#endif
};

+#define MY_NAME "selinux"
__init int selinux_init(void)
{
struct task_security_struct *tsec;

+ secondary = 0;
+
if (!selinux_enabled) {
printk(KERN_INFO "SELinux: Disabled at boot.\n");
return 0;
@@ -4333,16 +4140,24 @@
/* Set the security state for the initial task. */
if (task_alloc_security(current))
panic("SELinux: Failed to initialize initial task.\n");
- tsec = current->security;
+ tsec = current->security[selinux_idx];
tsec->osid = tsec->sid = SECINITSID_KERNEL;

avc_init();

- original_ops = secondary_ops = security_ops;
- if (!secondary_ops)
- panic ("SELinux: No initial security operations\n");
- if (register_security (&selinux_ops))
- panic("SELinux: Unable to register with kernel.\n");
+ selinux_idx = register_security (&selinux_ops);
+ if (selinux_idx) {
+ secondary = 1;
+ selinux_idx = mod_reg_security( MY_NAME, &selinux_ops);
+ if (selinux_idx < 0) {
+ printk(KERN_ERR "%s: mod_reg_security returned %d.\n",
+ __FUNCTION__, selinux_idx);
+ panic("SELinux: Unable to register with kernel.\n");
+ } else {
+ printk(KERN_ERR "%s: registered with id %d\n",
+ __FUNCTION__, selinux_idx);
+ }
+ }

if (selinux_enforcing) {
printk(KERN_INFO "SELinux: Starting in enforcing mode\n");
@@ -4473,9 +4288,14 @@

selinux_disabled = 1;

- /* Reset security_ops to the secondary module, dummy or capability. */
- security_ops = secondary_ops;
-
+ /* Unregister selinux */
+ if (secondary) {
+ if (mod_unreg_security(MY_NAME, &selinux_ops))
+ printk(KERN_INFO "Failure unregistering selinux.\n");
+ } else {
+ if (unregister_security(&selinux_ops))
+ printk(KERN_INFO "Failure unregistering selinux.\n");
+ }
/* Unregister netfilter hooks. */
selinux_nf_ip_exit();

Index: linux-2.6.10-rc1-bk12-stack/security/selinux/selinuxfs.c
===================================================================
---
linux-2.6.10-rc1-bk12-stack.orig/security/selinux/selinuxfs.c 2004-11-02
19:56:04.454410920 -0600
+++ linux-2.6.10-rc1-bk12-stack/security/selinux/selinuxfs.c 2004-11-02
20:05:51.038236624 -0600
@@ -33,6 +33,8 @@

static DECLARE_MUTEX(sel_sem);

+extern int selinux_idx;
+
/* global data for booleans */
static struct dentry *bool_dir = NULL;
static int bool_num = 0;
@@ -46,7 +48,7 @@
{
struct task_security_struct *tsec;

- tsec = tsk->security;
+ tsec = tsk->security[selinux_idx];
if (!tsec)
return -EACCES;

@@ -856,7 +858,7 @@
ret = -ENAMETOOLONG;
goto err;
}
- isec = (struct inode_security_struct*)inode->i_security;
+ isec = (struct inode_security_struct*)inode->i_security[selinux_idx];
if ((ret = security_genfs_sid("selinuxfs", page, SECCLASS_FILE,
&sid)))
goto err;
isec->sid = sid;
@@ -934,7 +936,7 @@
inode = sel_make_inode(sb, S_IFCHR | S_IRUGO | S_IWUGO);
if (!inode)
goto out;
- isec = (struct inode_security_struct*)inode->i_security;
+ isec = (struct inode_security_struct*)inode->i_security[selinux_idx];
isec->sid = SECINITSID_DEVNULL;
isec->sclass = SECCLASS_CHR_FILE;
isec->initialized = 1;


2004-11-04 22:23:41

by Serge E. Hallyn

[permalink] [raw]
Subject: Re: [RFC] [PATCH] [6/6] LSM Stacking: temporary setprocattr hack

Stacker assumes that data written to /proc/<pid>/attr/* is of the
form:

module_name: data

Until the SELinux tools are rewritten to use this form, or it is decided
other LSMs should use a different way of communicating with userspace,
this patch will assume that data not in this form is intended for SELinux.

Signed-off-by: Serge E. Hallyn <[email protected]>

Index: linux-2.6.10-rc1-bk14/security/stacker.c
===================================================================
--- linux-2.6.10-rc1-bk14.orig/security/stacker.c 2004-11-04
12:31:02.636390608 -0600
+++ linux-2.6.10-rc1-bk14/security/stacker.c 2004-11-04
12:31:12.248929280 -0600
@@ -91,6 +91,8 @@
pointed to by stacked_modules. It's initially NULL (an empty list).
*/
struct module_entry *stacked_modules;

+static struct module_entry *selinux_module;
+static int selinux_is_loaded;

/* penultimate_stacked_module points to the next-to-last
* entry in the stacked list if there are 2+ entries, else it's NULL;
@@ -1007,12 +1009,20 @@

s = value;
e = strnchr(s, size, ':');
+ if (!e && selinux_is_loaded) {
+ m = selinux_module;
+ goto cont;
+ }
if (!e) {
printk(KERN_INFO "%s: couln't find module name end\n",
__FUNCTION__);
return -EINVAL;
}
m = find_lsm_module_by_name(s,e);
+ if (!m && selinux_is_loaded) {
+ m = selinux_module;
+ goto cont;
+ }
if (!m) {
strncpy(modname, s, 50);
modname[49] = '\0';
@@ -1021,6 +1031,7 @@
return -EINVAL;
}
s = e+1;
+cont:
while ((void *)s < value+size && *s == ' ')
s++;
if (s == value+size) {
@@ -1028,9 +1039,7 @@
return -EINVAL;
}
len = size - (int)((void *)s - value);
- printk(KERN_INFO "%s: sending to module\n", __FUNCTION__);
ret = m->module_operations.setprocattr(p,name,s,len);
- printk(KERN_INFO "%s: ret was %d, returning %d\n", __FUNCTION__, ret,
ret+(size-len));
if (ret < 0)
return ret;
return ret + (size-len);
@@ -1092,6 +1101,10 @@
new_module_entry->namelen = namelen;

add_module_entry(new_module_entry);
+ if (strcmp(name, "selinux") == 0) {
+ selinux_is_loaded = 1;
+ selinux_module = new_module_entry;
+ }

/* One more write barrier; this one is to _ensure_ that the
* inactive list is valid before releasing the locking. */
@@ -1139,6 +1152,10 @@
penultimate_stacked_module = bb;
}
num_stacked_modules--;
+ if (strcmp(m->module_name, "selinux") == 0) {
+ selinux_is_loaded = 0;
+ selinux_module = NULL;
+ }
kfree(m->module_name);
kfree(m);
out:
@@ -1567,6 +1584,9 @@
sysfsfiles_registered = 0;
num_stacked_modules = 0;

+ selinux_is_loaded = 0;
+ selinux_module = NULL;
+
INIT_STACKER_LOCKING;

if (register_security (&stacker_ops)) {


2004-11-04 22:25:58

by Serge E. Hallyn

[permalink] [raw]
Subject: Re: [RFC] [PATCH] [3/3] LSM Stacking: stackable bsdjail (Documentation)

The documentation for the bsdjail LSM.

Signed-off-by: Serge E. Hallyn <[email protected]>

diff -Nrup linux-2.6.9/Documentation/bsdjail.txt
linux-2.6.9-jail/Documentation/bsdjail.txt
--- linux-2.6.9/Documentation/bsdjail.txt 1969-12-31 18:00:00.000000000
-0600
+++ linux-2.6.9-jail/Documentation/bsdjail.txt 2004-10-20
14:41:28.266075800 -0500
@@ -0,0 +1,135 @@
+BSD Jail Linux Security Module
+Serge E. Hallyn <[email protected]>
+
+Description:
+
+Used in conjunction with per-process namespaces, this implements
+a subset of the BSD Jail functionality as a Linux LSM. What is
+currently implemented:
+
+ If a proces is in a jail, it:
+
+ 2. Cannot mount or umount
+ 3. Cannot send signals outside of jail
+ 4. Cannot ptrace processes outside of jail
+ 5. Cannot create devices
+ 6. Cannot renice processes
+ 7. Cannot load or unload modules
+ 8. Cannot change network settings
+ 9. May be assigned a specific ip address which will be used
+ for all it's socket binds.
+ 10. Cannot see contents of /proc/<pid> entries of processes not in
the
+ same jail. (We hide their existence for convenience's sake,
but
+ their existance can still be detected using, for instance,
statfs)
+ 11. Has no CAP_SYS_RAWIO capability (no ioperm/iopl)
+ 12. May not share IPC resources with processes outside its own jail.
+ 13. May find it's valid network address (if restricted) under
+ /proc/$$/attr/current.
+
+ If properly locked into its own namespace, processes will not be able
+ to escape to parts of the system's filesystem which were made
+ unavailable (without outside help).
+
+WARNINGS:
+The security of this module is very much dependent on the security
+of the rest of the system. You must carefully think through your
+use of the system.
+
+Some examples:
+ 1. If you leave /dev/hda1 in the jail, processes in the
+ jail can access that filesystem (i.e. /sbin/debugfs).
+ 2. If you provide root access within a jail, this can of
+ course be used to setuid binaries in the jail. Combined
+ with an unjailed regular user account, this gives jailed
+ users unjailed root access. (thanks to Brad Spender for
+ pointing this out).
+
+How to use:
+ 1. Load the bsdjail module if not already loaded or compiled in:
+
+ modprobe bsdjail
+
+ 3. (Optional) Set up an ipv4 alias for the jail
+
+ # /sbin/ifconfig eth0:0 192.168.1.101
+ # /sbin/route add -host 192.168.1.101 dev eth0:0
+
+ 3. Execute a shell under a new namespace:
+
+ exec clone_ns
+
+ (see http://www.win.tue.nl/~aeb/linux/lk/lk-6.html#6.3)
+
+ 4. If not already done, set up the filesystem for the jail. in our
+ example, we will set it up under /opt.
+
+ mount /dev/hdc5 /opt
+ mount -t proc proc /opt/proc
+
+ 5. Make sure there is an empty directory to put the old root in.
We
+ will just use /opt/mnt
+
+ mkdir /opt/mnt
+
+ 6. Pivot the old and new roots:
+
+ cd /opt
+ /sbin/pivot_root . mnt
+ /usr/sbin/chroot . /bin/sh
+
+ 7. Unmount the old root
+
+ umount -l /mnt
+
+ 6. Give the desired arguments for the jail. If no arguments are
+ necessary, just say:
+
+ echo lock > /proc/$$/attr/exec
+
+ To lock the process into an ip alias, say:
+
+ echo "ip 192.168.1.101" > /proc/$$/attr/exec
+
+ 7. Execute a new shell. The shell will be under the new jail, and
in
+ the private namespace you've been setting up.
+
+ exec /bin/sh
+
+ 8. To allow friends/customers/whoever to use this system, you might
start
+ start some services.
+
+ sshd
+
+ 9. Ssh is now running under the jail, so you no longer need the
original
+ shell:
+
+ exit
+
+The new shell runs in a private jail on the filesystem on /dev/hdc5. If
proc
+has been mounted under /dev/hdc5, then a "ps -auxw" under the jailed
shell
+will show only entries for processes started under that jail.
+
+If a private IP was specified for the jail, then
+ cat /proc/$$/attr/current
+will show the address for the private network device. Other network
+devices will be visible through /sbin/ifconfig -a, but not usable.
+
+If the reading process is not in a jail, then
+ cat /proc/$$/attr/current
+returns information about the root and ip * for the target process,
+or "Not Jailed" if the target process is not jailed.
+
+Cat /proc/$$/attr/exec gives a list of the valid keywords to cat into
+/proc/$$/attr/exec when starting a jail.
+
+Current valid keywords for creating a jail are:
+
+ lock: specifies the next exec should land us in a jail. (only
needed
+ if you don't want to give any other keywords)
+ ip: IPV4 addr for this jail
+ ip6: IPV6 addr for this jail
+ nrtask: Number of tasks in this jail
+ nice: The nice level for this jail. (maybe should be min/max?)
+ slice: Max timeslice per process
+ data: Max size of DATA segment per process
+ memlock: Max size of memory which can be locked per process


2004-11-04 22:25:59

by Serge E. Hallyn

[permalink] [raw]
Subject: Re: [RFC] [PATCH] [1/3] LSM Stacking: stackable bsdjail (tasklookup)

The next three patches add bsdjail as another example of stacking
LSMs. This patch adds the security_task_lookup hook required for
bsdjail.

Signed-off-by: Serge E. Hallyn <[email protected]>

Index: linux-2.6.10-rc1-bk14/fs/proc/base.c
===================================================================
--- linux-2.6.10-rc1-bk14.orig/fs/proc/base.c 2004-11-04
12:28:00.402094432 -0600
+++ linux-2.6.10-rc1-bk14/fs/proc/base.c 2004-11-04 12:31:28.974386624
-0600
@@ -1687,6 +1687,8 @@
int tgid = p->pid;
if (!pid_alive(p))
continue;
+ if (security_task_lookup(p))
+ continue;
if (--index >= 0)
continue;
tgids[nr_tgids] = tgid;
Index: linux-2.6.10-rc1-bk14/include/linux/security.h
===================================================================
--- linux-2.6.10-rc1-bk14.orig/include/linux/security.h 2004-11-04
12:28:12.736219360 -0600
+++ linux-2.6.10-rc1-bk14/include/linux/security.h 2004-11-04
12:31:28.977386168 -0600
@@ -623,6 +623,11 @@
* Set the security attributes in @p->security for a kernel thread
that
* is being reparented to the init task.
* @p contains the task_struct for the kernel thread.
+ * @task_lookup:
+ * Check permission to see the /proc/<pid> entry for process @p.
+ * @p contains the task_struct for task <pid> which is being looked
+ * up under /proc
+ * return 0 if permission is granted.
* @task_to_inode:
* Set the security attributes for an inode based on an associated
task's
* security attributes, e.g. for /proc/pid inodes.
@@ -1154,6 +1159,7 @@
unsigned long arg3, unsigned long arg4,
unsigned long arg5);
void (*task_reparent_to_init) (struct task_struct * p);
+ int (*task_lookup)(struct task_struct *p);
void (*task_to_inode)(struct task_struct *p, struct inode *inode);

int (*ipc_permission) (struct kern_ipc_perm * ipcp, short flag);
@@ -1759,6 +1765,11 @@
security_ops->task_reparent_to_init (p);
}

+static inline int security_task_lookup(struct task_struct *p)
+{
+ return security_ops->task_lookup(p);
+}
+
static inline void security_task_to_inode(struct task_struct *p, struct
inode *inode)
{
security_ops->task_to_inode(p, inode);
@@ -2399,6 +2410,11 @@
cap_task_reparent_to_init (p);
}

+static inline int security_task_lookup(struct task_struct *p)
+{
+ return 0;
+}
+
static inline void security_task_to_inode(struct task_struct *p, struct
inode *inode)
{ }

Index: linux-2.6.10-rc1-bk14/security/dummy.c
===================================================================
--- linux-2.6.10-rc1-bk14.orig/security/dummy.c 2004-11-04
12:28:13.741066600 -0600
+++ linux-2.6.10-rc1-bk14/security/dummy.c 2004-11-04 12:31:28.979385864
-0600
@@ -622,6 +622,11 @@
return;
}

+static int dummy_task_lookup(struct task_struct *p)
+{
+ return 0;
+}
+
static void dummy_task_to_inode(struct task_struct *p, struct inode
*inode)
{ }

@@ -985,6 +990,7 @@
set_to_dummy_if_null(ops, task_kill);
set_to_dummy_if_null(ops, task_prctl);
set_to_dummy_if_null(ops, task_reparent_to_init);
+ set_to_dummy_if_null(ops, task_lookup);
set_to_dummy_if_null(ops, task_to_inode);
set_to_dummy_if_null(ops, ipc_permission);
set_to_dummy_if_null(ops, msg_msg_alloc_security);
Index: linux-2.6.10-rc1-bk14/security/stacker.c
===================================================================
--- linux-2.6.10-rc1-bk14.orig/security/stacker.c 2004-11-04
12:31:12.248929280 -0600
+++ linux-2.6.10-rc1-bk14/security/stacker.c 2004-11-04
12:31:28.998382976 -0600
@@ -756,6 +756,11 @@
CALL_ALL(task_reparent_to_init,task_reparent_to_init(p));
}

+static int stacker_task_lookup(struct task_struct *p)
+{
+ RETURN_ERROR_IF_ANY_ERROR(task_lookup,task_lookup(p));
+}
+
static void stacker_task_to_inode(struct task_struct *p, struct inode
*inode)
{
CALL_ALL(task_to_inode,task_to_inode(p, inode));
@@ -1264,6 +1269,7 @@
.task_wait = stacker_task_wait,
.task_prctl = stacker_task_prctl,
.task_reparent_to_init = stacker_task_reparent_to_init,
+ .task_lookup = stacker_task_lookup,
.task_to_inode = stacker_task_to_inode,

.ipc_permission = stacker_ipc_permission,


2004-11-04 22:34:39

by Serge E. Hallyn

[permalink] [raw]
Subject: Re: [RFC] [PATCH] [2/3] LSM Stacking: stackable bsdjail (bsdjail lsm)

This patch adds the bsdjail LSM as another test of stacking.

Signed-off-by: Serge E. Hallyn <[email protected]>

Index: linux-2.6.10-rc1-bk12-stack/security/Kconfig
===================================================================
--- linux-2.6.10-rc1-bk12-stack.orig/security/Kconfig 2004-11-02
20:05:25.000000000 -0600
+++ linux-2.6.10-rc1-bk12-stack/security/Kconfig 2004-11-02
20:05:46.000000000 -0600
@@ -95,6 +95,17 @@

If you are unsure how to answer this question, answer N.

+config SECURITY_BSDJAIL
+ tristate "BSD Jail LSM"
+ depends on SECURITY
+ select SECURITY_NETWORK
+ help
+ Provides BSD Jail compartmentalization functionality.
+ See Documentation/bsdjail.txt for more information and
+ usage instructions.
+
+ If you are unsure how to answer this question, answer N.
+
source security/selinux/Kconfig

config SECURITY_STACKER
Index: linux-2.6.10-rc1-bk12-stack/security/Makefile
===================================================================
--- linux-2.6.10-rc1-bk12-stack.orig/security/Makefile 2004-11-02
20:05:25.000000000 -0600
+++ linux-2.6.10-rc1-bk12-stack/security/Makefile 2004-11-02
20:05:46.000000000 -0600
@@ -18,3 +18,4 @@
obj-$(CONFIG_SECURITY_CAPABILITIES) += commoncap.o capability.o
obj-$(CONFIG_SECURITY_ROOTPLUG) += commoncap.o root_plug.o
obj-$(CONFIG_SECURITY_SECLVL) += seclvl.o
+obj-$(CONFIG_SECURITY_BSDJAIL) += bsdjail.o
Index: linux-2.6.10-rc1-bk12-stack/security/bsdjail.c
===================================================================
--- linux-2.6.10-rc1-bk12-stack.orig/security/bsdjail.c 2004-11-02
13:46:30.056668176 -0600
+++ linux-2.6.10-rc1-bk12-stack/security/bsdjail.c 2004-11-02
20:29:19.142172288 -0600
@@ -0,0 +1,1365 @@
+/*
+ * File: linux/security/bsdjail.c
+ * Author: Serge Hallyn ([email protected])
+ * Date: Sep 12, 2004
+ *
+ * (See Documentation/bsdjail.txt for more information)
+ *
+ * Copyright (C) 2004 International Business Machines
<[email protected]>
+ *
+ * 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.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/security.h>
+#include <linux/namei.h>
+#include <linux/namespace.h>
+#include <linux/proc_fs.h>
+#include <linux/in.h>
+#include <linux/in6.h>
+#include <linux/pagemap.h>
+#include <linux/ip.h>
+#include <net/ipv6.h>
+#include <linux/mount.h>
+#include <linux/netdevice.h>
+#include <linux/inetdevice.h>
+#include <linux/seq_file.h>
+#include <linux/un.h>
+#include <linux/smp_lock.h>
+#include <linux/kref.h>
+#include <asm/uaccess.h>
+
+static int jail_debug;
+module_param(jail_debug, int, 0);
+MODULE_PARM_DESC(jail_debug, "Print bsd jail debugging messages.\n");
+
+static int jail_idx; /* index into security[CONFIG_NUM_LSMS] field */
+
+#define DBG 0
+#define WARN 1
+#define bsdj_debug(how, fmt, arg... ) \
+ do { \
+ if ( how || jail_debug ) \
+ printk(KERN_NOTICE "%s: %s: " fmt, \
+ MY_NAME, __FUNCTION__ , \
+ ## arg ); \
+ } while ( 0 )
+
+#define MY_NAME "bsdjail"
+
+/* flag to keep track of how we were registered */
+static int secondary;
+
+/*
+ * The task structure holding jail information.
+ * Taskp->security points to one of these (or is null).
+ * There is exactly one jail_struct for each jail. If >1 process
+ * are in the same jail, they share the same jail_struct.
+ */
+struct jail_struct {
+ struct kref kref;
+
+ /* these are set on writes to /proc/<pid>/attr/exec */
+ char *ip4_addr_name; /* char * containing ip4 addr to use for jail */
+ char *ip6_addr_name; /* char * containing ip6 addr to use for jail */
+
+ /* these are set when a jail becomes active */
+ __u32 addr4; /* internal form of ip4_addr_name */
+ struct in6_addr addr6; /* internal form of ip6_addr_name */
+
+ /* Resource limits. 0 = no limit */
+ int max_nrtask; /* maximum number of tasks within this jail. */
+ int cur_nrtask; /* current number of tasks within this jail. */
+ long maxtimeslice; /* max timeslice in ms for procs in this jail
*/
+ long nice; /* nice level for processes in this jail */
+ long max_data, max_memlock; /* equivalent to RLIMIT_{DATA, MEMLOCK}
*/
+/* values for the jail_flags field */
+#define IN_USE 1 /* if 0, task is setting up jail, not yet in it */
+#define GOT_IPV4 2
+#define GOT_IPV6 4 /* if 0, ipv4, else ipv6 */
+ char jail_flags;
+};
+
+/*
+ * disable_jail: A jail which was in use, but has no references
+ * left, is disabled - we free up the mountpoint and dentry, and
+ * give up our reference on the module.
+ *
+ * don't need to put namespace, it will be done automatically
+ * when the last process in jail is put.
+ * DO need to put the dentry and vfsmount
+ */
+static void
+disable_jail(struct jail_struct *tsec)
+{
+ module_put(THIS_MODULE);
+}
+
+
+static void free_jail(struct jail_struct *tsec)
+{
+ if (!tsec)
+ return;
+
+ kfree(tsec->ip4_addr_name);
+ kfree(tsec->ip6_addr_name);
+ kfree(tsec);
+}
+
+/* release_jail:
+ * Callback for kref_put to use for releasing a jail when its
+ * last user exits.
+ */
+static void release_jail(struct kref *kref)
+{
+ struct jail_struct *tsec;
+
+ tsec = container_of(kref, struct jail_struct, kref);
+ disable_jail(tsec);
+ free_jail(tsec);
+}
+
+/*
+ * jail_task_free_security: this is the callback hooked into LSM.
+ * If there was no task->security field for bsdjail, do nothing.
+ * If there was, but it was never put into use, free the jail.
+ * If there was, and the jail is in use, then decrement the usage
+ * count, and disable and free the jail if the usage count hits 0.
+ */
+static void jail_task_free_security(struct task_struct *task)
+{
+ struct jail_struct *tsec = task->security[jail_idx];
+
+ if (!tsec)
+ return;
+
+ if (!(tsec->jail_flags & IN_USE)) {
+ /*
+ * someone did 'echo -n x > /proc/<pid>/attr/exec' but
+ * then forked before execing. Nuke the old info.
+ */
+ free_jail(tsec);
+ task->security[jail_idx] = NULL;
+ return;
+ }
+ tsec->cur_nrtask--;
+ /* If this was the last process in the jail, delete the jail */
+ kref_put(&tsec->kref, release_jail);
+}
+
+static struct jail_struct *
+alloc_task_security(struct task_struct *tsk)
+{
+ struct jail_struct *tsec;
+
+ tsec = kmalloc(sizeof(struct jail_struct), GFP_KERNEL);
+ if (tsec) {
+ memset(tsec, 0, sizeof(struct jail_struct));
+ tsk->security[jail_idx] = tsec;
+ }
+ return tsec;
+}
+
+static inline int
+in_jail(struct task_struct *t)
+{
+ struct jail_struct *tsec = t->security[jail_idx];
+
+ if (tsec && (tsec->jail_flags & IN_USE))
+ return 1;
+
+ return 0;
+}
+
+/*
+ * If a network address was passed into /proc/<pid>/attr/exec,
+ * then process in its jail will only be allowed to bind/listen
+ * to that address.
+ */
+static void
+setup_netaddress(struct jail_struct *tsec)
+{
+ unsigned int a, b, c, d, i;
+ unsigned int x[8];
+
+ tsec->jail_flags &= ~(GOT_IPV4 | GOT_IPV6);
+ tsec->addr4 = 0;
+ ipv6_addr_set(&tsec->addr6, 0, 0, 0, 0);
+
+ if (tsec->ip4_addr_name) {
+ if (sscanf(tsec->ip4_addr_name, "%u.%u.%u.%u",
+ &a, &b, &c, &d) != 4)
+ return;
+ if (a>255 || b>255 || c>255 || d>255)
+ return;
+ tsec->addr4 = htonl((a<<24) | (b<<16) | (c<<8) | d);
+ tsec->jail_flags |= GOT_IPV4;
+ bsdj_debug(DBG, "Network (ipv4) set up (%s)\n",
+ tsec->ip4_addr_name);
+ }
+
+ if (tsec->ip6_addr_name) {
+ if (sscanf(tsec->ip6_addr_name, "%x:%x:%x:%x:%x:%x:%x:%x",
+ &x[0], &x[1], &x[2], &x[3], &x[4], &x[5], &x[6],
+ &x[7]) != 8) {
+ printk(KERN_INFO "%s: bad ipv6 addr %s\n", __FUNCTION__,
+ tsec->ip6_addr_name);
+ return;
+ }
+ for (i=0; i<8; i++) {
+ if (x[i] > 65535) {
+ printk("%s: %x > 65535 at %d\n", __FUNCTION__, x[i], i);
+ return;
+ }
+ tsec->addr6.in6_u.u6_addr16[i] = htons(x[i]);
+ }
+ tsec->jail_flags |= GOT_IPV6;
+ bsdj_debug(DBG, "Network (ipv6) set up (%s)\n",
+ tsec->ip6_addr_name);
+ }
+}
+
+/*
+ * enable_jail:
+ * Called when a process is placed into a new jail to handle the
+ * actual creation of the jail.
+ * Creates namespace
+ * Stores the requested ip address
+ * Registers a unique pseudo-proc filesystem for this jail
+ */
+static int enable_jail(struct task_struct *tsk)
+{
+ struct jail_struct *tsec = tsk->security[jail_idx];
+ int retval = -EFAULT;
+
+ if (!tsec)
+ goto out;
+
+ /* set up networking */
+ if (tsec->ip4_addr_name || tsec->ip6_addr_name)
+ setup_netaddress(tsec);
+
+ tsec->cur_nrtask = 1;
+ if (tsec->nice)
+ set_user_nice(current, tsec->nice);
+ if (tsec->max_data) {
+ current->signal->rlim[RLIMIT_DATA].rlim_cur = tsec->max_data;
+ current->signal->rlim[RLIMIT_DATA].rlim_max = tsec->max_data;
+ }
+ if (tsec->max_memlock) {
+ current->signal->rlim[RLIMIT_MEMLOCK].rlim_cur =
+ tsec->max_memlock;
+ current->signal->rlim[RLIMIT_MEMLOCK].rlim_max =
+ tsec->max_memlock;
+ }
+ if (tsec->maxtimeslice) {
+ current->signal->rlim[RLIMIT_CPU].rlim_cur = tsec->maxtimeslice;
+ current->signal->rlim[RLIMIT_CPU].rlim_max = tsec->maxtimeslice;
+ }
+ /* success and end */
+ kref_init(&tsec->kref);
+ tsec->jail_flags |= IN_USE;
+
+ /* won't let ourselves be removed until this jail goes away */
+ try_module_get(THIS_MODULE);
+
+ return 0;
+
+out:
+ return retval;
+}
+
+/*
+ * LSM /proc/<pid>/attr hooks.
+ * You may write into /proc/<pid>/attr/exec:
+ * lock (no value, just to specify a jail)
+ * ip 2.2.2.2
+ etc...
+ * These values will be used on the next exec() to set up your jail
+ * (assuming you're not already in a jail)
+ */
+static int
+jail_setprocattr(struct task_struct *p, char *name, void *value, size_t
rsize)
+{
+ struct jail_struct *tsec = current->security[jail_idx];
+ long val;
+ char *v = value;
+ int start, len;
+ size_t size = rsize;
+
+ if (tsec && (tsec->jail_flags & IN_USE))
+ return -EINVAL; /* let them guess why */
+
+ if (p != current || strcmp(name, "exec"))
+ return -EPERM;
+
+ if (!tsec) {
+ tsec = alloc_task_security(current);
+ if (!tsec)
+ return -ENOMEM;
+ }
+
+ if (v[size-1] == '\n')
+ size--;
+
+ if (strncmp(value, "ip ", 3) == 0) {
+ kfree(tsec->ip4_addr_name);
+ start = 3;
+ len = size - start + 1;
+ tsec->ip4_addr_name = kmalloc(len, GFP_KERNEL);
+ if (!tsec->ip4_addr_name)
+ return -ENOMEM;
+ strlcpy(tsec->ip4_addr_name, value+start, len);
+ } else if (strncmp(value, "ip6 ", 4) == 0) {
+ kfree(tsec->ip6_addr_name);
+ start = 4;
+ len = size - start + 1;
+ tsec->ip6_addr_name = kmalloc(len, GFP_KERNEL);
+ if (!tsec->ip6_addr_name)
+ return -ENOMEM;
+ strlcpy(tsec->ip6_addr_name, value+start, len);
+
+ /* the next two are equivalent */
+ } else if (strncmp(value, "slice ", 6) == 0) {
+ val = simple_strtoul(value+6, NULL, 0);
+ tsec->maxtimeslice = val;
+ } else if (strncmp(value, "timeslice ", 10) == 0) {
+ val = simple_strtoul(value+10, NULL, 0);
+ tsec->maxtimeslice = val;
+ } else if (strncmp(value, "nrtask ", 7) == 0) {
+ val = (int) simple_strtol(value+7, NULL, 0);
+ if (val < 1)
+ return -EINVAL;
+ tsec->max_nrtask = val;
+ } else if (strncmp(value, "memlock ", 8) == 0) {
+ val = simple_strtoul(value+8, NULL, 0);
+ tsec->max_memlock = val;
+ } else if (strncmp(value, "data ", 5) == 0) {
+ val = simple_strtoul(value+5, NULL, 0);
+ tsec->max_data = val;
+ } else if (strncmp(value, "nice ", 5) == 0) {
+ val = simple_strtoul(value+5, NULL, 0);
+ tsec->nice = val;
+ } else if (strncmp(value, "lock", 4) != 0)
+ return -EINVAL;
+
+ return rsize;
+}
+
+static int print_jail_net_info(struct jail_struct *j, char *buf, int
maxcnt)
+{
+ int len = 0;
+
+ if (j->ip4_addr_name)
+ len += snprintf(buf, maxcnt, "%s\n", j->ip4_addr_name);
+ if (j->ip6_addr_name)
+ len += snprintf(buf, maxcnt-len, "%s\n", j->ip6_addr_name);
+
+ return snprintf(buf, maxcnt, "No network information\n");
+}
+
+/*
+ * LSM /proc/<pid>/attr read hook.
+ *
+ * /proc/$$/attr/current output:
+ * If the reading process, say process 1001, is in a jail, then
+ * cat /proc/999/attr/current
+ * will print networking information.
+ * If the reading process, say process 1001, is not in a jail, then
+ * cat /proc/999/attr/current
+ * will return
+ * ip: (ip address of jail)
+ * if 999 is in a jail, or
+ * -EINVAL
+ * if 999 is not in a jail.
+ *
+ * /proc/$$/attr/exec output:
+ * A process in a jail gets -EINVAL for /proc/$$/attr/exec.
+ * A process not in a jail gets hints on starting a jail.
+ */
+static int
+jail_getprocattr(struct task_struct *p, char *name, void *value, size_t
size)
+{
+ struct jail_struct *tsec;
+ int err = 0;
+
+ if (in_jail(current)) {
+ if (strcmp(name, "current") == 0) {
+ /* provide network info */
+ err = print_jail_net_info(current->security[jail_idx], value,
+ size);
+ return err;
+ }
+ return -EINVAL; /* let them guess why */
+ }
+
+ if (strcmp(name, "exec") == 0) {
+ /* Print usage some help */
+ err = snprintf(value, size,
+ "Valid keywords:\n"
+ "lock\n"
+ "ip <ip4-addr>\n"
+ "ip6 <ip6-addr>\n"
+ "nrtask <max number of tasks in this jail>\n"
+ "nice <nice level for processes in this jail>\n"
+ "slice <max timeslice per process in msecs>\n"
+ "data <max data size per process in bytes>\n"
+ "memlock <max lockable memory per process in bytes>\n");
+ return err;
+ }
+
+ if (strcmp(name, "current"))
+ return -EPERM;
+
+ tsec = p->security[jail_idx];
+ if (!tsec || !(tsec->jail_flags & IN_USE)) {
+ err = snprintf(value, size, "Not Jailed\n");
+ } else {
+ err = snprintf(value, size,
+ "IPv4: %s\nIPv6: %s\n"
+ "max_nrtask %d current nrtask %d max_timeslice %lu "
+ "nice %lu\n"
+ "max_memlock %lu max_data %lu\n",
+ tsec->ip4_addr_name ? tsec->ip4_addr_name : "(none)",
+ tsec->ip6_addr_name ? tsec->ip6_addr_name : "(none)",
+ tsec->max_nrtask, tsec->cur_nrtask, tsec->maxtimeslice,
+ tsec->nice, tsec->max_data, tsec->max_memlock);
+ }
+
+ return err;
+}
+
+/*
+ * Forbid a process in a jail from sending a signal to a process in
another
+ * (or no) jail through file sigio.
+ *
+ * We consider the process which set the fowner to be the one sending
the
+ * signal, rather than the one writing to the file. Therefore we store
the
+ * jail of a process during jail_file_set_fowner, then check that
against
+ * the jail of the process receiving the signal.
+ */
+static int
+jail_file_send_sigiotask(struct task_struct *tsk, struct fown_struct
*fown,
+ int sig)
+{
+ struct file *file;
+
+ if (!in_jail(current))
+ return 0;
+
+ file = container_of(fown, struct file, f_owner);
+ if (file->f_security[jail_idx] != tsk->security[jail_idx])
+ return -EPERM;
+
+ return 0;
+}
+
+static int
+jail_file_set_fowner(struct file *file)
+{
+ struct jail_struct *tsec;
+
+ tsec = current->security[jail_idx];
+ file->f_security[jail_idx] = tsec;
+ if (tsec)
+ kref_get(&tsec->kref);
+
+ return 0;
+}
+
+static void free_ipc_security(struct kern_ipc_perm *ipc)
+{
+ struct jail_struct *tsec;
+
+ tsec = ipc->security[jail_idx];
+ if (!tsec)
+ return;
+ kref_put(&tsec->kref, release_jail);
+ ipc->security[jail_idx] = NULL;
+}
+
+static void free_file_security(struct file *file)
+{
+ struct jail_struct *tsec;
+
+ tsec = file->f_security[jail_idx];
+ if (!tsec)
+ return;
+ kref_put(&tsec->kref, release_jail);
+ file->f_security[jail_idx] = NULL;
+}
+
+static void free_inode_security(struct inode *inode)
+{
+ struct jail_struct *tsec;
+
+ tsec = inode->i_security[jail_idx];
+ if (!tsec)
+ return;
+ kref_put(&tsec->kref, release_jail);
+ inode->i_security[jail_idx] = NULL;
+}
+
+/*
+ * LSM ptrace hook:
+ * process in jail may not ptrace process not in the same jail
+ */
+static int
+jail_ptrace (struct task_struct *tracer, struct task_struct *tracee)
+{
+ struct jail_struct *tsec = tracer->security[jail_idx];
+
+ if (tsec && (tsec->jail_flags & IN_USE)) {
+ if (tsec == tracee->security[jail_idx])
+ return 0;
+ return -EPERM;
+ }
+ return 0;
+}
+
+/*
+ * process in jail may only use one (aliased) ip address. If they try
to
+ * attach to 127.0.0.1, that is remapped to their own address. If some
+ * other address (and not their own), deny permission
+ */
+static int jail_socket_unix_bind(struct socket *sock, struct sockaddr
*address,
+ int addrlen);
+
+#define loopbackaddr htonl((127 << 24) | 1)
+
+static inline int jail_inet4_bind(struct socket *sock, struct sockaddr
*address,
+ int addrlen, struct jail_struct *tsec)
+{
+ struct sockaddr_in *inaddr;
+ __u32 sin_addr, jailaddr;
+
+ if (!(tsec->jail_flags & GOT_IPV4))
+ return -EPERM;
+
+ inaddr = (struct sockaddr_in *) address;
+ sin_addr = inaddr->sin_addr.s_addr;
+ jailaddr = tsec->addr4;
+
+ if (sin_addr == jailaddr)
+ return 0;
+
+ if (sin_addr == loopbackaddr || !sin_addr) {
+ bsdj_debug(DBG, "Got a loopback or 0 address\n");
+ sin_addr = jailaddr;
+ bsdj_debug(DBG, "Converted to: %u.%u.%u.%u\n",
+ NIPQUAD(sin_addr));
+ return 0;
+ }
+
+ return -EPERM;
+}
+
+static inline int
+jail_inet6_bind(struct socket *sock, struct sockaddr *address, int
addrlen,
+ struct jail_struct *tsec)
+{
+ struct sockaddr_in6 *inaddr6;
+ struct in6_addr *sin6_addr, *jailaddr;
+
+ if (!(tsec->jail_flags & GOT_IPV6))
+ return -EPERM;
+
+ inaddr6 = (struct sockaddr_in6 *) address;
+ sin6_addr = &inaddr6->sin6_addr;
+ jailaddr = &tsec->addr6;
+
+ if (ipv6_addr_cmp(jailaddr, sin6_addr) == 0)
+ return 0;
+
+ if (ipv6_addr_cmp(sin6_addr, &in6addr_loopback) == 0) {
+ ipv6_addr_copy(sin6_addr, jailaddr);
+ return 0;
+ }
+
+ printk(KERN_NOTICE "%s: DENYING\n", __FUNCTION__);
+ printk(KERN_NOTICE "%s: a %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x "
+ "j %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
+ __FUNCTION__,
+ NIP6(*sin6_addr),
+ NIP6(*jailaddr));
+
+ return -EPERM;
+}
+
+static int
+jail_socket_bind(struct socket *sock, struct sockaddr *address, int
addrlen)
+{
+ struct jail_struct *tsec = current->security[jail_idx];
+
+ if (!tsec || !(tsec->jail_flags & IN_USE))
+ return 0;
+
+ if (sock->sk->sk_family == AF_UNIX)
+ return jail_socket_unix_bind(sock, address, addrlen);
+
+ if (!(tsec->jail_flags & (GOT_IPV4 | GOT_IPV6)))
+ /* If we want to be strict, we could just
+ * deny net access when lacking a pseudo ip.
+ * For now we just allow it. */
+ return 0;
+
+ switch(address->sa_family) {
+ case AF_INET:
+ return jail_inet4_bind(sock, address, addrlen, tsec);
+
+ case AF_INET6:
+ return jail_inet6_bind(sock, address, addrlen, tsec);
+
+ default:
+ return 0;
+ }
+}
+
+/*
+ * If locked in an ipv6 jail, don't let them use ipv4, and vice versa
+ */
+static int
+jail_socket_create(int family, int type, int protocol, int kern)
+{
+ struct jail_struct *tsec = current->security[jail_idx];
+
+ if (!tsec || kern || !(tsec->jail_flags & IN_USE) ||
+ !(tsec->jail_flags & (GOT_IPV4 | GOT_IPV6)))
+ return 0;
+
+ switch(family) {
+ case AF_INET:
+ if (tsec->jail_flags & GOT_IPV4)
+ return 0;
+ return -EPERM;
+ case AF_INET6:
+ if (tsec->jail_flags & GOT_IPV6)
+ return 0;
+ return -EPERM;
+ default:
+ return 0;
+ };
+
+ return 0;
+}
+
+static void
+jail_socket_post_create(struct socket *sock, int family, int type,
+ int protocol, int kern)
+{
+ struct inet_opt *inet;
+ struct ipv6_pinfo *inet6;
+ struct jail_struct *tsec = current->security[jail_idx];
+
+ if (!tsec || kern || !(tsec->jail_flags & IN_USE) ||
+ !(tsec->jail_flags & (GOT_IPV4 | GOT_IPV6)))
+ return;
+
+ switch(family) {
+ case AF_INET:
+ inet = inet_sk(sock->sk);
+ inet->saddr = tsec->addr4;
+ break;
+ case AF_INET6:
+ inet6 = inet6_sk(sock->sk);
+ ipv6_addr_copy(&inet6->saddr, &tsec->addr6);
+ break;
+ default:
+ break;
+ };
+
+ return;
+}
+
+static int
+jail_socket_listen(struct socket *sock, int backlog)
+{
+ struct inet_opt *inet;
+ struct ipv6_pinfo *inet6;
+ struct jail_struct *tsec = current->security[jail_idx];
+
+ if (!tsec || !(tsec->jail_flags & IN_USE) ||
+ !(tsec->jail_flags & (GOT_IPV4 | GOT_IPV6)))
+ return 0;
+
+ switch (sock->sk->sk_family) {
+ case AF_INET:
+ inet = inet_sk(sock->sk);
+ if (inet->saddr == tsec->addr4)
+ return 0;
+ return -EPERM;
+
+ case AF_INET6:
+ inet6 = inet6_sk(sock->sk);
+ if (ipv6_addr_cmp(&inet6->saddr, &tsec->addr6) == 0)
+ return 0;
+ return -EPERM;
+
+ default:
+ return 0;
+
+ }
+}
+
+static void free_sock_security(struct sock *sk)
+{
+ struct jail_struct *tsec;
+
+ tsec = sk->sk_security[jail_idx];
+ if (!tsec)
+ return;
+ kref_put(&tsec->kref, release_jail);
+ sk->sk_security[jail_idx] = NULL;
+}
+
+/*
+ * The next three (socket) hooks prevent a process in a jail from
sending
+ * data to a abstract unix domain socket which was bound outside the
jail.
+ */
+static int
+jail_socket_unix_bind(struct socket *sock, struct sockaddr *address,
+ int addrlen)
+{
+ struct sockaddr_un *sunaddr;
+ struct jail_struct *tsec;
+
+ if (sock->sk->sk_family != AF_UNIX)
+ return 0;
+
+ sunaddr = (struct sockaddr_un *) address;
+ if (sunaddr->sun_path[0] != 0)
+ return 0;
+
+ tsec = current->security[jail_idx];
+ sock->sk->sk_security[jail_idx] = tsec;
+ if (tsec)
+ kref_get(&tsec->kref);
+ return 0;
+}
+
+/*
+ * Note - we deny sends both from unjailed to jailed, and from jailed
+ * to unjailed. As well as, of course between different jails.
+ */
+static int
+jail_socket_unix_may_send(struct socket *sock, struct socket *other)
+{
+ struct jail_struct *tsec, *ssec;
+
+ tsec = current->security[jail_idx]; /* jail of sending process */
+ ssec = other->sk->sk_security[jail_idx]; /* jail of receiver */
+
+ if (tsec != ssec)
+ return -EPERM;
+
+ return 0;
+}
+
+static int
+jail_socket_unix_stream_connect(struct socket *sock,
+ struct socket *other, struct sock *newsk)
+{
+ struct jail_struct *tsec, *ssec;
+
+ tsec = current->security[jail_idx]; /* jail of sending process */
+ ssec = other->sk->sk_security[jail_idx]; /* jail of receiver */
+
+ if (tsec != ssec)
+ return -EPERM;
+
+ return 0;
+}
+
+static int
+jail_mount(char * dev_name, struct nameidata *nd, char * type,
+ unsigned long flags, void * data)
+{
+ if (in_jail(current))
+ return -EPERM;
+
+ return 0;
+}
+
+static int
+jail_umount(struct vfsmount *mnt, int flags)
+{
+ if (in_jail(current))
+ return -EPERM;
+
+ return 0;
+}
+
+/*
+ * process in jail may not:
+ * use nice
+ * change network config
+ * load/unload modules
+ */
+static int
+jail_capable (struct task_struct *tsk, int cap)
+{
+ if (in_jail(tsk)) {
+ if (cap == CAP_SYS_NICE)
+ return -EPERM;
+ if (cap == CAP_NET_ADMIN)
+ return -EPERM;
+ if (cap == CAP_SYS_MODULE)
+ return -EPERM;
+ if (cap == CAP_SYS_RAWIO)
+ return -EPERM;
+ }
+
+ if (cap_is_fs_cap (cap) ? tsk->fsuid == 0 : tsk->euid == 0)
+ return 0;
+ return -EPERM;
+}
+
+/*
+ * jail_security_task_create:
+ *
+ * If the current process is ina a jail, and that jail is about to
exceed a
+ * maximum number of processes, then refuse to fork. If the maximum
number
+ * of jails is listed as 0, then there is no limit for this jail, and
we allow
+ * all forks.
+ */
+static inline int
+jail_security_task_create (unsigned long clone_flags)
+{
+ struct jail_struct *tsec = current->security[jail_idx];
+
+ if (!tsec || !(tsec->jail_flags & IN_USE))
+ return 0;
+
+ if (tsec->max_nrtask && tsec->cur_nrtask >= tsec->max_nrtask)
+ return -EPERM;
+ return 0;
+}
+
+/*
+ * The child of a process in a jail belongs in the same jail
+ */
+static int
+jail_task_alloc_security(struct task_struct *tsk)
+{
+ struct jail_struct *tsec = current->security[jail_idx];
+
+ if (!tsec || !(tsec->jail_flags & IN_USE))
+ return 0;
+
+ tsk->security[jail_idx] = tsec;
+ kref_get(&tsec->kref);
+ tsec->cur_nrtask++;
+ if (tsec->maxtimeslice) {
+ tsk->signal->rlim[RLIMIT_CPU].rlim_max = tsec->maxtimeslice;
+ tsk->signal->rlim[RLIMIT_CPU].rlim_cur = tsec->maxtimeslice;
+ }
+ if (tsec->max_data) {
+ tsk->signal->rlim[RLIMIT_CPU].rlim_max = tsec->max_data;
+ tsk->signal->rlim[RLIMIT_CPU].rlim_cur = tsec->max_data;
+ }
+ if (tsec->max_memlock) {
+ tsk->signal->rlim[RLIMIT_CPU].rlim_max = tsec->max_memlock;
+ tsk->signal->rlim[RLIMIT_CPU].rlim_cur = tsec->max_memlock;
+ }
+ if (tsec->nice)
+ set_user_nice(current, tsec->nice);
+
+ return 0;
+}
+
+static int
+jail_bprm_alloc_security(struct linux_binprm *bprm)
+{
+ struct jail_struct *tsec = current->security[jail_idx];
+ int ret;
+
+ if (!tsec)
+ return 0;
+
+ if (tsec->jail_flags & IN_USE)
+ return 0;
+
+ ret = enable_jail(current);
+ if (ret) {
+ /* if we failed, nix out the ip requests */
+ jail_task_free_security(current);
+ return ret;
+ }
+ return 0;
+}
+
+/*
+ * Process in jail may not create devices
+ * Thanks to Brad Spender for pointing out fifos should be allowed.
+ */
+/* TODO: We may want to allow /dev/log, at least... */
+static int
+jail_inode_mknod(struct inode *dir, struct dentry *dentry, int mode,
dev_t dev)
+{
+ if (!in_jail(current))
+ return 0;
+
+ if (S_ISFIFO(mode))
+ return 0;
+
+ return -EPERM;
+}
+
+/* yanked from fs/proc/base.c */
+static unsigned name_to_int(struct dentry *dentry)
+{
+ const char *name = dentry->d_name.name;
+ int len = dentry->d_name.len;
+ unsigned n = 0;
+
+ if (len > 1 && *name == '0')
+ goto out;
+ while (len-- > 0) {
+ unsigned c = *name++ - '0';
+ if (c > 9)
+ goto out;
+ if (n >= (~0U-9)/10)
+ goto out;
+ n *= 10;
+ n += c;
+ }
+ return n;
+out:
+ return ~0U;
+}
+
+/*
+ * jail_proc_inode_permission:
+ * called only when current is in a jail, and is trying to reach
+ * /proc/<pid>. We check whether <pid> is in the same jail as
+ * current. If not, permission is denied.
+ *
+ * NOTE: On the one hand, the task_to_inode(inode)->i_security
+ * approach seems cleaner, but on the other, this prevents us
+ * from unloading bsdjail for awhile...
+ */
+static int
+jail_proc_inode_permission(struct inode *inode, int mask,
+ struct nameidata *nd)
+{
+ struct jail_struct *tsec = current->security[jail_idx];
+ struct dentry *dentry = nd->dentry;
+ unsigned pid;
+
+ pid = name_to_int(dentry);
+ if (pid == ~0U) {
+ return 0;
+ }
+
+ if (dentry->d_parent != dentry->d_sb->s_root)
+ return 0;
+ if (inode->i_security[jail_idx] != tsec)
+ return -ENOENT;
+
+ return 0;
+}
+
+/*
+ * A process in a jail may not see that /proc/<pid> exists for
+ * process not in its jail
+ * Unfortunately we can't pretend that pid for the starting process
+ * is 1, as vserver does.
+ */
+static int jail_task_lookup(struct task_struct *p)
+{
+ struct jail_struct *tsec = current->security[jail_idx];
+
+ if (!tsec)
+ return 0;
+ if (tsec == p->security[jail_idx])
+ return 0;
+ return -EPERM;
+}
+/*
+ * security_task_to_inode:
+ * Set inode->security = task's jail.
+ */
+static void jail_task_to_inode(struct task_struct *p, struct inode
*inode)
+{
+ struct jail_struct *tsec = p->security[jail_idx];
+
+ if (!tsec || !(tsec->jail_flags & IN_USE))
+ return;
+ if (inode->i_security[jail_idx])
+ return;
+ kref_get(&tsec->kref);
+ inode->i_security[jail_idx] = tsec;
+}
+
+/*
+ * inode_permission:
+ * If we are trying to look into certain /proc files from in a jail, we
+ * may deny permission.
+ */
+static int
+jail_inode_permission(struct inode *inode, int mask,
+ struct nameidata *nd)
+{
+ struct jail_struct *tsec = current->security[jail_idx];
+
+ if (!tsec || !(tsec->jail_flags & IN_USE))
+ return 0;
+
+ if (!nd)
+ return 0;
+
+ if (nd->dentry &&
+ strcmp(nd->dentry->d_sb->s_type->name, "proc") == 0) {
+ return jail_proc_inode_permission(inode, mask, nd);
+
+ }
+
+ return 0;
+}
+
+/*
+ * A function which returns -ENOENT if dentry is the dentry for
+ * a /proc/<pid> directory. It returns 0 otherwise.
+ */
+static inline int
+generic_procpid_check(struct dentry *dentry)
+{
+ struct jail_struct *jail = current->security[jail_idx];
+ unsigned pid = name_to_int(dentry);
+
+ if (!jail || !(jail->jail_flags & IN_USE))
+ return 0;
+ if (pid == ~0U)
+ return 0;
+ if (strcmp(dentry->d_sb->s_type->name, "proc") != 0)
+ return 0;
+ if (dentry->d_parent != dentry->d_sb->s_root)
+ return 0;
+ if (dentry->d_inode->i_security[jail_idx] != jail)
+ return -ENOENT;
+ return 0;
+}
+
+/*
+ * We want getattr to fail on /proc/<pid> to prevent leakage through,
for
+ * instance, ls -d.
+ */
+static int
+jail_inode_getattr(struct vfsmount *mnt, struct dentry *dentry)
+{
+ return generic_procpid_check(dentry);
+}
+
+/* This probably is not necessary - /proc does not support xattrs? */
+static int
+jail_inode_getxattr(struct dentry *dentry, char *name)
+{
+ return generic_procpid_check(dentry);
+}
+
+/* process in jail may not send signal to process not in the same jail
*/
+static int
+jail_task_kill(struct task_struct *p, struct siginfo *info, int sig)
+{
+ struct jail_struct *tsec = current->security[jail_idx];
+
+ if (!tsec || !(tsec->jail_flags & IN_USE))
+ return 0;
+
+ if (tsec == p->security[jail_idx])
+ return 0;
+
+ if (sig==SIGCHLD)
+ return 0;
+
+ return -EPERM;
+}
+
+/*
+ * LSM hooks to limit jailed process' abilities to muck with resource
+ * limits
+ */
+static int jail_task_setrlimit (unsigned int resource, struct rlimit
*new_rlim)
+{
+ if (!in_jail(current))
+ return 0;
+
+ return -EPERM;
+}
+
+static int jail_task_setscheduler (struct task_struct *p, int policy,
+ struct sched_param *lp)
+{
+ if (!in_jail(current))
+ return 0;
+
+ return -EPERM;
+}
+
+/*
+ * LSM hooks to limit IPC access.
+ */
+
+static inline int
+basic_ipc_security_check(struct kern_ipc_perm *p, struct task_struct
*target)
+{
+ struct jail_struct *tsec = target->security[jail_idx];
+
+ if (!tsec || !(tsec->jail_flags & IN_USE))
+ return 0;
+
+ if (p->security[jail_idx] != tsec)
+ return -EPERM;
+
+ return 0;
+}
+
+static int
+jail_ipc_permission(struct kern_ipc_perm *ipcp, short flag)
+{
+ return basic_ipc_security_check(ipcp, current);
+}
+
+static int
+jail_shm_alloc_security (struct shmid_kernel *shp)
+{
+ struct jail_struct *tsec = current->security[jail_idx];
+
+ if (!tsec || !(tsec->jail_flags & IN_USE))
+ return 0;
+ shp->shm_perm.security[jail_idx] = tsec;
+ kref_get(&tsec->kref);
+ return 0;
+}
+
+static void
+jail_shm_free_security (struct shmid_kernel *shp)
+{
+ free_ipc_security(&shp->shm_perm);
+}
+
+static int
+jail_shm_associate (struct shmid_kernel *shp, int shmflg)
+{
+ return basic_ipc_security_check(&shp->shm_perm, current);
+}
+
+static int
+jail_shm_shmctl(struct shmid_kernel *shp, int cmd)
+{
+ if (cmd == IPC_INFO || cmd == SHM_INFO)
+ return 0;
+
+ return basic_ipc_security_check(&shp->shm_perm, current);
+}
+
+static int
+jail_shm_shmat(struct shmid_kernel *shp, char *shmaddr, int shmflg)
+{
+ return basic_ipc_security_check(&shp->shm_perm, current);
+}
+
+static int
+jail_msg_queue_alloc(struct msg_queue *msq)
+{
+ struct jail_struct *tsec = current->security[jail_idx];
+
+ if (!tsec || !(tsec->jail_flags & IN_USE))
+ return 0;
+ msq->q_perm.security[jail_idx] = tsec;
+ kref_get(&tsec->kref);
+ return 0;
+}
+
+static void
+jail_msg_queue_free(struct msg_queue *msq)
+{
+ free_ipc_security(&msq->q_perm);
+}
+
+static int jail_msg_queue_associate(struct msg_queue *msq, int flag)
+{
+ return basic_ipc_security_check(&msq->q_perm, current);
+}
+
+static int
+jail_msg_queue_msgctl(struct msg_queue *msq, int cmd)
+{
+ if (cmd == IPC_INFO || cmd == MSG_INFO)
+ return 0;
+
+ return basic_ipc_security_check(&msq->q_perm, current);
+}
+
+static int
+jail_msg_queue_msgsnd(struct msg_queue *msq, struct msg_msg *msg, int
msqflg)
+{
+ return basic_ipc_security_check(&msq->q_perm, current);
+}
+
+static int
+jail_msg_queue_msgrcv(struct msg_queue *msq, struct msg_msg *msg,
+ struct task_struct *target, long type, int mode)
+
+{
+ return basic_ipc_security_check(&msq->q_perm, target);
+}
+
+static int
+jail_sem_alloc_security(struct sem_array *sma)
+{
+ struct jail_struct *tsec = current->security[jail_idx];
+
+ if (!tsec || !(tsec->jail_flags & IN_USE))
+ return 0;
+ sma->sem_perm.security[jail_idx] = tsec;
+ kref_get(&tsec->kref);
+ return 0;
+}
+
+static void
+jail_sem_free_security(struct sem_array *sma)
+{
+ free_ipc_security(&sma->sem_perm);
+}
+
+static int
+jail_sem_associate(struct sem_array *sma, int semflg)
+{
+ return basic_ipc_security_check(&sma->sem_perm, current);
+}
+
+static int
+jail_sem_semctl(struct sem_array *sma, int cmd)
+{
+ if (cmd == IPC_INFO || cmd == SEM_INFO)
+ return 0;
+ return basic_ipc_security_check(&sma->sem_perm, current);
+}
+
+static int
+jail_sem_semop(struct sem_array *sma, struct sembuf *sops, unsigned
nsops,
+ int alter)
+{
+ return basic_ipc_security_check(&sma->sem_perm, current);
+}
+
+static int
+jail_sysctl(struct ctl_table *table, int op)
+{
+ if (!in_jail(current))
+ return 0;
+
+ if (op & 002)
+ return -EPERM;
+
+ return 0;
+}
+
+static struct security_operations bsdjail_security_ops = {
+ .ptrace = jail_ptrace,
+ .capable = jail_capable,
+
+ .task_kill = jail_task_kill,
+ .task_alloc_security = jail_task_alloc_security,
+ .task_free_security = jail_task_free_security,
+ .bprm_alloc_security = jail_bprm_alloc_security,
+ .task_create = jail_security_task_create,
+ .task_to_inode = jail_task_to_inode,
+ .task_lookup = jail_task_lookup,
+
+ .task_setrlimit = jail_task_setrlimit,
+ .task_setscheduler = jail_task_setscheduler,
+
+ .setprocattr = jail_setprocattr,
+ .getprocattr = jail_getprocattr,
+
+ .file_set_fowner = jail_file_set_fowner,
+ .file_send_sigiotask = jail_file_send_sigiotask,
+ .file_free_security = free_file_security,
+
+ .socket_bind = jail_socket_bind,
+ .socket_listen = jail_socket_listen,
+ .socket_create = jail_socket_create,
+ .socket_post_create = jail_socket_post_create,
+ .unix_stream_connect = jail_socket_unix_stream_connect,
+ .unix_may_send = jail_socket_unix_may_send,
+ .sk_free_security = free_sock_security,
+
+ .inode_mknod = jail_inode_mknod,
+ .inode_permission = jail_inode_permission,
+ .inode_free_security = free_inode_security,
+ .inode_getattr = jail_inode_getattr,
+ .inode_getxattr = jail_inode_getxattr,
+ .sb_mount = jail_mount,
+ .sb_umount = jail_umount,
+
+ .ipc_permission = jail_ipc_permission,
+ .shm_alloc_security = jail_shm_alloc_security,
+ .shm_free_security = jail_shm_free_security,
+ .shm_associate = jail_shm_associate,
+ .shm_shmctl = jail_shm_shmctl,
+ .shm_shmat = jail_shm_shmat,
+
+ .msg_queue_alloc_security = jail_msg_queue_alloc,
+ .msg_queue_free_security = jail_msg_queue_free,
+ .msg_queue_associate = jail_msg_queue_associate,
+ .msg_queue_msgctl = jail_msg_queue_msgctl,
+ .msg_queue_msgsnd = jail_msg_queue_msgsnd,
+ .msg_queue_msgrcv = jail_msg_queue_msgrcv,
+
+ .sem_alloc_security = jail_sem_alloc_security,
+ .sem_free_security = jail_sem_free_security,
+ .sem_associate = jail_sem_associate,
+ .sem_semctl = jail_sem_semctl,
+ .sem_semop = jail_sem_semop,
+
+ .sysctl = jail_sysctl,
+};
+
+static int __init bsdjail_init (void)
+{
+ if (register_security (&bsdjail_security_ops)) {
+ printk (KERN_INFO
+ "Failure registering BSD Jail module with the kernel\n");
+
+ jail_idx = mod_reg_security(MY_NAME, &bsdjail_security_ops);
+ if (jail_idx < 0) {
+ printk (KERN_INFO "Failure registering BSD Jail "
+ " module with primary security module.\n");
+ return -EINVAL;
+ }
+ secondary = 1;
+ }
+ printk (KERN_INFO "BSD Jail module initialized.\n");
+
+ return 0;
+}
+
+static void __exit bsdjail_exit (void)
+{
+ if (secondary) {
+ if (mod_unreg_security (MY_NAME, &bsdjail_security_ops))
+ printk (KERN_INFO "Failure unregistering BSD Jail "
+ " module with primary module.\n");
+ } else {
+ if (unregister_security (&bsdjail_security_ops)) {
+ printk (KERN_INFO "Failure unregistering BSD Jail "
+ "module with the kernel\n");
+ }
+ }
+
+ printk (KERN_INFO "BSD Jail module removed\n");
+}
+
+security_initcall (bsdjail_init);
+module_exit (bsdjail_exit);
+
+MODULE_DESCRIPTION("BSD Jail LSM.");
+MODULE_LICENSE("GPL");


2004-11-04 22:44:13

by Chris Wright

[permalink] [raw]
Subject: Re: [RFC] [PATCH] [2/6] LSM Stacking: Add stacker LSM

* Serge Hallyn ([email protected]) wrote:
> This patch adds the stacker module, still required in order to
> stack several LSMs. In order to use this with SELinux, it must be
> compiled into the kernel. Otherwise, it can be loaded as a module.

all the stacker_rwsem business has got to go. can't you make the same
array assumptions as the blobs, and assign modules to slots? also the
sysfs stuff should go into a (not quite yet) existing subsystem. i'm
not positive the authoritative capable() hook logic is right.

thanks,
-chris
--
Linux Security Modules http://lsm.immunix.org http://lsm.bkbits.net

2004-11-04 22:47:50

by Chris Wright

[permalink] [raw]
Subject: Re: [RFC] [PATCH] [5/6] LSM Stacking: SELinux LSM stacking support

* Serge Hallyn ([email protected]) wrote:
> This patch adds stacking support to the SELinux LSM.

Please Cc: Stephen Smalley and James Morris on this one. I'm sure
they'd like to look at the impact as well.

thanks,
-chris
--
Linux Security Modules http://lsm.immunix.org http://lsm.bkbits.net

2004-11-04 22:38:59

by Chris Wright

[permalink] [raw]
Subject: Re: [RFC] [PATCH] [1/6] LSM Stacking: Replace LSM void* with arrays

* Serge Hallyn ([email protected]) wrote:
> The attached patch replaced the LSM security fields on kernel
> objects with an array of pointers, so that more than 1 LSM
> can annotate information on kernel objects.

This will add (default) 12 extra bytes to each LSM tagged object
(including inodes, which a quick snapshot on my system is ~365000 inodes ==
~4M). Also, I don't think the assignment loop should be exposed to core.

thanks,
-chris
--
Linux Security Modules http://lsm.immunix.org http://lsm.bkbits.net

2004-11-04 22:55:02

by Chris Wright

[permalink] [raw]
Subject: Re: [RFC] [PATCH] [6/6] LSM Stacking: temporary setprocattr hack

* Serge Hallyn ([email protected]) wrote:
> Stacker assumes that data written to /proc/<pid>/attr/* is of the
> form:
>
> module_name: data

This breaks current tools where fields are space-delimited. procps does
filtering that way, and I believe libselinux does as well.

thanks,
-chris
--
Linux Security Modules http://lsm.immunix.org http://lsm.bkbits.net

2004-11-04 22:58:56

by Chris Wright

[permalink] [raw]
Subject: Re: [RFC] [PATCH] [0/6] LSM Stacking

* Serge Hallyn ([email protected]) wrote:
> The following set of patches add support required to stack most
> LSMs. The most important patch is the first, which provides a
> method for more than one LSM to annotate information to kernel
> objects. LSM's known to use the LSM fields include selinux, bsdjail,
> seclvl, and digsig. Without this patch (or something like it),
> none of these modules can be used together.

I think, all in all, this needs more work and more justification (esp.
w.r.t. overhead and impact on the current common use of a single
module).

> 2. A 2.6.10-rc1-bk10 system with the stacking patches, and capabilities
> and SELinux compiled into the kernel under the stacker LSM. Other
> than stacker being compiled in and the size of the LSM void* array
> being set to 4, the exact same .config was used.

How many CPU's?

> 3. The same kernel as in (2), but with bsdjail and seclvl also stacked.
>
> On each of these configurations, I ran unixbench twice, and compiled
> a kernel twice (with the same .config, and all files in the cached
> each time).
>
> The kernel compilation results are as follows:
>
> No stacking (1) Stacking (2) More Stacking (3)
> Run 1 real 9m51.647s real 9m48.045s real 9m53.292s
> user 8m28.637s user 8m29.108s user 8m33.319s
> sys 1m13.900s sys 1m14.993s sys 1m15.377s
>
> Run 2 real 9m48.154s real 9m53.369s real 9m53.292s
> user 8m28.983s user 8m29.101s user 8m34.407s
> sys 1m13.981s sys 1m15.307s sys 1m15.611s
>
> Run 3 real 9m51.105s real 9m51.840s real 9m58.840s
> user 8m28.894s user 8m29.192s user 8m33.538s
> sys 1m14.183s sys 1m15.345s sys 1m16.146s
>
> Unixbench summaries are as follows. (I can send the full output if
> anyone asks)

Sorry, I forget what the Unixbench scores mean....

> No stacking (1) Stacking (2) More Stacking (3)
> Run 1 651.5 647.1 634.3
> Run 2 648.2 642.8 632.7

How did lmbench numbers look? And other workloads, like network?

thanks,
-chris
--
Linux Security Modules http://lsm.immunix.org http://lsm.bkbits.net

2004-11-04 23:52:52

by James Morris

[permalink] [raw]
Subject: Re: [RFC] [PATCH] [0/6] LSM Stacking

This code has not been ratified on the LSM list, there are still several
open issues, including opposition to the idea of arbitrary stacking of
security modules by myself.

I don't think that the kernel should provide such a service.

This is the initial LSM thread:
http://mail.wirex.com/pipermail/linux-security-module/2004-October/5567.html

Here are posts where I explain my objections:

http://mail.wirex.com/pipermail/linux-security-module/2004-October/5582.html
http://mail.wirex.com/pipermail/linux-security-module/2004-October/5584.html
http://mail.wirex.com/pipermail/linux-security-module/2004-October/5602.html

Please don't apply.


- James
--
James Morris
<[email protected]>



2004-11-05 01:03:49

by Serge E. Hallyn

[permalink] [raw]
Subject: Re: [RFC] [PATCH] [0/6] LSM Stacking

Hi,

Quoting Chris Wright ([email protected]):
...
> I think, all in all, this needs more work and more justification (esp.
> w.r.t. overhead and impact on the current common use of a single
> module).

Would it help to make CONFIG_NUM_LSMS a boot time option, and default
to 1?

As for justification, the fact that many LSMS currently cannot be
used simultaneously seemed the most prominent. It certainly seems viable
to use SELinux to protect audit logs and shadow files, use bsdjail to
offer certain services, and use securelevel for some generic hardening,
for instance.

> > 2. A 2.6.10-rc1-bk10 system with the stacking patches, and capabilities
> > and SELinux compiled into the kernel under the stacker LSM. Other
> > than stacker being compiled in and the size of the LSM void* array
> > being set to 4, the exact same .config was used.
>
> How many CPU's?

This was one cpu. (All I have available at the moment)

> How did lmbench numbers look? And other workloads, like network?

I will run lmbench tomorrow, and look for some network benchmark.

thanks,
-serge

2004-11-05 01:03:48

by Chris Wright

[permalink] [raw]
Subject: Re: [RFC] [PATCH] [6/6] LSM Stacking: temporary setprocattr hack

* Serge E. Hallyn ([email protected]) wrote:
> Quoting Chris Wright ([email protected]):
> > * Serge Hallyn ([email protected]) wrote:
> > > Stacker assumes that data written to /proc/<pid>/attr/* is of the
> > > form:
> > >
> > > module_name: data
> >
> > This breaks current tools where fields are space-delimited. procps does
> > filtering that way, and I believe libselinux does as well.
>
> Oh, are you talking about the output of getprocattr? Perhaps the output
> should (temporarily) be default list the selinux info on the first line,
> without a "selinux: " prepended, and list any other modules after?

Ah, yeah, getprocattr, not the stacker setprocattr, sorry. For quick
testing, something like that is sufficient. But for longterm it's got
to be less hackish.

> You mentioned a common LSM sysfs framework. Does it offer support for
> both per-module and per-pid-per-module files? If so, then I suppose it
> would be fair to force LSMs to use those, and reserve the existing
> {gs}etprocattr files for selinux use (or nuke them).

Right now it's just per-module files. The latter adds extra expense per
fork that would be nice to avoid. Could be a sort of transaction file,
write the pid, get back the assoiciated data, and thus be static with
the directory.

thanks,
-chris
--
Linux Security Modules http://lsm.immunix.org http://lsm.bkbits.net

2004-11-05 01:11:35

by Chris Wright

[permalink] [raw]
Subject: Re: [RFC] [PATCH] [0/6] LSM Stacking

* Serge E. Hallyn ([email protected]) wrote:
> Hi,
>
> Quoting Chris Wright ([email protected]):
> ...
> > I think, all in all, this needs more work and more justification (esp.
> > w.r.t. overhead and impact on the current common use of a single
> > module).
>
> Would it help to make CONFIG_NUM_LSMS a boot time option, and default
> to 1?

That number is only valid at compile time (it defines structure sizes,
etc).

> As for justification, the fact that many LSMS currently cannot be
> used simultaneously seemed the most prominent. It certainly seems viable
> to use SELinux to protect audit logs and shadow files, use bsdjail to
> offer certain services, and use securelevel for some generic hardening,
> for instance.

Understood, although I don't think you'll get SELinux folks to agree
that it could be useful in conjuction with other modules like that. The
real bottom line is that it can't slow anything down for the single
module case.

thanks,
-chris
--
Linux Security Modules http://lsm.immunix.org http://lsm.bkbits.net

2004-11-05 01:36:00

by Serge E. Hallyn

[permalink] [raw]
Subject: Re: [RFC] [PATCH] [6/6] LSM Stacking: temporary setprocattr hack

Hi,

Quoting Chris Wright ([email protected]):
> * Serge Hallyn ([email protected]) wrote:
> > Stacker assumes that data written to /proc/<pid>/attr/* is of the
> > form:
> >
> > module_name: data
>
> This breaks current tools where fields are space-delimited. procps does
> filtering that way, and I believe libselinux does as well.

Oh, are you talking about the output of getprocattr? Perhaps the output
should (temporarily) be default list the selinux info on the first line,
without a "selinux: " prepended, and list any other modules after?

Or do you mean something else?

You mentioned a common LSM sysfs framework. Does it offer support for
both per-module and per-pid-per-module files? If so, then I suppose it
would be fair to force LSMs to use those, and reserve the existing
{gs}etprocattr files for selinux use (or nuke them).

-serge

2004-11-05 02:16:48

by James Morris

[permalink] [raw]
Subject: Re: [RFC] [PATCH] [0/6] LSM Stacking

On Thu, 4 Nov 2004, Chris Wright wrote:

> Understood, although I don't think you'll get SELinux folks to agree
> that it could be useful in conjuction with other modules like that.

SELinux folks have differing opinions. Security module stacking would
obviously be useful for research & development, but there is a question in
my mind of whether this is justification enough for the feature in a
mainline kernel.

If it turns out to be generally useful to stack some security modules,
then it might be better to incorporate their functionality into SELinux
(or some other single module) so that policy and configuration can be
managed and analyzed under one system.

Something to keep in mind is that SELinux is a generic access control
framework specifically designed to allow flexible composition of different
security models under a centralized security policy.

LSM is a useful framework for implementing different security _systems_,
but I don't think it's suitable for composition of security models.
That's what SELinux is for.

> The real bottom line is that it can't slow anything down for the single
> module case.

Agreed.


- James
--
James Morris
<[email protected]>


2004-11-05 16:01:25

by Valdis Klētnieks

[permalink] [raw]
Subject: Re: [RFC] [PATCH] [0/6] LSM Stacking

On Thu, 04 Nov 2004 17:04:31 CST, Serge Hallyn said:
> The following set of patches add support required to stack most
> LSMs. The most important patch is the first, which provides a
> method for more than one LSM to annotate information to kernel
> objects. LSM's known to use the LSM fields include selinux, bsdjail,
> seclvl, and digsig. Without this patch (or something like it),
> none of these modules can be used together.

After fixing some blecherous line-wrap issues, the patches applied
OK to a 2.6.10-rc1-mm2-RT-V0.7.11 tree (a few offsets here and there,
no rejects).

I'm currently running with 3 LSM's stacked (SELinux, capabilities, and
a local LSM that provides OpenWall functionality), and all seems to be
working *mostly* OK. I'll post the LSM as soon as I refactor a few
patches.

One issue: I'm seeing this from /usr/sbin/sendmail:

% /usr/sbin/sendmail
drop_privileges: setuid(0) succeeded (when it should not)

I've not tracked down what's causing this indigestion yet (I suspect some
bad interaction with capabilities - that's what caused that message to be
added to Sendmail in the first place).


Attachments:
(No filename) (226.00 B)

2004-11-05 16:32:59

by Serge E. Hallyn

[permalink] [raw]
Subject: Re: [RFC] [PATCH] [0/6] LSM Stacking

> One issue: I'm seeing this from /usr/sbin/sendmail:
>
> % /usr/sbin/sendmail
> drop_privileges: setuid(0) succeeded (when it should not)
>
> I've not tracked down what's causing this indigestion yet (I suspect some
> bad interaction with capabilities - that's what caused that message to be
> added to Sendmail in the first place).

Chris had mentioned the stacker_capable not being quite right. I'm
looking at it now, and yeah, it's a little convoluted. I think a simpler

if (!stacked_modules) {
/* same as before
}

final_result = 0;
for (m = stacked_modules; m; m = m->next) {
result = (m->module_operations).capable(tsk,cap);
if (result && !final_result) {
final_result = result;
if (short_circuit_capable)
break;
}
}
return final_result;

should be correct.

-serge

2004-11-05 16:56:51

by Stephen Smalley

[permalink] [raw]
Subject: Re: [RFC] [PATCH] [0/6] LSM Stacking

On Fri, 2004-11-05 at 02:23, [email protected] wrote:
> One issue: I'm seeing this from /usr/sbin/sendmail:
>
> % /usr/sbin/sendmail
> drop_privileges: setuid(0) succeeded (when it should not)
>
> I've not tracked down what's causing this indigestion yet (I suspect some
> bad interaction with capabilities - that's what caused that message to be
> added to Sendmail in the first place).

stacker module is granting capability if any security module allows it
under the view that the capable() hook is authoritative. But that is a
mistake; if you look at the existing stacking support in SELinux for
capabilities, we require both modules to approve the capability.

--
Stephen Smalley <[email protected]>
National Security Agency

2004-11-10 17:44:05

by Jonathan Corbet

[permalink] [raw]
Subject: Re: [RFC] [PATCH] [2/6] LSM Stacking: Add stacker LSM

Without addressing the question of whether stacking modules makes sense
in the first place, I'd like to note a couple of things which caught my
eye:

> +static int stacker_register (const char *name, struct
> security_operations *ops)
> +{
> + /* This function is the primary reason for the stacker module.
> + Add the stacked module (as specified by name and ops)
> + according to the current ordering policy. */
> +
> + char *new_module_name;
> + struct module_entry *new_module_entry;
> + int namelen;
> +
> + num_stacked_modules++;
> [...]
> + return num_stacked_modules-1;
> +}

Unless I've missed it, you never check num_stacked_modules against
CONFIG_NUM_LSMS. If somebody loads too many modules, they risk
overflowing all of those void * security arrays you've added to so many
kernel data structures, and thus corrupting those structures. That, in
technical terms, would be a bummer.

In stacker_unregister(), you do:

> + num_stacked_modules--;

What happens if you unload anything other than the last module, then
load something else? When you return num_stacked_modules-1 to the new
module, you'll point it to a slot in those security arrays which is
already used by another module. The result seems unlikely to improve
security.

Unless I'm simply confused? It's happened before...

jon

2004-11-10 19:32:40

by Serge E. Hallyn

[permalink] [raw]
Subject: Re: [RFC] [PATCH] [2/6] LSM Stacking: Add stacker LSM

> Unless I've missed it, you never check num_stacked_modules against
> CONFIG_NUM_LSMS. If somebody loads too many modules, they risk
> overflowing all of those void * security arrays you've added to so many
> kernel data structures, and thus corrupting those structures. That, in
> technical terms, would be a bummer.
>
> In stacker_unregister(), you do:
>
> > + num_stacked_modules--;
>
> What happens if you unload anything other than the last module, then
> load something else? When you return num_stacked_modules-1 to the new
> module, you'll point it to a slot in those security arrays which is
> already used by another module. The result seems unlikely to improve
> security.
>
> Unless I'm simply confused? It's happened before...

No, you're not. While I sent out all the patches to make the first
patch useful, the stacker patch was the same one I've been using with
several other approaches to sharing the void * security arrays. If
the first patch turned out to be acceptable, the stacker patch would
have been tweaked quite a bit. As Chris Wright pointed out, the list
of stacked modules would no longer need to be a linked list, and so
the semaphore guarding that list could be dropped. And of course
your points are valid.

I am working on a new implementation, which I will send first to the
lsm list and lsm and selinux maintainers. Lmbench numbers from this
morning show that with this approach, a kernel with selinux +
capabilities shows no performance degradation.

thanks,
-serge