| From: |
| "Serge E. Hallyn" <serue@us.ibm.com> |
| To: |
| lsm <linux-security-module@wirex.com> |
| Subject: |
| [RFC] [PATCH] Replace security fields with hashtable |
| Date: |
| Tue, 26 Oct 2004 18:04:56 -0400 |
| Cc: |
| Chris Wright <chrisw@osdl.org>, hallyn@CS.WM.EDU |
Attached is a patch which removes all lsm security annotations on
kernel objects and replaces them with a hash table. An LSM can store
and retrieve information using functions exported from
security/security.c.
These are:
int security_set_value(void *ptr, int lsm_id, void *data,
int gfp_flags);
void *security_get_value(void *ptr, int lsm_id)
void *security_del_value(void *ptr, int lsm_id)
This naturally solves the lsm stacking problem. It has the added
benefit of saving memory (and presumably time) on a system with LSM
compiled out.
I am attaching the following patches (against 2.6.9):
lsm-hash.patch: implements the hash table described above,
and removes the kernel object ->security fields.
stacker.patch: adds the stacker module
tasklookup.patch: adds the necessary tasklookup LSM hookd
for testing bsdjail.
bsdjail-full.patch: adds a bsdjail module rewritten to use
this hash table
On request, I can also send out the dte and digsig versions which I
stacked on top of bsdjail in order to test this patch.
Feedback is much appreciated.
thanks,
-serge
Index: linux-2.6.9/fs/exec.c
===================================================================
--- linux-2.6.9.orig/fs/exec.c 2004-10-18 16:53:51.000000000 -0500
+++ linux-2.6.9/fs/exec.c 2004-10-26 10:48:56.577289288 -0500
@@ -1173,8 +1173,7 @@
__free_page(page);
}
- if (bprm->security)
- security_bprm_free(bprm);
+ security_bprm_free(bprm);
out_mm:
if (bprm->mm)
Index: linux-2.6.9/fs/inode.c
===================================================================
--- linux-2.6.9.orig/fs/inode.c 2004-10-18 16:55:29.000000000 -0500
+++ linux-2.6.9/fs/inode.c 2004-10-26 10:48:56.578289136 -0500
@@ -134,7 +134,6 @@
inode->i_bdev = NULL;
inode->i_cdev = NULL;
inode->i_rdev = 0;
- inode->i_security = NULL;
inode->dirtied_when = 0;
if (security_inode_alloc(inode)) {
if (inode->i_sb->s_op->destroy_inode)
Index: linux-2.6.9/include/linux/binfmts.h
===================================================================
--- linux-2.6.9.orig/include/linux/binfmts.h 2004-10-18 16:54:30.000000000 -0500
+++ linux-2.6.9/include/linux/binfmts.h 2004-10-26 10:48:56.578289136 -0500
@@ -29,7 +29,6 @@
struct file * file;
int e_uid, e_gid;
kernel_cap_t cap_inheritable, cap_permitted, cap_effective;
- void *security;
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.9/include/linux/fs.h
===================================================================
--- linux-2.6.9.orig/include/linux/fs.h 2004-10-18 16:53:44.000000000 -0500
+++ linux-2.6.9/include/linux/fs.h 2004-10-26 10:48:56.580288832 -0500
@@ -468,7 +468,6 @@
unsigned int i_flags;
atomic_t i_writecount;
- void *i_security;
union {
void *generic_ip;
} u;
@@ -542,7 +541,6 @@
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;
int signum; /* posix.1b rt signal to be delivered on IO */
};
@@ -578,7 +576,6 @@
struct file_ra_state f_ra;
unsigned long f_version;
- void *f_security;
/* needed for tty driver, and maybe others */
void *private_data;
@@ -757,7 +754,6 @@
int s_syncing;
int s_need_sync_fs;
atomic_t s_active;
- void *s_security;
struct list_head s_dirty; /* dirty inodes */
struct list_head s_io; /* parked for writeback */
Index: linux-2.6.9/include/linux/ipc.h
===================================================================
--- linux-2.6.9.orig/include/linux/ipc.h 2004-10-18 16:53:05.000000000 -0500
+++ linux-2.6.9/include/linux/ipc.h 2004-10-26 10:48:56.580288832 -0500
@@ -65,7 +65,6 @@
gid_t cgid;
mode_t mode;
unsigned long seq;
- void *security;
};
#endif /* __KERNEL__ */
Index: linux-2.6.9/include/linux/msg.h
===================================================================
--- linux-2.6.9.orig/include/linux/msg.h 2004-10-18 16:54:31.000000000 -0500
+++ linux-2.6.9/include/linux/msg.h 2004-10-26 10:48:56.580288832 -0500
@@ -70,7 +70,6 @@
long m_type;
int m_ts; /* message text size */
struct msg_msgseg* next;
- void *security;
/* the actual message follows immediately */
};
Index: linux-2.6.9/include/linux/sched.h
===================================================================
--- linux-2.6.9.orig/include/linux/sched.h 2004-10-18 16:53:13.000000000 -0500
+++ linux-2.6.9/include/linux/sched.h 2004-10-26 10:48:56.581288680 -0500
@@ -547,7 +547,6 @@
void *notifier_data;
sigset_t *notifier_mask;
- void *security;
struct audit_context *audit_context;
/* Thread group tracking */
Index: linux-2.6.9/include/linux/security.h
===================================================================
--- linux-2.6.9.orig/include/linux/security.h 2004-10-18 16:54:40.000000000 -0500
+++ linux-2.6.9/include/linux/security.h 2004-10-26 10:48:56.583288376 -0500
@@ -1892,6 +1892,10 @@
extern int unregister_security (struct security_operations *ops);
extern int mod_reg_security (const char *name, struct security_operations *ops);
extern int mod_unreg_security (const char *name, struct security_operations *ops);
+extern int security_set_value (void *ptr, int lsm_id, void *data,
+ int gfp_flags);
+extern void *security_get_value (void *ptr, int lsm_id);
+extern void *security_del_value (void *ptr, int lsm_id);
#else /* CONFIG_SECURITY */
Index: linux-2.6.9/include/net/sock.h
===================================================================
--- linux-2.6.9.orig/include/net/sock.h 2004-10-18 16:54:40.000000000 -0500
+++ linux-2.6.9/include/net/sock.h 2004-10-26 10:48:56.584288224 -0500
@@ -254,7 +254,6 @@
__u32 sk_sndmsg_off;
struct sk_buff *sk_send_head;
int sk_write_pending;
- void *sk_security;
__u8 sk_queue_shrunk;
/* three bytes hole, try to pack */
void (*sk_state_change)(struct sock *sk);
Index: linux-2.6.9/ipc/msg.c
===================================================================
--- linux-2.6.9.orig/ipc/msg.c 2004-10-18 16:53:06.000000000 -0500
+++ linux-2.6.9/ipc/msg.c 2004-10-26 10:48:56.585288072 -0500
@@ -97,7 +97,6 @@
msq->q_perm.mode = (msgflg & S_IRWXUGO);
msq->q_perm.key = key;
- msq->q_perm.security = NULL;
retval = security_msg_queue_alloc(msq);
if (retval) {
ipc_rcu_putref(msq);
Index: linux-2.6.9/ipc/msgutil.c
===================================================================
--- linux-2.6.9.orig/ipc/msgutil.c 2004-10-18 16:54:07.000000000 -0500
+++ linux-2.6.9/ipc/msgutil.c 2004-10-26 10:48:56.585288072 -0500
@@ -41,7 +41,6 @@
return ERR_PTR(-ENOMEM);
msg->next = NULL;
- msg->security = NULL;
if (copy_from_user(msg + 1, src, alen)) {
err = -EFAULT;
Index: linux-2.6.9/ipc/sem.c
===================================================================
--- linux-2.6.9.orig/ipc/sem.c 2004-10-18 16:53:50.000000000 -0500
+++ linux-2.6.9/ipc/sem.c 2004-10-26 10:48:56.586287920 -0500
@@ -176,7 +176,6 @@
sma->sem_perm.mode = (semflg & S_IRWXUGO);
sma->sem_perm.key = key;
- sma->sem_perm.security = NULL;
retval = security_sem_alloc(sma);
if (retval) {
ipc_rcu_putref(sma);
Index: linux-2.6.9/ipc/shm.c
===================================================================
--- linux-2.6.9.orig/ipc/shm.c 2004-10-18 16:54:08.000000000 -0500
+++ linux-2.6.9/ipc/shm.c 2004-10-26 10:48:56.587287768 -0500
@@ -195,7 +195,6 @@
shp->shm_flags = (shmflg & S_IRWXUGO);
shp->mlock_user = NULL;
- shp->shm_perm.security = NULL;
error = security_shm_alloc(shp);
if (error) {
ipc_rcu_putref(shp);
Index: linux-2.6.9/kernel/fork.c
===================================================================
--- linux-2.6.9.orig/kernel/fork.c 2004-10-18 16:53:13.000000000 -0500
+++ linux-2.6.9/kernel/fork.c 2004-10-26 10:48:56.588287616 -0500
@@ -993,7 +993,6 @@
p->utime = p->stime = 0;
p->lock_depth = -1; /* -1 = no lock */
do_posix_clock_monotonic_gettime(&p->start_time);
- p->security = NULL;
p->io_context = NULL;
p->io_wait = NULL;
p->audit_context = NULL;
Index: linux-2.6.9/security/security.c
===================================================================
--- linux-2.6.9.orig/security/security.c 2004-10-18 16:53:06.000000000 -0500
+++ linux-2.6.9/security/security.c 2004-10-26 10:57:36.387266160 -0500
@@ -17,6 +17,8 @@
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/security.h>
+#include <linux/list.h>
+#include <linux/hash.h>
#define SECURITY_SCAFFOLD_VERSION "1.0.0"
@@ -26,6 +28,189 @@
struct security_operations *security_ops; /* Initialized to NULL */
+#define ENTRIES_PER_BUCKET 8
+
+/*
+ * lsm_cache_entry: data for a pointer for an lsm.
+ *
+ * 20 bytes
+ */
+struct lsm_cache_entry {
+ struct hlist_node list;
+ void *ptr;
+ int lsm_id;
+ void *data;
+};
+
+#define LSM_CACHE_BUCKETS 16384
+int lsm_cache_buckets = LSM_CACHE_BUCKETS;
+static kmem_cache_t *lsm_htable_cache;
+module_param(lsm_cache_buckets, int, 0);
+MODULE_PARM_DESC(lsm_cache_buckets, "Number of lsm cache entries to allocate (default 16384)\n");
+
+static struct hlist_head *lsm_hash_table;
+static spinlock_t lsm_spinlock = SPIN_LOCK_UNLOCKED;
+static int lsm_hash_bits;
+
+static int security_cache_startup(void)
+{
+ int i;
+
+ lsm_hash_bits = long_log2(lsm_cache_buckets);
+ if (lsm_cache_buckets != (1 << lsm_hash_bits)) {
+ lsm_hash_bits++;
+ lsm_cache_buckets = 1 << lsm_hash_bits;
+ printk(KERN_NOTICE
+ "%s: lsm_cache_buckets set to %d (bits %d)\n",
+ __FUNCTION__, lsm_cache_buckets, lsm_hash_bits);
+ }
+
+ lsm_hash_table = kmalloc(lsm_cache_buckets *
+ sizeof(struct hlist_head), GFP_KERNEL);
+
+ if (!lsm_hash_table) {
+ printk(KERN_NOTICE "No memory to initialize lsm cache.\n");
+ return -ENOMEM;
+ }
+
+ lsm_htable_cache = kmem_cache_create("lsm_hash_entries",
+ sizeof(struct lsm_cache_entry), 0, 0, NULL, NULL);
+
+ if (!lsm_htable_cache) {
+ printk(KERN_NOTICE "No memory for lsm_htable_cache.\n");
+ kfree(lsm_hash_table);
+ return -ENOMEM;
+ }
+
+ for (i=0; i<lsm_cache_buckets; i++) {
+ INIT_HLIST_HEAD(&lsm_hash_table[i]);
+ }
+
+ return 0;
+}
+
+static inline int lsm_hash(void *ptr, int lsm_id)
+{
+#if 0
+ return hash_long( ptr + lsm_id + (lsm_id << 16));
+#else
+ return hash_ptr ( ptr + lsm_id, lsm_hash_bits);
+#endif
+}
+
+/*
+ * Add security data to hash table.
+ * Returns: 0 on success, -ENOMEM on alloc failure.
+ * Called by LSMs, grabs the lsm_spinlock (for now).
+ *
+ * What about the case where an LSM wants to
+ * if (task->security) release(task->security);
+ * task->security = new;
+ * Maybe set_value should return a pointer...
+ */
+int security_set_value(void *ptr, int lsm_id, void *data, int gfp_flags)
+{
+ int h = lsm_hash(ptr, lsm_id);
+ struct hlist_node *tmp;
+ struct lsm_cache_entry *e, *e2 = NULL;
+ int ret = 0;
+
+ spin_lock(&lsm_spinlock);
+ hlist_for_each(tmp, &lsm_hash_table[h]) {
+ e = hlist_entry(tmp, struct lsm_cache_entry, list);
+ if (e->ptr == ptr && e->lsm_id == lsm_id) {
+ e->data = data;
+ goto out_unlock;
+ }
+ }
+
+
+ spin_unlock(&lsm_spinlock);
+ e2 = kmem_cache_alloc(lsm_htable_cache, gfp_flags);
+ if (!e2) {
+ return -ENOMEM;
+ }
+
+ spin_lock(&lsm_spinlock);
+ hlist_for_each(tmp, &lsm_hash_table[h]) {
+ e = hlist_entry(tmp, struct lsm_cache_entry, list);
+ if (e->ptr == ptr && e->lsm_id == lsm_id) {
+ e->data = data;
+ goto out_free;
+ }
+ }
+
+ INIT_HLIST_NODE(&e2->list);
+ e2->ptr = ptr;
+ e2->lsm_id = lsm_id;
+ e2->data = data;
+ hlist_add_head(&e2->list, &lsm_hash_table[h]);
+ goto out_unlock;
+
+out_free:
+ kmem_cache_free(lsm_htable_cache, e2);
+
+out_unlock:
+ spin_unlock(&lsm_spinlock);
+ return ret;
+}
+
+/*
+ * security_get_value: return data stored for a particular object by lsm_id
+ * Returns: data if found, NULL otherwise
+ * Called from LSMs, grabs the lsm_spinlock (for now)
+ */
+void *security_get_value(void *ptr, int lsm_id)
+{
+ int h = lsm_hash(ptr, lsm_id);
+ struct hlist_node *tmp;
+ struct lsm_cache_entry *e;
+ void *ret = NULL;
+
+ spin_lock(&lsm_spinlock);
+ hlist_for_each(tmp, &lsm_hash_table[h]) {
+ e = hlist_entry(tmp, struct lsm_cache_entry, list);
+ if (e->ptr == ptr && e->lsm_id == lsm_id) {
+ ret = e->data;
+ goto out;
+ }
+ }
+
+out:
+ spin_unlock(&lsm_spinlock);
+ return ret;
+}
+
+/*
+ * security_del_value: delete storage for lsm_id for object ptr
+ * Returns data if found and cache entry deleted, NULL if not found.
+ * Called by LSMs, holds the lsm_spinlock
+ * Note that we do NOT delete data, only the container. The LSM is
+ * responsible for deleting the returned data.
+ */
+void *security_del_value(void *ptr, int lsm_id)
+{
+ int h = lsm_hash(ptr, lsm_id);
+ struct hlist_node *tmp;
+ struct lsm_cache_entry *e;
+ void *ret = NULL;
+
+ spin_lock(&lsm_spinlock);
+ hlist_for_each(tmp, &lsm_hash_table[h]) {
+ e = hlist_entry(tmp, struct lsm_cache_entry, list);
+ if (e->ptr == ptr && e->lsm_id == lsm_id) {
+ hlist_del(tmp);
+ ret = e->data;
+ kmem_cache_free(lsm_htable_cache, e);
+ goto out;
+ }
+ }
+
+out:
+ spin_unlock(&lsm_spinlock);
+ return ret;
+}
+
static inline int verify (struct security_operations *ops)
{
/* verify the security_operations structure exists */
@@ -58,6 +243,11 @@
printk (KERN_INFO "Security Scaffold v" SECURITY_SCAFFOLD_VERSION
" initialized\n");
+ if (security_cache_startup()) {
+ printk(KERN_ERR "%s: out of memory\n", __FUNCTION__);
+ return -ENOMEM;
+ }
+
if (verify (&dummy_security_ops)) {
printk (KERN_ERR "%s could not verify "
"dummy_security_ops structure.\n", __FUNCTION__);
@@ -201,6 +391,9 @@
return 1;
}
+EXPORT_SYMBOL_GPL(security_set_value);
+EXPORT_SYMBOL_GPL(security_get_value);
+EXPORT_SYMBOL_GPL(security_del_value);
EXPORT_SYMBOL_GPL(register_security);
EXPORT_SYMBOL_GPL(unregister_security);
EXPORT_SYMBOL_GPL(mod_reg_security);
Index: linux-2.6.9/security/Kconfig
===================================================================
--- linux-2.6.9.orig/security/Kconfig 2004-10-18 16:54:39.000000000 -0500
+++ linux-2.6.9/security/Kconfig 2004-10-26 15:30:51.322041496 -0500
@@ -46,5 +46,11 @@
source security/selinux/Kconfig
+config SECURITY_STACKER
+ tristate "LSM Stacking"
+ depends on SECURITY
+ help
+ Stack multiple LSMs.
+
endmenu
Index: linux-2.6.9/security/Makefile
===================================================================
--- linux-2.6.9.orig/security/Makefile 2004-10-18 16:54:39.000000000 -0500
+++ linux-2.6.9/security/Makefile 2004-10-26 15:30:51.323041344 -0500
@@ -15,3 +15,4 @@
obj-$(CONFIG_SECURITY_SELINUX) += selinux/built-in.o
obj-$(CONFIG_SECURITY_CAPABILITIES) += commoncap.o capability.o
obj-$(CONFIG_SECURITY_ROOTPLUG) += commoncap.o root_plug.o
+obj-$(CONFIG_SECURITY_STACKER) += stacker.o
Index: linux-2.6.9/security/stacker.c
===================================================================
--- linux-2.6.9.orig/security/stacker.c 2003-01-30 04:24:37.000000000 -0600
+++ linux-2.6.9/security/stacker.c 2004-10-26 15:30:53.033781272 -0500
@@ -0,0 +1,1610 @@
+/* "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 <dwheeler@dwheeler.com>.
+ * 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 (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 dentry *dentry, const char *name, void *buffer, size_t size)
+{
+ RETURN_ERROR_IF_ANY_ERROR(inode_getsecurity,inode_getsecurity(dentry,name,buffer,size));
+}
+
+static int stacker_inode_setsecurity(struct dentry *dentry, const char *name, const void *value, size_t size, int flags)
+{
+ RETURN_ERROR_IF_ANY_ERROR(inode_setsecurity,inode_setsecurity(dentry,name,value,size,flags));
+}
+
+static int stacker_inode_listsecurity(struct dentry *dentry, char *buffer)
+{
+ RETURN_ERROR_IF_ANY_ERROR(inode_listsecurity,inode_listsecurity(dentry,buffer));
+}
+
+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 fd,
+ int reason)
+{
+ RETURN_ERROR_IF_ANY_ERROR(file_send_sigiotask,file_send_sigiotask(tsk,fown,fd,reason));
+}
+
+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);
+#if 0
+ return num_stacked_modules-1;
+#else
+ return 0;
+#endif
+}
+
+/*
+ * 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 register_sysfs_files(void);
+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;
+}
+
+void register_sysfs_files(void)
+{
+ int result;
+
+ result = subsystem_register(&stacker_subsys);
+ if (result) {
+ printk(KERN_NOTICE "Error (%d) registering stacker sysfssubsystem\n",
+ result);
+ return;
+ }
+
+ 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");
+}
+
+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.9/security/stacker.h
===================================================================
--- linux-2.6.9.orig/security/stacker.h 2003-01-30 04:24:37.000000000 -0600
+++ linux-2.6.9/security/stacker.h 2004-10-26 14:14:51.000000000 -0500
@@ -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
Index: linux-2.6.9/fs/proc/base.c
===================================================================
--- linux-2.6.9.orig/fs/proc/base.c 2004-10-26 15:30:53.031781576 -0500
+++ linux-2.6.9/fs/proc/base.c 2004-10-26 15:31:01.805447776 -0500
@@ -1683,6 +1683,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.9/include/linux/security.h
===================================================================
--- linux-2.6.9.orig/include/linux/security.h 2004-10-26 15:30:53.032781424 -0500
+++ linux-2.6.9/include/linux/security.h 2004-10-26 15:31:01.807447472 -0500
@@ -627,6 +627,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.
@@ -1152,6 +1157,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);
@@ -1751,6 +1757,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);
@@ -2390,6 +2401,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.9/security/dummy.c
===================================================================
--- linux-2.6.9.orig/security/dummy.c 2004-10-26 15:30:53.032781424 -0500
+++ linux-2.6.9/security/dummy.c 2004-10-26 15:31:01.808447320 -0500
@@ -616,6 +616,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)
{ }
@@ -978,6 +983,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.9/security/stacker.c
===================================================================
--- linux-2.6.9.orig/security/stacker.c 2004-10-26 15:30:53.033781272 -0500
+++ linux-2.6.9/security/stacker.c 2004-10-26 15:31:01.809447168 -0500
@@ -754,6 +754,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));
@@ -1251,6 +1256,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,
Index: linux-2.6.9/Documentation/bsdjail.txt
===================================================================
--- linux-2.6.9.orig/Documentation/bsdjail.txt 2003-01-30 04:24:37.000000000 -0600
+++ linux-2.6.9/Documentation/bsdjail.txt 2004-10-26 15:31:28.610372808 -0500
@@ -0,0 +1,135 @@
+BSD Jail Linux Security Module
+Serge E. Hallyn <serue@us.ibm.com>
+
+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
Index: linux-2.6.9/fs/proc/base.c
===================================================================
--- linux-2.6.9.orig/fs/proc/base.c 2004-10-26 15:31:21.224495632 -0500
+++ linux-2.6.9/fs/proc/base.c 2004-10-26 15:31:27.509540160 -0500
@@ -1683,6 +1683,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.9/include/linux/security.h
===================================================================
--- linux-2.6.9.orig/include/linux/security.h 2004-10-26 15:31:21.225495480 -0500
+++ linux-2.6.9/include/linux/security.h 2004-10-26 15:31:27.511539856 -0500
@@ -627,6 +627,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.
@@ -1152,6 +1157,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);
@@ -1751,6 +1757,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);
@@ -2390,6 +2401,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.9/security/Kconfig
===================================================================
--- linux-2.6.9.orig/security/Kconfig 2004-10-26 15:31:16.547206688 -0500
+++ linux-2.6.9/security/Kconfig 2004-10-26 15:31:28.610372808 -0500
@@ -52,5 +52,16 @@
help
Stack multiple LSMs.
+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.
+
endmenu
Index: linux-2.6.9/security/Makefile
===================================================================
--- linux-2.6.9.orig/security/Makefile 2004-10-26 15:31:16.547206688 -0500
+++ linux-2.6.9/security/Makefile 2004-10-26 15:31:28.610372808 -0500
@@ -16,3 +16,4 @@
obj-$(CONFIG_SECURITY_CAPABILITIES) += commoncap.o capability.o
obj-$(CONFIG_SECURITY_ROOTPLUG) += commoncap.o root_plug.o
obj-$(CONFIG_SECURITY_STACKER) += stacker.o
+obj-$(CONFIG_SECURITY_BSDJAIL) += bsdjail.o
Index: linux-2.6.9/security/bsdjail.c
===================================================================
--- linux-2.6.9.orig/security/bsdjail.c 2003-01-30 04:24:37.000000000 -0600
+++ linux-2.6.9/security/bsdjail.c 2004-10-26 15:31:29.588224152 -0500
@@ -0,0 +1,1421 @@
+/*
+ * File: linux/security/bsdjail.c
+ * Author: Serge Hallyn (serue@us.ibm.com)
+ * Date: Sep 12, 2004
+ *
+ * (See Documentation/bsdjail.txt for more information)
+ *
+ * Copyright (C) 2004 International Business Machines <serue@us.ibm.com>
+ *
+ * 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");
+
+#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"
+#define BSDJAIL_LSM_ID 0x89A
+
+/* 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 = security_del_value(task, BSDJAIL_LSM_ID);
+
+ 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);
+ 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) {
+ int err;
+
+ memset(tsec, 0, sizeof(struct jail_struct));
+ err = security_set_value(tsk, BSDJAIL_LSM_ID, tsec, GFP_KERNEL);
+ if (unlikely(err)) {
+ kfree(tsec);
+ tsec = NULL;
+ }
+ }
+ return tsec;
+}
+
+static inline int
+in_jail(struct task_struct *t)
+{
+ struct jail_struct *tsec = security_get_value(t, BSDJAIL_LSM_ID);
+
+ 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;
+ int retval = -EFAULT;
+
+ tsec = security_get_value(tsk, BSDJAIL_LSM_ID);
+ 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->rlim[RLIMIT_DATA].rlim_cur = tsec->max_data;
+ current->rlim[RLIMIT_DATA].rlim_max = tsec->max_data;
+ }
+ if (tsec->max_memlock) {
+ current->rlim[RLIMIT_MEMLOCK].rlim_cur =
+ tsec->max_memlock;
+ current->rlim[RLIMIT_MEMLOCK].rlim_max =
+ tsec->max_memlock;
+ }
+ if (tsec->maxtimeslice) {
+ current->rlim[RLIMIT_CPU].rlim_cur = tsec->maxtimeslice;
+ current->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 = security_get_value(current, BSDJAIL_LSM_ID);
+ 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, *csec;
+ int err = 0;
+
+ csec = security_get_value(current, BSDJAIL_LSM_ID);
+ if (csec && (csec->jail_flags & IN_USE)) {
+ if (strcmp(name, "current") == 0) {
+ /* provide network info */
+ err = print_jail_net_info(csec, 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 = security_get_value(p, BSDJAIL_LSM_ID);
+ 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 fd, int reason)
+{
+ struct file *file;
+ struct jail_struct *tsec, *fsec;
+
+ tsec = security_get_value(current, BSDJAIL_LSM_ID);
+ if (!tsec || !(tsec->jail_flags & IN_USE))
+ return 0;
+
+ file = container_of(fown, struct file, f_owner);
+ fsec = security_get_value(file, BSDJAIL_LSM_ID);
+ if (fsec != tsec)
+ return -EPERM;
+
+ return 0;
+}
+
+static int
+jail_file_set_fowner(struct file *file)
+{
+ struct jail_struct *tsec;
+ int err;
+
+ tsec = security_get_value(current, BSDJAIL_LSM_ID);
+ if (tsec) {
+ kref_get(&tsec->kref);
+ err = security_set_value(file, BSDJAIL_LSM_ID, tsec, GFP_KERNEL);
+ if (unlikely(err)) {
+ kref_put(&tsec->kref, release_jail);
+ return -ENOMEM;
+ }
+ } else
+ security_del_value(file, BSDJAIL_LSM_ID);
+
+ return 0;
+}
+
+static void free_ipc_security(struct kern_ipc_perm *ipc)
+{
+ struct jail_struct *tsec;
+
+ tsec = security_del_value(ipc, BSDJAIL_LSM_ID);
+ if (tsec)
+ kref_put(&tsec->kref, release_jail);
+}
+
+static void free_file_security(struct file *file)
+{
+ struct jail_struct *tsec;
+
+ tsec = security_del_value(file, BSDJAIL_LSM_ID);
+ if (tsec)
+ kref_put(&tsec->kref, release_jail);
+}
+
+static void free_inode_security(struct inode *inode)
+{
+ struct jail_struct *tsec;
+
+ tsec = security_del_value(inode, BSDJAIL_LSM_ID);
+ if (tsec)
+ kref_put(&tsec->kref, release_jail);
+}
+
+/*
+ * 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 = security_get_value(tracer, BSDJAIL_LSM_ID);
+
+ if (tsec && (tsec->jail_flags & IN_USE)) {
+ struct jail_struct *vsec = security_get_value(tracee,
+ BSDJAIL_LSM_ID);
+ if (tsec == vsec)
+ 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 = security_get_value(current, BSDJAIL_LSM_ID);
+
+ 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 = security_get_value(current, BSDJAIL_LSM_ID);
+
+ 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 = security_get_value(current, BSDJAIL_LSM_ID);
+
+ 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 = security_get_value(current, BSDJAIL_LSM_ID);
+
+ 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 = security_del_value(sk, BSDJAIL_LSM_ID);
+ if (tsec)
+ kref_put(&tsec->kref, release_jail);
+}
+
+/*
+ * 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 = security_get_value(current, BSDJAIL_LSM_ID);
+ if (tsec) {
+ int err;
+ kref_get(&tsec->kref);
+ err = security_set_value(sock->sk, BSDJAIL_LSM_ID, tsec, GFP_KERNEL);
+ if (unlikely(err)) {
+ kref_put(&tsec->kref, release_jail);
+ return -ENOMEM;
+ }
+ } else
+ security_del_value(sock->sk, BSDJAIL_LSM_ID);
+ 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 = security_get_value(current, BSDJAIL_LSM_ID); /* jail of sending process */
+ ssec = security_get_value(other->sk, BSDJAIL_LSM_ID); /* 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 = security_get_value(current, BSDJAIL_LSM_ID); /* jail of sending process */
+ ssec = security_get_value(other->sk, BSDJAIL_LSM_ID); /* 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 = security_get_value(current, BSDJAIL_LSM_ID);
+
+ 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 = security_get_value(current, BSDJAIL_LSM_ID);
+ int err;
+
+ if (!tsec || !(tsec->jail_flags & IN_USE))
+ return 0;
+
+ kref_get(&tsec->kref);
+ err = security_set_value(tsk, BSDJAIL_LSM_ID, tsec, GFP_KERNEL);
+ if (unlikely(err)) {
+ kref_put(&tsec->kref, release_jail);
+ return -ENOMEM;
+ }
+ tsec->cur_nrtask++;
+ if (tsec->maxtimeslice) {
+ tsk->rlim[RLIMIT_CPU].rlim_max = tsec->maxtimeslice;
+ tsk->rlim[RLIMIT_CPU].rlim_cur = tsec->maxtimeslice;
+ }
+ if (tsec->max_data) {
+ tsk->rlim[RLIMIT_CPU].rlim_max = tsec->max_data;
+ tsk->rlim[RLIMIT_CPU].rlim_cur = tsec->max_data;
+ }
+ if (tsec->max_memlock) {
+ tsk->rlim[RLIMIT_CPU].rlim_max = tsec->max_memlock;
+ tsk->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 = security_get_value(current, BSDJAIL_LSM_ID);
+ 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, *isec;
+ 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;
+
+ tsec = security_get_value(current, BSDJAIL_LSM_ID);
+ isec = security_get_value(inode, BSDJAIL_LSM_ID);
+ if (isec != 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 *cursec, *psec;
+
+ cursec = security_get_value(current, BSDJAIL_LSM_ID);
+
+ if (!cursec)
+ return 0;
+ psec = security_get_value(p, BSDJAIL_LSM_ID);
+ if (cursec == psec)
+ 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;
+
+ tsec = security_get_value(p, BSDJAIL_LSM_ID);
+
+ if (!tsec || !(tsec->jail_flags & IN_USE))
+ return;
+
+ if (security_get_value(inode, BSDJAIL_LSM_ID))
+ return;
+ kref_get(&tsec->kref);
+ security_set_value(inode, BSDJAIL_LSM_ID, tsec, GFP_KERNEL);
+}
+
+/*
+ * 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 = security_get_value(current, BSDJAIL_LSM_ID);
+
+ 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, *isec;
+ unsigned pid = name_to_int(dentry);
+
+ jail = security_get_value(current, BSDJAIL_LSM_ID);
+
+ 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;
+ isec = security_get_value(dentry->d_inode, BSDJAIL_LSM_ID);
+ if (isec != 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, *psec;
+
+ tsec = security_get_value(current, BSDJAIL_LSM_ID);
+
+ if (!tsec || !(tsec->jail_flags & IN_USE))
+ return 0;
+
+ psec = security_get_value(p, BSDJAIL_LSM_ID);
+ if (tsec == psec)
+ 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, *psec;
+
+ tsec = security_get_value(target, BSDJAIL_LSM_ID);
+
+ if (!tsec || !(tsec->jail_flags & IN_USE))
+ return 0;
+
+ psec = security_get_value(p, BSDJAIL_LSM_ID);
+ if (psec != 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 = security_get_value(current, BSDJAIL_LSM_ID);
+ int err;
+
+ if (!tsec || !(tsec->jail_flags & IN_USE))
+ return 0;
+ kref_get(&tsec->kref);
+ err = security_set_value(&shp->shm_perm, BSDJAIL_LSM_ID, tsec, GFP_KERNEL);
+ if (unlikely(err)) {
+ kref_put(&tsec->kref, release_jail);
+ return -ENOMEM;
+ }
+ 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 = security_get_value(current, BSDJAIL_LSM_ID);
+ int err;
+
+ if (!tsec || !(tsec->jail_flags & IN_USE))
+ return 0;
+ kref_get(&tsec->kref);
+ err = security_set_value(&msq->q_perm, BSDJAIL_LSM_ID, tsec, GFP_KERNEL);
+ if (unlikely(err)) {
+ kref_put(&tsec->kref, release_jail);
+ return -ENOMEM;
+ }
+ 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 = security_get_value(current, BSDJAIL_LSM_ID);
+ int err;
+
+ if (!tsec || !(tsec->jail_flags & IN_USE))
+ return 0;
+ kref_get(&tsec->kref);
+ err = security_set_value(&sma->sem_perm, BSDJAIL_LSM_ID, tsec, GFP_KERNEL);
+ if (unlikely(err)) {
+ kref_put(&tsec->kref, release_jail);
+ return -ENOMEM;
+ }
+ 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)
+{
+ int rc = 0;
+
+ if (register_security (&bsdjail_security_ops)) {
+ printk (KERN_INFO
+ "Failure registering BSD Jail module with the kernel\n");
+
+ rc = mod_reg_security(MY_NAME, &bsdjail_security_ops);
+ if (rc < 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");
Index: linux-2.6.9/security/dummy.c
===================================================================
--- linux-2.6.9.orig/security/dummy.c 2004-10-26 15:31:21.225495480 -0500
+++ linux-2.6.9/security/dummy.c 2004-10-26 15:31:27.512539704 -0500
@@ -616,6 +616,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)
{ }
@@ -978,6 +983,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.9/security/stacker.c
===================================================================
--- linux-2.6.9.orig/security/stacker.c 2004-10-26 15:31:21.226495328 -0500
+++ linux-2.6.9/security/stacker.c 2004-10-26 15:31:27.513539552 -0500
@@ -754,6 +754,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));
@@ -1251,6 +1256,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,