| From: |
| David Safford <safford@watson.ibm.com> |
| To: |
| linux-security-module@wirex.com |
| Subject: |
| [RFC][PATCH] EVM and SLIM LSM modules |
| Date: |
| Mon, 17 Oct 2005 15:08:44 -0400 |
| Archive-link: |
| Article,
Thread
|
This is a request for comments on the attached patches which implement
two LSM modules, EVM and SLIM. These patches are also available, along
with sources for associated user space programs, and technical papers,
at http://www.research.ibm.com/gsal/tcpa in the tpm-3.0.2 package.
EVM (Extended Verification Module) is similar to digsig, in that it
provides access control based on file integrity, but it provides this
protection for all files (not just executables) through a general
mechanism of authenticated extended attributes, based on keys
protected by "TPM trusted boot". EVM is configurable to protect any
extended attributes, including those for SLIM and selinux. In addition,
when EVM is LSM stacked, the data and metadata integrity information can
be passed to subsequent modules for further access control enforcement,
such as demoting the integrity level of any process allowed to access
the questionable file (i.e. sandboxing), and SLIM demonstrates this
stacking.
SLIM provides a simple integrity mandatory access control, similar
to LOMAC, but using EVM information to aid decisions, and to ensure
the integrity of guard processes. The former IMA (Integrity Measurement
Architecture) is included as a configurable part of SLIM. While IMA is
not an access control component, if integrity attestation is desired, it
is most efficiently implemented here, as EVM has already measured all
files, and SLIM knows which ones are integrity sensitive, and which
should therefore be added to the TPM registers.
We believe that EVM and SLIM help demonstrate the usefulness of LSM
stacking, and of data and metadata integrity verification as an
integral part of access control decisions.
The attached patches (against 2.6.14-rc4-git1) are (in order):
stacker.patch (a roll-up of Serge's stacker, as of 23 Sept)
tcfl-lsm.patch (to reintroduce inode_post_create and _mkdir)
tpm.patch (adds trusted boot support to TPM driver)
(it's not LSM, but is included for convenience)
evm.patch (evm module)
slim-ima.patch (slim-ima module)
The tcfl-lsm.patch reintroduces the inode_post_create and
inode_post_mkdir hooks. EVM needs a way to HMAC newly created
extended attributes after they have been written, which is
not possible through the inode_init_security hook. We would
certainly like to see these hooks reintroduced to LSM, if EVM
is eventually accepted.
This is the first public RFC release, and as such, all
questions and comments will be most appreciated.
dave safford
Mimi Zohar
diff -ruN linux-2.6.14-rc4-git1.orig/Documentation/LSM-stacker-locking.txt
linux-2.6.14-rc4-git1.new/Documentation/LSM-stacker-locking.txt
--- linux-2.6.14-rc4-git1.orig/Documentation/LSM-stacker-locking.txt 1969-12-31 19:00:00.000000000
-0500
+++ linux-2.6.14-rc4-git1.new/Documentation/LSM-stacker-locking.txt 2005-10-12 15:54:45.000000000
-0400
@@ -0,0 +1,77 @@
+The following describes the locking used by the lsm stacker as of
+July 1, 2005:
+
+Things which require locking include:
+
+ 1. module list
+ 2. per-kernel-object security lists
+
+Clearly, the safety of the data being appended itself is up to
+the module. For instance, digsig uses a single spinlock to
+protect the inode security data, while securelevel uses a
+spinlock located in the inode security object itself.
+
+The module list is protected as follows:
+
+ Walking the list is done under a partial rcu_read_lock. We
+ cannot hold the rcu_read_lock while calling a
+ module_entry->lsm_hook(), as these are very likely to sleep.
+ Therefore we call rcu_read_lock() only when we rcu_dereference
+ module_entry->next.
+
+ We must be careful about deleting module_entries. Stacker does
+ a try_module_get() on each LSM as it is stacked, to prevent its
+ being unloaded. It will module_put the LSM when stacker_unload
+ is called for the module. At that time the module is removed
+ from the stacked_modules list. The forward pointer on the
+ struct module_entry is not changed, so that any stacker hook
+ which is currently on module_entry can safely and correctly
+ dereference module_entry->next. The module_entry remains on the
+ all_modules list, which is used to find the module when it is
+ actually unregistered. In this way any task which is executing
+ any of the module's hooks should have finished execution between
+ stacker_unload() and stacker_unregister(), making it safe to
+ delete the module_entry.
+
+The kernel object security lists are protected as follows:
+
+ The security_set_value and security_del_value are only to
+ be called during security_alloc_object and security_del_object,
+ respectively. Since these are automatically safe from
+ concurrent accesses, no locking is required here.
+
+ The security_add_value() function is protected from concurrent
+ access using the stacker_value_spinlock. security_get_value()
+ is protected from security_add_value() using rcu.
+
+ To allow module deletion, it is desirable for modules to be
+ able to delete kernel object security entries at any time.
+ This is supported using security_unlink_value(). This
+ function will remove the object under the
+ stacker_value_spinlock. In order to protect racing readers,
+ however, the module must wait an rcu cycle before deleting
+ the object, either using call_rcu to call the deletion
+ function, or simply calling synchronize_rcu() as is done by
+ digsig. In order to minimize the performance impact, both
+ digsig and securelevel call security_unlink_value() on each
+ to be deleted item in a loop, then wait an rcu cycle, and
+ then delete the objects.
+
+ This dynamic object deletion scheme still has one potential
+ race. In order to minimize the performance impact on the
+ expected case, security_del_value() does not take the
+ stacker_value_spinlock. This is generally safe because this
+ function must only be called while the kernel object is being
+ freed, so that this function is naturally serialized with
+ respect to write (no writes are possible). However, the
+ following may be possible:
+
+ 1. echo -n lsm1 > /security/stacker/unload (CPU 0)
+ 2. rmmod lsm1 (CPU 0)
+ At the same time, a file object is being freed on CPU 1.
+ While lsm2 is calling security_del_value() on the file, lsm1
+ is calling security_unlink_value().
+
+ One solution is to call the spinlock during the object
+ deletion. The performance of this approach will be
+ measured.
diff -ruN linux-2.6.14-rc4-git1.orig/Documentation/LSM-stacking.txt
linux-2.6.14-rc4-git1.new/Documentation/LSM-stacking.txt
--- linux-2.6.14-rc4-git1.orig/Documentation/LSM-stacking.txt 1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.14-rc4-git1.new/Documentation/LSM-stacking.txt 2005-10-12 15:54:45.000000000 -0400
@@ -0,0 +1,186 @@
+------------
+LSM stacking
+------------
+
+This document consists of two parts. The first describes the stacker LSM.
+The second describes what is needed from an LSM in order to permit it to
+stack with other LSMs.
+
+--------------------------------------------------------
+stacker LSM - enable stacking multiple security modules.
+--------------------------------------------------------
+
+Stacker is compiled into the kernel. Find the "Stacker" option under
+the Security submenu, and say 'Y'. Now, any security modules which are
+loaded or compiled into the kernel will be managed by stacker.
+
+You may interact with stacker through its securityfs interface, located
+under /sys/kernel/security/stacker/ (henceforth simply /security/stacker).
+This consists of the following files:
+
+/security/stacker/lockdown:
+Once you write to this file, you will no longer be able to load
+LSMs.
+
+/security/stacker/list_modules:
+Reading this file will show which LSMs are being stacked.
+
+/security/stacker/stop_responding:
+Unregisters the /security/stacker directory, so that you can no longer
+interact with stacker.
+
+/security/stacker/unload:
+Disables the specified module. The module will actually still be
+loaded, but will no longer be asked to mediate accesses or update
+security information. Stacker will release it's refcount on the
+module, so that after this you are able to rmmod the module. By
+separating unload into these two steps, no cpu should be executing
+any of the module's hooks by the time you rmmod, so that the module
+can be safely freed.
+
+---------------------------------------------
+Readying an LSM for stacking with other LSMs.
+---------------------------------------------
+
+LSM stacking is not a simple matter. You must consider the cumulative
+behavior of all stacked LSMs very carefully, as well as certain subtle
+effects of the LSM implementation. Please do not try to stack arbitrary
+modules! For instance, while SELinux and cap-stack should always be
+used together, SELinux cannot be combined with the original capability
+module. The reason for this is that capability enforces that a process
+must have CAP_SYS_ADMIN when writing "security.*" extended attributes.
+However selinux requires that non-CAP_SYS_ADMIN processes be able to
+write security.selinux attributes, instead enforcing its own permission
+check. More subtle interactions are certainly imaginable, such as a
+first security module updating state on a kernel object such that a
+second security module denies or allows the action when it otherwise
+would not have.
+
+If you have any questions about the proper or actual behavior of
+modules, whether existing or ones to be written by yourself, a good
+place to engage in discussion is the lsm mailing list,
+linux-security-module@wirex.com. Information about the mailing list can
+be found at lsm.immunix.org.
+
+If your module will be annotating security information to kernel
+objects, then you must use the provided API. The functions intended
+for use by modules are defined in include/linux/security.h. A
+good example of a user of these functions is the SELinux module. The
+following describes the API usage.
+
+When registering your security module with the kernel, the last
+parameter to register_security() is an integer pointer. If you will
+not use any security annotations to kernel objects, pass in NULL.
+Otherwise, define a global integer to store your security annotation
+index, ie
+
+int mylsm_secidx;
+
+and pass a pointer to this index to register_security() and
+mod_reg_security().
+
+ret = register_security(&mylsm_ops, &mylsm_secidx);
+
+The int mylsm_secidx will be set to the slot to which you are
+assigned, which may be a private slot, or may be the shared slot.
+In either case, you will pass this variable to all
+security_{get,set,del}_value_type functions, but otherwise leave
+the variable alone.
+
+Now assume you wish to annotate an instance of the following struct to
+an inode:
+
+struct my_security_info {
+ int a;
+ struct list_head some_list;
+ spinlock_t lock;
+};
+
+At the top of the struct, you must add a struct security_list lsm_list,
+as follows:
+
+struct my_security_info {
++ struct security_list lsm_list;
+ int count;
+ struct list_head some_list;
+ spinlock_t lock;
+};
+
+This will add the information which the API will need to tell your
+information apart from that of other modules. You also need to define a
+unique ID to distinguish information owned by your module. Usually
+people "echo <module_name> | sha1sum" and use the first several digits.
+For instance, if
+#echo seclvl | sha1sum | awk --field-separator="" '{ print \
+$1$2$3$4$5$6$7$8 '}
+40e81e47
+
+then in your my_lsm.h, add
+#define MY_LSM_ID 0x40e81e47
+
+Do make sure that no other module happens to have the same ID.
+
+Now when the kernel object is created, you may use
+security_set_value_type() to append the struct to the object's list of
+security information. Note that you may ONLY use this while the kernel
+object is being created, ie during the security_<KERNEL_OBJECT>_alloc
+function. Since you are appending my_security_info to the inode, you
+will do so during the security_inode_alloc() hook. For instance,
+
+static inline int my_inode_alloc(struct inode *inode)
+{
+ struct my_security_info *my_data;
+
+ my_data = kmalloc(sizeof(struct my_security_info), GFP_KERNEL);
+ if (!my_data)
+ return -ENOMEM;
+ init_inode_data(my_data);
+
+ security_set_value_type(&inode->i_security, MY_LSM_ID, my_data,
+ mylsm_secidx);
+}
+
+If you need to append your information after the kernel object has been
+created, you may do so using security_add_value_type() hook. However,
+for both performance and security reasons, it is preferable to compile
+your module into the kernel and always append your info while the object
+is created. See security/seclvl.c or the digsig stacking patch for
+examples of this usage.
+
+To get your information back, you may use security_get_value_type().
+For instance,
+
+static inline int my_inode_create(struct inode *dir,
+ struct dentry *dentry,
+ int mode)
+{
+ struct my_security_info *my_data;
+
+ my_data = security_get_value_type(&dir->i_security, MY_LSM_ID,
+ struct my_security_info, mylsm_secidx);
+ if (!my_data || my_data->count)
+ return -EPERM;
+ return 0;
+}
+
+There are two ways of removing kernel object data for freeing.
+If you can wait until security_<object>_free(), ie security_inode_free,()
+then you may use
+
+ my_data = security_del_value_type(&dir->i_security, MY_LSM_ID,
+ struct my_security_info, mylsm_secidx);
+ kfree(my_data);
+
+See security/selinux/hooks.c for example usage.
+
+If you must free the data before the object is freed, because your
+module is being unloaded, then you must use
+
+ security_unlink_value(&dir->i_security->lsm_list.list);
+
+and wait a full rcu cycle before freeing the data in order to
+ensure proper locking. See security/seclvl.c and the digsig
+stacking patch for sample usage. Both of these modules simply
+link together all the objects in one list_head chain, and, if
+unloaded, unlink each object from the object, wait a full rcu
+cycle, then walk the same chain again to free the objects.
diff -ruN linux-2.6.14-rc4-git1.orig/fs/compat.c linux-2.6.14-rc4-git1.new/fs/compat.c
--- linux-2.6.14-rc4-git1.orig/fs/compat.c 2005-10-12 15:52:25.000000000 -0400
+++ linux-2.6.14-rc4-git1.new/fs/compat.c 2005-10-12 15:54:45.000000000 -0400
@@ -53,6 +53,8 @@
#include <asm/mmu_context.h>
#include <asm/ioctls.h>
+extern int sec_shared_slot;
+
/*
* Not all architectures have sys_utime, so implement this in terms
* of sys_utimes.
@@ -1445,6 +1447,7 @@
bprm->file = file;
bprm->filename = filename;
bprm->interp = filename;
+ INIT_HLIST_HEAD((struct hlist_head *)&(bprm->security[sec_shared_slot]));
bprm->mm = mm_alloc();
retval = -ENOMEM;
if (!bprm->mm)
@@ -1503,8 +1506,7 @@
__free_page(page);
}
- if (bprm->security)
- security_bprm_free(bprm);
+ security_bprm_free(bprm);
out_mm:
if (bprm->mm)
diff -ruN linux-2.6.14-rc4-git1.orig/fs/exec.c linux-2.6.14-rc4-git1.new/fs/exec.c
--- linux-2.6.14-rc4-git1.orig/fs/exec.c 2005-10-12 15:52:25.000000000 -0400
+++ linux-2.6.14-rc4-git1.new/fs/exec.c 2005-10-12 15:54:45.000000000 -0400
@@ -1162,6 +1162,7 @@
bprm->file = file;
bprm->filename = filename;
bprm->interp = filename;
+ INIT_HLIST_HEAD((struct hlist_head *)&(bprm->security[sec_shared_slot]));
bprm->mm = mm_alloc();
retval = -ENOMEM;
if (!bprm->mm)
@@ -1220,8 +1221,7 @@
__free_page(page);
}
- if (bprm->security)
- security_bprm_free(bprm);
+ security_bprm_free(bprm);
out_mm:
if (bprm->mm)
diff -ruN linux-2.6.14-rc4-git1.orig/fs/ext2/xattr_security.c
linux-2.6.14-rc4-git1.new/fs/ext2/xattr_security.c
--- linux-2.6.14-rc4-git1.orig/fs/ext2/xattr_security.c 2005-10-12 15:52:25.000000000 -0400
+++ linux-2.6.14-rc4-git1.new/fs/ext2/xattr_security.c 2005-10-12 15:54:45.000000000 -0400
@@ -50,20 +50,26 @@
ext2_init_security(struct inode *inode, struct inode *dir)
{
int err;
- size_t len;
- void *value;
- char *name;
+ struct xattr_data *p, *n;
+ LIST_HEAD(head);
- err = security_inode_init_security(inode, dir, &name, &value, &len);
+ err = security_inode_init_security(inode, dir, &head);
if (err) {
if (err == -EOPNOTSUPP)
return 0;
return err;
}
- err = ext2_xattr_set(inode, EXT2_XATTR_INDEX_SECURITY,
- name, value, len, 0);
- kfree(name);
- kfree(value);
+
+ list_for_each_entry_safe(p, n, &head, list) {
+ if (!err)
+ err = ext2_xattr_set(inode,
+ EXT2_XATTR_INDEX_SECURITY,
+ p->name, p->value, p->len, 0);
+ list_del(&p->list);
+ kfree(p->name);
+ kfree(p->value);
+ kfree(p);
+ }
return err;
}
diff -ruN linux-2.6.14-rc4-git1.orig/fs/ext3/xattr_security.c
linux-2.6.14-rc4-git1.new/fs/ext3/xattr_security.c
--- linux-2.6.14-rc4-git1.orig/fs/ext3/xattr_security.c 2005-10-12 15:52:25.000000000 -0400
+++ linux-2.6.14-rc4-git1.new/fs/ext3/xattr_security.c 2005-10-12 15:54:45.000000000 -0400
@@ -52,20 +52,26 @@
ext3_init_security(handle_t *handle, struct inode *inode, struct inode *dir)
{
int err;
- size_t len;
- void *value;
- char *name;
+ struct xattr_data *p, *n;
+ LIST_HEAD(head);
- err = security_inode_init_security(inode, dir, &name, &value, &len);
+ err = security_inode_init_security(inode, dir, &head);
if (err) {
if (err == -EOPNOTSUPP)
return 0;
return err;
}
- err = ext3_xattr_set_handle(handle, inode, EXT3_XATTR_INDEX_SECURITY,
- name, value, len, 0);
- kfree(name);
- kfree(value);
+
+ list_for_each_entry_safe(p, n, &head, list) {
+ if (!err)
+ err = ext3_xattr_set_handle(handle, inode,
+ EXT3_XATTR_INDEX_SECURITY,
+ p->name, p->value, p->len, 0);
+ list_del(&p->list);
+ kfree(p->name);
+ kfree(p->value);
+ kfree(p);
+ }
return err;
}
diff -ruN linux-2.6.14-rc4-git1.orig/fs/inode.c linux-2.6.14-rc4-git1.new/fs/inode.c
--- linux-2.6.14-rc4-git1.orig/fs/inode.c 2005-10-12 15:52:25.000000000 -0400
+++ linux-2.6.14-rc4-git1.new/fs/inode.c 2005-10-12 15:54:45.000000000 -0400
@@ -133,7 +133,8 @@
inode->i_bdev = NULL;
inode->i_cdev = NULL;
inode->i_rdev = 0;
- inode->i_security = NULL;
+ memset(&inode->i_security, 0, (sec_shared_slot)*sizeof(void *));
+ INIT_HLIST_HEAD((struct hlist_head *)&(inode->i_security[sec_shared_slot]));
inode->dirtied_when = 0;
if (security_inode_alloc(inode)) {
if (inode->i_sb->s_op->destroy_inode)
diff -ruN linux-2.6.14-rc4-git1.orig/include/linux/binfmts.h
linux-2.6.14-rc4-git1.new/include/linux/binfmts.h
--- linux-2.6.14-rc4-git1.orig/include/linux/binfmts.h 2005-08-28 19:41:01.000000000 -0400
+++ linux-2.6.14-rc4-git1.new/include/linux/binfmts.h 2005-10-12 15:54:45.000000000 -0400
@@ -29,7 +29,7 @@
struct file * file;
int e_uid, e_gid;
kernel_cap_t cap_inheritable, cap_permitted, cap_effective;
- void *security;
+ void * security[CONFIG_SECURITY_STACKER_NUMFIELDS];
int argc, envc;
char * filename; /* Name of binary as seen by procps */
char * interp; /* Name of the binary really executed. Most
diff -ruN linux-2.6.14-rc4-git1.orig/include/linux/fs.h
linux-2.6.14-rc4-git1.new/include/linux/fs.h
--- linux-2.6.14-rc4-git1.orig/include/linux/fs.h 2005-10-12 15:52:26.000000000 -0400
+++ linux-2.6.14-rc4-git1.new/include/linux/fs.h 2005-10-12 15:54:45.000000000 -0400
@@ -477,7 +477,7 @@
unsigned int i_flags;
atomic_t i_writecount;
- void *i_security;
+ void *i_security[CONFIG_SECURITY_STACKER_NUMFIELDS];
union {
void *generic_ip;
} u;
@@ -551,7 +551,7 @@
rwlock_t lock; /* protects pid, uid, euid fields */
int pid; /* pid or -pgrp where SIGIO should be sent */
uid_t uid, euid; /* uid/euid of process setting the owner */
- void *security;
+ void *security[CONFIG_SECURITY_STACKER_NUMFIELDS];
int signum; /* posix.1b rt signal to be delivered on IO */
};
@@ -587,7 +587,7 @@
struct file_ra_state f_ra;
unsigned long f_version;
- void *f_security;
+ void *f_security[CONFIG_SECURITY_STACKER_NUMFIELDS];
/* needed for tty driver, and maybe others */
void *private_data;
@@ -777,7 +777,7 @@
int s_syncing;
int s_need_sync_fs;
atomic_t s_active;
- void *s_security;
+ void *s_security[CONFIG_SECURITY_STACKER_NUMFIELDS];
struct xattr_handler **s_xattr;
struct list_head s_inodes; /* all inodes */
diff -ruN linux-2.6.14-rc4-git1.orig/include/linux/ipc.h
linux-2.6.14-rc4-git1.new/include/linux/ipc.h
--- linux-2.6.14-rc4-git1.orig/include/linux/ipc.h 2005-08-28 19:41:01.000000000 -0400
+++ linux-2.6.14-rc4-git1.new/include/linux/ipc.h 2005-10-12 15:54:45.000000000 -0400
@@ -2,6 +2,7 @@
#define _LINUX_IPC_H
#include <linux/types.h>
+#include <linux/list.h>
#define IPC_PRIVATE ((__kernel_key_t) 0)
@@ -65,7 +66,7 @@
gid_t cgid;
mode_t mode;
unsigned long seq;
- void *security;
+ void *security[CONFIG_SECURITY_STACKER_NUMFIELDS];
};
#endif /* __KERNEL__ */
diff -ruN linux-2.6.14-rc4-git1.orig/include/linux/msg.h
linux-2.6.14-rc4-git1.new/include/linux/msg.h
--- linux-2.6.14-rc4-git1.orig/include/linux/msg.h 2005-10-12 15:52:26.000000000 -0400
+++ linux-2.6.14-rc4-git1.new/include/linux/msg.h 2005-10-12 15:54:45.000000000 -0400
@@ -70,7 +70,7 @@
long m_type;
int m_ts; /* message text size */
struct msg_msgseg* next;
- void *security;
+ void *security[CONFIG_SECURITY_STACKER_NUMFIELDS];
/* the actual message follows immediately */
};
diff -ruN linux-2.6.14-rc4-git1.orig/include/linux/sched.h
linux-2.6.14-rc4-git1.new/include/linux/sched.h
--- linux-2.6.14-rc4-git1.orig/include/linux/sched.h 2005-10-12 15:52:26.000000000 -0400
+++ linux-2.6.14-rc4-git1.new/include/linux/sched.h 2005-10-12 15:54:45.000000000 -0400
@@ -764,7 +764,7 @@
void *notifier_data;
sigset_t *notifier_mask;
- void *security;
+ void *security[CONFIG_SECURITY_STACKER_NUMFIELDS];
struct audit_context *audit_context;
seccomp_t seccomp;
diff -ruN linux-2.6.14-rc4-git1.orig/include/linux/security.h
linux-2.6.14-rc4-git1.new/include/linux/security.h
--- linux-2.6.14-rc4-git1.orig/include/linux/security.h 2005-10-12 15:52:26.000000000 -0400
+++ linux-2.6.14-rc4-git1.new/include/linux/security.h 2005-10-12 15:54:46.000000000 -0400
@@ -32,6 +32,79 @@
#include <linux/sched.h>
struct ctl_table;
+struct module;
+
+extern int sec_shared_slot;
+
+/*
+ * structure to be embedded at top of each LSM's security
+ * objects.
+ */
+struct security_list {
+ struct hlist_node list;
+ int security_id;
+};
+
+static inline void *__get_value(void *head, int idx)
+{
+ void **p = head;
+ pr_debug("__get_value: %s (%d): head %lx p %lx idx %d returning %lx"
+ " at %lx\n",
+ __FUNCTION__, __LINE__, (long)head, (long)p,
+ idx, (long)p[idx], (long)&p[idx]);
+ return p[idx];
+}
+
+static inline void __set_value(void *head, int idx, void *v)
+{
+ void **p = head;
+ p[idx] = v;
+ pr_debug("%s (%d): hd %lx, p %lx, idx %d,"
+ "v %lx, p[idx] %lx at %lx\n",
+ __FUNCTION__, __LINE__, (long) (head),
+ (long) p, idx, (long) (v),
+ (long)p[idx], (long)&p[idx]);
+}
+
+/*
+ * These #defines present more convenient interfaces to
+ * LSMs for using the security{g,d,s}et_value functions.
+ */
+#define security_get_value_type(head, id, type, idx) (idx < sec_shared_slot) ? \
+ (type *)__get_value((head), idx) \
+ : ({ \
+ struct security_list *v = security_get_value((head), id); \
+ v ? hlist_entry(v, type, lsm_list) : NULL; \
+ })
+
+#define security_set_value_type(head, id, value, idx) \
+ do { \
+ if (idx < sec_shared_slot) { \
+ __set_value((head), idx, (value)); \
+ } else { \
+ security_set_value((head), id, &(value)->lsm_list); \
+ } \
+ } while (0);
+
+#define security_add_value_type(head, id, value, idx) \
+ do { \
+ if (idx < sec_shared_slot) { \
+ __set_value((head), idx, (value)); \
+ } else { \
+ security_add_value((head), id, &(value)->lsm_list); \
+ } \
+ } while (0);
+
+#define security_del_value_type(head, id, type, idx) (idx < sec_shared_slot) ? \
+ (type *)__get_value((head), idx) \
+ : ( { \
+ struct security_list *v; \
+ v = security_del_value((head), id); \
+ v ? hlist_entry(v, type, lsm_list) : NULL; \
+ } )
+
+/* security_disown_value is really only to be used by stacker */
+extern void security_disown_value(struct hlist_head *);
/*
* These functions are in security/capability.c and are used
@@ -89,11 +162,27 @@
#ifdef CONFIG_SECURITY
+struct xattr_data {
+ struct list_head list;
+ char *name;
+ void *value;
+ size_t len;
+};
+
+
/**
* struct security_operations - main security structure
*
+ * When adding functions to this structure, please add them to
+ * dummy.c and stacker.c, and run linux/scripts/lsm_verify_hooks.sh
+ * to verify their inclusion in these modules.
+ *
* Security hooks for program execution operations.
*
+ *
+ * @owner:
+ * Module owning this security_operations. NULL if not a module.
+ *
* @bprm_alloc_security:
* Allocate and attach a security structure to the @bprm->security field.
* The security field is initialized to NULL when the bprm structure is
@@ -263,9 +352,13 @@
* then it should return -EOPNOTSUPP to skip this processing.
* @inode contains the inode structure of the newly created inode.
* @dir contains the inode structure of the parent directory.
- * @name will be set to the allocated name suffix (e.g. selinux).
- * @value will be set to the allocated attribute value.
- * @len will be set to the length of the value.
+ * @head, if not null, points to a listhead to which to append a
+ * newly allocated struct xattr_data with the following data:
+ * @data->name will be set to the allocated name suffix
+ * (e.g. selinux).
+ * @data->value will be set to the allocated attribute value.
+ * @data->len will be set to the length of the value.
+ * @data->list is used to add the data to the list_head
* Returns 0 if @name and @value have been successfully set,
* -EOPNOTSUPP if no security attribute is needed, or
* -ENOMEM on memory allocation failure.
@@ -1010,6 +1103,8 @@
* This is the main security structure.
*/
struct security_operations {
+ struct module *owner;
+
int (*ptrace) (struct task_struct * parent, struct task_struct * child);
int (*capget) (struct task_struct * target,
kernel_cap_t * effective,
@@ -1064,7 +1159,7 @@
int (*inode_alloc_security) (struct inode *inode);
void (*inode_free_security) (struct inode *inode);
int (*inode_init_security) (struct inode *inode, struct inode *dir,
- char **name, void **value, size_t *len);
+ struct list_head *head);
int (*inode_create) (struct inode *dir,
struct dentry *dentry, int mode);
int (*inode_link) (struct dentry *old_dentry,
@@ -1415,13 +1510,11 @@
static inline int security_inode_init_security (struct inode *inode,
struct inode *dir,
- char **name,
- void **value,
- size_t *len)
+ struct list_head *head)
{
if (unlikely (IS_PRIVATE (inode)))
return -EOPNOTSUPP;
- return security_ops->inode_init_security (inode, dir, name, value, len);
+ return security_ops->inode_init_security (inode, dir, head);
}
static inline int security_inode_create (struct inode *dir,
@@ -1903,9 +1996,11 @@
/* prototypes */
extern int security_init (void);
-extern int register_security (struct security_operations *ops);
+extern int register_security (struct security_operations *ops, int *idx);
extern int unregister_security (struct security_operations *ops);
-extern int mod_reg_security (const char *name, struct security_operations *ops);
+extern int mod_reg_security (const char *name,
+ struct security_operations *ops,
+ int *idx);
extern int mod_unreg_security (const char *name, struct security_operations *ops);
extern struct dentry *securityfs_create_file(const char *name, mode_t mode,
struct dentry *parent, void *data,
@@ -2103,9 +2198,7 @@
static inline int security_inode_init_security (struct inode *inode,
struct inode *dir,
- char **name,
- void **value,
- size_t *len)
+ struct list_head *head)
{
return -EOPNOTSUPP;
}
diff -ruN linux-2.6.14-rc4-git1.orig/include/linux/security-stack.h
linux-2.6.14-rc4-git1.new/include/linux/security-stack.h
--- linux-2.6.14-rc4-git1.orig/include/linux/security-stack.h 1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.14-rc4-git1.new/include/linux/security-stack.h 2005-10-12 15:54:46.000000000 -0400
@@ -0,0 +1,23 @@
+/*
+ * security-stack.h
+ *
+ * Contains function prototypes or inline definitions for the
+ * function which manipulate kernel object security annotations.
+ *
+ * If stacker is compiled in, then we use the full functions as
+ * defined in security/security.c. Otherwise we use the #defines
+ * here.
+ */
+
+extern fastcall struct security_list *security_get_value(
+ void **sec, int security_id);
+
+extern fastcall struct security_list *security_set_value(
+ void **sec, int security_id,
+ struct security_list *obj_node);
+extern fastcall struct security_list *security_add_value(
+ void **sec, int security_id,
+ struct security_list *obj_node);
+extern int security_unlink_value(struct hlist_node *n, int idx);
+extern fastcall struct security_list *security_del_value(
+ void **sec, int security_id);
diff -ruN linux-2.6.14-rc4-git1.orig/include/net/sock.h
linux-2.6.14-rc4-git1.new/include/net/sock.h
--- linux-2.6.14-rc4-git1.orig/include/net/sock.h 2005-10-12 15:52:26.000000000 -0400
+++ linux-2.6.14-rc4-git1.new/include/net/sock.h 2005-10-12 15:54:46.000000000 -0400
@@ -243,7 +243,7 @@
struct sk_buff *sk_send_head;
__u32 sk_sndmsg_off;
int sk_write_pending;
- void *sk_security;
+ void *sk_security[CONFIG_SECURITY_STACKER_NUMFIELDS];
void (*sk_state_change)(struct sock *sk);
void (*sk_data_ready)(struct sock *sk, int bytes);
void (*sk_write_space)(struct sock *sk);
diff -ruN linux-2.6.14-rc4-git1.orig/ipc/msg.c linux-2.6.14-rc4-git1.new/ipc/msg.c
--- linux-2.6.14-rc4-git1.orig/ipc/msg.c 2005-10-12 15:52:26.000000000 -0400
+++ linux-2.6.14-rc4-git1.new/ipc/msg.c 2005-10-12 15:54:46.000000000 -0400
@@ -100,7 +100,8 @@
msq->q_perm.mode = (msgflg & S_IRWXUGO);
msq->q_perm.key = key;
- msq->q_perm.security = NULL;
+ memset(&msq->q_perm.security, 0, (sec_shared_slot)*sizeof(void *));
+ INIT_HLIST_HEAD((struct hlist_head *)&(msq->q_perm.security[sec_shared_slot]));
retval = security_msg_queue_alloc(msq);
if (retval) {
ipc_rcu_putref(msq);
diff -ruN linux-2.6.14-rc4-git1.orig/ipc/msgutil.c linux-2.6.14-rc4-git1.new/ipc/msgutil.c
--- linux-2.6.14-rc4-git1.orig/ipc/msgutil.c 2005-08-28 19:41:01.000000000 -0400
+++ linux-2.6.14-rc4-git1.new/ipc/msgutil.c 2005-10-12 15:54:46.000000000 -0400
@@ -41,7 +41,8 @@
return ERR_PTR(-ENOMEM);
msg->next = NULL;
- msg->security = NULL;
+ memset(&msg->security, 0, (sec_shared_slot)*sizeof(void *));
+ INIT_HLIST_HEAD((struct hlist_head *)&(msg->security[sec_shared_slot]));
if (copy_from_user(msg + 1, src, alen)) {
err = -EFAULT;
diff -ruN linux-2.6.14-rc4-git1.orig/ipc/sem.c linux-2.6.14-rc4-git1.new/ipc/sem.c
--- linux-2.6.14-rc4-git1.orig/ipc/sem.c 2005-10-12 15:52:26.000000000 -0400
+++ linux-2.6.14-rc4-git1.new/ipc/sem.c 2005-10-12 15:54:46.000000000 -0400
@@ -179,7 +179,9 @@
sma->sem_perm.mode = (semflg & S_IRWXUGO);
sma->sem_perm.key = key;
- sma->sem_perm.security = NULL;
+ memset(&sma->sem_perm.security, 0, (sec_shared_slot)*sizeof(void *));
+ INIT_HLIST_HEAD((struct hlist_head *)
+ &(sma->sem_perm.security[sec_shared_slot]));
retval = security_sem_alloc(sma);
if (retval) {
ipc_rcu_putref(sma);
diff -ruN linux-2.6.14-rc4-git1.orig/ipc/shm.c linux-2.6.14-rc4-git1.new/ipc/shm.c
--- linux-2.6.14-rc4-git1.orig/ipc/shm.c 2005-10-12 15:52:26.000000000 -0400
+++ linux-2.6.14-rc4-git1.new/ipc/shm.c 2005-10-12 15:54:46.000000000 -0400
@@ -200,7 +200,8 @@
shp->shm_flags = (shmflg & S_IRWXUGO);
shp->mlock_user = NULL;
- shp->shm_perm.security = NULL;
+ memset(&shp->shm_perm.security, 0, (sec_shared_slot)*sizeof(void *));
+ INIT_HLIST_HEAD((struct hlist_head *)&(shp->shm_perm.security[sec_shared_slot]));
error = security_shm_alloc(shp);
if (error) {
ipc_rcu_putref(shp);
diff -ruN linux-2.6.14-rc4-git1.orig/kernel/fork.c linux-2.6.14-rc4-git1.new/kernel/fork.c
--- linux-2.6.14-rc4-git1.orig/kernel/fork.c 2005-10-12 15:52:26.000000000 -0400
+++ linux-2.6.14-rc4-git1.new/kernel/fork.c 2005-10-12 15:54:46.000000000 -0400
@@ -971,7 +971,8 @@
p->lock_depth = -1; /* -1 = no lock */
do_posix_clock_monotonic_gettime(&p->start_time);
- p->security = NULL;
+ memset(&p->security, 0, (sec_shared_slot)*sizeof(void *));
+ INIT_HLIST_HEAD((struct hlist_head *)&(p->security[sec_shared_slot]));
p->io_context = NULL;
p->io_wait = NULL;
p->audit_context = NULL;
diff -ruN linux-2.6.14-rc4-git1.orig/mm/shmem.c linux-2.6.14-rc4-git1.new/mm/shmem.c
--- linux-2.6.14-rc4-git1.orig/mm/shmem.c 2005-10-12 15:52:26.000000000 -0400
+++ linux-2.6.14-rc4-git1.new/mm/shmem.c 2005-10-12 15:54:46.000000000 -0400
@@ -1607,8 +1607,7 @@
int error = -ENOSPC;
if (inode) {
- error = security_inode_init_security(inode, dir, NULL, NULL,
- NULL);
+ error = security_inode_init_security(inode, dir, NULL);
if (error) {
if (error != -EOPNOTSUPP) {
iput(inode);
@@ -1754,8 +1753,7 @@
if (!inode)
return -ENOSPC;
- error = security_inode_init_security(inode, dir, NULL, NULL,
- NULL);
+ error = security_inode_init_security(inode, dir, NULL);
if (error) {
if (error != -EOPNOTSUPP) {
iput(inode);
diff -ruN linux-2.6.14-rc4-git1.orig/scripts/lsm_verify_hooks.sh
linux-2.6.14-rc4-git1.new/scripts/lsm_verify_hooks.sh
--- linux-2.6.14-rc4-git1.orig/scripts/lsm_verify_hooks.sh 1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.14-rc4-git1.new/scripts/lsm_verify_hooks.sh 2005-10-12 15:54:46.000000000 -0400
@@ -0,0 +1,44 @@
+#!/bin/sh
+
+# Author: Serge E. Hallyn <serue@us.ibm.com>
+
+# Checks that both the dummy and stacker modules define all the
+# security hooks.
+
+# This probably should be done in perl
+
+# Copyright (C) 2002,2003,2004,2005 Serge E. Hallyn <serue@us.ibm.com>
+# 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.
+
+# Grab the relevant pieces of text
+sed -n '/^void security_fixup_ops /,/}/p' dummy.c > dummy.out
+sed -n '/^static struct security_operations/,/}/p' stacker.c > stack.out
+../scripts/Lindent -o tmpsec.h ../include/linux/security.h
+sed -n '/^struct security_operations {/,/}/p' tmpsec.h > sech.out
+rm tmpsec.h
+
+# Get a list of functions in security.h
+cat sech.out | sed -n '/\t[a-z]/p' | sed -e 's/^\t[a-z]* (\*\([^)]*\).*$/\1/' > sech.out
+
+# check dummy.c
+for line in `cat sech.out`; do
+ grep $line dummy.out > /dev/null 2>&1
+ if [ $? -ne 0 ]; then
+ echo "WARNING: $line missing from dummy module!"
+ fi
+done
+
+# check stacker.c
+for line in `cat sech.out`; do
+ grep $line stack.out > /dev/null 2>&1
+ if [ $? -ne 0 ]; then
+ echo "WARNING: $line missing from stacker module!"
+ fi
+done
+
+rm sech.out stack.out dummy.out
+echo "LSM hook verification done."
diff -ruN linux-2.6.14-rc4-git1.orig/security/capability.c
linux-2.6.14-rc4-git1.new/security/capability.c
--- linux-2.6.14-rc4-git1.orig/security/capability.c 2005-08-28 19:41:01.000000000 -0400
+++ linux-2.6.14-rc4-git1.new/security/capability.c 2005-10-12 15:54:46.000000000 -0400
@@ -25,6 +25,7 @@
#include <linux/moduleparam.h>
static struct security_operations capability_ops = {
+ .owner = THIS_MODULE,
.ptrace = cap_ptrace,
.capget = cap_capget,
.capset_check = cap_capset_check,
@@ -65,9 +66,9 @@
return 0;
}
/* register ourselves with the security framework */
- if (register_security (&capability_ops)) {
+ if (register_security (&capability_ops, NULL)) {
/* try registering with primary module */
- if (mod_reg_security (MY_NAME, &capability_ops)) {
+ if (mod_reg_security (MY_NAME, &capability_ops, NULL)) {
printk (KERN_INFO "Failure registering capabilities "
"with primary security module.\n");
return -EINVAL;
diff -ruN linux-2.6.14-rc4-git1.orig/security/cap_stack.c
linux-2.6.14-rc4-git1.new/security/cap_stack.c
--- linux-2.6.14-rc4-git1.orig/security/cap_stack.c 1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.14-rc4-git1.new/security/cap_stack.c 2005-10-12 15:54:46.000000000 -0400
@@ -0,0 +1,101 @@
+/*
+ * Capabilities Linux Security Module
+ *
+ * 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/init.h>
+#include <linux/kernel.h>
+#include <linux/security.h>
+#include <linux/file.h>
+#include <linux/mm.h>
+#include <linux/mman.h>
+#include <linux/pagemap.h>
+#include <linux/swap.h>
+#include <linux/smp_lock.h>
+#include <linux/skbuff.h>
+#include <linux/netlink.h>
+#include <linux/ptrace.h>
+#include <linux/moduleparam.h>
+
+static struct security_operations capability_ops = {
+ .ptrace = cap_ptrace,
+ .capget = cap_capget,
+ .capset_check = cap_capset_check,
+ .capset_set = cap_capset_set,
+ .capable = cap_capable,
+ .settime = cap_settime,
+ .netlink_send = cap_netlink_send,
+ .netlink_recv = cap_netlink_recv,
+
+ .bprm_apply_creds = cap_bprm_apply_creds,
+ .bprm_set_security = cap_bprm_set_security,
+ .bprm_secureexec = cap_bprm_secureexec,
+
+ .task_post_setuid = cap_task_post_setuid,
+ .task_reparent_to_init = cap_task_reparent_to_init,
+
+ .syslog = cap_syslog,
+
+ .vm_enough_memory = cap_vm_enough_memory,
+};
+
+#define MY_NAME __stringify(KBUILD_MODNAME)
+
+/* flag to keep track of how we were registered */
+static int secondary;
+
+static int capability_disable;
+module_param_named(disable, capability_disable, int, 0);
+MODULE_PARM_DESC(disable, "To disable capabilities module set disable = 1");
+
+static int __init capability_init (void)
+{
+ if (capability_disable) {
+ printk(KERN_INFO "Capabilities disabled at initialization\n");
+ return 0;
+ }
+ /* register ourselves with the security framework */
+ if (register_security (&capability_ops, NULL)) {
+ /* try registering with primary module */
+ if (mod_reg_security (MY_NAME, &capability_ops, NULL)) {
+ printk (KERN_INFO "Failure registering capabilities "
+ "with primary security module.\n");
+ return -EINVAL;
+ }
+ secondary = 1;
+ }
+ printk (KERN_INFO "Capability LSM initialized%s\n",
+ secondary ? " as secondary" : "");
+ return 0;
+}
+
+static void __exit capability_exit (void)
+{
+ if (capability_disable)
+ return;
+ /* remove ourselves from the security framework */
+ if (secondary) {
+ if (mod_unreg_security (MY_NAME, &capability_ops))
+ printk (KERN_INFO "Failure unregistering capabilities "
+ "with primary module.\n");
+ return;
+ }
+
+ if (unregister_security (&capability_ops)) {
+ printk (KERN_INFO
+ "Failure unregistering capabilities with the kernel\n");
+ }
+}
+
+security_initcall (capability_init);
+module_exit (capability_exit);
+
+MODULE_DESCRIPTION("Standard Linux Capabilities Security Module");
+MODULE_LICENSE("GPL");
diff -ruN linux-2.6.14-rc4-git1.orig/security/dummy.c linux-2.6.14-rc4-git1.new/security/dummy.c
--- linux-2.6.14-rc4-git1.orig/security/dummy.c 2005-10-12 15:52:27.000000000 -0400
+++ linux-2.6.14-rc4-git1.new/security/dummy.c 2005-10-12 15:54:46.000000000 -0400
@@ -259,7 +259,7 @@
}
static int dummy_inode_init_security (struct inode *inode, struct inode *dir,
- char **name, void **value, size_t *len)
+ struct list_head *head)
{
return -EOPNOTSUPP;
}
diff -ruN linux-2.6.14-rc4-git1.orig/security/Kconfig linux-2.6.14-rc4-git1.new/security/Kconfig
--- linux-2.6.14-rc4-git1.orig/security/Kconfig 2005-10-12 15:52:27.000000000 -0400
+++ linux-2.6.14-rc4-git1.new/security/Kconfig 2005-10-12 15:54:46.000000000 -0400
@@ -57,10 +57,29 @@
config SECURITY_CAPABILITIES
tristate "Default Linux Capabilities"
depends on SECURITY
+ depends on SECURITY_SELINUX=n && SECURITY_CAP_STACK=n
help
- This enables the "default" Linux capabilities functionality.
+ This enables the default Linux capabilities functionality.
+ This module may not be used in conjunction with the stackable
+ capabilities or SELinux modules.
+
If you are unsure how to answer this question, answer Y.
+ If you are using SELinux, answer N here and look at the
+ Stackable Linux Capabilities instead.
+
+config SECURITY_CAP_STACK
+ tristate "Stackable Linux Capabilities"
+ depends on SECURITY
+ help
+ This enables the "stackable" Linux capabilities functionality.
+
+ If you are using SELinux, this option will be automatically
+ enabled.
+
+ If you are not using any other LSMs, answer N here and see above
+ for the Default Linux Capabilities.
+
config SECURITY_ROOTPLUG
tristate "Root Plug Support"
depends on USB && SECURITY
@@ -88,5 +107,27 @@
source security/selinux/Kconfig
+config SECURITY_STACKER
+ boolean "LSM Stacking"
+ depends on SECURITY
+ help
+ Stack multiple LSMs.
+
+config SECURITY_STACKER_NUMFIELDS
+ int "Number of security fields to reserve"
+ depends on SECURITY_STACKER
+ default 3
+ help
+ This option reserves extra space in each kernel object
+ for security information. This may speed up modules which
+ make a lot of use of these fields, as these accesses can
+ be lock-free. However each kernel object requires one
+ extra byte for each field reserved her, which can slow
+ the system down.
+
+ The actual number of static slots will be 1 greater than
+ this number, and this number must always be at least 1.
+ That one will be the shared slot for all LSMs to share.
+
endmenu
diff -ruN linux-2.6.14-rc4-git1.orig/security/Makefile linux-2.6.14-rc4-git1.new/security/Makefile
--- linux-2.6.14-rc4-git1.orig/security/Makefile 2005-10-12 15:52:27.000000000 -0400
+++ linux-2.6.14-rc4-git1.new/security/Makefile 2005-10-12 15:54:46.000000000 -0400
@@ -3,6 +3,7 @@
#
obj-$(CONFIG_KEYS) += keys/
+obj-$(CONFIG_SECURITY_STACKER) += stacker.o
subdir-$(CONFIG_SECURITY_SELINUX) += selinux
# if we don't select a security model, use the default capabilities
@@ -15,5 +16,7 @@
# Must precede capability.o in order to stack properly.
obj-$(CONFIG_SECURITY_SELINUX) += selinux/built-in.o
obj-$(CONFIG_SECURITY_CAPABILITIES) += commoncap.o capability.o
+obj-$(CONFIG_SECURITY_CAP_STACK) += commoncap.o cap_stack.o
obj-$(CONFIG_SECURITY_ROOTPLUG) += commoncap.o root_plug.o
obj-$(CONFIG_SECURITY_SECLVL) += seclvl.o
+obj-m += testinitsec1.o testinitsec2.o
diff -ruN linux-2.6.14-rc4-git1.orig/security/root_plug.c
linux-2.6.14-rc4-git1.new/security/root_plug.c
--- linux-2.6.14-rc4-git1.orig/security/root_plug.c 2005-08-28 19:41:01.000000000 -0400
+++ linux-2.6.14-rc4-git1.new/security/root_plug.c 2005-10-12 15:54:46.000000000 -0400
@@ -83,6 +83,8 @@
}
static struct security_operations rootplug_security_ops = {
+ .owner = THIS_MODULE,
+
/* Use the capability functions for some of the hooks */
.ptrace = cap_ptrace,
.capget = cap_capget,
@@ -102,11 +104,11 @@
static int __init rootplug_init (void)
{
/* register ourselves with the security framework */
- if (register_security (&rootplug_security_ops)) {
+ if (register_security (&rootplug_security_ops, NULL)) {
printk (KERN_INFO
"Failure registering Root Plug module with the kernel\n");
/* try registering with primary module */
- if (mod_reg_security (MY_NAME, &rootplug_security_ops)) {
+ if (mod_reg_security (MY_NAME, &rootplug_security_ops, NULL)) {
printk (KERN_INFO "Failure registering Root Plug "
" module with primary security module.\n");
return -EINVAL;
diff -ruN linux-2.6.14-rc4-git1.orig/security/seclvl.c linux-2.6.14-rc4-git1.new/security/seclvl.c
--- linux-2.6.14-rc4-git1.orig/security/seclvl.c 2005-10-12 15:52:27.000000000 -0400
+++ linux-2.6.14-rc4-git1.new/security/seclvl.c 2005-10-12 15:54:46.000000000 -0400
@@ -21,6 +21,7 @@
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/security.h>
+#include <linux/security-stack.h>
#include <linux/netlink.h>
#include <linux/fs.h>
#include <linux/namei.h>
@@ -35,6 +36,9 @@
#include <linux/sysfs.h>
#define SHA1_DIGEST_SIZE 20
+#define SECLVL_LSM_ID 0xF45
+
+static int seclvl_secidx;
/**
* Module parameter that defines the initial secure level.
@@ -385,12 +389,46 @@
return 0;
}
+/*
+ * A structure which is stored with the inode when a process
+ * has a unmounted block device open for writing.
+ */
+struct seclvl_i_sec {
+ struct security_list lsm_list;
+ struct task_struct *task;
+ spinlock_t spinlock;
+ struct list_head chain;
+};
+
+static LIST_HEAD(seclvl_ichain);
+static DEFINE_SPINLOCK(seclvl_ichain_lock);
+
+static void seclvl_inode_free(struct inode *inode)
+{
+ struct seclvl_i_sec *isec;
+
+ isec = security_del_value_type(inode->i_security, SECLVL_LSM_ID,
+ struct seclvl_i_sec, seclvl_secidx);
+ if (isec) {
+ spin_lock(&seclvl_ichain_lock);
+ list_del(&isec->chain);
+ spin_unlock(&seclvl_ichain_lock);
+ if (isec->task == current)
+ isec->task = NULL;
+ kfree(isec);
+ }
+}
+
/* claim the blockdev to exclude mounters, release on file close */
-static int seclvl_bd_claim(struct inode *inode)
+static int seclvl_bd_claim(struct inode *inode, struct seclvl_i_sec *isec)
{
int holder;
struct block_device *bdev = NULL;
dev_t dev = inode->i_rdev;
+
+ if (isec->task && isec->task != current)
+ return -EPERM;
+
bdev = open_by_devnum(dev, FMODE_WRITE);
if (bdev) {
if (bd_claim(bdev, &holder)) {
@@ -398,7 +436,7 @@
return -EPERM;
}
/* claimed, mark it to release on close */
- inode->i_security = current;
+ isec->task = current;
}
return 0;
}
@@ -406,17 +444,62 @@
/* release the blockdev if you claimed it */
static void seclvl_bd_release(struct inode *inode)
{
- if (inode && S_ISBLK(inode->i_mode) && inode->i_security == current) {
- struct block_device *bdev = inode->i_bdev;
- if (bdev) {
- bd_release(bdev);
- blkdev_put(bdev);
- inode->i_security = NULL;
- }
+ struct seclvl_i_sec *isec;
+
+ if (inode && S_ISBLK(inode->i_mode)) {
+ isec = security_get_value_type(inode->i_security,
+ SECLVL_LSM_ID, struct seclvl_i_sec,
+ seclvl_secidx);
+ if (!isec)
+ return;
+ spin_lock(&isec->spinlock);
+ if (isec->task == current) {
+ struct block_device *bdev = inode->i_bdev;
+ if (bdev) {
+ bd_release(bdev);
+ blkdev_put(bdev);
+ isec->task = NULL;
+ }
+ }
+ spin_unlock(&isec->spinlock);
}
}
/**
+ * Either returns the existing inode isec, or creates a new
+ * isec, places it on the inode->i_security list, and returns
+ * it.
+ * On error, return NULL.
+ */
+static struct seclvl_i_sec *
+seclvl_inode_get_or_alloc(struct inode *inode)
+{
+ struct seclvl_i_sec *isec;
+
+ isec = security_get_value_type(inode->i_security,
+ SECLVL_LSM_ID, struct seclvl_i_sec, seclvl_secidx);
+ if (isec)
+ return isec;
+ spin_lock(&seclvl_ichain_lock);
+ isec = security_get_value_type(inode->i_security,
+ SECLVL_LSM_ID, struct seclvl_i_sec, seclvl_secidx);
+ if (isec)
+ goto out;
+ isec = kmalloc(sizeof(struct seclvl_i_sec), GFP_KERNEL);
+ if (!isec)
+ goto out;
+ spin_lock_init(&isec->spinlock);
+ INIT_LIST_HEAD(&isec->chain);
+ list_add(&isec->chain, &seclvl_ichain);
+ security_add_value_type(inode->i_security, SECLVL_LSM_ID, isec,
+ seclvl_secidx);
+
+out:
+ spin_unlock(&seclvl_ichain_lock);
+ return isec;
+}
+
+/**
* Security for writes to block devices is regulated by this seclvl
* function. Deny all writes to block devices in seclvl 2. In
* seclvl 1, we only deny writes to *mounted* block devices.
@@ -424,6 +507,8 @@
static int
seclvl_inode_permission(struct inode *inode, int mask, struct nameidata *nd)
{
+ struct seclvl_i_sec *isec;
+
if (current->pid != 1 && S_ISBLK(inode->i_mode) && (mask & MAY_WRITE)) {
switch (seclvl) {
case 2:
@@ -431,13 +516,19 @@
"denied in secure level [%d]\n", seclvl);
return -EPERM;
case 1:
- if (seclvl_bd_claim(inode)) {
+ isec = seclvl_inode_get_or_alloc(inode);
+ if (!isec)
+ return -ENOMEM;
+ spin_lock(&isec->spinlock);
+ if (seclvl_bd_claim(inode, isec)) {
seclvl_printk(1, KERN_WARNING,
"Write to mounted block device "
"denied in secure level [%d]\n",
seclvl);
+ spin_unlock(&isec->spinlock);
return -EPERM;
}
+ spin_unlock(&isec->spinlock);
}
}
return 0;
@@ -490,10 +581,12 @@
}
static struct security_operations seclvl_ops = {
+ .owner = THIS_MODULE,
.ptrace = seclvl_ptrace,
.capable = seclvl_capable,
.inode_permission = seclvl_inode_permission,
.inode_setattr = seclvl_inode_setattr,
+ .inode_free_security = seclvl_inode_free,
.file_free_security = seclvl_file_free_security,
.settime = seclvl_settime,
.sb_umount = seclvl_umount,
@@ -601,12 +694,13 @@
goto exit;
}
/* register ourselves with the security framework */
- if (register_security(&seclvl_ops)) {
+ seclvl_secidx = 1; /* yes we want to use a security field */
+ if (register_security(&seclvl_ops, &seclvl_secidx)) {
seclvl_printk(0, KERN_ERR,
"seclvl: Failure registering with the "
"kernel.\n");
/* try registering with primary module */
- rc = mod_reg_security(MY_NAME, &seclvl_ops);
+ rc = mod_reg_security(MY_NAME, &seclvl_ops, &seclvl_secidx);
if (rc) {
seclvl_printk(0, KERN_ERR, "seclvl: Failure "
"registering with primary security "
@@ -628,6 +722,29 @@
return rc;
}
+/*
+ * free_ichain: Called when seclvl is unloaded to free all the memory
+ * it has attached to inodes.
+ * First we go through the list and remove the objects from the
+ * object chains (ie inode->i_security). Then we wait an rcu cycle.
+ * Then we can safely delete the object, as any any stacker m->hook()
+ * loop should have moved on to isec->lsm_list.next.
+ */
+static void free_ichain(void)
+{
+ struct seclvl_i_sec *isec, *n;
+
+ list_for_each_entry_safe(isec, n, &seclvl_ichain, chain) {
+ security_unlink_value(&isec->lsm_list.list, seclvl_secidx);
+ }
+
+ synchronize_rcu();
+ list_for_each_entry_safe(isec, n, &seclvl_ichain, chain) {
+ list_del(&isec->chain);
+ kfree(isec);
+ }
+}
+
/**
* Remove the seclvl module.
*/
@@ -644,6 +761,8 @@
"seclvl: Failure unregistering with the "
"kernel\n");
}
+
+ free_ichain();
}
module_init(seclvl_init);
diff -ruN linux-2.6.14-rc4-git1.orig/security/security.c
linux-2.6.14-rc4-git1.new/security/security.c
--- linux-2.6.14-rc4-git1.orig/security/security.c 2005-08-28 19:41:01.000000000 -0400
+++ linux-2.6.14-rc4-git1.new/security/security.c 2005-10-12 15:54:46.000000000 -0400
@@ -20,6 +20,172 @@
#define SECURITY_FRAMEWORK_VERSION "1.0.0"
+int sec_numslots = CONFIG_SECURITY_STACKER_NUMFIELDS;
+int sec_shared_slot;
+EXPORT_SYMBOL_GPL(sec_shared_slot);
+
+static struct security_operations
+ *security_field_owners[CONFIG_SECURITY_STACKER_NUMFIELDS];
+
+fastcall struct security_list *
+security_get_value(void **sec, int security_id)
+{
+ struct security_list *e, *ret = NULL;
+ struct hlist_head *head;
+ struct hlist_node *tmp;
+
+ head = (struct hlist_head *) &(sec[sec_shared_slot]);
+
+ rcu_read_lock();
+ for (tmp = head->first; tmp;
+ tmp = rcu_dereference(tmp->next)) {
+ e = hlist_entry(tmp, struct security_list, list);
+ if (e->security_id == security_id) {
+ ret = e;
+ goto out;
+ }
+ }
+
+out:
+ rcu_read_unlock();
+ return ret;
+}
+
+/*
+ * Only to be called from security_*_alloc hooks, so there is
+ * no locking as it is naturally serialized.
+ */
+fastcall void
+security_set_value(void **sec, int security_id,
+ struct security_list *obj_node)
+{
+ struct hlist_head *head;
+
+ head = (struct hlist_head *) &(sec[sec_shared_slot]);
+ obj_node->security_id = security_id;
+ hlist_add_head(&obj_node->list, head);
+}
+
+static DEFINE_SPINLOCK(stacker_value_spinlock);
+static DEFINE_SPINLOCK(security_field_spinlock);
+
+/*
+ * Used outside of security_*_alloc hooks, so we need to
+ * lock. Hopefully this won't be used much, so we use a
+ * spinlock for now.
+ */
+fastcall void
+security_add_value(void **sec, int security_id,
+ struct security_list *obj_node)
+{
+ struct hlist_head *head;
+
+ head = (struct hlist_head *) &(sec[sec_shared_slot]);
+ spin_lock(&stacker_value_spinlock);
+ obj_node->security_id = security_id;
+ hlist_add_head_rcu(&obj_node->list, head);
+ spin_unlock(&stacker_value_spinlock);
+}
+
+/*
+ * Unlink a security value from object->security, at a time other
+ * than when the object is being destroyed. Note that you MUST
+ * wait a full rcu cycle before deleting the object. If you are
+ * deleting a lot of objects (ie when unloading an LSM), then you
+ * likely will want to build a list of objects to be deleted as
+ * you remove them using this function, wait an rcu cycle, and then
+ * delete all the objects in your list.
+ * NOTE you obviously can't use the .next pointer for the to-be-
+ * deleted list, but you should be able to use the .prev pointer.
+ *
+ * XXX TODO - switch this to take a type, and deref
+ * obj->lsm_list.list here.
+ */
+int security_unlink_value(struct hlist_node *n, int idx)
+{
+ int ret = 0;
+
+ if (idx == sec_shared_slot)
+ return 0;
+
+ spin_lock(&stacker_value_spinlock);
+ if (n->pprev == LIST_POISON2) {
+ ret = 1;
+ goto out;
+ }
+ hlist_del_rcu(n);
+
+out:
+ spin_unlock(&stacker_value_spinlock);
+ return ret;
+}
+
+/*
+ * When a security module is unloaded, it is first removed from
+ * the list of callable modules
+ * (echo -n lsmname > /security/stacker/unload). Then at some
+ * later time the module's exit function will be called, which
+ * may try to free memory attached to kernel objects. The kernel
+ * objects may be deleted during this time. If that happens,
+ * then stacker will call security_disown_value so that the
+ * in-limbo LSM's data can be safely deleted. The LSM calls
+ * security_unlink_value() to remove the item from the list.
+ * security_unlink_value() will check whether
+ * lsm_list.pprev == LIST_POISON2. If not, then it removes the
+ * item from the (valid) list, else it simply returns, as there
+ * is no more list.
+ */
+void security_disown_value(struct hlist_head *h)
+{
+ spin_lock(&stacker_value_spinlock);
+ if (h->first) {
+ h->first->pprev = LIST_POISON2;
+ h->first = NULL;
+ }
+ spin_unlock(&stacker_value_spinlock);
+}
+
+int lsm_unloading;
+
+/* No locking needed: only called during object_destroy */
+fastcall struct security_list *
+security_del_value(void **sec, int security_id)
+{
+ struct security_list *e;
+ struct hlist_node *tmp;
+ struct hlist_head *head;
+ char d = 0;
+
+ if (lsm_unloading) {
+ d = 1;
+ spin_lock(&stacker_value_spinlock);
+ }
+
+ head = (struct hlist_head *) &(sec[sec_shared_slot]);
+ for (tmp = head->first; tmp; tmp = tmp->next) {
+ e = hlist_entry(tmp, struct security_list, list);
+ if (e->security_id == security_id) {
+ hlist_del(&e->list);
+ goto out;
+ }
+ }
+
+ e = NULL;
+
+out:
+ if (d)
+ spin_unlock(&stacker_value_spinlock);
+
+ return e;
+}
+
+EXPORT_SYMBOL_GPL(security_get_value);
+EXPORT_SYMBOL_GPL(security_set_value);
+EXPORT_SYMBOL_GPL(security_add_value);
+EXPORT_SYMBOL_GPL(security_unlink_value);
+EXPORT_SYMBOL_GPL(security_disown_value);
+EXPORT_SYMBOL_GPL(security_del_value);
+
/* things that live in dummy.c */
extern struct security_operations dummy_security_ops;
extern void security_fixup_ops(struct security_operations *ops);
@@ -61,6 +227,7 @@
return -EIO;
}
+ sec_shared_slot = sec_numslots - 1;
security_ops = &dummy_security_ops;
do_security_initcalls();
@@ -79,16 +246,31 @@
* If there is already a security module registered with the kernel,
* an error will be returned. Otherwise 0 is returned on success.
*/
-int register_security(struct security_operations *ops)
+int register_security(struct security_operations *ops, int *idx)
{
+ if (security_ops != &dummy_security_ops)
+ return -EAGAIN;
+
if (verify(ops)) {
printk(KERN_DEBUG "%s could not verify "
"security_operations structure.\n", __FUNCTION__);
return -EINVAL;
}
- if (security_ops != &dummy_security_ops)
- return -EAGAIN;
+ spin_lock(&security_field_spinlock);
+ if (idx) {
+ int i;
+
+ for (i=0; i<sec_shared_slot; i++) {
+ if (security_field_owners[i] == NULL) {
+ security_field_owners[i] = ops;
+ break;
+ }
+ }
+
+ *idx = i;
+ }
+ spin_unlock(&security_field_spinlock);
security_ops = ops;
@@ -108,6 +290,8 @@
*/
int unregister_security(struct security_operations *ops)
{
+ int i;
+
if (ops != security_ops) {
printk(KERN_INFO "%s: trying to unregister "
"a security_opts structure that is not "
@@ -117,6 +301,15 @@
security_ops = &dummy_security_ops;
+ spin_lock(&security_field_spinlock);
+ for (i=0; i<sec_shared_slot; i++) {
+ if (security_field_owners[i] == ops) {
+ security_field_owners[i] = NULL;
+ break;
+ }
+ }
+ spin_unlock(&security_field_spinlock);
+
return 0;
}
@@ -132,11 +325,14 @@
* The return value depends on the currently loaded security module, with 0 as
* success.
*/
-int mod_reg_security(const char *name, struct security_operations *ops)
+int mod_reg_security(const char *name, struct security_operations *ops,
+ int *idx)
{
- if (verify(ops)) {
- printk(KERN_INFO "%s could not verify "
- "security operations.\n", __FUNCTION__);
+ int ret = 0;
+
+ if (!ops) {
+ printk(KERN_INFO "%s received NULL security operations",
+ __FUNCTION__);
return -EINVAL;
}
@@ -146,7 +342,28 @@
return -EINVAL;
}
- return security_ops->register_security(name, ops);
+ ret = security_ops->register_security(name, ops);
+
+ if (ret < 0)
+ goto out;
+
+ spin_lock(&security_field_spinlock);
+ if (idx) {
+ int i;
+
+ for (i=0; i<sec_shared_slot; i++) {
+ if (security_field_owners[i] == NULL) {
+ security_field_owners[i] = ops;
+ break;
+ }
+ }
+
+ *idx = i;
+ }
+ spin_unlock(&security_field_spinlock);
+
+out:
+ return ret;
}
/**
@@ -164,12 +381,23 @@
*/
int mod_unreg_security(const char *name, struct security_operations *ops)
{
+ int i;
+
if (ops == security_ops) {
printk(KERN_INFO "%s invalid attempt to unregister "
" primary security ops.\n", __FUNCTION__);
return -EINVAL;
}
+ spin_lock(&security_field_spinlock);
+ for (i=0; i<sec_shared_slot; i++) {
+ if (security_field_owners[i] == ops) {
+ security_field_owners[i] = NULL;
+ break;
+ }
+ }
+ spin_unlock(&security_field_spinlock);
+
return security_ops->unregister_security(name, ops);
}
diff -ruN linux-2.6.14-rc4-git1.orig/security/selinux/hooks.c
linux-2.6.14-rc4-git1.new/security/selinux/hooks.c
--- linux-2.6.14-rc4-git1.orig/security/selinux/hooks.c 2005-10-12 15:52:27.000000000 -0400
+++ linux-2.6.14-rc4-git1.new/security/selinux/hooks.c 2005-10-12 15:54:46.000000000 -0400
@@ -26,6 +26,7 @@
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/security.h>
+#include <linux/security-stack.h>
#include <linux/xattr.h>
#include <linux/capability.h>
#include <linux/unistd.h>
@@ -77,9 +78,13 @@
#define XATTR_SELINUX_SUFFIX "selinux"
#define XATTR_NAME_SELINUX XATTR_SECURITY_PREFIX XATTR_SELINUX_SUFFIX
+int selinux_secidx;
+
extern unsigned int policydb_loaded_version;
extern int selinux_nlmsg_lookup(u16 sclass, u16 nlmsg_type, u32 *perm);
+static int secondary; /* how were we registered? */
+
#ifdef CONFIG_SECURITY_SELINUX_DEVELOP
int selinux_enforcing = 0;
@@ -102,15 +107,6 @@
__setup("selinux=", selinux_enabled_setup);
#endif
-/* Original (dummy) security module. */
-static struct security_operations *original_ops = NULL;
-
-/* Minimal support for a secondary security module,
- just to allow the use of the dummy or capability modules.
- The owlsm module can alternatively be used as a secondary
- module as long as CONFIG_OWLSM_FD is not enabled. */
-static struct security_operations *secondary_ops = NULL;
-
/* Lists of inode and superblock security structures initialized
before the policy was loaded. */
static LIST_HEAD(superblock_security_head);
@@ -127,30 +123,33 @@
return -ENOMEM;
memset(tsec, 0, sizeof(struct task_security_struct));
- tsec->magic = SELINUX_MAGIC;
tsec->task = task;
tsec->osid = tsec->sid = tsec->ptrace_sid = SECINITSID_UNLABELED;
- task->security = tsec;
+ security_set_value_type(task->security, SELINUX_LSM_ID, tsec,
+ selinux_secidx);
return 0;
}
static void task_free_security(struct task_struct *task)
{
- struct task_security_struct *tsec = task->security;
+ struct task_security_struct *tsec;
- if (!tsec || tsec->magic != SELINUX_MAGIC)
- return;
+ tsec = security_del_value_type(task->security, SELINUX_LSM_ID,
+ struct task_security_struct,
+ selinux_secidx);
- task->security = NULL;
kfree(tsec);
}
static int inode_alloc_security(struct inode *inode)
{
- struct task_security_struct *tsec = current->security;
+ struct task_security_struct *tsec;
struct inode_security_struct *isec;
+ tsec = security_get_value_type(current->security, SELINUX_LSM_ID,
+ struct task_security_struct,
+ selinux_secidx);
isec = kmalloc(sizeof(struct inode_security_struct), GFP_KERNEL);
if (!isec)
return -ENOMEM;
@@ -158,68 +157,77 @@
memset(isec, 0, sizeof(struct inode_security_struct));
init_MUTEX(&isec->sem);
INIT_LIST_HEAD(&isec->list);
- isec->magic = SELINUX_MAGIC;
isec->inode = inode;
isec->sid = SECINITSID_UNLABELED;
isec->sclass = SECCLASS_FILE;
- if (tsec && tsec->magic == SELINUX_MAGIC)
+ if (tsec)
isec->task_sid = tsec->sid;
else
isec->task_sid = SECINITSID_UNLABELED;
- inode->i_security = isec;
+ security_set_value_type(inode->i_security, SELINUX_LSM_ID, isec,
+ selinux_secidx);
return 0;
}
static void inode_free_security(struct inode *inode)
{
- struct inode_security_struct *isec = inode->i_security;
- struct superblock_security_struct *sbsec = inode->i_sb->s_security;
+ struct inode_security_struct *isec;
+ struct superblock_security_struct *sbsec;
- if (!isec || isec->magic != SELINUX_MAGIC)
+ isec = security_del_value_type(inode->i_security, SELINUX_LSM_ID,
+ struct inode_security_struct,
+ selinux_secidx);
+ if (!isec)
return;
+ sbsec = security_get_value_type(inode->i_sb->s_security,
+ SELINUX_LSM_ID, struct superblock_security_struct,
+ selinux_secidx);
+
spin_lock(&sbsec->isec_lock);
if (!list_empty(&isec->list))
list_del_init(&isec->list);
spin_unlock(&sbsec->isec_lock);
- inode->i_security = NULL;
kfree(isec);
}
static int file_alloc_security(struct file *file)
{
- struct task_security_struct *tsec = current->security;
+ struct task_security_struct *tsec;
struct file_security_struct *fsec;
+ tsec = security_get_value_type(current->security, SELINUX_LSM_ID,
+ struct task_security_struct,
+ selinux_secidx);
fsec = kmalloc(sizeof(struct file_security_struct), GFP_ATOMIC);
if (!fsec)
return -ENOMEM;
memset(fsec, 0, sizeof(struct file_security_struct));
- fsec->magic = SELINUX_MAGIC;
fsec->file = file;
- if (tsec && tsec->magic == SELINUX_MAGIC) {
+ if (tsec) {
fsec->sid = tsec->sid;
fsec->fown_sid = tsec->sid;
} else {
fsec->sid = SECINITSID_UNLABELED;
fsec->fown_sid = SECINITSID_UNLABELED;
}
- file->f_security = fsec;
+ security_set_value_type(file->f_security, SELINUX_LSM_ID, fsec,
+ selinux_secidx);
return 0;
}
static void file_free_security(struct file *file)
{
- struct file_security_struct *fsec = file->f_security;
+ struct file_security_struct *fsec;
- if (!fsec || fsec->magic != SELINUX_MAGIC)
- return;
+ fsec = security_del_value_type(file->f_security, SELINUX_LSM_ID,
+ struct file_security_struct,
+ selinux_secidx);
- file->f_security = NULL;
kfree(fsec);
}
@@ -236,20 +244,22 @@
INIT_LIST_HEAD(&sbsec->list);
INIT_LIST_HEAD(&sbsec->isec_head);
spin_lock_init(&sbsec->isec_lock);
- sbsec->magic = SELINUX_MAGIC;
sbsec->sb = sb;
sbsec->sid = SECINITSID_UNLABELED;
sbsec->def_sid = SECINITSID_FILE;
- sb->s_security = sbsec;
+ security_set_value_type(sb->s_security, SELINUX_LSM_ID, sbsec,
+ selinux_secidx);
return 0;
}
static void superblock_free_security(struct super_block *sb)
{
- struct superblock_security_struct *sbsec = sb->s_security;
+ struct superblock_security_struct *sbsec;
- if (!sbsec || sbsec->magic != SELINUX_MAGIC)
+ sbsec = security_del_value_type(sb->s_security, SELINUX_LSM_ID,
+ struct superblock_security_struct, selinux_secidx);
+ if (!sbsec)
return;
spin_lock(&sb_security_lock);
@@ -257,7 +267,6 @@
list_del_init(&sbsec->list);
spin_unlock(&sb_security_lock);
- sb->s_security = NULL;
kfree(sbsec);
}
@@ -274,22 +283,24 @@
return -ENOMEM;
memset(ssec, 0, sizeof(*ssec));
- ssec->magic = SELINUX_MAGIC;
ssec->sk = sk;
ssec->peer_sid = SECINITSID_UNLABELED;
- sk->sk_security = ssec;
+ security_set_value_type(sk->sk_security, SELINUX_LSM_ID, ssec,
+ selinux_secidx);
return 0;
}
static void sk_free_security(struct sock *sk)
{
- struct sk_security_struct *ssec = sk->sk_security;
+ struct sk_security_struct *ssec;
- if (sk->sk_family != PF_UNIX || ssec->magic != SELINUX_MAGIC)
+ if (sk->sk_family != PF_UNIX)
return;
- sk->sk_security = NULL;
+ ssec = security_del_value_type(sk->sk_security, SELINUX_LSM_ID,
+ struct sk_security_struct, selinux_secidx);
+
kfree(ssec);
}
#endif /* CONFIG_SECURITY_NETWORK */
@@ -336,8 +347,15 @@
const char *name;
u32 sid;
int alloc = 0, rc = 0, seen = 0;
- struct task_security_struct *tsec = current->security;
- struct superblock_security_struct *sbsec = sb->s_security;
+ struct task_security_struct *tsec;
+ struct superblock_security_struct *sbsec;
+
+ tsec = security_get_value_type(current->security, SELINUX_LSM_ID,
+ struct task_security_struct,
+ selinux_secidx);
+ sbsec = security_get_value_type(sb->s_security, SELINUX_LSM_ID,
+ struct superblock_security_struct,
+ selinux_secidx);
if (!data)
goto out;
@@ -503,11 +521,15 @@
static int superblock_doinit(struct super_block *sb, void *data)
{
- struct superblock_security_struct *sbsec = sb->s_security;
+ struct superblock_security_struct *sbsec;
struct dentry *root = sb->s_root;
struct inode *inode = root->d_inode;
int rc = 0;
+ sbsec = security_get_value_type(sb->s_security, SELINUX_LSM_ID,
+ struct superblock_security_struct,
+ selinux_secidx);
+
down(&sbsec->sem);
if (sbsec->initialized)
goto out;
@@ -748,7 +770,7 @@
static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dentry)
{
struct superblock_security_struct *sbsec = NULL;
- struct inode_security_struct *isec = inode->i_security;
+ struct inode_security_struct *isec;
u32 sid;
struct dentry *dentry;
#define INITCONTEXTLEN 255
@@ -757,6 +779,10 @@
int rc = 0;
int hold_sem = 0;
+ isec = security_get_value_type(inode->i_security, SELINUX_LSM_ID,
+ struct inode_security_struct,
+ selinux_secidx);
+
if (isec->initialized)
goto out;
@@ -765,7 +791,9 @@
if (isec->initialized)
goto out;
- sbsec = inode->i_sb->s_security;
+ sbsec = security_get_value_type(inode->i_sb->s_security,
+ SELINUX_LSM_ID, struct superblock_security_struct,
+ selinux_secidx);
if (!sbsec->initialized) {
/* Defer initialization until selinux_complete_init,
after the initial policy is loaded and the security
@@ -940,8 +968,12 @@
{
struct task_security_struct *tsec1, *tsec2;
- tsec1 = tsk1->security;
- tsec2 = tsk2->security;
+ tsec1 = security_get_value_type(tsk1->security, SELINUX_LSM_ID,
+ struct task_security_struct,
+ selinux_secidx);
+ tsec2 = security_get_value_type(tsk2->security, SELINUX_LSM_ID,
+ struct task_security_struct,
+ selinux_secidx);
return avc_has_perm(tsec1->sid, tsec2->sid,
SECCLASS_PROCESS, perms, NULL);
}
@@ -953,7 +985,9 @@
struct task_security_struct *tsec;
struct avc_audit_data ad;
- tsec = tsk->security;
+ tsec = security_get_value_type(tsk->security, SELINUX_LSM_ID,
+ struct task_security_struct,
+ selinux_secidx);
AVC_AUDIT_DATA_INIT(&ad,CAP);
ad.tsk = tsk;
@@ -969,7 +1003,9 @@
{
struct task_security_struct *tsec;
- tsec = tsk->security;
+ tsec = security_get_value_type(tsk->security, SELINUX_LSM_ID,
+ struct task_security_struct,
+ selinux_secidx);
return avc_has_perm(tsec->sid, SECINITSID_KERNEL,
SECCLASS_SYSTEM, perms, NULL);
@@ -987,8 +1023,12 @@
struct inode_security_struct *isec;
struct avc_audit_data ad;
- tsec = tsk->security;
- isec = inode->i_security;
+ tsec = security_get_value_type(tsk->security, SELINUX_LSM_ID,
+ struct task_security_struct,
+ selinux_secidx);
+ isec = security_get_value_type(inode->i_security, SELINUX_LSM_ID,
+ struct inode_security_struct,
+ selinux_secidx);
if (!adp) {
adp = &ad;
@@ -1027,14 +1067,21 @@
struct file *file,
u32 av)
{
- struct task_security_struct *tsec = tsk->security;
- struct file_security_struct *fsec = file->f_security;
+ struct task_security_struct *tsec;
+ struct file_security_struct *fsec;
struct vfsmount *mnt = file->f_vfsmnt;
struct dentry *dentry = file->f_dentry;
struct inode *inode = dentry->d_inode;
struct avc_audit_data ad;
int rc;
+ tsec = security_get_value_type(tsk->security, SELINUX_LSM_ID,
+ struct task_security_struct,
+ selinux_secidx);
+ fsec = security_get_value_type(file->f_security, SELINUX_LSM_ID,
+ struct file_security_struct,
+ selinux_secidx);
+
AVC_AUDIT_DATA_INIT(&ad, FS);
ad.u.fs.mnt = mnt;
ad.u.fs.dentry = dentry;
@@ -1067,9 +1114,15 @@
struct avc_audit_data ad;
int rc;
- tsec = current->security;
- dsec = dir->i_security;
- sbsec = dir->i_sb->s_security;
+ tsec = security_get_value_type(current->security, SELINUX_LSM_ID,
+ struct task_security_struct,
+ selinux_secidx);
+ dsec = security_get_value_type(dir->i_security, SELINUX_LSM_ID,
+ struct inode_security_struct,
+ selinux_secidx);
+ sbsec = security_get_value_type(dir->i_sb->s_security, SELINUX_LSM_ID,
+ struct superblock_security_struct,
+ selinux_secidx);
AVC_AUDIT_DATA_INIT(&ad, FS);
ad.u.fs.dentry = dentry;
@@ -1114,9 +1167,15 @@
u32 av;
int rc;
- tsec = current->security;
- dsec = dir->i_security;
- isec = dentry->d_inode->i_security;
+ tsec = security_get_value_type(current->security, SELINUX_LSM_ID,
+ struct task_security_struct,
+ selinux_secidx);
+ dsec = security_get_value_type(dir->i_security, SELINUX_LSM_ID,
+ struct inode_security_struct,
+ selinux_secidx);
+ isec = security_get_value_type(dentry->d_inode->i_security,
+ SELINUX_LSM_ID, struct inode_security_struct,
+ selinux_secidx);
AVC_AUDIT_DATA_INIT(&ad, FS);
ad.u.fs.dentry = dentry;
@@ -1158,11 +1217,19 @@
int old_is_dir, new_is_dir;
int rc;
- tsec = current->security;
- old_dsec = old_dir->i_security;
- old_isec = old_dentry->d_inode->i_security;
+ tsec = security_get_value_type(current->security, SELINUX_LSM_ID,
+ struct task_security_struct,
+ selinux_secidx);
+ old_dsec = security_get_value_type(old_dir->i_security, SELINUX_LSM_ID,
+ struct inode_security_struct,
+ selinux_secidx);
+ old_isec = security_get_value_type(old_dentry->d_inode->i_security,
+ SELINUX_LSM_ID, struct inode_security_struct,
+ selinux_secidx);
old_is_dir = S_ISDIR(old_dentry->d_inode->i_mode);
- new_dsec = new_dir->i_security;
+ new_dsec = security_get_value_type(new_dir->i_security, SELINUX_LSM_ID,
+ struct inode_security_struct,
+ selinux_secidx);
AVC_AUDIT_DATA_INIT(&ad, FS);
@@ -1190,7 +1257,9 @@
if (rc)
return rc;
if (new_dentry->d_inode) {
- new_isec = new_dentry->d_inode->i_security;
+ new_isec = security_get_value_type(new_dentry->d_inode->i_security,
+ SELINUX_LSM_ID, struct inode_security_struct,
+ selinux_secidx);
new_is_dir = S_ISDIR(new_dentry->d_inode->i_mode);
rc = avc_has_perm(tsec->sid, new_isec->sid,
new_isec->sclass,
@@ -1211,8 +1280,12 @@
struct task_security_struct *tsec;
struct superblock_security_struct *sbsec;
- tsec = tsk->security;
- sbsec = sb->s_security;
+ tsec = security_get_value_type(tsk->security, SELINUX_LSM_ID,
+ struct task_security_struct,
+ selinux_secidx);
+ sbsec = security_get_value_type(sb->s_security, SELINUX_LSM_ID,
+ struct superblock_security_struct,
+ selinux_secidx);
return avc_has_perm(tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM,
perms, ad);
}
@@ -1265,8 +1338,15 @@
/* Set an inode's SID to a specified value. */
static int inode_security_set_sid(struct inode *inode, u32 sid)
{
- struct inode_security_struct *isec = inode->i_security;
- struct superblock_security_struct *sbsec = inode->i_sb->s_security;
+ struct inode_security_struct *isec;
+ struct superblock_security_struct *sbsec;
+
+ isec = security_get_value_type(inode->i_security, SELINUX_LSM_ID,
+ struct inode_security_struct,
+ selinux_secidx);
+ sbsec = security_get_value_type(inode->i_sb->s_security,
+ SELINUX_LSM_ID, struct superblock_security_struct,
+ selinux_secidx);
if (!sbsec->initialized) {
/* Defer initialization to selinux_complete_init. */
@@ -1285,13 +1365,16 @@
static int selinux_ptrace(struct task_struct *parent, struct task_struct *child)
{
- struct task_security_struct *psec = parent->security;
- struct task_security_struct *csec = child->security;
+ struct task_security_struct *psec;
+ struct task_security_struct *csec;
int rc;
- rc = secondary_ops->ptrace(parent,child);
- if (rc)
- return rc;
+ psec = security_get_value_type(parent->security, SELINUX_LSM_ID,
+ struct task_security_struct,
+ selinux_secidx);
+ csec = security_get_value_type(child->security, SELINUX_LSM_ID,
+ struct task_security_struct,
+ selinux_secidx);
rc = task_has_perm(parent, child, PROCESS__PTRACE);
/* Save the SID of the tracing process for later use in apply_creds. */
@@ -1303,41 +1386,17 @@
static int selinux_capget(struct task_struct *target, kernel_cap_t *effective,
kernel_cap_t *inheritable, kernel_cap_t *permitted)
{
- int error;
-
- error = task_has_perm(current, target, PROCESS__GETCAP);
- if (error)
- return error;
-
- return secondary_ops->capget(target, effective, inheritable, permitted);
+ return task_has_perm(current, target, PROCESS__GETCAP);
}
static int selinux_capset_check(struct task_struct *target, kernel_cap_t *effective,
kernel_cap_t *inheritable, kernel_cap_t *permitted)
{
- int error;
-
- error = secondary_ops->capset_check(target, effective, inheritable, permitted);
- if (error)
- return error;
-
return task_has_perm(current, target, PROCESS__SETCAP);
}
-static void selinux_capset_set(struct task_struct *target, kernel_cap_t *effective,
- kernel_cap_t *inheritable, kernel_cap_t *permitted)
-{
- secondary_ops->capset_set(target, effective, inheritable, permitted);
-}
-
static int selinux_capable(struct task_struct *tsk, int cap)
{
- int rc;
-
- rc = secondary_ops->capable(tsk, cap);
- if (rc)
- return rc;
-
return task_has_capability(tsk,cap);
}
@@ -1349,11 +1408,9 @@
u32 tsid;
int rc;
- rc = secondary_ops->sysctl(table, op);
- if (rc)
- return rc;
-
- tsec = current->security;
+ tsec = security_get_value_type(current->security, SELINUX_LSM_ID,
+ struct task_security_struct,
+ selinux_secidx);
rc = selinux_proc_get_sid(table->de, (op == 001) ?
SECCLASS_DIR : SECCLASS_FILE, &tsid);
@@ -1419,11 +1476,7 @@
static int selinux_syslog(int type)
{
- int rc;
-
- rc = secondary_ops->syslog(type);
- if (rc)
- return rc;
+ int rc = 0;
switch (type) {
case 3: /* Read last kernel messages */
@@ -1447,36 +1500,6 @@
return rc;
}
-/*
- * Check that a process has enough memory to allocate a new virtual
- * mapping. 0 means there is enough memory for the allocation to
- * succeed and -ENOMEM implies there is not.
- *
- * Note that secondary_ops->capable and task_has_perm_noaudit return 0
- * if the capability is granted, but __vm_enough_memory requires 1 if
- * the capability is granted.
- *
- * Do not audit the selinux permission check, as this is applied to all
- * processes that allocate mappings.
- */
-static int selinux_vm_enough_memory(long pages)
-{
- int rc, cap_sys_admin = 0;
- struct task_security_struct *tsec = current->security;
-
- rc = secondary_ops->capable(current, CAP_SYS_ADMIN);
- if (rc == 0)
- rc = avc_has_perm_noaudit(tsec->sid, tsec->sid,
- SECCLASS_CAPABILITY,
- CAP_TO_MASK(CAP_SYS_ADMIN),
- NULL);
-
- if (rc == 0)
- cap_sys_admin = 1;
-
- return __vm_enough_memory(pages, cap_sys_admin);
-}
-
/* binprm security operations */
static int selinux_bprm_alloc_security(struct linux_binprm *bprm)
@@ -1488,12 +1511,12 @@
return -ENOMEM;
memset(bsec, 0, sizeof *bsec);
- bsec->magic = SELINUX_MAGIC;
bsec->bprm = bprm;
bsec->sid = SECINITSID_UNLABELED;
bsec->set = 0;
- bprm->security = bsec;
+ security_set_value_type(bprm->security, SELINUX_LSM_ID, bsec,
+ selinux_secidx);
return 0;
}
@@ -1507,17 +1530,19 @@
struct avc_audit_data ad;
int rc;
- rc = secondary_ops->bprm_set_security(bprm);
- if (rc)
- return rc;
-
- bsec = bprm->security;
+ bsec = security_get_value_type(bprm->security, SELINUX_LSM_ID,
+ struct bprm_security_struct,
+ selinux_secidx);
if (bsec->set)
return 0;
- tsec = current->security;
- isec = inode->i_security;
+ tsec = security_get_value_type(current->security, SELINUX_LSM_ID,
+ struct task_security_struct,
+ selinux_secidx);
+ isec = security_get_value_type(inode->i_security, SELINUX_LSM_ID,
+ struct inode_security_struct,
+ selinux_secidx);
/* Default to the current task SID. */
bsec->sid = tsec->sid;
@@ -1572,17 +1597,14 @@
return 0;
}
-static int selinux_bprm_check_security (struct linux_binprm *bprm)
-{
- return secondary_ops->bprm_check_security(bprm);
-}
-
-
static int selinux_bprm_secureexec (struct linux_binprm *bprm)
{
- struct task_security_struct *tsec = current->security;
+ struct task_security_struct *tsec;
int atsecure = 0;
+ tsec = security_get_value_type(current->security, SELINUX_LSM_ID,
+ struct task_security_struct,
+ selinux_secidx);
if (tsec->osid != tsec->sid) {
/* Enable secure mode for SIDs transitions unless
the noatsecure permission is granted between
@@ -1592,13 +1614,16 @@
PROCESS__NOATSECURE, NULL);
}
- return (atsecure || secondary_ops->bprm_secureexec(bprm));
+ return atsecure;
}
static void selinux_bprm_free_security(struct linux_binprm *bprm)
{
- kfree(bprm->security);
- bprm->security = NULL;
+ struct bprm_security_struct *bsec;
+
+ bsec = security_del_value_type(bprm->security, SELINUX_LSM_ID,
+ struct bprm_security_struct, selinux_secidx);
+ kfree(bsec);
}
extern struct vfsmount *selinuxfs_mount;
@@ -1695,11 +1720,12 @@
u32 sid;
int rc;
- secondary_ops->bprm_apply_creds(bprm, unsafe);
-
- tsec = current->security;
-
- bsec = bprm->security;
+ tsec = security_get_value_type(current->security, SELINUX_LSM_ID,
+ struct task_security_struct,
+ selinux_secidx);
+ bsec = security_get_value_type(bprm->security, SELINUX_LSM_ID,
+ struct bprm_security_struct,
+ selinux_secidx);
sid = bsec->sid;
tsec->osid = tsec->sid;
@@ -1742,8 +1768,12 @@
struct bprm_security_struct *bsec;
int rc, i;
- tsec = current->security;
- bsec = bprm->security;
+ tsec = security_get_value_type(current->security, SELINUX_LSM_ID,
+ struct task_security_struct,
+ selinux_secidx);
+ bsec = security_get_value_type(bprm->security, SELINUX_LSM_ID,
+ struct bprm_security_struct,
+ selinux_secidx);
if (bsec->unsafe) {
force_sig_specific(SIGKILL, current);
@@ -1919,12 +1949,6 @@
unsigned long flags,
void * data)
{
- int rc;
-
- rc = secondary_ops->sb_mount(dev_name, nd, type, flags, data);
- if (rc)
- return rc;
-
if (flags & MS_REMOUNT)
return superblock_has_perm(current, nd->mnt->mnt_sb,
FILESYSTEM__REMOUNT, NULL);
@@ -1935,12 +1959,6 @@
static int selinux_umount(struct vfsmount *mnt, int flags)
{
- int rc;
-
- rc = secondary_ops->sb_umount(mnt, flags);
- if (rc)
- return rc;
-
return superblock_has_perm(current,mnt->mnt_sb,
FILESYSTEM__UNMOUNT,NULL);
}
@@ -1958,21 +1976,29 @@
}
static int selinux_inode_init_security(struct inode *inode, struct inode *dir,
- char **name, void **value,
- size_t *len)
+ struct list_head *head)
{
struct task_security_struct *tsec;
struct inode_security_struct *dsec;
struct superblock_security_struct *sbsec;
struct inode_security_struct *isec;
+ struct xattr_data *datap;
u32 newsid, clen;
int rc;
char *namep = NULL, *context;
- tsec = current->security;
- dsec = dir->i_security;
- sbsec = dir->i_sb->s_security;
- isec = inode->i_security;
+ tsec = security_get_value_type(current->security, SELINUX_LSM_ID,
+ struct task_security_struct,
+ selinux_secidx);
+ dsec = security_get_value_type(dir->i_security, SELINUX_LSM_ID,
+ struct inode_security_struct,
+ selinux_secidx);
+ sbsec = security_get_value_type(dir->i_sb->s_security, SELINUX_LSM_ID,
+ struct superblock_security_struct,
+ selinux_secidx);
+ isec = security_get_value_type(inode->i_security, SELINUX_LSM_ID,
+ struct inode_security_struct,
+ selinux_secidx);
if (tsec->create_sid && sbsec->behavior != SECURITY_FS_USE_MNTPOINT) {
newsid = tsec->create_sid;
@@ -1992,24 +2018,34 @@
inode_security_set_sid(inode, newsid);
- if (name) {
- namep = kstrdup(XATTR_SELINUX_SUFFIX, GFP_KERNEL);
- if (!namep)
- return -ENOMEM;
- *name = namep;
- }
+ if (!head)
+ return 0;
- if (value && len) {
- rc = security_sid_to_context(newsid, &context, &clen);
- if (rc) {
- kfree(namep);
- return rc;
- }
- *value = context;
- *len = clen;
+ datap = kmalloc(sizeof(struct xattr_data), GFP_KERNEL);
+ if (!datap)
+ return -ENOMEM;
+
+ namep = kstrdup(XATTR_SELINUX_SUFFIX, GFP_KERNEL);
+ if (!namep) {
+ rc = -ENOMEM;
+ goto err;
}
+ rc = security_sid_to_context(newsid, &context, &clen);
+ if (rc)
+ goto err;
+ datap->value = context;
+ datap->len = clen;
+ datap->name = namep;
+ INIT_LIST_HEAD(&datap->list);
+
+ list_add_tail(&datap->list, head);
return 0;
+
+err:
+ kfree(namep);
+ kfree(datap);
+ return rc;
}
static int selinux_inode_create(struct inode *dir, struct dentry *dentry, int mask)
@@ -2019,21 +2055,11 @@
static int selinux_inode_link(struct dentry *old_dentry, struct inode *dir, struct dentry
*new_dentry)
{
- int rc;
-
- rc = secondary_ops->inode_link(old_dentry,dir,new_dentry);
- if (rc)
- return rc;
return may_link(dir, old_dentry, MAY_LINK);
}
static int selinux_inode_unlink(struct inode *dir, struct dentry *dentry)
{
- int rc;
-
- rc = secondary_ops->inode_unlink(dir, dentry);
- if (rc)
- return rc;
return may_link(dir, dentry, MAY_UNLINK);
}
@@ -2054,12 +2080,6 @@
static int selinux_inode_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev)
{
- int rc;
-
- rc = secondary_ops->inode_mknod(dir, dentry, mode, dev);
- if (rc)
- return rc;
-
return may_create(dir, dentry, inode_mode_to_security_class(mode));
}
@@ -2076,23 +2096,12 @@
static int selinux_inode_follow_link(struct dentry *dentry, struct nameidata *nameidata)
{
- int rc;
-
- rc = secondary_ops->inode_follow_link(dentry,nameidata);
- if (rc)
- return rc;
return dentry_has_perm(current, NULL, dentry, FILE__READ);
}
static int selinux_inode_permission(struct inode *inode, int mask,
struct nameidata *nd)
{
- int rc;
-
- rc = secondary_ops->inode_permission(inode, mask, nd);
- if (rc)
- return rc;
-
if (!mask) {
/* No permission to check. Existence test. */
return 0;
@@ -2104,12 +2113,6 @@
static int selinux_inode_setattr(struct dentry *dentry, struct iattr *iattr)
{
- int rc;
-
- rc = secondary_ops->inode_setattr(dentry, iattr);
- if (rc)
- return rc;
-
if (iattr->ia_valid & ATTR_FORCE)
return 0;
@@ -2127,9 +2130,9 @@
static int selinux_inode_setxattr(struct dentry *dentry, char *name, void *value, size_t size, int
flags)
{
- struct task_security_struct *tsec = current->security;
+ struct task_security_struct *tsec;
struct inode *inode = dentry->d_inode;
- struct inode_security_struct *isec = inode->i_security;
+ struct inode_security_struct *isec;
struct superblock_security_struct *sbsec;
struct avc_audit_data ad;
u32 newsid;
@@ -2149,7 +2152,9 @@
return dentry_has_perm(current, NULL, dentry, FILE__SETATTR);
}
- sbsec = inode->i_sb->s_security;
+ sbsec = security_get_value_type(inode->i_sb->s_security,
+ SELINUX_LSM_ID, struct superblock_security_struct,
+ selinux_secidx);
if (sbsec->behavior == SECURITY_FS_USE_MNTPOINT)
return -EOPNOTSUPP;
@@ -2159,6 +2164,12 @@
AVC_AUDIT_DATA_INIT(&ad,FS);
ad.u.fs.dentry = dentry;
+ tsec = security_get_value_type(current->security, SELINUX_LSM_ID,
+ struct task_security_struct,
+ selinux_secidx);
+ isec = security_get_value_type(inode->i_security, SELINUX_LSM_ID,
+ struct inode_security_struct,
+ selinux_secidx);
rc = avc_has_perm(tsec->sid, isec->sid, isec->sclass,
FILE__RELABELFROM, &ad);
if (rc)
@@ -2189,10 +2200,14 @@
void *value, size_t size, int flags)
{
struct inode *inode = dentry->d_inode;
- struct inode_security_struct *isec = inode->i_security;
+ struct inode_security_struct *isec;
u32 newsid;
int rc;
+ isec = security_get_value_type(inode->i_security, SELINUX_LSM_ID,
+ struct inode_security_struct,
+ selinux_secidx);
+
if (strcmp(name, XATTR_NAME_SELINUX)) {
/* Not an attribute we recognize, so nothing to do. */
return;
@@ -2212,7 +2227,11 @@
static int selinux_inode_getxattr (struct dentry *dentry, char *name)
{
struct inode *inode = dentry->d_inode;
- struct superblock_security_struct *sbsec = inode->i_sb->s_security;
+ struct superblock_security_struct *sbsec;
+
+ sbsec = security_get_value_type(inode->i_sb->s_security,
+ SELINUX_LSM_ID, struct superblock_security_struct,
+ selinux_secidx);
if (sbsec->behavior == SECURITY_FS_USE_MNTPOINT)
return -EOPNOTSUPP;
@@ -2249,7 +2268,7 @@
static int selinux_inode_getsecurity(struct inode *inode, const char *name, void *buffer, size_t
size)
{
- struct inode_security_struct *isec = inode->i_security;
+ struct inode_security_struct *isec;
char *context;
unsigned len;
int rc;
@@ -2259,6 +2278,10 @@
if (strcmp(name, XATTR_SELINUX_SUFFIX))
return -EOPNOTSUPP;
+ isec = security_get_value_type(inode->i_security, SELINUX_LSM_ID,
+ struct inode_security_struct,
+ selinux_secidx);
+
rc = security_sid_to_context(isec->sid, &context, &len);
if (rc)
return rc;
@@ -2279,13 +2302,17 @@
static int selinux_inode_setsecurity(struct inode *inode, const char *name,
const void *value, size_t size, int flags)
{
- struct inode_security_struct *isec = inode->i_security;
+ struct inode_security_struct *isec;
u32 newsid;
int rc;
if (strcmp(name, XATTR_SELINUX_SUFFIX))
return -EOPNOTSUPP;
+ isec = security_get_value_type(inode->i_security, SELINUX_LSM_ID,
+ struct inode_security_struct,
+ selinux_secidx);
+
if (!value || !size)
return -EACCES;
@@ -2414,12 +2441,6 @@
static int selinux_file_mmap(struct file *file, unsigned long reqprot,
unsigned long prot, unsigned long flags)
{
- int rc;
-
- rc = secondary_ops->file_mmap(file, reqprot, prot, flags);
- if (rc)
- return rc;
-
if (selinux_checkreqprot)
prot = reqprot;
@@ -2433,10 +2454,6 @@
{
int rc;
- rc = secondary_ops->file_mprotect(vma, reqprot, prot);
- if (rc)
- return rc;
-
if (selinux_checkreqprot)
prot = reqprot;
@@ -2459,7 +2476,7 @@
* check ability to execute the possibly modified content.
* This typically should only occur for text relocations.
*/
- int rc = file_has_perm(current, vma->vm_file, FILE__EXECMOD);
+ rc = file_has_perm(current, vma->vm_file, FILE__EXECMOD);
if (rc)
return rc;
}
@@ -2532,8 +2549,12 @@
struct task_security_struct *tsec;
struct file_security_struct *fsec;
- tsec = current->security;
- fsec = file->f_security;
+ tsec = security_get_value_type(current->security, SELINUX_LSM_ID,
+ struct task_security_struct,
+ selinux_secidx);
+ fsec = security_get_value_type(file->f_security, SELINUX_LSM_ID,
+ struct file_security_struct,
+ selinux_secidx);
fsec->fown_sid = tsec->sid;
return 0;
@@ -2550,8 +2571,12 @@
/* struct fown_struct is never outside the context of a struct file */
file = (struct file *)((long)fown - offsetof(struct file,f_owner));
- tsec = tsk->security;
- fsec = file->f_security;
+ tsec = security_get_value_type(tsk->security, SELINUX_LSM_ID,
+ struct task_security_struct,
+ selinux_secidx);
+ fsec = security_get_value_type(file->f_security, SELINUX_LSM_ID,
+ struct file_security_struct,
+ selinux_secidx);
if (!signum)
perm = signal_to_av(SIGIO); /* as per send_sigio_to_task */
@@ -2571,12 +2596,6 @@
static int selinux_task_create(unsigned long clone_flags)
{
- int rc;
-
- rc = secondary_ops->task_create(clone_flags);
- if (rc)
- return rc;
-
return task_has_perm(current, current, PROCESS__FORK);
}
@@ -2585,12 +2604,16 @@
struct task_security_struct *tsec1, *tsec2;
int rc;
- tsec1 = current->security;
+ tsec1 = security_get_value_type(current->security, SELINUX_LSM_ID,
+ struct task_security_struct,
+ selinux_secidx);
rc = task_alloc_security(tsk);
if (rc)
return rc;
- tsec2 = tsk->security;
+ tsec2 = security_get_value_type(tsk->security, SELINUX_LSM_ID,
+ struct task_security_struct,
+ selinux_secidx);
tsec2->osid = tsec1->osid;
tsec2->sid = tsec1->sid;
@@ -2623,11 +2646,6 @@
return 0;
}
-static int selinux_task_post_setuid(uid_t id0, uid_t id1, uid_t id2, int flags)
-{
- return secondary_ops->task_post_setuid(id0,id1,id2,flags);
-}
-
static int selinux_task_setgid(gid_t id0, gid_t id1, gid_t id2, int flags)
{
/* See the comment for setuid above. */
@@ -2657,24 +2675,12 @@
static int selinux_task_setnice(struct task_struct *p, int nice)
{
- int rc;
-
- rc = secondary_ops->task_setnice(p, nice);
- if (rc)
- return rc;
-
return task_has_perm(current,p, PROCESS__SETSCHED);
}
static int selinux_task_setrlimit(unsigned int resource, struct rlimit *new_rlim)
{
struct rlimit *old_rlim = current->signal->rlim + resource;
- int rc;
-
- rc = secondary_ops->task_setrlimit(resource, new_rlim);
- if (rc)
- return rc;
-
/* Control the ability to change the hard limit (whether
lowering or raising it), so that the hard limit can
later be used as a safe reset point for the soft limit
@@ -2698,11 +2704,6 @@
static int selinux_task_kill(struct task_struct *p, struct siginfo *info, int sig)
{
u32 perm;
- int rc;
-
- rc = secondary_ops->task_kill(p, info, sig);
- if (rc)
- return rc;
if (info && ((unsigned long)info == 1 ||
(unsigned long)info == 2 || SI_FROMKERNEL(info)))
@@ -2741,9 +2742,9 @@
{
struct task_security_struct *tsec;
- secondary_ops->task_reparent_to_init(p);
-
- tsec = p->security;
+ tsec = security_get_value_type(p->security, SELINUX_LSM_ID,
+ struct task_security_struct,
+ selinux_secidx);
tsec->osid = tsec->sid;
tsec->sid = SECINITSID_KERNEL;
return;
@@ -2752,8 +2753,15 @@
static void selinux_task_to_inode(struct task_struct *p,
struct inode *inode)
{
- struct task_security_struct *tsec = p->security;
- struct inode_security_struct *isec = inode->i_security;
+ struct task_security_struct *tsec;
+ struct inode_security_struct *isec;
+
+ tsec = security_get_value_type(p->security, SELINUX_LSM_ID,
+ struct task_security_struct,
+ selinux_secidx);
+ isec = security_get_value_type(inode->i_security, SELINUX_LSM_ID,
+ struct inode_security_struct,
+ selinux_secidx);
isec->sid = tsec->sid;
isec->initialized = 1;
@@ -2921,8 +2929,12 @@
struct avc_audit_data ad;
int err = 0;
- tsec = task->security;
- isec = SOCK_INODE(sock)->i_security;
+ tsec = security_get_value_type(task->security, SELINUX_LSM_ID,
+ struct task_security_struct,
+ selinux_secidx);
+ isec = security_get_value_type(SOCK_INODE(sock)->i_security,
+ SELINUX_LSM_ID, struct inode_security_struct,
+ selinux_secidx);
if (isec->sid == SECINITSID_KERNEL)
goto out;
@@ -2944,7 +2956,9 @@
if (kern)
goto out;
- tsec = current->security;
+ tsec = security_get_value_type(current->security, SELINUX_LSM_ID,
+ struct task_security_struct,
+ selinux_secidx);
err = avc_has_perm(tsec->sid, tsec->sid,
socket_type_to_security_class(family, type,
protocol), SOCKET__CREATE, NULL);
@@ -2959,9 +2973,13 @@
struct inode_security_struct *isec;
struct task_security_struct *tsec;
- isec = SOCK_INODE(sock)->i_security;
-
- tsec = current->security;
+ isec = security_get_value_type(SOCK_INODE(sock)->i_security,
+ SELINUX_LSM_ID, struct inode_security_struct,
+ selinux_secidx);
+
+ tsec = security_get_value_type(current->security, SELINUX_LSM_ID,
+ struct task_security_struct,
+ selinux_secidx);
isec->sclass = socket_type_to_security_class(family, type, protocol);
isec->sid = kern ? SECINITSID_KERNEL : tsec->sid;
isec->initialized = 1;
@@ -3001,8 +3019,12 @@
struct sock *sk = sock->sk;
u32 sid, node_perm, addrlen;
- tsec = current->security;
- isec = SOCK_INODE(sock)->i_security;
+ tsec = security_get_value_type(current->security,
+ SELINUX_LSM_ID, struct task_security_struct,
+ selinux_secidx);
+ isec = security_get_value_type(SOCK_INODE(sock)->i_security,
+ SELINUX_LSM_ID, struct inode_security_struct,
+ selinux_secidx);
if (family == PF_INET) {
addr4 = (struct sockaddr_in *)address;
@@ -3080,7 +3102,9 @@
/*
* If a TCP socket, check name_connect permission for the port.
*/
- isec = SOCK_INODE(sock)->i_security;
+ isec = security_get_value_type(SOCK_INODE(sock)->i_security,
+ SELINUX_LSM_ID, struct inode_security_struct,
+ selinux_secidx);
if (isec->sclass == SECCLASS_TCP_SOCKET) {
struct sock *sk = sock->sk;
struct avc_audit_data ad;
@@ -3134,9 +3158,13 @@
if (err)
return err;
- newisec = SOCK_INODE(newsock)->i_security;
-
- isec = SOCK_INODE(sock)->i_security;
+ newisec = security_get_value_type(SOCK_INODE(newsock)->i_security,
+ SELINUX_LSM_ID, struct inode_security_struct,
+ selinux_secidx);
+
+ isec = security_get_value_type(SOCK_INODE(sock)->i_security,
+ SELINUX_LSM_ID, struct inode_security_struct,
+ selinux_secidx);
newisec->sclass = isec->sclass;
newisec->sid = isec->sid;
newisec->initialized = 1;
@@ -3192,12 +3220,12 @@
struct avc_audit_data ad;
int err;
- err = secondary_ops->unix_stream_connect(sock, other, newsk);
- if (err)
- return err;
-
- isec = SOCK_INODE(sock)->i_security;
- other_isec = SOCK_INODE(other)->i_security;
+ isec = security_get_value_type(SOCK_INODE(sock)->i_security,
+ SELINUX_LSM_ID, struct inode_security_struct,
+ selinux_secidx);
+ other_isec = security_get_value_type(SOCK_INODE(other)->i_security,
+ SELINUX_LSM_ID, struct inode_security_struct,
+ selinux_secidx);
AVC_AUDIT_DATA_INIT(&ad,NET);
ad.u.net.sk = other->sk;
@@ -3209,11 +3237,15 @@
return err;
/* connecting socket */
- ssec = sock->sk->sk_security;
+ ssec = security_get_value_type(sock->sk->sk_security, SELINUX_LSM_ID,
+ struct sk_security_struct,
+ selinux_secidx);
ssec->peer_sid = other_isec->sid;
/* server child socket */
- ssec = newsk->sk_security;
+ ssec = security_get_value_type(newsk->sk_security, SELINUX_LSM_ID,
+ struct sk_security_struct,
+ selinux_secidx);
ssec->peer_sid = isec->sid;
return 0;
@@ -3227,8 +3259,12 @@
struct avc_audit_data ad;
int err;
- isec = SOCK_INODE(sock)->i_security;
- other_isec = SOCK_INODE(other)->i_security;
+ isec = security_get_value_type(SOCK_INODE(sock)->i_security,
+ SELINUX_LSM_ID, struct inode_security_struct,
+ selinux_secidx);
+ other_isec = security_get_value_type(SOCK_INODE(other)->i_security,
+ SELINUX_LSM_ID, struct inode_security_struct,
+ selinux_secidx);
AVC_AUDIT_DATA_INIT(&ad,NET);
ad.u.net.sk = other->sk;
@@ -3268,7 +3304,9 @@
inode = SOCK_INODE(sock);
if (inode) {
struct inode_security_struct *isec;
- isec = inode->i_security;
+ isec = security_get_value_type(inode->i_security,
+ SELINUX_LSM_ID, struct inode_security_struct,
+ selinux_secidx);
sock_sid = isec->sid;
sock_class = isec->sclass;
}
@@ -3351,13 +3389,17 @@
struct sk_security_struct *ssec;
struct inode_security_struct *isec;
- isec = SOCK_INODE(sock)->i_security;
+ isec = security_get_value_type(SOCK_INODE(sock)->i_security,
+ SELINUX_LSM_ID, struct inode_security_struct,
+ selinux_secidx);
if (isec->sclass != SECCLASS_UNIX_STREAM_SOCKET) {
err = -ENOPROTOOPT;
goto out;
}
- ssec = sock->sk->sk_security;
+ ssec = security_get_value_type(sock->sk->sk_security, SELINUX_LSM_ID,
+ struct sk_security_struct,
+ selinux_secidx);
err = security_sid_to_context(ssec->peer_sid, &scontext, &scontext_len);
if (err)
@@ -3396,7 +3438,11 @@
u32 perm;
struct nlmsghdr *nlh;
struct socket *sock = sk->sk_socket;
- struct inode_security_struct *isec = SOCK_INODE(sock)->i_security;
+ struct inode_security_struct *isec;
+
+ isec = security_get_value_type(SOCK_INODE(sock)->i_security,
+ SELINUX_LSM_ID, struct inode_security_struct,
+ selinux_secidx);
if (skb->len < NLMSG_SPACE(0)) {
err = -EINVAL;
@@ -3462,7 +3508,9 @@
if (err)
goto out;
- isec = inode->i_security;
+ isec = security_get_value_type(inode->i_security, SELINUX_LSM_ID,
+ struct inode_security_struct,
+ selinux_secidx);
switch (isec->sclass) {
case SECCLASS_UDP_SOCKET:
@@ -3566,13 +3614,11 @@
{
struct task_security_struct *tsec;
struct av_decision avd;
- int err;
-
- err = secondary_ops->netlink_send(sk, skb);
- if (err)
- return err;
+ int err = 0;
- tsec = current->security;
+ tsec = security_get_value_type(current->security,
+ SELINUX_LSM_ID, struct task_security_struct,
+ selinux_secidx);
avd.allowed = 0;
avc_has_perm_noaudit(tsec->sid, tsec->sid,
@@ -3585,26 +3631,22 @@
return err;
}
-static int selinux_netlink_recv(struct sk_buff *skb)
-{
- if (!cap_raised(NETLINK_CB(skb).eff_cap, CAP_NET_ADMIN))
- return -EPERM;
- return 0;
-}
-
static int ipc_alloc_security(struct task_struct *task,
struct kern_ipc_perm *perm,
u16 sclass)
{
- struct task_security_struct *tsec = task->security;
+ struct task_security_struct *tsec;
struct ipc_security_struct *isec;
+ tsec = security_get_value_type(task->security, SELINUX_LSM_ID,
+ struct task_security_struct,
+ selinux_secidx);
+
isec = kmalloc(sizeof(struct ipc_security_struct), GFP_KERNEL);
if (!isec)
return -ENOMEM;
memset(isec, 0, sizeof(struct ipc_security_struct));
- isec->magic = SELINUX_MAGIC;
isec->sclass = sclass;
isec->ipc_perm = perm;
if (tsec) {
@@ -3612,18 +3654,19 @@
} else {
isec->sid = SECINITSID_UNLABELED;
}
- perm->security = isec;
+ security_set_value_type(perm->security, SELINUX_LSM_ID, isec,
+ selinux_secidx);
return 0;
}
static void ipc_free_security(struct kern_ipc_perm *perm)
{
- struct ipc_security_struct *isec = perm->security;
- if (!isec || isec->magic != SELINUX_MAGIC)
- return;
+ struct ipc_security_struct *isec;
+
+ isec = security_del_value_type(perm->security, SELINUX_LSM_ID,
+ struct ipc_security_struct, selinux_secidx);
- perm->security = NULL;
kfree(isec);
}
@@ -3636,21 +3679,22 @@
return -ENOMEM;
memset(msec, 0, sizeof(struct msg_security_struct));
- msec->magic = SELINUX_MAGIC;
msec->msg = msg;
msec->sid = SECINITSID_UNLABELED;
- msg->security = msec;
+ security_set_value_type(msg->security, SELINUX_LSM_ID, msec,
++ selinux_secidx);
return 0;
}
static void msg_msg_free_security(struct msg_msg *msg)
{
- struct msg_security_struct *msec = msg->security;
- if (!msec || msec->magic != SELINUX_MAGIC)
- return;
+ struct msg_security_struct *msec;
+
+ msec = security_del_value_type(msg->security, SELINUX_LSM_ID,
+ struct msg_security_struct,
+ selinux_secidx);
- msg->security = NULL;
kfree(msec);
}
@@ -3661,8 +3705,12 @@
struct ipc_security_struct *isec;
struct avc_audit_data ad;
- tsec = current->security;
- isec = ipc_perms->security;
+ tsec = security_get_value_type(current->security, SELINUX_LSM_ID,
+ struct task_security_struct,
+ selinux_secidx);
+ isec = security_get_value_type(ipc_perms->security, SELINUX_LSM_ID,
+ struct ipc_security_struct,
+ selinux_secidx);
AVC_AUDIT_DATA_INIT(&ad, IPC);
ad.u.ipc_id = ipc_perms->key;
@@ -3692,8 +3740,12 @@
if (rc)
return rc;
- tsec = current->security;
- isec = msq->q_perm.security;
+ tsec = security_get_value_type(current->security, SELINUX_LSM_ID,
+ struct task_security_struct,
+ selinux_secidx);
+ isec = security_get_value_type(msq->q_perm.security, SELINUX_LSM_ID,
+ struct ipc_security_struct,
+ selinux_secidx);
AVC_AUDIT_DATA_INIT(&ad, IPC);
ad.u.ipc_id = msq->q_perm.key;
@@ -3718,8 +3770,12 @@
struct ipc_security_struct *isec;
struct avc_audit_data ad;
- tsec = current->security;
- isec = msq->q_perm.security;
+ tsec = security_get_value_type(current->security, SELINUX_LSM_ID,
+ struct task_security_struct,
+ selinux_secidx);
+ isec = security_get_value_type(msq->q_perm.security, SELINUX_LSM_ID,
+ struct ipc_security_struct,
+ selinux_secidx);
AVC_AUDIT_DATA_INIT(&ad, IPC);
ad.u.ipc_id = msq->q_perm.key;
@@ -3764,9 +3820,15 @@
struct avc_audit_data ad;
int rc;
- tsec = current->security;
- isec = msq->q_perm.security;
- msec = msg->security;
+ tsec = security_get_value_type(current->security, SELINUX_LSM_ID,
+ struct task_security_struct,
+ selinux_secidx);
+ isec = security_get_value_type(msq->q_perm.security, SELINUX_LSM_ID,
+ struct ipc_security_struct,
+ selinux_secidx);
+ msec = security_get_value_type(msg->security, SELINUX_LSM_ID,
+ struct msg_security_struct,
+ selinux_secidx);
/*
* First time through, need to assign label to the message
@@ -3812,9 +3874,15 @@
struct avc_audit_data ad;
int rc;
- tsec = target->security;
- isec = msq->q_perm.security;
- msec = msg->security;
+ tsec = security_get_value_type(target->security, SELINUX_LSM_ID,
+ struct task_security_struct,
+ selinux_secidx);
+ isec = security_get_value_type(msq->q_perm.security, SELINUX_LSM_ID,
+ struct ipc_security_struct,
+ selinux_secidx);
+ msec = security_get_value_type(msg->security, SELINUX_LSM_ID,
+ struct msg_security_struct,
+ selinux_secidx);
AVC_AUDIT_DATA_INIT(&ad, IPC);
ad.u.ipc_id = msq->q_perm.key;
@@ -3839,8 +3907,12 @@
if (rc)
return rc;
- tsec = current->security;
- isec = shp->shm_perm.security;
+ tsec = security_get_value_type(current->security, SELINUX_LSM_ID,
+ struct task_security_struct,
+ selinux_secidx);
+ isec = security_get_value_type(shp->shm_perm.security, SELINUX_LSM_ID,
+ struct ipc_security_struct,
+ selinux_secidx);
AVC_AUDIT_DATA_INIT(&ad, IPC);
ad.u.ipc_id = shp->shm_perm.key;
@@ -3865,8 +3937,12 @@
struct ipc_security_struct *isec;
struct avc_audit_data ad;
- tsec = current->security;
- isec = shp->shm_perm.security;
+ tsec = security_get_value_type(current->security, SELINUX_LSM_ID,
+ struct task_security_struct,
+ selinux_secidx);
+ isec = security_get_value_type(shp->shm_perm.security, SELINUX_LSM_ID,
+ struct ipc_security_struct,
+ selinux_secidx);
AVC_AUDIT_DATA_INIT(&ad, IPC);
ad.u.ipc_id = shp->shm_perm.key;
@@ -3912,11 +3988,6 @@
char __user *shmaddr, int shmflg)
{
u32 perms;
- int rc;
-
- rc = secondary_ops->shm_shmat(shp, shmaddr, shmflg);
- if (rc)
- return rc;
if (shmflg & SHM_RDONLY)
perms = SHM__READ;
@@ -3938,8 +4009,12 @@
if (rc)
return rc;
- tsec = current->security;
- isec = sma->sem_perm.security;
+ tsec = security_get_value_type(current->security, SELINUX_LSM_ID,
+ struct task_security_struct,
+ selinux_secidx);
+ isec = security_get_value_type(sma->sem_perm.security, SELINUX_LSM_ID,
+ struct ipc_security_struct,
+ selinux_secidx);
AVC_AUDIT_DATA_INIT(&ad, IPC);
ad.u.ipc_id = sma->sem_perm.key;
@@ -3964,8 +4039,12 @@
struct ipc_security_struct *isec;
struct avc_audit_data ad;
- tsec = current->security;
- isec = sma->sem_perm.security;
+ tsec = security_get_value_type(current->security, SELINUX_LSM_ID,
+ struct task_security_struct,
+ selinux_secidx);
+ isec = security_get_value_type(sma->sem_perm.security, SELINUX_LSM_ID,
+ struct ipc_security_struct,
+ selinux_secidx);
AVC_AUDIT_DATA_INIT(&ad, IPC);
ad.u.ipc_id = sma->sem_perm.key;
@@ -4048,32 +4127,12 @@
/* module stacking operations */
static int selinux_register_security (const char *name, struct security_operations *ops)
{
- if (secondary_ops != original_ops) {
- printk(KERN_INFO "%s: There is already a secondary security "
- "module registered.\n", __FUNCTION__);
- return -EINVAL;
- }
-
- secondary_ops = ops;
-
- printk(KERN_INFO "%s: Registering secondary module %s\n",
- __FUNCTION__,
- name);
-
- return 0;
+ return -EINVAL;
}
static int selinux_unregister_security (const char *name, struct security_operations *ops)
{
- if (ops != secondary_ops) {
- printk (KERN_INFO "%s: trying to unregister a security module "
- "that is not registered.\n", __FUNCTION__);
- return -EINVAL;
- }
-
- secondary_ops = original_ops;
-
- return 0;
+ return -EINVAL;
}
static void selinux_d_instantiate (struct dentry *dentry, struct inode *inode)
@@ -4099,7 +4158,9 @@
if (!size)
return -ERANGE;
- tsec = p->security;
+ tsec = security_get_value_type(p->security, SELINUX_LSM_ID,
+ struct task_security_struct,
+ selinux_secidx);
if (!strcmp(name, "current"))
sid = tsec->sid;
@@ -4174,7 +4235,9 @@
operation. See selinux_bprm_set_security for the execve
checks and may_create for the file creation checks. The
operation will then fail if the context is not permitted. */
- tsec = p->security;
+ tsec = security_get_value_type(p->security, SELINUX_LSM_ID,
+ struct task_security_struct,
+ selinux_secidx);
if (!strcmp(name, "exec"))
tsec->exec_sid = sid;
else if (!strcmp(name, "fscreate"))
@@ -4231,26 +4294,24 @@
}
static struct security_operations selinux_ops = {
+ .owner = THIS_MODULE,
+
.ptrace = selinux_ptrace,
.capget = selinux_capget,
.capset_check = selinux_capset_check,
- .capset_set = selinux_capset_set,
.sysctl = selinux_sysctl,
.capable = selinux_capable,
.quotactl = selinux_quotactl,
.quota_on = selinux_quota_on,
.syslog = selinux_syslog,
- .vm_enough_memory = selinux_vm_enough_memory,
.netlink_send = selinux_netlink_send,
- .netlink_recv = selinux_netlink_recv,
.bprm_alloc_security = selinux_bprm_alloc_security,
.bprm_free_security = selinux_bprm_free_security,
.bprm_apply_creds = selinux_bprm_apply_creds,
.bprm_post_apply_creds = selinux_bprm_post_apply_creds,
.bprm_set_security = selinux_bprm_set_security,
- .bprm_check_security = selinux_bprm_check_security,
.bprm_secureexec = selinux_bprm_secureexec,
.sb_alloc_security = selinux_sb_alloc_security,
@@ -4302,7 +4363,6 @@
.task_alloc_security = selinux_task_alloc_security,
.task_free_security = selinux_task_free_security,
.task_setuid = selinux_task_setuid,
- .task_post_setuid = selinux_task_post_setuid,
.task_setgid = selinux_task_setgid,
.task_setpgid = selinux_task_setpgid,
.task_getpgid = selinux_task_getpgid,
@@ -4374,10 +4434,12 @@
#endif
};
+#define MY_NAME "selinux"
static __init int selinux_init(void)
{
struct task_security_struct *tsec;
+ secondary = 0;
if (!selinux_enabled) {
printk(KERN_INFO "SELinux: Disabled at boot.\n");
return 0;
@@ -4385,20 +4447,28 @@
printk(KERN_INFO "SELinux: Initializing.\n");
+ if (register_security (&selinux_ops, &selinux_secidx)) {
+ if (mod_reg_security( MY_NAME, &selinux_ops, &selinux_secidx)) {
+ printk(KERN_ERR "%s: Failed to register with primary LSM.\n",
+ __FUNCTION__);
+ panic("SELinux: Unable to register with kernel.\n");
+ } else {
+ printk(KERN_ERR "%s: registered with primary LSM.\n",
+ __FUNCTION__);
+ }
+ secondary = 1;
+ }
+
/* Set the security state for the initial task. */
if (task_alloc_security(current))
panic("SELinux: Failed to initialize initial task.\n");
- tsec = current->security;
+ tsec = security_get_value_type(current->security, SELINUX_LSM_ID,
+ struct task_security_struct,
+ selinux_secidx);
tsec->osid = tsec->sid = SECINITSID_KERNEL;
avc_init();
- original_ops = secondary_ops = security_ops;
- if (!secondary_ops)
- panic ("SELinux: No initial security operations\n");
- if (register_security (&selinux_ops))
- panic("SELinux: Unable to register with kernel.\n");
-
if (selinux_enforcing) {
printk(KERN_INFO "SELinux: Starting in enforcing mode\n");
} else {
@@ -4513,6 +4583,7 @@
{
extern void exit_sel_fs(void);
static int selinux_disabled = 0;
+ int ret;
if (ss_initialized) {
/* Not permitted after initial policy load. */
@@ -4528,8 +4599,15 @@
selinux_disabled = 1;
- /* Reset security_ops to the secondary module, dummy or capability. */
- security_ops = secondary_ops;
+ if (secondary)
+ ret = mod_unreg_security(MY_NAME, &selinux_ops);
+ else
+ ret = unregister_security(&selinux_ops);
+
+ if (ret)
+ printk(KERN_INFO "Failure unregistering selinux.\n");
+ else
+ printk(KERN_INFO "Unregistered selinux.\n");
/* Unregister netfilter hooks. */
selinux_nf_ip_exit();
diff -ruN linux-2.6.14-rc4-git1.orig/security/selinux/include/objsec.h
linux-2.6.14-rc4-git1.new/security/selinux/include/objsec.h
--- linux-2.6.14-rc4-git1.orig/security/selinux/include/objsec.h 2005-08-28 19:41:01.000000000
-0400
+++ linux-2.6.14-rc4-git1.new/security/selinux/include/objsec.h 2005-10-12 15:54:46.000000000
-0400
@@ -23,11 +23,14 @@
#include <linux/fs.h>
#include <linux/binfmts.h>
#include <linux/in.h>
+#include <linux/security.h>
#include "flask.h"
#include "avc.h"
+#define SELINUX_LSM_ID 0xB65
+
struct task_security_struct {
- unsigned long magic; /* magic number for this module */
+ struct security_list lsm_list; /* chained security objects */
struct task_struct *task; /* back pointer to task object */
u32 osid; /* SID prior to last execve */
u32 sid; /* current SID */
@@ -37,7 +40,7 @@
};
struct inode_security_struct {
- unsigned long magic; /* magic number for this module */
+ struct security_list lsm_list; /* chained security objects */
struct inode *inode; /* back pointer to inode object */
struct list_head list; /* list of inode_security_struct */
u32 task_sid; /* SID of creating task */
@@ -49,14 +52,14 @@
};
struct file_security_struct {
- unsigned long magic; /* magic number for this module */
+ struct security_list lsm_list; /* chained security objects */
struct file *file; /* back pointer to file object */
u32 sid; /* SID of open file description */
u32 fown_sid; /* SID of file owner (for SIGIO) */
};
struct superblock_security_struct {
- unsigned long magic; /* magic number for this module */
+ struct security_list lsm_list; /* chained security objects */
struct super_block *sb; /* back pointer to sb object */
struct list_head list; /* list of superblock_security_struct */
u32 sid; /* SID of file system */
@@ -70,20 +73,20 @@
};
struct msg_security_struct {
- unsigned long magic; /* magic number for this module */
+ struct security_list lsm_list; /* chained security objects */
struct msg_msg *msg; /* back pointer */
u32 sid; /* SID of message */
};
struct ipc_security_struct {
- unsigned long magic; /* magic number for this module */
+ struct security_list lsm_list; /* chained security objects */
struct kern_ipc_perm *ipc_perm; /* back pointer */
u16 sclass; /* security class of this object */
u32 sid; /* SID of IPC resource */
};
struct bprm_security_struct {
- unsigned long magic; /* magic number for this module */
+ struct security_list lsm_list; /* chained security objects */
struct linux_binprm *bprm; /* back pointer to bprm object */
u32 sid; /* SID for transformed process */
unsigned char set;
@@ -102,7 +105,7 @@
};
struct sk_security_struct {
- unsigned long magic; /* magic number for this module */
+ struct security_list lsm_list; /* chained security objects */
struct sock *sk; /* back pointer to sk object */
u32 peer_sid; /* SID of peer */
};
diff -ruN linux-2.6.14-rc4-git1.orig/security/selinux/Kconfig
linux-2.6.14-rc4-git1.new/security/selinux/Kconfig
--- linux-2.6.14-rc4-git1.orig/security/selinux/Kconfig 2005-08-28 19:41:01.000000000 -0400
+++ linux-2.6.14-rc4-git1.new/security/selinux/Kconfig 2005-10-12 15:54:46.000000000 -0400
@@ -2,6 +2,8 @@
bool "NSA SELinux Support"
depends on SECURITY && NET && INET
default n
+ select SECURITY_CAP_STACK
+ select SECURITY_STACKER
help
This selects NSA Security-Enhanced Linux (SELinux).
You will also need a policy configuration and a labeled filesystem.
diff -ruN linux-2.6.14-rc4-git1.orig/security/selinux/selinuxfs.c
linux-2.6.14-rc4-git1.new/security/selinux/selinuxfs.c
--- linux-2.6.14-rc4-git1.orig/security/selinux/selinuxfs.c 2005-08-28 19:41:01.000000000 -0400
+++ linux-2.6.14-rc4-git1.new/security/selinux/selinuxfs.c 2005-10-12 15:54:46.000000000 -0400
@@ -18,6 +18,7 @@
#include <linux/init.h>
#include <linux/string.h>
#include <linux/security.h>
+#include <linux/security-stack.h>
#include <linux/major.h>
#include <linux/seq_file.h>
#include <linux/percpu.h>
@@ -34,6 +35,8 @@
#include "objsec.h"
#include "conditional.h"
+extern int selinux_secidx;
+
unsigned int selinux_checkreqprot = CONFIG_SECURITY_SELINUX_CHECKREQPROT_VALUE;
static int __init checkreqprot_setup(char *str)
@@ -59,7 +62,9 @@
{
struct task_security_struct *tsec;
- tsec = tsk->security;
+ tsec = security_get_value_type(tsk->security, SELINUX_LSM_ID,
+ struct task_security_struct,
+ selinux_secidx);
if (!tsec)
return -EACCES;
@@ -982,7 +987,9 @@
ret = -ENAMETOOLONG;
goto err;
}
- isec = (struct inode_security_struct*)inode->i_security;
+ isec = security_get_value_type(inode->i_security,
+ SELINUX_LSM_ID, struct inode_security_struct,
+ selinux_secidx);
if ((ret = security_genfs_sid("selinuxfs", page, SECCLASS_FILE, &sid)))
goto err;
isec->sid = sid;
@@ -1267,7 +1274,9 @@
inode = sel_make_inode(sb, S_IFCHR | S_IRUGO | S_IWUGO);
if (!inode)
goto out;
- isec = (struct inode_security_struct*)inode->i_security;
+ isec = security_get_value_type(inode->i_security, SELINUX_LSM_ID,
+ struct inode_security_struct,
+ selinux_secidx);
isec->sid = SECINITSID_DEVNULL;
isec->sclass = SECCLASS_CHR_FILE;
isec->initialized = 1;
diff -ruN linux-2.6.14-rc4-git1.orig/security/stacker.c
linux-2.6.14-rc4-git1.new/security/stacker.c
--- linux-2.6.14-rc4-git1.orig/security/stacker.c 1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.14-rc4-git1.new/security/stacker.c 2005-10-12 15:54:46.000000000 -0400
@@ -0,0 +1,1674 @@
+/* "Stacker" Linux security module (LSM).
+ *
+ * Allows you to load (stack) multiple (additional) security modules.
+ *
+ * Copyright (C) 2002,2003,2004,2005 Serge E. Hallyn <serue@us.ibm.com>
+ * 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.
+ */
+
+#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 <linux/netdevice.h>
+#include <linux/string.h>
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <net/sock.h>
+#include <asm/system.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 list_head lsm_list; /* list of active lsms */
+ struct list_head all_lsms; /* list of active lsms */
+ char *module_name;
+ int namelen;
+ struct security_operations module_operations;
+ struct module *owner;
+ struct rcu_head m_rcu;
+};
+static LIST_HEAD(stacked_modules); /* list of stacked modules */
+static LIST_HEAD(all_modules); /* all modules, including unloaded */
+
+static spinlock_t stacker_lock;
+static int forbid_stacker_register; /* = 0; if 1, can't register */
+
+/*
+ * when !modules_registered, the default_module, defined
+ * below, will be stacked
+ */
+static short modules_registered;
+
+/*
+ * Workarounds for the fact that get and setprocattr are used only by
+ * selinux. (Maybe)
+ */
+static struct module_entry *selinux_module;
+static struct module_entry default_module;
+
+/* Maximum number of characters in a stacked LSM module name */
+#define MAX_MODULE_NAME_LEN 128
+
+static int debug = 0;
+
+module_param(debug, bool, 0600);
+MODULE_PARM_DESC(debug, "Debug enabled or not");
+#define MY_NAME "stacker"
+#define stacker_dbg(fmt, arg...) \
+ do { \
+ if (debug) \
+ printk(KERN_DEBUG "%s: %s: " fmt , \
+ MY_NAME , __FUNCTION__ , \
+ ## arg); \
+ } while (0)
+
+static void stacker_del_module(struct rcu_head *head)
+{
+ struct module_entry *m;
+
+ m = container_of(head, struct module_entry, m_rcu);
+ kfree(m->module_name);
+ kfree(m);
+}
+
+/* Walk through the 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.
+ *
+ * We return as soon as an error is returned.
+ */
+
+#define stack_for_each_entry(pos, head, member) \
+ for (pos = list_entry((head)->next, typeof(*pos), member); \
+ &pos->member != (head); \
+ pos = rcu_dereference(list_entry(pos->member.next, \
+ typeof(*pos), member)))
+
+/* to make this safe for module deletion, we would need to
+ * add a reference count to m as we had before
+ */
+#define RETURN_ERROR_IF_ANY_ERROR(BASE_FUNC, FUNC_WITH_ARGS) do { \
+ int result = 0; \
+ struct module_entry *m; \
+ rcu_read_lock(); \
+ stack_for_each_entry(m, &stacked_modules, lsm_list) { \
+ if (!m->module_operations.BASE_FUNC) \
+ continue; \
+ rcu_read_unlock(); \
+ result = m->module_operations.FUNC_WITH_ARGS; \
+ rcu_read_lock(); \
+ if (result) \
+ break; \
+ } \
+ rcu_read_unlock(); \
+ return result; \
+} while (0)
+
+/* Call all modules in stacked_modules' routine */
+#define CALL_ALL(BASE_FUNC, FUNC_WITH_ARGS) do { \
+ struct module_entry *m; \
+ rcu_read_lock(); \
+ stack_for_each_entry(m, &stacked_modules, lsm_list) { \
+ if (m->module_operations.BASE_FUNC) { \
+ rcu_read_unlock(); \
+ m->module_operations.FUNC_WITH_ARGS; \
+ rcu_read_lock(); \
+ } \
+ } \
+ rcu_read_unlock(); \
+} while (0)
+
+#define FREE_ALL(BASE_FREE,FREE_WITH_ARGS) do { \
+ struct module_entry *m; \
+ rcu_read_lock(); \
+ stack_for_each_entry(m, &stacked_modules, lsm_list ) { \
+ if (m->module_operations.BASE_FREE) { \
+ rcu_read_unlock(); \
+ m->module_operations.FREE_WITH_ARGS; \
+ rcu_read_lock(); \
+ } \
+ } \
+ rcu_read_unlock(); \
+} while (0)
+
+#define ALLOC_SECURITY(BASE_FUNC,FUNC_WITH_ARGS,BASE_FREE,FREE_WITH_ARGS) do { \
+ int result; \
+ struct module_entry *m, *m2; \
+ rcu_read_lock(); \
+ stack_for_each_entry(m, &stacked_modules, lsm_list) { \
+ if (!m->module_operations.BASE_FUNC) \
+ continue; \
+ rcu_read_unlock(); \
+ result = m->module_operations.FUNC_WITH_ARGS; \
+ rcu_read_lock(); \
+ if (result) \
+ goto bad; \
+ } \
+ rcu_read_unlock(); \
+ return 0; \
+bad: \
+ stack_for_each_entry(m2, &stacked_modules, lsm_list) { \
+ if (m == m2) \
+ break; \
+ if (!m2->module_operations.BASE_FREE) \
+ continue; \
+ rcu_read_unlock(); \
+ m2->module_operations.FREE_WITH_ARGS; \
+ rcu_read_lock(); \
+ } \
+ rcu_read_unlock(); \
+ return result; \
+} while (0)
+
+/*
+ * The list of functions for stacker_ops
+ */
+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)
+{
+ RETURN_ERROR_IF_ANY_ERROR(capable,capable(tsk,cap));
+}
+
+
+static int stacker_sysctl (struct ctl_table * table, int op)
+{
+ RETURN_ERROR_IF_ANY_ERROR(sysctl,sysctl(table, op));
+}
+
+static int stacker_quotactl (int cmds, int type, int id, struct super_block *sb)
+{
+ RETURN_ERROR_IF_ANY_ERROR(quotactl,quotactl(cmds,type,id,sb));
+}
+
+static int stacker_quota_on (struct dentry *dentry)
+{
+ RETURN_ERROR_IF_ANY_ERROR(quota_on,quota_on(dentry));
+}
+
+static int stacker_syslog (int type)
+{
+ RETURN_ERROR_IF_ANY_ERROR(syslog,syslog(type));
+}
+
+static int stacker_settime(struct timespec *ts, struct timezone *tz)
+{
+ RETURN_ERROR_IF_ANY_ERROR(settime,settime(ts,tz));
+}
+
+/*
+ * vm_enough_memory performs actual updates of vm state. Most
+ * modules, including dummy, use the __vm_enough_memory helper
+ * to do this for them.
+ * This means we can't call more than one module's vm_enough.
+ * So we call the first module's, or if no modules are loaded,
+ * we call dummy_vm_enough_memory.
+ */
+static int stacker_vm_enough_memory(long pages)
+{
+ int result;
+ struct module_entry *m;
+ int cap_sys_admin = 0;
+
+ if (unlikely(list_empty(&stacked_modules)))
+ goto no_module_vmenough;
+
+ rcu_read_lock();
+ stack_for_each_entry(m, &stacked_modules, lsm_list) {
+ if (!m->module_operations.vm_enough_memory)
+ continue;
+ rcu_read_unlock();
+ result = m->module_operations.vm_enough_memory(pages);
+ return result;
+ }
+ rcu_read_unlock();
+
+no_module_vmenough:
+ if (stacker_capable(current, CAP_SYS_ADMIN) == 0)
+ cap_sys_admin = 1;
+ result = __vm_enough_memory(pages, cap_sys_admin);
+ return result;
+}
+
+/*
+ * Each module may have it's own idea of how to set netlink perms.
+ * We use the intersection of all as the final alloted perms.
+ */
+static int stacker_netlink_send (struct sock *sk, struct sk_buff *skb)
+{
+ kernel_cap_t tmpcap = ~0;
+ int result = 0;
+ struct module_entry *m;
+
+ rcu_read_lock();
+ stack_for_each_entry(m, &stacked_modules, lsm_list) {
+ if (!m->module_operations.netlink_send)
+ continue;
+ NETLINK_CB(skb).eff_cap = ~0;
+ rcu_read_unlock();
+ result = m->module_operations.netlink_send(sk, skb);
+ rcu_read_lock();
+ tmpcap &= NETLINK_CB(skb).eff_cap;
+ if (result)
+ break;
+ }
+ rcu_read_unlock();
+
+ NETLINK_CB(skb).eff_cap = tmpcap;
+ return result;
+}
+
+
+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)
+{
+ ALLOC_SECURITY(bprm_alloc_security,bprm_alloc_security(bprm),bprm_free_security,bprm_free_security(bprm));
+}
+
+static void stacker_bprm_free_security (struct linux_binprm *bprm)
+{
+ struct hlist_head *h;
+ FREE_ALL(bprm_free_security,bprm_free_security(bprm));
+ h = (struct hlist_head *)&bprm->security[sec_shared_slot];
+ if (unlikely(!hlist_empty(h)))
+ security_disown_value(h);
+}
+
+static void stacker_bprm_apply_creds (struct linux_binprm *bprm, int unsafe)
+{
+ CALL_ALL(bprm_apply_creds,bprm_apply_creds(bprm, unsafe));
+}
+
+static void stacker_bprm_post_apply_creds (struct linux_binprm * bprm)
+{
+ CALL_ALL(bprm_post_apply_creds,bprm_post_apply_creds(bprm));
+}
+
+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)
+{
+ ALLOC_SECURITY(sb_alloc_security,sb_alloc_security(sb),sb_free_security,sb_free_security(sb));
+}
+
+static void stacker_sb_free_security (struct super_block *sb)
+{
+ struct hlist_head *h;
+ FREE_ALL(sb_free_security,sb_free_security(sb));
+ h = (struct hlist_head *)&sb->s_security[sec_shared_slot];
+ if (unlikely(!hlist_empty(h)))
+ security_disown_value(h);
+}
+
+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)
+{
+ ALLOC_SECURITY(inode_alloc_security,inode_alloc_security(inode),inode_free_security,inode_free_security(inode));
+}
+
+static void stacker_inode_free_security (struct inode *inode)
+{
+ struct hlist_head *h;
+ FREE_ALL(inode_free_security,inode_free_security(inode));
+ h = (struct hlist_head *)&inode->i_security[sec_shared_slot];
+ if (unlikely(!hlist_empty(h)))
+ security_disown_value(h);
+}
+
+static int stacker_inode_init_security(struct inode *inode, struct inode *dir,
+ struct list_head *head)
+{
+ int ret, succ_ret = -EOPNOTSUPP;
+ struct xattr_data *p, *n;
+ struct module_entry *m;
+
+ rcu_read_lock();
+ stack_for_each_entry(m, &stacked_modules, lsm_list) {
+ if (!m->module_operations.inode_init_security)
+ continue;
+ rcu_read_unlock();
+ ret = m->module_operations.inode_init_security(inode,dir,head);
+ rcu_read_lock();
+ if (ret && ret != -EOPNOTSUPP)
+ goto out_free_data;
+ if (ret == 0)
+ succ_ret = 0;
+ }
+ rcu_read_unlock();
+ return succ_ret;
+
+out_free_data:
+ if (!head)
+ return ret;
+ list_for_each_entry_safe(p, n, head, list) {
+ list_del(&p->list);
+ kfree(p->value);
+ kfree(p->name);
+ kfree(p);
+ }
+ return ret;
+}
+
+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 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 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 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 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 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 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));
+}
+
+/*
+ * inode_getsecurity: We loop through all modules until one does not return
+ * -EOPNOTSUPP.
+ * Note that if some LSM returns -EPERM, stacker assumes the LSM knows what
+ * it's doing. If you don't want to control the name, then return
+ * -EOPNOTSUPP!
+ */
+static int stacker_inode_getsecurity(struct inode *inode, const char *name, void *buffer, size_t
size)
+{
+ struct module_entry *m;
+ int ret = -EOPNOTSUPP;
+
+ rcu_read_lock();
+ stack_for_each_entry(m, &stacked_modules, lsm_list) {
+ if (!m->module_operations.inode_getsecurity)
+ continue;
+ rcu_read_unlock();
+ ret = m->module_operations.inode_getsecurity(inode,name,buffer,size);
+ rcu_read_lock();
+ if (ret != -EOPNOTSUPP)
+ break;
+ }
+ rcu_read_unlock();
+
+ return ret;
+}
+
+/*
+ * inode_setsecurity: We loop through all modules until one does not return
+ * -EOPNOTSUPP.
+ * Note that if some LSM returns -EPERM, stacker assumes the LSM knows what
+ * it's doing. If you don't want to control the name, then return
+ * -EOPNOTSUPP!
+ */
+static int stacker_inode_setsecurity(struct inode *inode, const char *name, const void *value,
size_t size, int flags)
+{
+ struct module_entry *m;
+ int ret = -EOPNOTSUPP;
+
+ rcu_read_lock();
+ stack_for_each_entry(m, &stacked_modules, lsm_list) {
+ if (!m->module_operations.inode_setsecurity)
+ continue;
+ rcu_read_unlock();
+ ret = m->module_operations.inode_setsecurity(inode, name,
+ value, size, flags);
+ rcu_read_lock();
+ if (ret != -EOPNOTSUPP)
+ break;
+ }
+ rcu_read_unlock();
+
+ return ret;
+}
+
+/*
+ * inode_listsecurity: We loop through all modules appending to buffer, and return
+ * the \0-separated list of security names defined for this inode.
+ */
+static int stacker_inode_listsecurity(struct inode *inode, char *buffer, size_t buffer_size)
+{
+ int ret = 0;
+ struct module_entry *m;
+
+ rcu_read_lock();
+ stack_for_each_entry(m, &stacked_modules, lsm_list) {
+ int thislen;
+
+ if (!m->module_operations.inode_listsecurity)
+ continue;
+ rcu_read_unlock();
+ thislen = m->module_operations.inode_listsecurity(inode,
+ buffer+ret, buffer_size-ret);
+ rcu_read_lock();
+ if (thislen < 0)
+ continue;
+ ret += thislen;
+ if (ret >= buffer_size) {
+ ret = -ERANGE;
+ break;
+ }
+ }
+ rcu_read_unlock();
+
+ return ret;
+}
+
+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)
+{
+ ALLOC_SECURITY(file_alloc_security,file_alloc_security(file),file_free_security,file_free_security(file));
+}
+
+static void stacker_file_free_security (struct file *file)
+{
+ struct hlist_head *h;
+ FREE_ALL(file_free_security,file_free_security(file));
+ h = (struct hlist_head *)&file->f_security[sec_shared_slot];
+ if (unlikely(!hlist_empty(h)))
+ security_disown_value(h);
+}
+
+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 reqprot,
+ unsigned long prot, unsigned long flags)
+{
+ RETURN_ERROR_IF_ANY_ERROR(file_mmap,file_mmap(file, reqprot, prot, flags));
+}
+
+static int stacker_file_mprotect (struct vm_area_struct *vma,
+ unsigned long reqprot, unsigned long prot)
+{
+ RETURN_ERROR_IF_ANY_ERROR(file_mprotect,file_mprotect(vma,reqprot,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));
+}
+
+static int stacker_file_set_fowner (struct file *file)
+{
+ RETURN_ERROR_IF_ANY_ERROR(file_set_fowner,file_set_fowner(file));
+}
+
+static int stacker_file_send_sigiotask (struct task_struct *tsk,
+ struct fown_struct *fown, int sig)
+{
+ RETURN_ERROR_IF_ANY_ERROR(file_send_sigiotask,file_send_sigiotask(tsk,fown,sig));
+}
+
+static int stacker_file_receive (struct file *file)
+{
+ RETURN_ERROR_IF_ANY_ERROR(file_receive,file_receive(file));
+}
+
+static int stacker_task_create (unsigned long clone_flags)
+{
+ RETURN_ERROR_IF_ANY_ERROR(task_create,task_create(clone_flags));
+}
+
+static int stacker_task_alloc_security (struct task_struct *p)
+{
+ ALLOC_SECURITY(task_alloc_security,task_alloc_security(p),task_free_security,task_free_security(p));
+}
+
+static void stacker_task_free_security (struct task_struct *p)
+{
+ struct hlist_head *h;
+ FREE_ALL(task_free_security,task_free_security(p));
+ h = (struct hlist_head *)&p->security[sec_shared_slot];
+ if (unlikely(!hlist_empty(h)))
+ security_disown_value(h);
+}
+
+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)
+{
+ ALLOC_SECURITY(sk_alloc_security,sk_alloc_security(sk,family,priority),sk_free_security,sk_free_security(sk));
+}
+
+static void stacker_sk_free_security (struct sock *sk)
+{
+ struct hlist_head *h;
+ FREE_ALL(sk_free_security,sk_free_security(sk));
+ h = (struct hlist_head *)&sk->sk_security[sec_shared_slot];
+ if (unlikely(!hlist_empty(h)))
+ security_disown_value(h);
+}
+
+#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)
+{
+ ALLOC_SECURITY(msg_msg_alloc_security,msg_msg_alloc_security(msg),msg_msg_free_security,msg_msg_free_security(msg));
+}
+
+static void stacker_msg_msg_free_security (struct msg_msg *msg)
+{
+ struct hlist_head *h;
+ FREE_ALL(msg_msg_free_security,msg_msg_free_security(msg));
+ h = (struct hlist_head *)&msg->security[sec_shared_slot];
+ if (unlikely(!hlist_empty(h)))
+ security_disown_value(h);
+}
+
+static int stacker_msg_queue_alloc_security (struct msg_queue *msq)
+{
+ ALLOC_SECURITY(msg_queue_alloc_security,msg_queue_alloc_security(msq),msg_queue_free_security,msg_queue_free_security(msq));
+}
+
+static void stacker_msg_queue_free_security (struct msg_queue *msq)
+{
+ struct hlist_head *h;
+ FREE_ALL(msg_queue_free_security,msg_queue_free_security(msq));
+ h = (struct hlist_head *)&msq->q_perm.security[sec_shared_slot];
+ if (unlikely(!hlist_empty(h)))
+ security_disown_value(h);
+}
+
+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)
+{
+ ALLOC_SECURITY(shm_alloc_security,shm_alloc_security(shp),shm_free_security,shm_free_security(shp));
+}
+
+static void stacker_shm_free_security (struct shmid_kernel *shp)
+{
+ struct hlist_head *h;
+ FREE_ALL(shm_free_security,shm_free_security(shp));
+ h = (struct hlist_head *)&shp->shm_perm.security[sec_shared_slot];
+ if (unlikely(!hlist_empty(h)))
+ security_disown_value(h);
+}
+
+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 __user *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)
+{
+ ALLOC_SECURITY(sem_alloc_security,sem_alloc_security(sma),sem_free_security,sem_free_security(sma));
+}
+
+static void stacker_sem_free_security (struct sem_array *sma)
+{
+ struct hlist_head *h;
+ FREE_ALL(sem_free_security,sem_free_security(sma));
+ h = (struct hlist_head *)&sma->sem_perm.security[sec_shared_slot];
+ if (unlikely(!hlist_empty(h)))
+ security_disown_value(h);
+}
+
+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));
+}
+
+/*
+ * Query all LSMs.
+ * If all return EINVAL, we return EINVAL. If any returns any other
+ * error, then we return that error. Otherwise, we concatenate all
+ * modules' results.
+ */
+static int
+stacker_getprocattr(struct task_struct *p, char *name, void *value, size_t size)
+{
+ struct module_entry *m;
+ int len = 0, ret;
+ int found_noneinval = 0;
+
+
+ if (list_empty(&stacked_modules))
+ return -EINVAL;
+
+ rcu_read_lock();
+ stack_for_each_entry(m, &stacked_modules, lsm_list) {
+ if (!m->module_operations.getprocattr)
+ continue;
+ rcu_read_unlock();
+ ret = m->module_operations.getprocattr(p, name,
+ value+len, size-len);
+ rcu_read_lock();
+ if (ret == -EINVAL)
+ continue;
+ found_noneinval = 1;
+ if (ret < 0) {
+ memset(value, 0, len);
+ len = ret;
+ break;
+ }
+ if (ret == 0)
+ continue;
+ len += ret;
+ if (len+m->namelen+4 < size) {
+ char *v = value;
+ if (v[len-1]=='\n')
+ len--;
+ len += sprintf(value+len, " (%s)\n", m->module_name);
+ }
+ }
+ rcu_read_unlock();
+
+ return found_noneinval ? len : -EINVAL;
+}
+
+static struct module_entry *
+find_active_lsm(const char *name, int len)
+{
+ struct module_entry *m, *ret = NULL;
+
+ rcu_read_lock();
+ stack_for_each_entry(m, &stacked_modules, lsm_list) {
+ if (m->namelen == len && !strncmp(m->module_name, name, len)) {
+ ret = m;
+ break;
+ }
+ }
+
+ rcu_read_unlock();
+ return ret;
+}
+
+/*
+ * We assume input will be either
+ * "data" - in which case it goes to selinux, or
+ * "data (mod_name)" in which case the data goes to module mod_name.
+ */
+static int
+stacker_setprocattr(struct task_struct *p, char *name, void *value, size_t size)
+{
+ struct module_entry *callm = selinux_module;
+ char *realv = (char *)value;
+ size_t dsize = size;
+ int loc = 0, end_data = size;
+
+ if (list_empty(&stacked_modules))
+ return -EINVAL;
+
+ if (dsize && realv[dsize-1] == '\n')
+ dsize--;
+
+ if (!dsize || realv[dsize-1]!=')')
+ goto call;
+
+ dsize--;
+ loc = dsize-1;
+ while (loc && realv[loc]!='(')
+ loc--;
+ if (!loc)
+ goto call;
+
+ callm = find_active_lsm(realv+loc+1, dsize-loc-1);
+ if (!callm)
+ goto call;
+
+
+ loc--;
+ while (loc && realv[loc]==' ')
+ loc--;
+
+ end_data = loc+1;
+call:
+ if (!callm || !callm->module_operations.setprocattr)
+ return -EINVAL;
+
+ return callm->module_operations.setprocattr(p, name, value, end_data) +
+ (size-end_data);
+}
+
+/*
+ * Add the stacked module (as specified by name and ops).
+ * If the module is not compiled in, the symbol_get at the end will
+ * prevent the the module from being unloaded.
+*/
+static int stacker_register (const char *name, struct security_operations *ops)
+{
+ char *new_module_name;
+ struct module_entry *new_module_entry;
+ int namelen = strnlen(name, MAX_MODULE_NAME_LEN);
+ int ret = 0;
+
+ spin_lock(&stacker_lock);
+
+ if (forbid_stacker_register) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ 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) {
+ printk(KERN_WARNING
+ "%s: Failure registering module - out of memory\n",
+ __FUNCTION__);
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ memset(new_module_entry, 0, sizeof(struct module_entry));
+ strncpy(new_module_name, name, namelen);
+ new_module_name[namelen] = '\0';
+ memcpy(&new_module_entry->module_operations, ops,
+ sizeof(struct security_operations));
+
+ new_module_entry->module_name = new_module_name;
+ new_module_entry->namelen = namelen;
+
+ if (!try_module_get(ops->owner)) {
+ printk(KERN_WARNING "%s: Error: could not module_get %s\n",
+ __FUNCTION__, name);
+ }
+ new_module_entry->owner = ops->owner;
+
+ INIT_LIST_HEAD(&new_module_entry->lsm_list);
+ INIT_LIST_HEAD(&new_module_entry->all_lsms);
+
+ rcu_read_lock();
+ if (!modules_registered) {
+ modules_registered++;
+ list_del_rcu(&default_module.lsm_list);
+ }
+ list_add_tail_rcu(&new_module_entry->lsm_list, &stacked_modules);
+ list_add_tail_rcu(&new_module_entry->all_lsms, &all_modules);
+ if (strcmp(name, "selinux") == 0)
+ selinux_module = new_module_entry;
+ rcu_read_unlock();
+
+ printk(KERN_INFO "%s: registered %s module\n", __FUNCTION__,
+ new_module_entry->module_name);
+
+ symbol_get(ops);
+
+out:
+ spin_unlock(&stacker_lock);
+ return ret;
+}
+
+/*
+ * find_lsm_module_by_name:
+ * Find a module by name. Used by stacker_unregister. Called with
+ * stacker spinlock held.
+ */
+static struct module_entry *
+find_lsm_with_namelen(const char *name, int len)
+{
+ struct module_entry *m, *ret = NULL;
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(m, &all_modules, all_lsms) {
+ if (m->namelen == len && !strncmp(m->module_name, name, len)) {
+ ret = m;
+ break;
+ }
+ }
+ rcu_read_unlock();
+
+ return ret;
+}
+
+extern int lsm_unloading; /* from security/security.c */
+
+static int stacker_unregister(const char *name,
+ struct security_operations *ops)
+{
+ struct module_entry *m;
+ int len = strnlen(name, MAX_MODULE_NAME_LEN);
+ int ret = 0;
+
+ spin_lock(&stacker_lock);
+ m = find_lsm_with_namelen(name, len);
+ if (!m) {
+ printk(KERN_WARNING "%s: could not find module %s.\n",
+ __FUNCTION__, name);
+ ret = -ENOENT;
+ goto out;
+ }
+ list_del_rcu(&m->all_lsms);
+ call_rcu(&m->m_rcu, stacker_del_module);
+ lsm_unloading = 1;
+
+out:
+ spin_unlock(&stacker_lock);
+ return 0;
+}
+
+static struct security_operations stacker_ops = {
+ .owner = THIS_MODULE,
+ .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,
+ .settime = stacker_settime,
+ .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_post_apply_creds = stacker_bprm_post_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_init_security = stacker_inode_init_security,
+ .inode_create = stacker_inode_create,
+ .inode_link = stacker_inode_link,
+ .inode_unlink = stacker_inode_unlink,
+ .inode_symlink = stacker_inode_symlink,
+ .inode_mkdir = stacker_inode_mkdir,
+ .inode_rmdir = stacker_inode_rmdir,
+ .inode_mknod = stacker_inode_mknod,
+ .inode_rename = stacker_inode_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
+};
+
+
+static u64 stacker_u8_get(void *data)
+{
+ return *(u8 *)data;
+}
+
+static void lockdown_write(void *data, u64 val)
+{
+ forbid_stacker_register = 1;
+}
+
+/* respond to a request to unload a module */
+static ssize_t stacker_unload_write (struct file * file,
+ const char __user * user_buf, size_t count,
+ loff_t *ppos)
+{
+ struct module_entry *m;
+ int ret;
+ char *page;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ if (count <= 0 || count >= PAGE_SIZE)
+ return -EINVAL;
+
+ if (!modules_registered)
+ return -EINVAL;
+
+ if (*ppos != 0) /* No partial writes. */
+ return -EINVAL;
+
+ page = (char*)get_zeroed_page(GFP_KERNEL);
+ if (!page)
+ return -ENOMEM;
+
+ if (copy_from_user(page, user_buf, count)) {
+ ret = -EFAULT;
+ goto out_free;
+ }
+
+ spin_lock(&stacker_lock);
+ ret = strnlen(page, MAX_MODULE_NAME_LEN);
+ m = find_active_lsm(page, ret);
+
+ if (!m) {
+ printk(KERN_INFO "%s: could not find module %s.\n",
+ __FUNCTION__, page);
+ ret = -ENOENT;
+ goto out;
+ }
+
+ if (strcmp(m->module_name, "selinux") == 0)
+ selinux_module = NULL;
+
+ rcu_read_lock();
+ list_del_rcu(&m->lsm_list);
+ if (list_empty(&stacked_modules)) {
+ INIT_LIST_HEAD(&default_module.lsm_list);
+ list_add_tail_rcu(&default_module.lsm_list, &stacked_modules);
+ modules_registered = 0;
+ }
+ rcu_read_unlock();
+ module_put(m->owner);
+
+ ret = count;
+
+out:
+ spin_unlock(&stacker_lock);
+out_free:
+ free_page((unsigned long)page);
+
+ return ret;
+}
+
+static struct file_operations unload_fops = {
+ .write = stacker_unload_write,
+};
+
+DEFINE_SIMPLE_ATTRIBUTE(lockdown_fops, stacker_u8_get, lockdown_write, "%llu\n");
+
+/* list modules */
+static ssize_t listmodules_read ( struct file *filp, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ ssize_t len = 0;
+ struct module_entry *m;
+ char *page;
+
+ page = (char *)__get_free_page(GFP_KERNEL);
+ if (!page)
+ return -ENOMEM;
+
+ rcu_read_lock();
+ stack_for_each_entry(m, &stacked_modules, lsm_list) {
+ len += snprintf(page+len, PAGE_SIZE - len, "%s\n",
+ m->module_name);
+ }
+ rcu_read_unlock();
+
+ if (len >= 0)
+ len = simple_read_from_buffer(user_buf, count, ppos, page, len);
+ free_page((unsigned long)page);
+
+ return len;
+ }
+
+static struct file_operations listmodules_fops = {
+ .read = listmodules_read,
+};
+
+static void unregister_stacker_files(void);
+
+static void stop_responding_write(void *data, u64 val)
+{
+ unregister_stacker_files();
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(stopresp_fops, NULL, stop_responding_write, "%llu\n");
+
+static struct dentry * stacker_dir_ino,
+ * lockdown_ino,
+ * listmodules_ino,
+ * stopresp_ino,
+ * modunload_ino;
+
+static void unregister_stacker_files(void)
+{
+ securityfs_remove(stacker_dir_ino);
+ securityfs_remove(lockdown_ino);
+ securityfs_remove(listmodules_ino);
+ securityfs_remove(stopresp_ino);
+ securityfs_remove(modunload_ino);
+}
+
+static int register_stacker_files(void)
+{
+ stacker_dir_ino = securityfs_create_dir("stacker", NULL);
+ if (!stacker_dir_ino)
+ return -EFAULT;
+
+ lockdown_ino = securityfs_create_file("lockdown", S_IRUGO | S_IWUSR,
+ stacker_dir_ino, &forbid_stacker_register,
+ &lockdown_fops);
+ listmodules_ino = securityfs_create_file("listmodules", S_IRUGO,
+ stacker_dir_ino, NULL, &listmodules_fops);
+ stopresp_ino = securityfs_create_file("stop_responding", S_IWUSR,
+ stacker_dir_ino, NULL, &stopresp_fops);
+ modunload_ino = securityfs_create_file("unload", S_IWUSR,
+ stacker_dir_ino, NULL, &unload_fops);
+
+ if (!lockdown_ino || !listmodules_ino || !stopresp_ino ||
+ !modunload_ino) {
+ unregister_stacker_files();
+ return -EFAULT;
+ }
+ return 0;
+}
+
+module_init(register_stacker_files);
+
+extern struct security_operations dummy_security_ops;
+#define DEFAULT_MODULE_NAME "dummy"
+
+static int __init stacker_init (void)
+{
+ forbid_stacker_register = 0;
+ spin_lock_init(&stacker_lock);
+
+ default_module.module_name = DEFAULT_MODULE_NAME;
+ default_module.namelen = strlen(DEFAULT_MODULE_NAME);
+ memcpy(&default_module.module_operations, &dummy_security_ops,
+ sizeof(struct security_operations));
+ INIT_LIST_HEAD(&default_module.lsm_list);
+ list_add_tail(&default_module.lsm_list, &stacked_modules);
+
+ if (register_security (&stacker_ops, NULL)) {
+ /*
+ * stacking stacker is just a stupid idea, so don't ask
+ * the current module to load us.
+ */
+ printk (KERN_WARNING "Failure registering stacker module "
+ "as primary security module.\n");
+ return -EINVAL;
+ }
+
+ printk(KERN_NOTICE "LSM stacker registered as the primary "
+ "security module\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_stacker_files();
+ if (unregister_security (&stacker_ops))
+ printk(KERN_WARNING "Error unregistering LSM stacker.\n");
+ else
+ printk(KERN_WARNING "LSM stacker removed.\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");
diff -ruN linux-2.6.14-rc4-git1.orig/security/testinitsec1.c
linux-2.6.14-rc4-git1.new/security/testinitsec1.c
--- linux-2.6.14-rc4-git1.orig/security/testinitsec1.c 1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.14-rc4-git1.new/security/testinitsec1.c 2005-10-12 15:54:46.000000000 -0400
@@ -0,0 +1,75 @@
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/security.h>
+
+#define VALUE "value1"
+#define NAME "name1"
+#define MY_NAME "testinitsec1"
+static int test_init_security(struct inode *inode, struct inode *dir,
+ struct list_head *head)
+{
+ char *namep = NULL, *valuep = NULL;
+ struct xattr_data *data = NULL;
+
+ if (!head)
+ return 0;
+
+ data = kmalloc(sizeof(struct xattr_data), GFP_KERNEL);
+ if (!data)
+ goto err;
+ namep = kmalloc(32, GFP_KERNEL);
+ if (!namep)
+ goto err;
+ valuep = kmalloc(32, GFP_KERNEL);
+ if (!valuep)
+ goto err;
+
+ strcpy(namep, NAME);
+ strcpy(valuep, VALUE);
+ data->name = namep;
+ data->value = valuep;
+ data->len = strlen(VALUE);
+ INIT_LIST_HEAD(&data->list);
+ list_add_tail(&data->list, head);
+ return 0;
+err:
+ kfree(namep);
+ kfree(valuep);
+ kfree(data);
+ return -ENOMEM;
+}
+
+static struct security_operations testlsm_security_ops = {
+ .owner = THIS_MODULE,
+
+ .inode_init_security = test_init_security,
+};
+
+static int __init testlsm_init (void)
+{
+ if (mod_reg_security (MY_NAME, &testlsm_security_ops, NULL)) {
+ printk (KERN_INFO "Failure registering testlsm "
+ " module with primary security module.\n");
+ return -EINVAL;
+ }
+ return 0;
+}
+
+
+static void __exit testlsm_exit (void)
+{
+ if (unregister_security (&testlsm_security_ops)) {
+ printk (KERN_INFO "Failure unregistering testlsm "
+ "module with the kernel\n");
+ }
+ printk (KERN_INFO "init_security test module removed\n");
+}
+
+security_initcall (testlsm_init);
+module_exit (testlsm_exit);
+
+MODULE_DESCRIPTION("inode_initsecurity test LSM module");
+MODULE_LICENSE("GPL");
+
diff -ruN linux-2.6.14-rc4-git1.orig/security/testinitsec2.c
linux-2.6.14-rc4-git1.new/security/testinitsec2.c
--- linux-2.6.14-rc4-git1.orig/security/testinitsec2.c 1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.14-rc4-git1.new/security/testinitsec2.c 2005-10-12 15:54:46.000000000 -0400
@@ -0,0 +1,75 @@
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/security.h>
+
+#define VALUE "value2"
+#define NAME "name2"
+#define MY_NAME "testinitsec2"
+static int test_init_security(struct inode *inode, struct inode *dir,
+ struct list_head *head)
+{
+ char *namep = NULL, *valuep = NULL;
+ struct xattr_data *data = NULL;
+
+ if (!head)
+ return 0;
+
+ data = kmalloc(sizeof(struct xattr_data), GFP_KERNEL);
+ if (!data)
+ goto err;
+ namep = kmalloc(32, GFP_KERNEL);
+ if (!namep)
+ goto err;
+ valuep = kmalloc(32, GFP_KERNEL);
+ if (!valuep)
+ goto err;
+
+ strcpy(namep, NAME);
+ strcpy(valuep, VALUE);
+ data->name = namep;
+ data->value = valuep;
+ data->len = strlen(VALUE);
+ INIT_LIST_HEAD(&data->list);
+ list_add_tail(&data->list, head);
+ return 0;
+err:
+ kfree(namep);
+ kfree(valuep);
+ kfree(data);
+ return -ENOMEM;
+}
+
+static struct security_operations testlsm_security_ops = {
+ .owner = THIS_MODULE,
+
+ .inode_init_security = test_init_security,
+};
+
+static int __init testlsm_init (void)
+{
+ if (mod_reg_security (MY_NAME, &testlsm_security_ops, NULL)) {
+ printk (KERN_INFO "Failure registering testlsm "
+ " module with primary security module.\n");
+ return -EINVAL;
+ }
+ return 0;
+}
+
+
+static void __exit testlsm_exit (void)
+{
+ if (unregister_security (&testlsm_security_ops)) {
+ printk (KERN_INFO "Failure unregistering testlsm "
+ "module with the kernel\n");
+ }
+ printk (KERN_INFO "init_security test module removed\n");
+}
+
+security_initcall (testlsm_init);
+module_exit (testlsm_exit);
+
+MODULE_DESCRIPTION("inode_initsecurity test LSM module");
+MODULE_LICENSE("GPL");
+
--- linux-2.6.14-rc4-git1.orig/include/linux/security.h 2005-09-23 10:33:31.000000000 -0400
+++ linux-2.6.14-rc4-git1.new/include/linux/security.h 2005-09-23 10:15:13.000000000 -0400
@@ -368,6 +368,12 @@
* @dentry contains the dentry structure for the file to be created.
* @mode contains the file mode of the file to be created.
* Return 0 if permission is granted.
+ * @inode_post_create:
+ * Set the security attributes on a newly created regular file. This hook
+ * is called after a file has been successfully created.
+ * @dir contains the inode structure of the parent directory of the new file.
+ * @dentry contains the the dentry structure for the newly created file.
+ * @mode contains the file mode.
* @inode_link:
* Check permission before creating a new hard link to a file.
* @old_dentry contains the dentry structure for an existing link to the file.
@@ -392,6 +398,11 @@
* @dentry contains the dentry structure of new directory.
* @mode contains the mode of new directory.
* Return 0 if permission is granted.
+ * @inode_post_mkdir:
+ * Set security attributes on a newly created directory.
+ * @dir contains the inode structure of parent of the directory to be created.
+ * @dentry contains the dentry structure of new directory.
+ * @mode contains the mode of new directory.
* @inode_rmdir:
* Check the permission to remove a directory.
* @dir contains the inode structure of parent of the directory to be removed.
@@ -1162,12 +1173,16 @@
struct list_head *head);
int (*inode_create) (struct inode *dir,
struct dentry *dentry, int mode);
+ void (*inode_post_create) (struct inode *dir,
+ struct dentry *dentry, int mode);
int (*inode_link) (struct dentry *old_dentry,
struct inode *dir, struct dentry *new_dentry);
int (*inode_unlink) (struct inode *dir, struct dentry *dentry);
int (*inode_symlink) (struct inode *dir,
struct dentry *dentry, const char *old_name);
int (*inode_mkdir) (struct inode *dir, struct dentry *dentry, int mode);
+ void (*inode_post_mkdir) (struct inode *dir, struct dentry *dentry,
+ int mode);
int (*inode_rmdir) (struct inode *dir, struct dentry *dentry);
int (*inode_mknod) (struct inode *dir, struct dentry *dentry,
int mode, dev_t dev);
@@ -1526,6 +1541,15 @@
return security_ops->inode_create (dir, dentry, mode);
}
+static inline void security_inode_post_create (struct inode *dir,
+ struct dentry *dentry,
+ int mode)
+{
+ if (dentry->d_inode && unlikely (IS_PRIVATE (dentry->d_inode)))
+ return;
+ security_ops->inode_post_create (dir, dentry, mode);
+}
+
static inline int security_inode_link (struct dentry *old_dentry,
struct inode *dir,
struct dentry *new_dentry)
@@ -1561,6 +1585,15 @@
return security_ops->inode_mkdir (dir, dentry, mode);
}
+static inline void security_inode_post_mkdir (struct inode *dir,
+ struct dentry *dentry,
+ int mode)
+{
+ if (dentry->d_inode && unlikely (IS_PRIVATE (dentry->d_inode)))
+ return;
+ security_ops->inode_post_mkdir (dir, dentry, mode);
+}
+
static inline int security_inode_rmdir (struct inode *dir,
struct dentry *dentry)
{
@@ -2210,6 +2243,11 @@
return 0;
}
+static inline void security_inode_post_create (struct inode *dir,
+ struct dentry *dentry,
+ int mode)
+{ }
+
static inline int security_inode_link (struct dentry *old_dentry,
struct inode *dir,
struct dentry *new_dentry)
@@ -2237,6 +2275,11 @@
return 0;
}
+static inline void security_inode_post_mkdir (struct inode *dir,
+ struct dentry *dentry,
+ int mode)
+{ }
+
static inline int security_inode_rmdir (struct inode *dir,
struct dentry *dentry)
{
--- linux-2.6.14-rc4-git1.orig/fs/namei.c 2005-09-23 10:24:49.000000000 -0400
+++ linux-2.6.14-rc4-git1.new/fs/namei.c 2005-09-23 10:03:40.000000000 -0400
@@ -1316,8 +1316,10 @@
return error;
DQUOT_INIT(dir);
error = dir->i_op->create(dir, dentry, mode, nd);
- if (!error)
+ if (!error) {
fsnotify_create(dir, dentry->d_name.name);
+ security_inode_post_create(dir, dentry, mode);
+ }
return error;
}
@@ -1704,8 +1706,10 @@
DQUOT_INIT(dir);
error = dir->i_op->mkdir(dir, dentry, mode);
- if (!error)
+ if (!error) {
fsnotify_mkdir(dir, dentry->d_name.name);
+ security_inode_post_mkdir(dir, dentry, mode);
+ }
return error;
}
--- linux-2.6.14-rc4-git1.orig/security/stacker.c 2005-09-23 10:33:33.000000000 -0400
+++ linux-2.6.14-rc4-git1.new/security/stacker.c 2005-09-23 10:18:26.000000000 -0400
@@ -483,6 +483,12 @@
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)
{
@@ -506,6 +512,12 @@
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));
@@ -1350,10 +1362,12 @@
.inode_free_security = stacker_inode_free_security,
.inode_init_security = stacker_inode_init_security,
.inode_create = stacker_inode_create,
+ .inode_post_create = stacker_inode_post_create,
.inode_link = stacker_inode_link,
.inode_unlink = stacker_inode_unlink,
.inode_symlink = stacker_inode_symlink,
.inode_mkdir = stacker_inode_mkdir,
+ .inode_post_mkdir = stacker_inode_post_mkdir,
.inode_rmdir = stacker_inode_rmdir,
.inode_mknod = stacker_inode_mknod,
.inode_rename = stacker_inode_rename,
diff -ruN linux-2.6.14-rc4-git1.orig/drivers/acpi/osl.c
linux-2.6.14-rc4-git1.new/drivers/acpi/osl.c
--- linux-2.6.14-rc4-git1.orig/drivers/acpi/osl.c 2005-10-13 13:24:55.000000000 -0400
+++ linux-2.6.14-rc4-git1.new/drivers/acpi/osl.c 2005-10-13 13:26:35.000000000 -0400
@@ -205,11 +205,15 @@
return AE_OK;
}
+EXPORT_SYMBOL(acpi_os_map_memory);
+
void acpi_os_unmap_memory(void __iomem * virt, acpi_size size)
{
iounmap(virt);
}
+EXPORT_SYMBOL(acpi_os_unmap_memory);
+
#ifdef ACPI_FUTURE_USAGE
acpi_status
acpi_os_get_physical_address(void *virt, acpi_physical_address * phys)
diff -ruN linux-2.6.14-rc4-git1.orig/drivers/char/tpm/tpm_atmel.c
linux-2.6.14-rc4-git1.new/drivers/char/tpm/tpm_atmel.c
--- linux-2.6.14-rc4-git1.orig/drivers/char/tpm/tpm_atmel.c 2005-10-13 13:24:58.000000000 -0400
+++ linux-2.6.14-rc4-git1.new/drivers/char/tpm/tpm_atmel.c 2005-10-13 13:26:43.000000000 -0400
@@ -19,6 +19,7 @@
*
*/
+#include <linux/mod_devicetable.h>
#include "tpm.h"
/* Atmel definitions */
@@ -45,7 +46,6 @@
u8 status, *hdr = buf;
u32 size;
int i;
- __be32 *native_size;
/* start reading header */
if (count < 6)
@@ -62,8 +62,7 @@
}
/* size of the data received */
- native_size = (__force __be32 *) (hdr + 2);
- size = be32_to_cpu(*native_size);
+ size = be32_to_cpu(*((__be32 *) (hdr + 2)));
if (count < size) {
dev_err(&chip->pci_dev->dev,
@@ -137,7 +136,7 @@
&dev_attr_pcrs.attr,
&dev_attr_caps.attr,
&dev_attr_cancel.attr,
- 0,
+ NULL,
};
static struct attribute_group atmel_attr_grp = { .attrs = atmel_attrs };
@@ -210,7 +209,6 @@
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH6_1)},
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH7_0)},
{PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8111_LPC)},
- {PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_CSB6LPC)},
{0,}
};
@@ -235,10 +233,11 @@
pci_unregister_driver(&atmel_pci_driver);
}
-module_init(init_atmel);
+fs_initcall(init_atmel);
module_exit(cleanup_atmel);
MODULE_AUTHOR("Leendert van Doorn (leendert@watson.ibm.com)");
MODULE_DESCRIPTION("TPM Driver");
MODULE_VERSION("2.0");
MODULE_LICENSE("GPL");
+
diff -ruN linux-2.6.14-rc4-git1.orig/drivers/char/tpm/tpm.c
linux-2.6.14-rc4-git1.new/drivers/char/tpm/tpm.c
--- linux-2.6.14-rc4-git1.orig/drivers/char/tpm/tpm.c 2005-08-28 19:41:01.000000000 -0400
+++ linux-2.6.14-rc4-git1.new/drivers/char/tpm/tpm.c 2005-10-13 13:26:43.000000000 -0400
@@ -26,8 +26,17 @@
#include <linux/sched.h>
#include <linux/poll.h>
#include <linux/spinlock.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <asm/scatterlist.h>
+#include <linux/string.h>
+#include <linux/crypto.h>
+#include <linux/fs.h>
#include "tpm.h"
+#define TPM_CHIP_NUM_MASK 0x0000ffff
+#define TPM_CHIP_TYPE_SHIFT 16
+
enum tpm_const {
TPM_MINOR = 224, /* officially assigned */
TPM_BUFSIZE = 2048,
@@ -39,6 +48,111 @@
static DEFINE_SPINLOCK(driver_lock);
static int dev_mask[TPM_NUM_MASK_ENTRIES];
+/* psuedo TPM Request for kernel key sealing/unsealing */
+#define TPM_PSEUDO_RQU 0xff
+#define TPM_SEAL 0x17
+#define TPM_UNSEAL 0x18
+#define TPM_KERNKEYLEN 20
+#define TPM_ORD_OFFSET 9
+#define TPM_RET_OFFSET 6
+#define TPM_BDO 14
+
+/* the kernel master key, loaded at boot time via TPM unsealing */
+static u8 tpm_kernel_key[TPM_KERNKEYLEN];
+static int tpm_kmk_loaded=0;
+
+extern int tpm_get_key(u8 key[], int *len)
+{
+ if(!tpm_kmk_loaded)
+ return(-1);
+ memcpy(key,tpm_kernel_key,TPM_KERNKEYLEN);
+ *len=TPM_KERNKEYLEN;
+ return(0);
+}
+
+EXPORT_SYMBOL_GPL(tpm_get_key);
+
+/* Pseudo Request: TPM_SealKernkey - seal the kernel master key
+ *
+ * The driver takes a TPM_Seal request packet, replaces the
+ * input data with the kernel master key, and fixes the authdata
+ * field, based on the user supplied OSAP session secret.
+ * We are doing this so that userspace does most of the work,
+ * but is never allowed to see the kernel key unsealed.
+ *
+ * The TPM request packet fields are:
+ * hash? field start length
+ * RQU type 0 2
+ * total len 2 4
+ * * ordinal 6 4
+ * key handle 10 4
+ * * encauth 14 20
+ * * pcrinfosz 34 4
+ * * pcrinfo 38 variable1
+ * * datasize 38+v1 4
+ * * data 42+v1 variable2 (should be 20)
+ * auth handle 42+v1+v2 4
+ * h nonceodd 46+v1+v2 20
+ * h zero 66+v1+v2 1
+ * hmac result goes in:
+ * authdata 67+v1+v2 20
+ *
+ * If the first byte of the packet is 0xff, this is a
+ * pseudo op packet. For SealKernkey packets, the packet is followed
+ * by a buffer for the hmac auth calculation, containing:
+ * 20 bytes for new paramdigest (sha1 of *'ed fields)
+ * 20 bytes sess.enonce
+ * 20 bytes nonceodd
+ * 1 byte continue (0)
+ * 20 bytes of session.secret
+ */
+static int insert_master_key(int len, u8 *pkt)
+{
+ int pktlen, pcrinfosize, datasize, klen;
+ char *p;
+ struct scatterlist sg[2];
+ struct crypto_tfm *tfm;
+
+ /* get pcrinfosize and datasize (which must be 20) */
+ pktlen = be32_to_cpu(*((__be32 *)(pkt+2)));
+ pcrinfosize = be32_to_cpu(*((__be32 *)(pkt+34)));
+ datasize = be32_to_cpu(*((__be32 *)(pkt+38+pcrinfosize)));
+
+ /* reset the RQU type */
+ pkt[0] = (u8) 0;
+
+ /* if we have kernel master key, put it into the data field */
+ if(tpm_kmk_loaded)
+ memcpy(pkt+42+pcrinfosize,tpm_kernel_key,
+ sizeof(tpm_kernel_key));
+
+ /* calculate a new paramdigest */
+ tfm = crypto_alloc_tfm("sha1",0);
+ p = pkt + 6; /* ordinal */
+ sg[0].page = virt_to_page(p);
+ sg[0].offset = offset_in_page(p);
+ sg[0].length = 4;
+ p = pkt + 14; /* encauth .. data */
+ sg[1].page = virt_to_page(p);
+ sg[1].offset = offset_in_page(p);
+ sg[1].length = 28 + pcrinfosize + datasize;
+ crypto_digest_init (tfm);
+ crypto_digest_update (tfm, sg, 2);
+ crypto_digest_final (tfm, pkt+pktlen); /* save to paramdigest trailer */
+ crypto_free_tfm(tfm);
+
+ tfm = crypto_alloc_tfm("sha1",0);
+ klen = 20;
+ p = pkt + pktlen; /* new paramdigest */
+ sg[0].page = virt_to_page(p);
+ sg[0].offset = offset_in_page(p);
+ sg[0].length = 61;
+ crypto_hmac(tfm,pkt+pktlen+61,&klen,sg,1,pkt+67+pcrinfosize+datasize);
+ crypto_free_tfm(tfm);
+
+ return(0);
+}
+
static void user_reader_timeout(unsigned long ptr)
{
struct tpm_chip *chip = (struct tpm_chip *) ptr;
@@ -49,26 +163,58 @@
up(&chip->buffer_mutex);
}
+static struct tpm_chip* tpm_chip_lookup(int chip_num, int chip_typ)
+{
+ struct tpm_chip *pos;
+
+ spin_lock(&driver_lock);
+ list_for_each_entry(pos, &tpm_chip_list, list)
+ if ( ( chip_num == TPM_ANY_NUM ||
+ pos->dev_num == chip_num ) &&
+ ( chip_typ == TPM_ANY_TYPE ||
+ pos->vendor->drv_type == chip_typ) ){
+ spin_unlock(&driver_lock);
+ return pos;
+ }
+
+ spin_unlock(&driver_lock);
+ return NULL;
+}
+
/*
* Internal kernel interface to transmit TPM commands
*/
-static ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf,
+ssize_t tpm_transmit(struct tpm_chip * chip, unsigned char *buf,
size_t bufsiz)
{
ssize_t rc;
u32 count;
unsigned long stop;
+ int kernkey=0;
+
+ if (!chip)
+ return -ENODEV;
count = be32_to_cpu(*((__be32 *) (buf + 2)));
if (count == 0)
return -ENODATA;
+
if (count > bufsiz) {
dev_err(&chip->pci_dev->dev,
"invalid count value %x %zx \n", count, bufsiz);
return -E2BIG;
}
+ /* are we sealing/unsealing kernel key? */
+ if (buf[0]==TPM_PSEUDO_RQU){
+ buf[0]=0;
+ kernkey=buf[TPM_ORD_OFFSET];
+ if (kernkey==TPM_SEAL){
+ insert_master_key(bufsiz,buf);
+ }
+ }
+
down(&chip->tpm_mutex);
if ((rc = chip->vendor->send(chip, (u8 *) buf, count)) < 0) {
@@ -80,24 +226,25 @@
stop = jiffies + 2 * 60 * HZ;
do {
u8 status = inb(chip->vendor->base + 1);
+
if ((status & chip->vendor->req_complete_mask) ==
chip->vendor->req_complete_val) {
goto out_recv;
}
- if ((status == chip->vendor->req_canceled)) {
- dev_err(&chip->pci_dev->dev, "Operation Canceled\n");
+ if (status == chip->vendor->req_canceled) {
+ dev_err(&chip->pci_dev->dev,
+ "Operation Canceled\n");
rc = -ECANCELED;
goto out;
}
-
msleep(TPM_TIMEOUT); /* CHECK */
rmb();
} while (time_before(jiffies, stop));
chip->vendor->cancel(chip);
- dev_err(&chip->pci_dev->dev, "Operation Timed out\n");
+ dev_err(&chip->pci_dev->dev, "Operation timed out\n");
rc = -ETIME;
goto out;
@@ -105,11 +252,26 @@
rc = chip->vendor->recv(chip, (u8 *) buf, bufsiz);
if (rc < 0)
dev_err(&chip->pci_dev->dev,
- "tpm_transmit: tpm_recv: error %zd\n", rc);
+ "tpm_transmit: tpm_recv: error %Zd\n", rc);
out:
+
up(&chip->tpm_mutex);
+
+ /* if we are unsealing kernel master key, save it and zero */
+ if (kernkey==TPM_UNSEAL){
+ memcpy(tpm_kernel_key,buf+TPM_BDO,TPM_KERNKEYLEN);
+ memset(buf+TPM_BDO,0,TPM_KERNKEYLEN);
+ if(*(u32 *)(buf+TPM_RET_OFFSET)==0){
+ tpm_kmk_loaded=1;
+ dev_info(&chip->pci_dev->dev,
+ "TPM_LoadKernKey succeeded\n");
+ } else
+ dev_info(&chip->pci_dev->dev,
+ "TPM_LoadKernKey failed\n");
+ }
return rc;
}
+EXPORT_SYMBOL_GPL(tpm_transmit);
#define TPM_DIGEST_SIZE 20
#define CAP_PCR_RESULT_SIZE 18
@@ -139,11 +301,12 @@
__be32 index;
char *str = buf;
- struct tpm_chip *chip =
- pci_get_drvdata(to_pci_dev(dev));
+ struct tpm_chip *chip = pci_get_drvdata(to_pci_dev(dev));
if (chip == NULL)
return -ENODEV;
+ dev_info( &chip->pci_dev->dev, "Size of buf: %d\n", sizeof(*buf));
+
memcpy(data, cap_pcr, sizeof(cap_pcr));
if ((len = tpm_transmit(chip, data, sizeof(data)))
< CAP_PCR_RESULT_SIZE) {
@@ -163,7 +326,8 @@
< READ_PCR_RESULT_SIZE){
dev_dbg(&chip->pci_dev->dev, "A TPM error (%d) occurred"
" attempting to read PCR %d of %d\n",
- be32_to_cpu(*((__be32 *) (data + 6))), i, num_pcrs);
+ be32_to_cpu(*((__be32 *) (data + 6))), i,
+ num_pcrs);
goto out;
}
str += sprintf(str, "PCR-%02d: ", i);
@@ -172,10 +336,82 @@
str += sprintf(str, "\n");
}
out:
+ dev_info( &chip->pci_dev->dev, "Size of data: %d\n", str-buf);
return str - buf;
}
+
EXPORT_SYMBOL_GPL(tpm_show_pcrs);
+/*
+ * Return 0 on success. On error pass along error code.
+ * chip_id Upper 2 bytes equal ANY, HW_ONLY or SW_ONLY
+ * Lower 2 bytes equal tpm idx # or AN&
+ * res_buf must fit a TPM_PCR (20 bytes) or NULL if you don't care
+ */
+int tpm_pcr_read( u32 chip_id, int pcr_idx, u8* res_buf, int res_buf_size )
+{
+ u8 data[READ_PCR_RESULT_SIZE];
+ int rc;
+ __be32 index;
+ int chip_num = chip_id & TPM_CHIP_NUM_MASK;
+ struct tpm_chip* chip;
+
+ if ( res_buf && res_buf_size < TPM_DIGEST_SIZE )
+ return -ENOSPC;
+
+ if ( (chip = tpm_chip_lookup( chip_num,
+ chip_id >> TPM_CHIP_TYPE_SHIFT ) ) == NULL )
+ return -ENODEV;
+
+ memcpy(data, pcrread, sizeof(pcrread));
+ index = cpu_to_be32(pcr_idx);
+ memcpy(data + 10, &index, 4);
+ if ((rc = tpm_transmit(chip, data, sizeof(data))) > 0 )
+ rc = be32_to_cpu(*((u32*)(data+6)));
+
+ if ( rc == 0 && res_buf )
+ memcpy(res_buf, data+10, TPM_DIGEST_SIZE);
+
+ return rc;
+
+}
+EXPORT_SYMBOL_GPL(tpm_pcr_read);
+
+#define EXTEND_PCR_SIZE 34
+static const u8 pcrextend[] = {
+ 0, 193, /* TPM_TAG_RQU_COMMAND */
+ 0, 0, 0, 34, /* length */
+ 0, 0, 0, 20, /* TPM_ORD_Extend */
+ 0, 0, 0, 0 /* PCR index */
+};
+
+/*
+ * Return 0 on success. On error pass along error code.
+ * chip_id Upper 2 bytes equal ANY, HW_ONLY or SW_ONLY
+ * Lower 2 bytes equal tpm idx # or ANY
+ */
+int tpm_pcr_extend(u32 chip_id, int pcr_idx, const u8* hash)
+{
+ u8 data[EXTEND_PCR_SIZE];
+ int rc;
+ __be32 index;
+ int chip_num = chip_id & TPM_CHIP_NUM_MASK;
+ struct tpm_chip* chip;
+
+ if ( (chip = tpm_chip_lookup( chip_num,
+ chip_id >> TPM_CHIP_TYPE_SHIFT )) == NULL )
+ return -ENODEV;
+
+ memcpy(data, pcrextend, sizeof(pcrextend));
+ index = cpu_to_be32(pcr_idx);
+ memcpy(data + 10, &index, 4);
+ memcpy( data + 14, hash, TPM_DIGEST_SIZE );
+ if ((rc = tpm_transmit(chip, data, sizeof(data))) > 0 )
+ rc = be32_to_cpu(*((u32*)(data+6)));
+ return rc;
+}
+EXPORT_SYMBOL_GPL(tpm_pcr_extend);
+
#define READ_PUBEK_RESULT_SIZE 314
static const u8 readpubek[] = {
0, 193, /* TPM_TAG_RQU_COMMAND */
@@ -191,8 +427,7 @@
int i, rc;
char *str = buf;
- struct tpm_chip *chip =
- pci_get_drvdata(to_pci_dev(dev));
+ struct tpm_chip *chip = pci_get_drvdata(to_pci_dev(dev));
if (chip == NULL)
return -ENODEV;
@@ -274,8 +509,7 @@
ssize_t len;
char *str = buf;
- struct tpm_chip *chip =
- pci_get_drvdata(to_pci_dev(dev));
+ struct tpm_chip *chip = pci_get_drvdata(to_pci_dev(dev));
if (chip == NULL)
return -ENODEV;
@@ -301,6 +535,7 @@
return str - buf;
}
+
EXPORT_SYMBOL_GPL(tpm_show_caps);
ssize_t tpm_store_cancel(struct device *dev, struct device_attribute *attr,
@@ -313,6 +548,7 @@
chip->vendor->cancel(chip);
return count;
}
+
EXPORT_SYMBOL_GPL(tpm_store_cancel);
@@ -470,7 +706,8 @@
pci_disable_device(pci_dev);
- dev_mask[chip->dev_num / TPM_NUM_MASK_ENTRIES ] &= !(1 << (chip->dev_num %
TPM_NUM_MASK_ENTRIES));
+ dev_mask[chip->dev_num / TPM_NUM_MASK_ENTRIES] &=
+ !(1 << (chip->dev_num % TPM_NUM_MASK_ENTRIES));
kfree(chip);
diff -ruN linux-2.6.14-rc4-git1.orig/drivers/char/tpm/tpm.h
linux-2.6.14-rc4-git1.new/drivers/char/tpm/tpm.h
--- linux-2.6.14-rc4-git1.orig/drivers/char/tpm/tpm.h 2005-08-28 19:41:01.000000000 -0400
+++ linux-2.6.14-rc4-git1.new/drivers/char/tpm/tpm.h 2005-10-13 13:26:43.000000000 -0400
@@ -24,6 +24,7 @@
#include <linux/delay.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
+#include <linux/tpm.h>
enum tpm_timeout {
TPM_TIMEOUT = 5, /* msecs */
@@ -51,6 +52,7 @@
u8 req_complete_val;
u8 req_canceled;
u16 base; /* TPM base address */
+ int drv_type;
int (*recv) (struct tpm_chip *, u8 *, size_t);
int (*send) (struct tpm_chip *, u8 *, size_t);
@@ -99,5 +101,11 @@
loff_t *);
extern ssize_t tpm_read(struct file *, char __user *, size_t, loff_t *);
extern void __devexit tpm_remove(struct pci_dev *);
+
extern int tpm_pm_suspend(struct pci_dev *, pm_message_t);
extern int tpm_pm_resume(struct pci_dev *);
+
+/* internal kernel interface */
+extern ssize_t tpm_transmit(struct tpm_chip *chip, unsigned char *buf,
+ size_t bufsiz);
+extern int tpm_get_key(u8 key[], int *len);
diff -ruN linux-2.6.14-rc4-git1.orig/drivers/char/tpm/tpm_nsc.c
linux-2.6.14-rc4-git1.new/drivers/char/tpm/tpm_nsc.c
--- linux-2.6.14-rc4-git1.orig/drivers/char/tpm/tpm_nsc.c 2005-08-28 19:41:01.000000000 -0400
+++ linux-2.6.14-rc4-git1.new/drivers/char/tpm/tpm_nsc.c 2005-10-13 13:26:35.000000000 -0400
@@ -20,6 +20,7 @@
*/
#include "tpm.h"
+#include <linux/mod_devicetable.h>
/* National definitions */
enum tpm_nsc_addr{
@@ -121,7 +122,6 @@
u8 *buffer = buf;
u8 data, *p;
u32 size;
- __be32 *native_size;
if (count < 6)
return -EIO;
@@ -161,8 +161,7 @@
return -EIO;
}
- native_size = (__force __be32 *) (buf + 2);
- size = be32_to_cpu(*native_size);
+ size = be32_to_cpu(*((__be32 *) (buf + 2)));
if (count < size)
return -EIO;
@@ -363,7 +362,7 @@
pci_unregister_driver(&nsc_pci_driver);
}
-module_init(init_nsc);
+fs_initcall(init_nsc);
module_exit(cleanup_nsc);
MODULE_AUTHOR("Leendert van Doorn (leendert@watson.ibm.com)");
diff -ruN linux-2.6.14-rc4-git1.orig/include/linux/tpm.h
linux-2.6.14-rc4-git1.new/include/linux/tpm.h
--- linux-2.6.14-rc4-git1.orig/include/linux/tpm.h 1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.14-rc4-git1.new/include/linux/tpm.h 2005-10-13 13:26:36.000000000 -0400
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2004 IBM Corporation
+ *
+ * Authors:
+ * Leendert van Doorn <leendert@watson.ibm.com>
+ * Dave Safford <safford@watson.ibm.com>
+ * Reiner Sailer <sailer@watson.ibm.com>
+ * Kylene Hall <kjhall@us.ibm.com>
+ *
+ * Maintained by: <tpmdd_devel@lists.sourceforge.net>
+ *
+ * Device driver for TCG/TCPA TPM (trusted platform module).
+ * Specifications at www.trustedcomputinggroup.org
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2 of the
+ * License.
+ *
+ */
+#ifndef __LINUX_TPM_H__
+#define __LINUX_TPM_H__
+
+#define CONFIG_TCG_TPM 1
+#define PCI_DEVICE_ID_AMD_8111_LPC 0x7468
+
+/*
+ * Chip type is one of these values in the upper two bytes of chip_id
+ */
+enum tpm_chip_type {
+ TPM_HW_TYPE = 0x0,
+ TPM_SW_TYPE = 0x1,
+ TPM_ANY_TYPE = 0xFFFF,
+};
+
+/*
+ * Chip num is this value or a valid tpm idx in lower two bytes of chip_id
+ */
+enum tpm_chip_num {
+ TPM_ANY_NUM = 0xFFFF,
+};
+
+
+#ifdef CONFIG_TCG_TPM
+extern int tpm_pcr_read(u32 chip_id, int pcr_idx, u8* res_buf, int res_buf_size);
+extern int tpm_pcr_extend(u32 chip_id, int pcr_idx, const u8* hash);
+#else
+static inline int tpm_pcr_read(u32 chip_id, int pcr_idx, u8* res_buf,
+ int res_buf_size)
+{
+ return -1;
+}
+
+static inline int tpm_pcr_extend(u32 chip_id, int pcr_idx, const u8* hash)
+{
+ return -1;
+}
+#endif
+#endif
+
diff -ruN linux-2.6.14-rc4-git1.orig/include/linux/evm.h
linux-2.6.14-rc4-git1.new/include/linux/evm.h
--- linux-2.6.14-rc4-git1.orig/include/linux/evm.h 1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.14-rc4-git1.new/include/linux/evm.h 2005-10-13 14:09:05.000000000 -0400
@@ -0,0 +1,17 @@
+/*
+ * evm_export.h - exported function call definitions
+ *
+ */
+
+extern int sandbox_avail; /* can't use MODULE_STATE_GOING notification */
+
+typedef enum {
+ EVM_PERMIT = 0, EVM_SANDBOX = -1, EVM_DENY = -2, EVM_NOLABEL = -3
+} evm_status;
+
+extern evm_status evm_analyze_cacheinfo(struct dentry *dentry);
+extern int evm_idx;
+extern int evm_setxattr (struct dentry *d, char *name, void *value,
+ size_t size, int flags);
+extern int evm_update_hmac(struct dentry *dentry, int flags);
+extern int mem2hex(char *mem, char *buffer, int count);
diff -ruN linux-2.6.14-rc4-git1.orig/security/evm/evm_cache.c
linux-2.6.14-rc4-git1.new/security/evm/evm_cache.c
--- linux-2.6.14-rc4-git1.orig/security/evm/evm_cache.c 1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.14-rc4-git1.new/security/evm/evm_cache.c 2005-10-13 14:09:05.000000000 -0400
@@ -0,0 +1,573 @@
+/*
+ * evm_cache.c
+ *
+ * Cache the extended verification results in inode->i_security.
+ *
+ * Copyright (C) 2005 IBM Corporation
+ * Author: Mimi Zohar <zohar@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/fs.h>
+#include <linux/proc_fs.h>
+#include <linux/devfs_fs_kernel.h>
+#include <linux/namei.h>
+#include <linux/evm.h>
+
+#include "evm.h"
+
+int evm_idx;
+
+/*
+ * Invalidate the cached information when the file actually changes.
+ * Flag mutable files in order to later update hash value, providing
+ * protection from offline attack.
+ */
+int evm_file_permission(struct file *file, int mask)
+{
+ struct inode *inode = file->f_dentry->d_inode;
+ struct evm_isec_cache *isec = NULL;
+ struct evm_isec_xattr *istatus, *istatus_p;
+ struct timespec now;
+
+ if ((mask & MAY_WRITE) || (mask & MAY_APPEND)) {
+ isec = evm_get_isec(inode->i_security);
+ if (isec != NULL) {
+ isec->file_mutable = 0;
+ istatus =
+ (void *) isec + sizeof(struct evm_isec_cache);
+ if ((istatus->xattr_id == 0) /* unlabeled HMAC */
+ &&(istatus->xattr_flags == 0))
+ return 0;
+
+ for_each_xattr(istatus_p, istatus + 1,
+ isec->cache_size) {
+ if (istatus_p->xattr_id == 0)
+ continue;
+
+ if (memcmp(istatus_p->xattr_name,
+ "security.evm.mutable", 20) == 0)
+ isec->file_mutable = 1;
+ }
+ if (!isec->file_mutable)
+ isec->cache_flag = EVM_CACHE_DIRTY;
+ now = CURRENT_TIME;
+ isec->cache_refresh = now.tv_sec;
+ }
+ }
+ return 0;
+}
+
+/*
+ * In install mode, update the evm.hash extended attribute value, not only
+ * for mutable files, but for all files and log it.
+ */
+void evm_file_install_free(struct file *file)
+{
+ struct inode *inode = NULL;
+ char hashValue[SHA1_DIGEST_SIZE + 1];
+ char hashStr[SHA1_STR_SIZE + 11];
+ mode_t sav_fmode;
+ int rc;
+
+ if (!file || !file->f_dentry || !(file->f_mode & MAY_WRITE))
+ return;
+
+ inode = file->f_dentry->d_inode;
+ if (!inode)
+ return;
+
+ if (!S_ISREG(inode->i_mode))
+ return;
+
+ sav_fmode = file->f_mode;
+ file->f_mode |= FMODE_READ;
+ rc = evm_calc_hash(file, hashValue, EVM_TYPE_MD5);
+ file->f_mode = sav_fmode;
+ if (rc == 0) {
+ struct timespec now;
+ int len;
+ time_t nl_time;
+
+ memset(hashStr, 0, sizeof hashStr);
+ now = CURRENT_TIME;
+ nl_time = htonl(now.tv_sec);
+ memcpy(hashStr, &nl_time, sizeof(time_t));
+ len = mem2hex(hashValue, hashStr + sizeof(time_t),
+ MD5_DIGEST_SIZE);
+ rc = evm_setxattr(file->f_dentry, "security.evm.hash",
+ hashStr, sizeof(time_t) + len, 0);
+ printk(KERN_INFO "install: %s updated hash \n",
+ file->f_dentry->d_name.name);
+ }
+}
+
+/*
+ * Called on close. For mutable files, update the evm.hash extended
+ * attribute value.
+ */
+void evm_file_free(struct file *file)
+{
+ struct inode *inode = NULL;
+ char hashValue[SHA1_DIGEST_SIZE + 1];
+ char hashStr[SHA1_STR_SIZE + 11];
+ struct evm_isec_cache *isec;
+ int rc;
+
+ if (!file || !file->f_dentry)
+ return;
+
+ inode = file->f_dentry->d_inode;
+ if (!inode)
+ return;
+
+ isec = evm_get_isec(inode->i_security);
+ if (!isec)
+ return;
+
+ if (isec->file_mutable) {
+ mode_t sav_fmode;
+
+ sav_fmode = file->f_mode;
+ file->f_mode |= FMODE_READ;
+ rc = evm_calc_hash(file, hashValue, EVM_TYPE_MD5);
+ file->f_mode = sav_fmode;
+ if (rc == 0) {
+ struct timespec now;
+ int len;
+ time_t nl_time;
+
+ memset(hashStr, 0, sizeof hashStr);
+ now = CURRENT_TIME;
+ nl_time = htonl(now.tv_sec);
+ memcpy(hashStr, &nl_time, sizeof(time_t));
+ len = mem2hex(hashValue, hashStr + sizeof(time_t),
+ MD5_DIGEST_SIZE);
+ rc = evm_setxattr(file->f_dentry,
+ "security.evm.hash", hashStr,
+ sizeof(time_t) + len, 0);
+ }
+ isec->file_mutable = 0;
+ }
+}
+
+static int evm_getxattr(struct dentry *dentry, char *xattrName,
+ char **xattr_value, int xattr_size)
+{
+ char *value = *xattr_value;
+ int size;
+ int error;
+
+ if (!dentry || !dentry->d_inode || !dentry->d_inode->i_op
+ || !dentry->d_inode->i_op->getxattr)
+ return xattr_size;
+
+ if ((size = dentry->d_inode->i_op->getxattr(dentry,
+ xattrName, NULL,
+ 0)) < 0)
+ return size;
+
+ if (size > xattr_size) {
+ if (value)
+ kfree(value);
+ xattr_size = size;
+ value = kmalloc(xattr_size + 1, GFP_KERNEL);
+ if (!value) {
+ xattr_size = 0;
+ return -ENOMEM;
+ }
+ memset(value, 0, xattr_size + 1);
+ } else
+ memset(value, 0, xattr_size);
+ error = dentry->d_inode->i_op->getxattr(dentry, xattrName,
+ value, xattr_size);
+ *xattr_value = value;
+ return xattr_size;
+
+}
+
+static void evm_verify_timestamp(struct evm_isec_xattr *istatus,
+ char *xattr_value, time_t xattrExpire)
+{
+ time_t timestamp;
+ struct timespec now;
+
+ now = CURRENT_TIME;
+ timestamp = ntohl(*((time_t *) xattr_value));
+ if (now.tv_sec > (timestamp + xattrExpire))
+ istatus->xattr_flags |= EVM_XATTR_EXPIRED;
+ return;
+}
+
+static void evm_verify_enum(struct evm_isec_xattr *istatus,
+ struct nameidata *nd, char value)
+{
+ const unsigned char *debug_name = "???";
+
+ if (nd && nd->dentry && nd->dentry->d_name.name)
+ debug_name = nd->dentry->d_name.name;
+
+ switch (value) {
+ case EVM_VALUE_PASS:
+ istatus->xattr_flags |= EVM_XATTR_PASS;
+ dprintk(EVM_CACHE, "%s: %s(%s) passed\n",
+ __FUNCTION__, debug_name, istatus->xattr_name);
+ break;
+ case EVM_VALUE_FAIL:
+ istatus->xattr_flags |= EVM_XATTR_FAIL;
+ dprintk(EVM_CACHE, "%s: %s(%s) failed\n",
+ __FUNCTION__, debug_name, istatus->xattr_name);
+ break;
+ case EVM_VALUE_EXPIRED:
+ istatus->xattr_flags |= EVM_XATTR_EXPIRED;
+ dprintk(EVM_CACHE, "%s: %s(%s) expired\n",
+ __FUNCTION__, debug_name, istatus->xattr_name);
+ break;
+ default:
+ dprintk(EVM_CACHE, "%s: %s(%s) unknown\n",
+ __FUNCTION__, debug_name, istatus->xattr_name);
+ break;
+ }
+ return;
+}
+
+static void evm_verify_md5(struct evm_isec_xattr *istatus,
+ struct nameidata *nd, char *xattr_value,
+ int xattr_size, char *hashValue)
+{
+ char hashStr[MD5_STR_SIZE + 1]; /* MD5 MAC as a string */
+ const unsigned char *debug_name = "???";
+
+
+ if (evm_calc_dhash(nd, hashValue, EVM_TYPE_MD5) != 0)
+ istatus->xattr_flags |= EVM_XATTR_PASS;
+ else {
+ int len;
+ memset(hashStr, 0, sizeof hashStr);
+ len = mem2hex(hashValue, hashStr, MD5_DIGEST_SIZE);
+ if (nd && nd->dentry && nd->dentry->d_name.name)
+ debug_name = nd->dentry->d_name.name;
+
+ if (memcmp(hashStr, xattr_value + sizeof(time_t),
+ xattr_size - sizeof(time_t)) != 0) {
+ dprintk(EVM_CACHE, "%s: %s(%s) failed\n",
+ __FUNCTION__, debug_name,
+ istatus->xattr_name);
+ dprintk(EVM_CACHE, "xattr %s\n",
+ xattr_value + sizeof(time_t));
+ dprintk(EVM_CACHE, "value %s\n", hashStr);
+ istatus->xattr_flags |= EVM_XATTR_FAIL;
+ } else {
+ dprintk(EVM_CACHE, "%s: %s(%s) succeeded\n",
+ __FUNCTION__, debug_name,
+ istatus->xattr_name);
+ istatus->xattr_flags |= EVM_XATTR_PASS;
+ }
+ }
+ return;
+}
+
+static void evm_verify_sha1(struct evm_isec_xattr *istatus,
+ struct nameidata *nd, char *xattr_value,
+ int xattr_size, char *hashValue)
+{
+ char hashStr[SHA1_STR_SIZE + 1]; /* SHA1 string */
+ const unsigned char *debug_name = "???";
+
+
+ if (evm_calc_dhash(nd, hashValue, EVM_TYPE_SHA1) != 0)
+ istatus->xattr_flags |= EVM_XATTR_PASS;
+ else {
+ int len;
+ memset(hashStr, 0, sizeof hashStr);
+ len = mem2hex(hashValue, hashStr, SHA1_DIGEST_SIZE);
+ if (nd && nd->dentry && nd->dentry->d_name.name)
+ debug_name = nd->dentry->d_name.name;
+
+ if (memcmp(hashStr, xattr_value + sizeof(time_t),
+ xattr_size - sizeof(time_t)) != 0) {
+ dprintk(EVM_CACHE, "%s: %s(%s) failed\n",
+ __FUNCTION__, debug_name,
+ istatus->xattr_name);
+ dprintk(EVM_CACHE, "xattr %s\n",
+ xattr_value + sizeof(time_t));
+ dprintk(EVM_CACHE, "value %s\n", hashStr);
+ istatus->xattr_flags |= EVM_XATTR_FAIL;
+ } else {
+ dprintk(EVM_CACHE, "%s: %s(%s) succeeded\n",
+ __FUNCTION__, debug_name,
+ istatus->xattr_name);
+ istatus->xattr_flags |= EVM_XATTR_PASS;
+ }
+ }
+ return;
+}
+
+static int evm_verify_hmac(struct dentry *dentry, struct inode *inode)
+{
+ int rc = 0;
+ struct evm_isec_cache *isec;
+ struct evm_isec_xattr *istatus;
+ char hmacVal[SHA1_DIGEST_SIZE];
+ char trustedHMAC[SHA1_DIGEST_SIZE];
+ struct timespec now;
+ const unsigned char *debug_name = "???";
+
+ if (!inode)
+ return -EINVAL;
+
+ isec = evm_get_isec(inode->i_security);
+ if (!isec)
+ return -EINVAL;
+
+ istatus = (void *) isec + sizeof(struct evm_isec_cache);
+ istatus->xattr_id = 0; /* HMAC */
+
+ if (!dentry || !dentry->d_inode || !dentry->d_inode->i_op
+ || !dentry->d_inode->i_op->getxattr) {
+ istatus->xattr_flags |= EVM_XATTR_PASS;
+ return -EINVAL;
+ }
+
+ rc = evm_calc_hmac(dentry, hmacVal);
+ if (rc < 0)
+ return rc;
+
+ if (dentry->d_name.name)
+ debug_name = dentry->d_name.name;
+ rc = evm_verify_xattr(dentry, hmacVal, "security.evm.hmac",
+ trustedHMAC, SHA1_DIGEST_SIZE);
+ if (rc < 0) {
+ switch (rc) {
+ case -EOPNOTSUPP: /* files not supported like /dev */
+ isec->cache_flag = EVM_CACHE_EOPNOTSUPP;
+ dprintk(EVM_CACHE,
+ "%s: %s getxattr not supported\n",
+ __FUNCTION__, debug_name);
+ break;
+ case -ENODATA: /* files not labelled don't fail */
+ istatus->xattr_flags = EVM_XATTR_NOLABEL;
+ dprintk(EVM_CACHE, "%s: '%s' no HMAC label \n",
+ __FUNCTION__, debug_name);
+ break;
+ case -EPERM:
+ istatus->xattr_flags |= EVM_XATTR_FAIL;
+ break;
+ default:
+ dprintk(EVM_CACHE, "%s: %s rc = %d not "
+ "EOPNOTSUPP, ENODATA or EPERM\n",
+ __FUNCTION__, debug_name, rc);
+ break;
+ }
+
+ now = CURRENT_TIME;
+ isec->cache_refresh = now.tv_sec + 300; /* min refresh */
+ } else {
+ istatus->xattr_flags |= EVM_XATTR_PASS;
+ dprintk(EVM_CACHE, "%s: %s HMAC verification "
+ "succeeeded\n", __FUNCTION__, debug_name);
+ }
+
+ return rc;
+}
+
+/*
+ * Determine whether or not the security section is valid and up to date.
+ */
+static int evm_valid_security(struct dentry *dentry, struct inode *inode)
+{
+ int error = 0;
+ struct evm_isec_cache *isec;
+ struct timespec now;
+
+ if (!inode)
+ return -EINVAL;
+
+ isec = evm_get_isec(inode->i_security);
+ if (!isec)
+ return -EINVAL;
+
+ switch (isec->cache_flag) {
+ case EVM_CACHE_DIRTY:
+ error = -EINVAL;
+ break;
+ case EVM_CACHE_EOPNOTSUPP:
+ error = 0;
+ default:
+ break;
+ }
+
+ now = CURRENT_TIME;
+ if ((isec->cache_refresh != 0)
+ && (now.tv_sec > isec->cache_refresh)) {
+ const unsigned char *debug_name = "???";
+
+ error = -EINVAL; /* Cache entry expired */
+ if (dentry && dentry->d_name.name)
+ debug_name = dentry->d_name.name;
+ dprintk(EVM_CACHE, "%s: %s cache entry expired\n",
+ __FUNCTION__, debug_name);
+ }
+ return error;
+}
+
+static struct evm_isec_cache *evm_alloc_security(void)
+{
+ struct evm_isec_cache *isec;
+ int evm_isec_size; /* size of inode->i_security buffer */
+ struct evm_xattr_config *config_data;
+
+ evm_isec_size = evm_get_isec_size();
+ isec =
+ (struct evm_isec_cache *) kmalloc(evm_isec_size, GFP_KERNEL);
+ if (isec) {
+ memset(isec, 0, evm_isec_size);
+ evm_get_config_info(&config_data, &isec->cache_size,
+ &isec->cache_ver);
+ }
+ return isec;
+}
+
+static void evm_update_cachehdr(struct evm_isec_cache *isec,
+ time_t xattrRefresh)
+{
+ struct timespec now;
+ struct evm_xattr_config *config_data;
+
+ if (!isec)
+ return;
+ now = CURRENT_TIME;
+ evm_get_config_info(&config_data, &isec->cache_size,
+ &isec->cache_ver);
+ isec->cache_refresh = now.tv_sec + xattrRefresh;
+ isec->cache_flag = EVM_CACHE_CLEAN;
+}
+
+static void update_istatus(struct evm_xattr_config *config_p,
+ struct nameidata *nd,
+ struct evm_isec_xattr *istatus,
+ char *xattr_value, int xattr_size,
+ char *hash_value)
+{
+ if (!config_p || !nd || !istatus || !xattr_value)
+ return;
+
+ istatus->xattr_flags = 0;
+
+ evm_verify_timestamp(istatus, xattr_value, config_p->xattr_expire);
+ istatus->xattr_name = config_p->xattr_name;
+ switch (config_p->xattr_type) {
+ case EVM_TYPE_SHA1:
+ istatus->xattr_id = config_p->xattr_id;
+ evm_verify_sha1(istatus, nd, xattr_value, xattr_size,
+ hash_value);
+ break;
+ case EVM_TYPE_MD5:
+ istatus->xattr_id = config_p->xattr_id;
+ evm_verify_md5(istatus, nd, xattr_value, xattr_size,
+ hash_value);
+ break;
+ case EVM_TYPE_ENUM:
+ istatus->xattr_id = config_p->xattr_id;
+ evm_verify_enum(istatus, nd,
+ *(xattr_value) + sizeof(time_t));
+ break;
+ case EVM_TYPE_STRING:
+ istatus->xattr_id = config_p->xattr_id;
+ istatus->xattr_flags |= EVM_XATTR_PASS;
+ break;
+ case EVM_TYPE_HMAC:
+ dprintk(EVM_CACHE, "%s: HMAC is special type\n",
+ __FUNCTION__);
+ break;
+ default:
+ printk("evm: DEFAULT\n");
+ break;
+ }
+ return;
+}
+
+/*
+ * Calculate the extended attribute "security.evm.hmac" based on
+ * the extended attributes listed in evm_config (i.e. /etc/evm.config)
+ * and verify it matches the "security.evm.hmac" value. Only after
+ * validating the HMAC, verify the individual extended attribute values
+ * (i.e. the file's hash).
+
+ * The evaluation results are "cached" in the inode security structure
+ * (inode->i_security).
+ */
+int evm_inode_permission(struct inode *inode, int mask,
+ struct nameidata *nd)
+{
+ struct evm_isec_cache *isec;
+ struct evm_isec_xattr *istatus;
+ struct dentry *dentry;
+ ssize_t error = 0;
+
+ int xattr_size = 0;
+ char *xattr_value = NULL;
+ struct evm_xattr_config *config_data, *config_p;
+ time_t xattr_refresh = 0;
+
+ if (!nd || !nd->dentry || !inode)
+ return 0;
+
+ dentry = nd->dentry;
+ if ((error = evm_valid_security(dentry, inode)) == 0) /* Valid? */
+ return 0;
+
+ isec = evm_get_isec(inode->i_security);
+ if (!isec) {
+ isec = evm_alloc_security();
+ if (!isec)
+ return (-ENOMEM);
+ evm_set_isec(inode->i_security, isec);
+ }
+
+ /*Numerous reasons for no hmac, errors cached in istatus->xattr_flags.*/
+ error = evm_verify_hmac(dentry, inode);
+ if (error < 0)
+ return 0;
+
+ istatus = (void *) isec + sizeof(struct evm_isec_cache);
+ evm_get_config_info(&config_data, &isec->cache_size,
+ &isec->cache_ver);
+
+ for_each_xattr(config_p, config_data, isec->cache_size) {
+ istatus++;
+ xattr_size = evm_getxattr(dentry, config_p->xattr_name,
+ &xattr_value, xattr_size);
+ if (xattr_size > 0) {
+ update_istatus(config_p, nd, istatus, xattr_value,
+ xattr_size, isec->hash);
+ if ((xattr_refresh > config_p->xattr_refresh)
+ || (xattr_refresh == 0))
+ xattr_refresh = config_p->xattr_refresh;
+ } else
+ istatus->xattr_flags = 0;
+ }
+
+ evm_update_cachehdr(isec, xattr_refresh);
+
+ if (xattr_value)
+ kfree(xattr_value);
+ return 0;
+}
+
+void evm_inode_free_security(struct inode *inode)
+{
+ struct evm_isec_cache *isec;
+
+ isec = evm_del_isec(inode->i_security);
+ if (isec)
+ kfree(isec);
+}
diff -ruN linux-2.6.14-rc4-git1.orig/security/evm/evm_config.c
linux-2.6.14-rc4-git1.new/security/evm/evm_config.c
--- linux-2.6.14-rc4-git1.orig/security/evm/evm_config.c 1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.14-rc4-git1.new/security/evm/evm_config.c 2005-10-13 14:09:05.000000000 -0400
@@ -0,0 +1,285 @@
+/*
+ * EVM - Extended Verification Module
+ *
+ * Copyright (C) 2005 IBM Corporation
+ * Author: Mimi Zohar <zohar@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 "evm.h"
+
+/*
+ * Hard coded default configuration values
+ */
+static struct evm_xattr_config evm_default_data[] = {
+ {"security.evm.hash", 300, 7776000, EVM_TYPE_MD5, 0},
+ {"security.evm.mutable", 300, 7776000, EVM_TYPE_STRING, 0},
+ {"security.evm.version", 300, 7776000, EVM_TYPE_ENUM, 0},
+ {"security.evm.antivirus", 300, 7776000, EVM_TYPE_ENUM, 0}
+};
+static int evm_default_datasize = 4; /* number of extended attributes */
+
+/*
+ * Pointer to running configuration data
+ */
+static struct evm_xattr_config *evm_config_data;
+static int evm_config_datasize = 0; /* number of extended attributes */
+static int evm_config_version; /* using a timestamp as a config version no. */
+
+/*
+ * inode->security information
+ */
+static int evm_isec_size = 0; /* size of inode->i_security buffer */
+static int evm_config_id = 1; /* HMAC is always 0; start with 1 */
+int evm_get_isec_size(void)
+{
+ return evm_isec_size;
+}
+
+int evm_get_config_info(struct evm_xattr_config **head, int *size,
+ time_t * version)
+{
+ *head = evm_config_data;
+ *size = evm_config_datasize;
+ *version = evm_config_version;
+ return 0;
+}
+
+/*
+ * Initialize the Extended Verification module
+ */
+int evm_update_config(struct evm_xattr_config *evm_new_data,
+ int evm_new_datasize)
+{
+ struct evm_xattr_config *config_p;
+ struct evm_xattr_config *evm_old_data;
+ int evm_old_datasize = 0;
+
+ ssize_t error = 0;
+
+ for_each_xattr(config_p, evm_new_data, evm_new_datasize) {
+ switch (config_p->xattr_type) {
+ case EVM_TYPE_ENUM:
+ case EVM_TYPE_HMAC:
+ case EVM_TYPE_MD5:
+ case EVM_TYPE_SHA1:
+ case EVM_TYPE_STRING:
+ break;
+ default:
+ dprintk(EVM_BASE, "%s: unknown type %d\n",
+ __FUNCTION__, config_p->xattr_type);
+ error = -EINVAL;
+ break;
+ }
+
+ if (!error)
+ config_p->xattr_id = evm_config_id++;
+ }
+
+ if (evm_new_datasize > 0) {
+ struct timespec now;
+
+ evm_old_data = evm_config_data;
+ evm_old_datasize = evm_config_datasize;
+
+ evm_config_data = evm_new_data;
+ evm_config_datasize = evm_new_datasize;
+ now = CURRENT_TIME;
+ evm_config_version = now.tv_sec;
+
+ evm_isec_size = sizeof(struct evm_isec_cache) +
+ ((evm_config_datasize + 1)
+ * sizeof(struct evm_isec_xattr));
+ } else {
+ printk(KERN_INFO "%s: config file definition missing\n",
+ __FUNCTION__);
+ error = -EINVAL;
+ }
+ return error;
+}
+
+static char *get_tag(char *bufStart, char *bufEnd, char delimiter,
+ int *taglen)
+{
+ char *bufp = bufStart;
+ char *tag;
+
+ /* Get start of tag */
+ while (bufp < bufEnd) {
+ if (*bufp == ' ') /* skip blanks */
+ while ((*bufp == ' ') && (bufp++ < bufEnd));
+ else if (*bufp == '#') { /* skip comment */
+ while ((*bufp != '\n') && (bufp++ < bufEnd));
+ bufp++;
+ } else if (*bufp == '\n') /* skip newline */
+ bufp++;
+ else if (*bufp == '\t') /* skip tabs */
+ bufp++;
+ else
+ break;
+ }
+ if (bufp < bufEnd)
+ tag = bufp;
+ else
+ return NULL;
+
+ /* Get tag */
+ *taglen = 0;
+ while ((bufp < bufEnd) && (*taglen == 0)) {
+ if ((*bufp == delimiter) || (*bufp == '\n'))
+ *taglen = bufp - tag;
+ bufp++;
+ }
+ if (*taglen == 0) /* Didn't find end delimiter */
+ tag = NULL;
+ return tag;
+}
+
+static int verify_config_parm(struct evm_xattr_config *test_p,
+ char *parmp, char *parmVal,
+ char **parmValEnd)
+{
+ unsigned int parmBase = 10;
+ int error = 0;
+
+ if (memcmp(parmp, "-cache", 6) == 0) {
+ test_p->xattr_refresh = simple_strtol(parmVal,
+ parmValEnd,
+ parmBase);
+ } else if (memcmp(parmp, "-expire", 7) == 0) {
+ test_p->xattr_expire = simple_strtol(parmVal,
+ parmValEnd, parmBase);
+ } else if (memcmp(parmp, "-type", 5) == 0) {
+ if ((memcmp(parmVal, "enum", 3) == 0)
+ || (memcmp(parmVal, "ENUM", 3) == 0))
+ test_p->xattr_type = EVM_TYPE_ENUM;
+ else if ((memcmp(parmVal, "md5", 3) == 0)
+ || (memcmp(parmVal, "MD5", 3) == 0))
+ test_p->xattr_type = EVM_TYPE_MD5;
+ else if ((memcmp(parmVal, "sha1", 3) == 0)
+ || (memcmp(parmVal, "SHA1", 3) == 0))
+ test_p->xattr_type = EVM_TYPE_SHA1;
+ else if ((memcmp(parmVal, "string", 6) == 0)
+ || (memcmp(parmVal, "STRING", 6) == 0))
+ test_p->xattr_type = EVM_TYPE_STRING;
+ else {
+ error = -EINVAL;
+ dprintk(EVM_BASE, "%s: %s unknown type %s\n",
+ __FUNCTION__, test_p->xattr_name, parmVal);
+ }
+ }
+ return error;
+}
+
+struct evm_xattr_config *evm_parse_config(char *data,
+ unsigned long datalen,
+ int *datasize)
+{
+ char *datap, *dataend;
+
+ struct evm_xattr_config *evm_test_data = NULL;
+ int evm_test_datasize = 0;
+
+ char *xattrParms[] =
+ { "xattr", "-cache", "-expire", "-type", "-log" };
+ char **parmp, **parmend;
+ char *tag, *parmVal, *parmValEnd;
+ int taglen;
+ struct evm_xattr_config *test_p = NULL;
+
+ evm_test_datasize = 0;
+
+ /* get number of extended attribute definitions */
+ datap = data;
+ dataend = data + datalen;
+
+ while ((tag = get_tag(datap, dataend, ' ', &taglen)) != NULL) {
+ datap = tag == NULL ? dataend : tag + taglen;
+ if ((strncmp(tag, *xattrParms, taglen)) == 0) {
+ evm_test_datasize++;
+ }
+ }
+ *datasize = evm_test_datasize;
+
+ /* Allocate memory for extended attributes */
+ datap = data;
+ evm_test_data = (struct evm_xattr_config *)
+ kmalloc(evm_test_datasize * sizeof(struct evm_xattr_config),
+ GFP_KERNEL);
+ if (!evm_test_data)
+ return NULL;
+
+ parmp = xattrParms;
+ parmend = xattrParms + sizeof xattrParms / sizeof xattrParms[0];
+
+ /* Get the extended attributes and their parameters */
+ while ((tag = get_tag(datap, dataend, ' ', &taglen)) != NULL) {
+ datap = tag == NULL ? dataend : tag + taglen;
+ if ((memcmp(tag, *xattrParms, taglen)) == 0) {
+ if (test_p == NULL)
+ test_p = evm_test_data;
+ else
+ test_p++;
+ parmVal = get_tag(datap, dataend, ' ', &taglen);
+ memset(test_p->xattr_name, 0,
+ sizeof test_p->xattr_name);
+ memcpy(test_p->xattr_name, parmVal, taglen);
+ continue;
+ }
+ if (!test_p)
+ continue;
+
+ for (parmp = xattrParms; parmp < parmend; parmp++) {
+ if ((memcmp(tag, *parmp, taglen)) == 0) {
+ parmVal =
+ get_tag(datap, dataend, ' ', &taglen);
+ if (!parmVal)
+ datap = dataend;
+ else if (verify_config_parm(test_p, *parmp,
+ parmVal, &parmValEnd) < 0) {
+ if (!evm_test_data) {
+ kfree(evm_test_data);
+ evm_test_data = NULL;
+ }
+ return evm_test_data;
+ } else
+ datap = parmVal + taglen;
+ break;
+ }
+ }
+ }
+
+ return evm_test_data;
+}
+
+/*
+ * Set up the hard coded EVM configuration defaults. To modify the default
+ * configuration, update the securityfs - /sys/kernel/security/evm/config.
+ */
+int evm_init_config(void)
+{
+ int error = 0;
+
+ evm_default_datasize =
+ sizeof(evm_default_data) / sizeof(struct evm_xattr_config);
+ if ((error = evm_update_config(evm_default_data,
+ evm_default_datasize)) < 0) {
+ dprintk(EVM_BASE, "%s: invalid config file\n",
+ __FUNCTION__);
+ }
+ return error;
+}
+
+void evm_cleanup_config(struct evm_xattr_config *evm_data)
+{
+ if (!evm_data)
+ kfree(evm_data);
+}
diff -ruN linux-2.6.14-rc4-git1.orig/security/evm/evm_crypto.c
linux-2.6.14-rc4-git1.new/security/evm/evm_crypto.c
--- linux-2.6.14-rc4-git1.orig/security/evm/evm_crypto.c 1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.14-rc4-git1.new/security/evm/evm_crypto.c 2005-10-13 14:09:05.000000000 -0400
@@ -0,0 +1,299 @@
+/*
+ * evm_crypto.c
+
+ * Functions which calculate a hash/hmac using the TPM's
+ * Storage Root Key (SRK).
+ *
+ * Copyright (C) 2005 IBM Corporation
+ * Author: Mimi Zohar <zohar@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/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/namei.h>
+#include <linux/security.h>
+#include <linux/mount.h>
+#include <linux/crypto.h>
+#include <asm/scatterlist.h>
+#include <linux/mm.h>
+
+#include "evm.h"
+
+static struct semaphore evm_crypto_sem;
+static unsigned char tpmKey[128];
+static int tpmKeylen;
+
+#define RBUF_SIZE 1024 /* size of temporary buffer */
+
+/*
+ * Calculate the MD5/SHA1 digest on a file using nameidatai
+ */
+int evm_calc_dhash(struct nameidata *nd, char *digest, int xattr_type)
+{
+ struct crypto_tfm *tfm = NULL;
+ struct scatterlist sg[1];
+ struct dentry *dentry;
+
+ char *rbuf;
+ int rbuf_len;
+ int offset;
+ struct file *file;
+
+ if (!nd)
+ return -1;
+
+ /* Load either MD5/SHA1, if not already loaded */
+ down(&evm_crypto_sem);
+ if (xattr_type == EVM_TYPE_MD5)
+ tfm = crypto_alloc_tfm("md5", 0);
+ else if (xattr_type == EVM_TYPE_SHA1)
+ tfm = crypto_alloc_tfm("sha1", 0);
+ if (!tfm) {
+ dprintk(EVM_CRYPTO,
+ "%s: failed to load MD5/SHA1 transfrom\n",
+ __FUNCTION__);
+ up(&evm_crypto_sem);
+ return -1;
+ }
+
+ dentry = nd->dentry;
+ file = dentry_open(dget(dentry), mntget(nd->mnt), O_RDONLY);
+ if (IS_ERR(file)) {
+ printk(KERN_INFO "%s: dentry_open failed\n", __FUNCTION__);
+ up(&evm_crypto_sem);
+ return -1;
+ }
+ if ((rbuf = (char *) kmalloc(RBUF_SIZE, GFP_KERNEL)) == NULL) {
+ up(&evm_crypto_sem);
+ return -ENOMEM;
+ }
+ crypto_digest_init(tfm);
+ for (offset = 0; offset < file->f_dentry->d_inode->i_size;
+ offset += RBUF_SIZE) {
+ rbuf_len = kernel_read(file, offset, rbuf, RBUF_SIZE);
+ if (rbuf_len <= 0)
+ break;
+ sg[0].page = virt_to_page(rbuf);
+ sg[0].offset = ((long) rbuf & ~PAGE_MASK);
+ sg[0].length = rbuf_len;
+
+ crypto_digest_update(tfm, sg, 1);
+ }
+ crypto_digest_final(tfm, digest);
+ crypto_free_tfm(tfm);
+
+ fput(file); /* clean up dentry_open() */
+ kfree(rbuf);
+ up(&evm_crypto_sem);
+ return 0;
+}
+
+EXPORT_SYMBOL_GPL(evm_calc_dhash);
+
+/*
+ * Calculate the MD5/SHA1 digest on a file
+ */
+int evm_calc_hash(struct file *file, char *digest, int xattr_type)
+{
+ struct crypto_tfm *tfm = NULL;
+ struct scatterlist sg[1];
+
+ char *rbuf;
+ int rbuf_len;
+ int offset = 0;
+
+ if (!file)
+ return -1;
+
+ /* Load either MD5/SHA1, if not already loaded */
+ down(&evm_crypto_sem);
+ if (xattr_type == EVM_TYPE_MD5)
+ tfm = crypto_alloc_tfm("md5", 0);
+ else if (xattr_type == EVM_TYPE_SHA1)
+ tfm = crypto_alloc_tfm("sha1", 0);
+
+ if (!tfm) {
+ dprintk(EVM_CRYPTO,
+ "%s: failed to load MD5/SHA1 transfrom\n",
+ __FUNCTION__);
+ up(&evm_crypto_sem);
+ return -1;
+ }
+
+ if ((rbuf = (char *) kmalloc(RBUF_SIZE, GFP_KERNEL)) == NULL) {
+ up(&evm_crypto_sem);
+ return -ENOMEM;
+ }
+ crypto_digest_init(tfm);
+ for (offset = 0; offset < file->f_dentry->d_inode->i_size;
+ offset += RBUF_SIZE) {
+ rbuf_len = kernel_read(file, offset, rbuf, RBUF_SIZE);
+ if (rbuf_len <= 0)
+ break;
+ sg[0].page = virt_to_page(rbuf);
+ sg[0].offset = ((long) rbuf & ~PAGE_MASK);
+ sg[0].length = rbuf_len;
+
+ crypto_digest_update(tfm, sg, 1);
+ }
+ crypto_digest_final(tfm, digest);
+ crypto_free_tfm(tfm);
+
+ kfree(rbuf);
+ up(&evm_crypto_sem);
+ return 0;
+}
+
+/*
+ * Calculate the SHA1 HMAC across all the extended attributes included
+ * in the HMAC policy as defined in /etc/evm.conf.
+ */
+int evm_calc_hmac(struct dentry *dentry, char *digest)
+{
+ ssize_t error;
+ struct crypto_tfm *tfm;
+ struct scatterlist sg[1];
+ const unsigned char *debug_name = "???";
+
+ struct evm_xattr_config *config_p, *config_data;
+ int xattrSize = 0;
+ char *xattrValue = NULL;
+ int config_datasize = 0; /* number of extended attributes */
+ time_t config_version;
+
+ if (!dentry || !dentry->d_inode || !dentry->d_inode->i_op
+ || !dentry->d_inode->i_op->getxattr)
+ return 0;
+
+ evm_get_config_info(&config_data, &config_datasize,
+ &config_version);
+
+ /* Load either SHA1, if not already loaded */
+ down(&evm_crypto_sem);
+ tfm = crypto_alloc_tfm("sha1", 0);
+ if (!tfm) {
+ dprintk(EVM_CRYPTO, "%s: failed to load SHA1 transfrom\n",
+ __FUNCTION__);
+ up(&evm_crypto_sem);
+ return -1;
+ }
+ if (dentry->d_name.name)
+ debug_name = dentry->d_name.name;
+
+ crypto_hmac_init(tfm, tpmKey, &tpmKeylen);
+
+ /* Get the HMAC policy extended attribute values */
+ for_each_xattr(config_p, config_data, config_datasize) {
+ int size;
+
+ if ((size = dentry->d_inode->i_op->getxattr(dentry,
+ config_p-> xattr_name, NULL, 0)) < 0)
+ continue;
+ if (size > xattrSize) {
+ if (xattrValue)
+ kfree(xattrValue);
+ xattrSize = size;
+ xattrValue = kmalloc(xattrSize + 1, GFP_KERNEL);
+ if (!xattrValue) {
+ up(&evm_crypto_sem);
+ return -ENOMEM;
+ }
+ }
+
+ if ((error = dentry->d_inode->i_op->getxattr(dentry,
+ config_p-> xattr_name, xattrValue,
+ xattrSize)) > 0) {
+ dprintk(EVM_CRYPTO, "%s: %s(%s) included\n",
+ __FUNCTION__, debug_name,
+ config_p->xattr_name);
+ sg[0].page = virt_to_page(xattrValue);
+ sg[0].offset = ((long) xattrValue & ~PAGE_MASK);
+ sg[0].length = error;
+ crypto_hmac_update(tfm, sg, 1);
+ } else {
+ if (strncmp(dentry->d_name.name, "/",
+ dentry->d_name.len) != 0)
+ dprintk(EVM_CRYPTO,
+ "%s: %s(%s) not found\n",
+ __FUNCTION__, debug_name,
+ config_p->xattr_name);
+ }
+ };
+
+ if (xattrValue)
+ kfree(xattrValue);
+ crypto_hmac_final(tfm, tpmKey, &tpmKeylen, digest);
+ crypto_free_tfm(tfm);
+ up(&evm_crypto_sem);
+ return 0;
+}
+
+/*
+ * Calculate the hmac, update it, and mark the cache dirty.
+ */
+int evm_update_hmac(struct dentry *dentry, int flags)
+{
+ char hmacVal[SHA1_DIGEST_SIZE];
+ int rc = -1;
+
+ if (!dentry || !dentry->d_inode || !dentry->d_inode->i_op
+ || !dentry->d_inode->i_op->setxattr)
+ return rc;
+
+ memset(hmacVal, 0, SHA1_DIGEST_SIZE);
+ if ((rc = evm_calc_hmac(dentry, hmacVal)) == 0) {
+ struct evm_isec_cache *isec;
+ struct inode *inode;
+
+ rc = dentry->d_inode->i_op->setxattr(dentry,
+ "security.evm.hmac", hmacVal,
+ SHA1_DIGEST_SIZE, flags);
+
+ inode = dentry->d_inode;
+ isec = evm_get_isec(inode->i_security);
+ if (isec)
+ isec->cache_flag = EVM_CACHE_DIRTY;
+ }
+ return rc;
+}
+EXPORT_SYMBOL_GPL(evm_update_hmac);
+
+/*
+ * Convert memory to a hex string
+ */
+int mem2hex(char *mem, char *buffer, int count)
+{
+ const char hexchars[] = "0123456789abcdef";
+ int i;
+ char *buf = buffer;
+ int ch;
+
+ memset(buffer, 0, count);
+ for (i = 0; i < count; i++) {
+ ch = (int) *mem++;
+ *buf++ = hexchars[(ch >> 4) & 0xf];
+ *buf++ = hexchars[ch & 0xf];
+ }
+ return (buf - buffer);
+}
+
+EXPORT_SYMBOL_GPL(mem2hex);
+
+/*
+ * Get the key from the TPM for the SHA1-HMAC
+ */
+int evm_init_crypto(void)
+{
+ init_MUTEX(&evm_crypto_sem);
+
+ return (tpm_get_key(tpmKey, &tpmKeylen));
+}
diff -ruN linux-2.6.14-rc4-git1.orig/security/evm/evm.h
linux-2.6.14-rc4-git1.new/security/evm/evm.h
--- linux-2.6.14-rc4-git1.orig/security/evm/evm.h 1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.14-rc4-git1.new/security/evm/evm.h 2005-10-13 14:09:05.000000000 -0400
@@ -0,0 +1,133 @@
+/*
+ * evm.h - extended verification module
+ *
+*/
+#include <linux/security.h>
+#include <linux/version.h>
+#include <linux/security-stack.h>
+
+#define CONFIG_SECURITY_STACKER 1
+
+#define SHA1_STR_SIZE 40 /* String value */
+#define SHA1_DIGEST_SIZE 20 /* SHA1 is 160-bits */
+#define MD5_STR_SIZE 32 /* String value */
+#define MD5_DIGEST_SIZE 16 /* MD5 is 128-bits */
+#define EVM_XATTR_SIZE 256 /* XATTR_SIZE_MAX causes kernel to crash */
+
+/*
+ * EVM configuration policies as defined in /etc/evm.config are
+ * stored in struct evm_config *evm_config_data.
+ */
+typedef enum {
+ EVM_TYPE_ENUM = 1, EVM_TYPE_HMAC, EVM_TYPE_MD5, EVM_TYPE_SHA1,
+ EVM_TYPE_STRING
+} evm_configtype_t;
+typedef enum {
+ EVM_VALUE_NOLABEL = '0', EVM_VALUE_FAIL = '1', EVM_VALUE_PASS = '2',
+ EVM_VALUE_EXPIRED = '3'
+} evm_configvalue_t;
+
+struct evm_xattr_config {
+ char xattr_name[XATTR_NAME_MAX + 1];
+ time_t xattr_refresh; /* cache refresh */
+ time_t xattr_expire; /* attribute expiration */
+ evm_configtype_t xattr_type;
+ int xattr_id; /* unique extended attribute ID */
+};
+
+/*
+ * The extended attribute verification results are memory cached in
+ * inode->i_security, preceeded by a header (structure evm_isec_cache),
+ * with the following format:
+ */
+typedef enum {
+ EVM_XATTR_NOLABEL = 0, EVM_XATTR_FAIL = 1, EVM_XATTR_EXPIRED = 2,
+ EVM_XATTR_PASS = 4, EVM_XATTR_UNVALIDATED = 8
+} evm_xattrflag_t;
+
+struct evm_isec_xattr {
+ int xattr_id; /* xattrID to xattr evm_xattr_config->xattrID */
+ evm_xattrflag_t xattr_flags; /* enumerated: fail, pass, expired */
+ char *xattr_name; /* xattrID to xattr evm_xattr_config->xattrName */
+};
+
+typedef enum {
+ EVM_CACHE_CLEAN = 0, EVM_CACHE_DIRTY = 01, EVM_CACHE_EOPNOTSUPP = 02
+} evm_cacheflag_t;
+
+struct evm_isec_cache {
+ struct security_list lsm_list;
+ evm_cacheflag_t cache_flag; /* cache status indication: clean|dirty*/
+ time_t cache_ver;/*Using a timestamp as a configuration version number*/
+ time_t cache_refresh;/* revalidate cached info after refresh time */
+ int cache_size; /* number of extended attributes */
+ int file_mutable;
+ char hash[SHA1_DIGEST_SIZE+1];
+};
+
+extern int evm_idx;
+#define EVM_LSM_ID 0x2ca18c41
+#define evm_get_isec(head) \
+ security_get_value_type(head, EVM_LSM_ID, struct evm_isec_cache,\
+ evm_idx);
+#define evm_set_isec(head, isec) \
+ security_set_value_type(head, EVM_LSM_ID, isec, evm_idx);
+#define evm_del_isec(head) \
+ security_del_value_type(head, EVM_LSM_ID, struct evm_isec_cache, \
+ evm_idx);
+
+/* semaphore for modifying the configuration file */
+extern struct semaphore evm_config_sem;
+
+extern int evm_init_config(void);
+extern void evm_init_mode(char *evm_mode, int *enforce, int *install);
+extern void evm_enable_lsm_hooks(void);
+extern struct evm_xattr_config *evm_parse_config(char *data,
+ unsigned long datalen, int *datasize);
+extern int evm_update_config(struct evm_xattr_config *evm_new_data,
+ int evm_new_datasize);
+extern void evm_cleanup_config(struct evm_xattr_config *evm_data);
+extern int evm_get_isec_size(void);
+
+extern int evm_get_config_info(struct evm_xattr_config **head, int *size,
+ time_t *version);
+#define for_each_xattr(ptr, head, size) \
+ for (ptr = head; ptr < (head + size); ptr++)
+
+extern int evm_inode_setxattr (struct dentry *d, char *name, void *value,
+ size_t size, int flags);
+extern void evm_inode_post_setxattr (struct dentry *d, char *name, void *value,
+ size_t size, int flags);
+extern ssize_t evm_verify_xattr(struct dentry *d, char *kVal, char *xattrName,
+ char *xattrVal, int xattrValSize);
+extern int evm_inode_alloc_security(struct inode *inode);
+extern void evm_inode_free_security(struct inode *inode);
+extern int evm_inode_permission(struct inode *inode, int mask,
+ struct nameidata *nd);
+
+extern int evm_file_permission(struct file *file, int mask);
+extern void evm_file_free(struct file *file);
+extern void evm_file_install_free(struct file *file);
+extern void evm_file_alloc(struct file *file);
+
+extern void evm_init_secfs(void);
+void evm_cleanup_secfs(void);
+
+extern int evm_init_crypto(void);
+extern int evm_update_hmac(struct dentry *dentry, int flags);
+extern int evm_calc_hmac(struct dentry *d, char *digest);
+extern int evm_calc_hash(struct file *file, char *digest, int xattrType);
+extern int evm_calc_dhash(struct nameidata *nd, char *digest, int xattrType);
+extern int mem2hex(char *mem, char *buffer, int count);
+extern int tpm_get_key(unsigned char key[], int *len);
+
+extern unsigned int evm_debug;
+extern unsigned int evm_install;
+enum evm_debug_level {
+ EVM_BASE = 1, EVM_CACHE = 2, EVM_XATTR = 4, EVM_CRYPTO=8
+} ;
+
+#define dprintk(level, format, a...) \
+ if (evm_debug & level) \
+ printk(KERN_INFO format, ##a)
+
diff -ruN linux-2.6.14-rc4-git1.orig/security/evm/evm_init.c
linux-2.6.14-rc4-git1.new/security/evm/evm_init.c
--- linux-2.6.14-rc4-git1.orig/security/evm/evm_init.c 1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.14-rc4-git1.new/security/evm/evm_init.c 2005-10-13 14:09:19.000000000 -0400
@@ -0,0 +1,348 @@
+/*
+ * EVM - Extended Verification Module
+ *
+ * Copyright (C) 2005 IBM Corporation
+ * Author: Mimi Zohar <zohar@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/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/namei.h>
+#include <linux/notifier.h>
+#include <linux/security.h>
+#include <linux/version.h>
+
+#include <linux/evm.h>
+#include "evm.h"
+
+unsigned int evm_debug = EVM_BASE;
+static char *evm_mode = "normal";
+unsigned int evm_install = 0; /* default: not install mode */
+static int evm_enforce = 1; /* enforcement enabled */
+static int secondary = 0;
+
+/*
+ * Replacing module_use with the global sandbox_avail,
+ * since we don't get MODULE_STATE_GOING notification,
+ * however using a global creates a dependency on EVM for other modules.
+ */
+int sandbox_avail = 0;
+
+/*
+ * Rigid enforcement of signed executables,
+ * when a sandboxing module is not present.
+ */
+static int evm_bprm_check_security(struct linux_binprm *bprm)
+{
+ struct nameidata nd;
+ struct dentry *dentry = NULL;
+ ssize_t error = 0;
+
+ error = path_lookup(bprm->filename, LOOKUP_FOLLOW, &nd);
+ if (error)
+ return error;
+
+ dentry = nd.dentry;
+ error = -EOPNOTSUPP;
+ if (!dentry || !dentry->d_inode || !dentry->d_inode->i_op
+ || !dentry->d_inode->i_op->getxattr)
+ return 0;
+
+ /* Possible return codes:
+ * EVM_PERMIT, EVM_SANDBOX, EVM_DENY, EVM_NOLABEL
+ */
+ if (!sandbox_avail) {
+ evm_status rc;
+
+ error = 0;
+ rc = evm_analyze_cacheinfo(dentry);
+ if (rc < 0) {
+ switch (rc) {
+ case (EVM_DENY):
+ error = -EACCES;
+ dprintk(EVM_BASE, "%s: %s - deny\n",
+ __FUNCTION__, bprm->filename);
+ break;
+ case (EVM_SANDBOX):
+ error = -EACCES;
+ dprintk(EVM_BASE,
+ "%s: %s - can't sandbox \n",
+ __FUNCTION__, bprm->filename);
+ break;
+ case (EVM_NOLABEL):
+ error = 0;
+ dprintk(EVM_BASE,
+ "%s: %s - no hmac label \n",
+ __FUNCTION__, bprm->filename);
+ break;
+ default:
+ dprintk(EVM_BASE,
+ "%s: %s - analyze rc=%d\n",
+ __FUNCTION__, bprm->filename, rc);
+ }
+ } else
+ dprintk(EVM_CACHE, " %s: %s - succeeded\n",
+ __FUNCTION__, bprm->filename);
+ } else {
+ dprintk(EVM_CACHE, "%s: %s sandbox_avail\n",
+ __FUNCTION__, bprm->filename);
+ error = 0;
+ }
+
+ path_release(&nd);
+ return (evm_enforce == 1) ? error : 0;
+}
+
+/*
+ * Display the cached values (FAIL=1, EXPIRED=2, PASS=4, UNVALIDATED=8)
+ */
+static void evm_display_xattrflags(const unsigned char *name,
+ char *xattr_name,
+ evm_xattrflag_t xattr_flags)
+{
+ if (!name || !xattr_name)
+ return;
+
+ switch (xattr_flags) {
+ case 0:
+ dprintk(EVM_CACHE, "%s: '%s' %s %s\n",
+ __FUNCTION__, name, xattr_name, "nolabel");
+ break;
+ case 1:
+ dprintk(EVM_CACHE, "%s: '%s' %s %s\n",
+ __FUNCTION__, name, xattr_name, "failed");
+ break;
+ case 2:
+ dprintk(EVM_CACHE, "%s: '%s' %s %s\n",
+ __FUNCTION__, name, xattr_name, "expired");
+ break;
+ case 3:
+ dprintk(EVM_CACHE, "%s: '%s' %s %s\n",
+ __FUNCTION__, name, xattr_name,
+ "failed | expired ");
+ break;
+ case 4: /* PASS */
+ break;
+ case 5:
+ dprintk(EVM_CACHE, "%s: '%s' %s %s\n",
+ __FUNCTION__, name, xattr_name,
+ "passed | nolabel");
+ break;
+ case 6:
+ dprintk(EVM_CACHE, "%s: '%s' %s %s\n",
+ __FUNCTION__, name, xattr_name,
+ "passed | expired");
+ break;
+ case 7:
+ dprintk(EVM_CACHE, "%s: '%s' %s %s\n",
+ __FUNCTION__, name, xattr_name,
+ "passed | nolabel | " " expired");
+ break;
+ case 8:
+ case 9:
+ case 10:
+ dprintk(EVM_CACHE, "%s: '%s' %s %s\n",
+ __FUNCTION__, name, xattr_name, "unvalidated");
+ break;
+ default:
+ dprintk(EVM_CACHE, "%s: '%s' %s %s\n",
+ __FUNCTION__, name, xattr_name, "weird value");
+ break;
+ }
+ return;
+}
+
+/*
+ * Simplistic analyzation of the cache results.
+ * For now, base the results on the attribute with the least cached value.
+ * (FAIL = 1, EXPIRED = 2, PASS = 4)
+ * Replace this simplistic analysis, with one based on a policy definition.
+ *
+ * Possible return codes: EVM_PERMIT, EVM_DENY, EVM_SANDBOX, EVM_NOLABEL
+ */
+evm_status evm_analyze_cacheinfo(struct dentry * dentry)
+{
+ struct inode *inode = NULL;
+ struct evm_isec_cache *isec;
+ struct evm_isec_xattr *istatus, *istatus_p;
+ evm_status error = EVM_PERMIT;
+ int xattrFlags = 0;
+
+
+ if (!dentry || !(dentry->d_inode))
+ return EVM_PERMIT;
+
+ inode = dentry->d_inode;
+ isec = evm_get_isec(inode->i_security);
+ if (!isec)
+ return EVM_PERMIT;
+
+ istatus = (void *) isec + sizeof(struct evm_isec_cache);
+ if ((istatus->xattr_id == 0) && (istatus->xattr_flags == 0))
+ return EVM_NOLABEL;
+ xattrFlags = istatus->xattr_flags;
+
+ /* Consolidate all of the extended attribute flags
+ possible xattrFlags: FAIL=1, EXPIRED=2, PASS=4, UNVALIDATED=8 */
+ for_each_xattr(istatus_p, istatus + 1, isec->cache_size) {
+ if (istatus_p->xattr_id == 0)
+ continue; /* Ignore missing extended attribute */
+ xattrFlags |= istatus_p->xattr_flags;
+ evm_display_xattrflags(dentry->d_name.name,
+ istatus_p->xattr_name,
+ istatus_p->xattr_flags);
+ }
+ if ((EVM_XATTR_FAIL == (xattrFlags & EVM_XATTR_FAIL))
+ || (isec->cache_flag != EVM_CACHE_CLEAN))
+ error = EVM_DENY;
+ else if (EVM_XATTR_EXPIRED == (xattrFlags & EVM_XATTR_EXPIRED))
+ // error = EVM_SANDBOX;
+ error = EVM_PERMIT;
+ else
+ error = EVM_PERMIT;
+ return error;
+}
+
+/*
+ * Dummy getprocattr defined here, otherwise
+ * either security_getprocattr() or dummy_getprocattr() return -EINVAL,
+ * preventing other getprocattr definitions from being executed.
+ */
+static inline int evm_getprocattr(struct task_struct *p,
+ char *name, void *value, size_t size)
+{
+ return 0;
+}
+
+static struct security_operations evm_security_ops = {
+ .bprm_check_security = evm_bprm_check_security,
+ .inode_setxattr = evm_inode_setxattr,
+ .inode_post_setxattr = evm_inode_post_setxattr,
+ .inode_free_security = evm_inode_free_security,
+ .inode_permission = evm_inode_permission,
+ .file_permission = evm_file_permission,
+ .file_free_security = evm_file_free,
+ .getprocattr = evm_getprocattr
+};
+
+static struct security_operations evm_install_ops = {
+ .inode_setxattr = evm_inode_setxattr,
+ .inode_post_setxattr = evm_inode_post_setxattr,
+ .file_free_security = evm_file_install_free,
+ .getprocattr = evm_getprocattr
+};
+
+void evm_init_mode(char *evm_mode, int *enforce, int *install)
+{
+ if (memcmp(evm_mode, "INSTALL", 7) == 0) {
+ *install = 1;
+ *enforce = 0;
+ } else if (memcmp(evm_mode, "LOGONLY", 7) == 0)
+ *enforce = 0;
+}
+
+void evm_enable_lsm_hooks(void)
+{
+ evm_idx = 1;
+ if (register_security(evm_install
+ ? &evm_install_ops : &evm_security_ops,
+ &evm_idx)) {
+ if (mod_reg_security("evm", evm_install
+ ? &evm_install_ops :
+ &evm_security_ops, &evm_idx)) {
+ printk(KERN_INFO "%s: security hooks registration "
+ "failed\n", __FUNCTION__);
+ return;
+ }
+ secondary = 1;
+ }
+ dprintk(EVM_BASE, "%s: registered security hooks (evm_idx = %d)\n",
+ __FUNCTION__, evm_idx);
+}
+
+static int __init init_evm(void)
+{
+ int error;
+
+ if ((error = evm_init_crypto()) < 0) {
+ dprintk(EVM_BASE, "%s: crypto initialization failed \n",
+ __FUNCTION__);
+ return error;
+ } else
+ dprintk(EVM_BASE, "%s: crypto initialized\n",
+ __FUNCTION__);
+
+ evm_init_mode(evm_mode, &evm_enforce, &evm_install);
+ if (evm_install) {
+ dprintk(EVM_BASE, "%s: running install mode\n",
+ __FUNCTION__);
+ } else if (!evm_enforce)
+ dprintk(EVM_BASE, "%s: running logonly mode\n",
+ __FUNCTION__);
+
+ if ((error = evm_init_config()) < 0) {
+ dprintk(EVM_BASE,
+ "%s: default config initialization failed\n",
+ __FUNCTION__);
+ return error;
+ } else
+ dprintk(EVM_BASE,
+ "%s: default configuration initialized\n",
+ __FUNCTION__);
+ evm_init_secfs();
+
+ return error;
+}
+
+static void __exit cleanup_evm(void)
+{
+ struct evm_xattr_config *config_data;
+ int config_datasize = 0; /* number of extended attributes */
+ time_t config_version;
+
+ if (secondary) {
+ if (mod_unreg_security("evm",
+ evm_install ? &evm_install_ops :
+ &evm_security_ops))
+ dprintk(EVM_BASE,
+ "%s: unregistering security hooks "
+ "failed \n", __FUNCTION__);
+ } else
+ if (unregister_security
+ (evm_install ? &evm_install_ops : &evm_security_ops)) {
+ dprintk(EVM_BASE,
+ "%s: unregistering security hooks failed\n",
+ __FUNCTION__);
+ }
+ evm_cleanup_secfs();
+ evm_get_config_info(&config_data, &config_datasize,
+ &config_version);
+ evm_cleanup_config(config_data);
+
+ dprintk(EVM_BASE, "%s: %s completed\n", __FUNCTION__,
+ evm_install ? "install mode" : "");
+}
+
+module_init(init_evm);
+module_exit(cleanup_evm);
+
+EXPORT_SYMBOL_GPL(evm_analyze_cacheinfo);
+EXPORT_SYMBOL_GPL(sandbox_avail);
+EXPORT_SYMBOL_GPL(evm_idx);
+
+module_param(evm_mode, charp, 0444);
+MODULE_PARM_DESC(evm_mode, "EVM mode: NORMAL, LOGONLY, INSTALL");
+
+module_param(evm_debug, uint, 0);
+MODULE_PARM_DESC(evm_debug, "EVM debug level");
+
+MODULE_DESCRIPTION("Extended Verification Module");
+MODULE_LICENSE("GPL");
diff -ruN linux-2.6.14-rc4-git1.orig/security/evm/evm_secfs.c
linux-2.6.14-rc4-git1.new/security/evm/evm_secfs.c
--- linux-2.6.14-rc4-git1.orig/security/evm/evm_secfs.c 1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.14-rc4-git1.new/security/evm/evm_secfs.c 2005-10-13 14:09:05.000000000 -0400
@@ -0,0 +1,163 @@
+/*
+ * evm securityfs support for config and debug control
+ *
+ * Copyright (C) 2005 IBM Corporation
+ * Author: Mimi Zohar <zohar@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 <asm/uaccess.h>
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/security.h>
+#include "evm.h"
+
+static struct dentry *evm_dir, *evm_config;
+static struct dentry *evm_debug_dir, *evm_cache, *evm_crypto, *evm_xattr;
+
+static int evm_open_debug(struct inode *inode, struct file *file)
+{
+ if (inode->u.generic_ip)
+ file->private_data = inode->u.generic_ip;
+ return 0;
+}
+
+static void evm_disable_config(void)
+{
+ securityfs_remove(evm_config);
+}
+
+static ssize_t evm_read_debug(struct file *file, char __user * buf,
+ size_t buflen, loff_t * ppos)
+{
+ ssize_t len;
+ char *page;
+
+ page = (char *) __get_free_page(GFP_KERNEL);
+ if (!page)
+ return -ENOMEM;
+
+ if (strcmp(file->private_data, "cache") == 0)
+ len = sprintf(page, "evm_debug: cache: %s\n",
+ ((evm_debug & EVM_CACHE) ==
+ EVM_CACHE) ? "ON" : "OFF");
+ else if (strcmp(file->private_data, "xattr") == 0)
+ len = sprintf(page, "evm_debug: xattr: %s\n",
+ ((evm_debug & EVM_XATTR) ==
+ EVM_XATTR) ? "ON" : "OFF");
+ else if (strcmp(file->private_data, "crypto") == 0)
+ len = sprintf(page, "evm_debug: crypto: %s\n",
+ ((evm_debug & EVM_CRYPTO) ==
+ EVM_CRYPTO) ? "ON" : "OFF");
+ else
+ len = sprintf(page, "unknown evm_debug option\n");
+
+ len = simple_read_from_buffer(buf, buflen, ppos, page, len);
+ free_page((unsigned long) page);
+ return len;
+}
+
+static ssize_t evm_write_debug(struct file *file, const char __user * buf,
+ size_t buflen, loff_t * ppos)
+{
+ char flag;
+
+ if (copy_from_user(&flag, buf, 1))
+ return -EFAULT;
+
+ if (strcmp(file->private_data, "cache") == 0)
+ evm_debug = (flag == '0') ? evm_debug & ~EVM_CACHE :
+ evm_debug | EVM_CACHE;
+ else if (strcmp(file->private_data, "xattr") == 0)
+ evm_debug = (flag == '0') ? evm_debug & ~EVM_XATTR :
+ evm_debug | EVM_XATTR;
+ else if (strcmp(file->private_data, "crypto") == 0)
+ evm_debug = (flag == '0') ? evm_debug & ~EVM_CRYPTO :
+ evm_debug | EVM_CRYPTO;
+ return buflen;
+}
+
+static ssize_t evm_write_secfs(struct file *file, const char __user * buf,
+ size_t buflen, loff_t * ppos)
+{
+ size_t rc = 0, datalen;
+ char *data;
+
+ struct evm_xattr_config *evm_new_data = NULL;
+ int evm_new_datasize = 0;
+
+ datalen = buflen >= 4095 ? 4095 : buflen;
+
+ if ((data = (char *) kmalloc(datalen + 1, GFP_KERNEL)) == NULL)
+ rc = -ENOMEM;
+
+ if (copy_from_user(data, buf, datalen)) {
+ kfree(data);
+ return -EFAULT;
+ }
+
+ rc = datalen;
+ *(data + datalen) = ' ';
+ evm_new_data = evm_parse_config(data, datalen, &evm_new_datasize);
+ if (!evm_new_data
+ || ((evm_update_config(evm_new_data, evm_new_datasize)) < 0)) {
+ printk(KERN_INFO "%s: invalid config file\n",
+ __FUNCTION__);
+ rc = -ENOMEM;
+ } else {
+ evm_disable_config();
+ evm_enable_lsm_hooks();
+ }
+
+ if (!data)
+ kfree(data);
+ return rc;
+}
+
+static struct file_operations evm_ops = {
+ .write = evm_write_secfs,
+};
+
+static struct file_operations evm_debug_ops = {
+ .read = evm_read_debug,
+ .write = evm_write_debug,
+ .open = evm_open_debug,
+};
+
+void evm_init_secfs(void)
+{
+ if ((evm_dir = securityfs_create_dir("evm", NULL)) == NULL)
+ return;
+ evm_config = securityfs_create_file("config", S_IRUSR | S_IRGRP
+ | S_IWUSR, evm_dir, NULL, &evm_ops);
+
+ if ((evm_debug_dir =
+ securityfs_create_dir("debug", evm_dir)) == NULL)
+ return;
+ evm_cache = securityfs_create_file("cache", S_IRUSR | S_IRGRP,
+ evm_debug_dir, "cache",
+ &evm_debug_ops);
+ evm_crypto =
+ securityfs_create_file("crypto", S_IRUSR | S_IRGRP,
+ evm_debug_dir, "crypto",
+ &evm_debug_ops);
+ evm_xattr =
+ securityfs_create_file("xattr", S_IRUSR | S_IRGRP,
+ evm_debug_dir, "xattr", &evm_debug_ops);
+ return;
+}
+
+
+void evm_cleanup_secfs(void)
+{
+ securityfs_remove(evm_xattr);
+ securityfs_remove(evm_crypto);
+ securityfs_remove(evm_cache);
+ securityfs_remove(evm_debug_dir);
+ securityfs_remove(evm_dir);
+}
diff -ruN linux-2.6.14-rc4-git1.orig/security/evm/evm_xattr.c
linux-2.6.14-rc4-git1.new/security/evm/evm_xattr.c
--- linux-2.6.14-rc4-git1.orig/security/evm/evm_xattr.c 1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.14-rc4-git1.new/security/evm/evm_xattr.c 2005-10-13 14:09:19.000000000 -0400
@@ -0,0 +1,236 @@
+/*
+ * evm_xattr.c
+
+ * Extended attribute functions to used compare, set, and validate
+ * syntax of an extended attribute.
+ *
+ * Copyright (C) 2005 IBM Corporation
+ * Author: Mimi Zohar <zohar@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/fs.h>
+#include <linux/xattr.h>
+#include <linux/evm.h>
+#include "evm.h"
+
+static unsigned int debug_level = EVM_XATTR | EVM_BASE;
+
+/*
+ * Compare an extended attribute value with a kernel value
+ */
+ssize_t evm_verify_xattr(struct dentry *dentry, char *kVal,
+ char *xattrName, char *xattrVal, int xattrValSize)
+{
+ ssize_t error;
+ const unsigned char *debug_name = "???";
+
+ if (!kVal || !xattrName || !xattrVal)
+ return -EINVAL;
+ if (!dentry || !dentry->d_inode || !dentry->d_inode->i_op
+ || !dentry->d_inode->i_op->getxattr)
+ return -EINVAL;
+
+ memset(xattrVal, 0, xattrValSize);
+ error =
+ dentry->d_inode->i_op->getxattr(dentry, xattrName, xattrVal,
+ xattrValSize);
+
+ if (dentry->d_name.name)
+ debug_name = dentry->d_name.name;
+
+ if (error > 0) {
+ if (memcmp(xattrVal, kVal, error - 2) != 0) {
+ error = -EPERM;
+ dprintk(debug_level,
+ "%s: %s(%s) verification failed\n",
+ __FUNCTION__, debug_name, xattrName);
+ } else {
+ error = 0;
+ dprintk(EVM_XATTR,
+ "%s: %s(%s) verification succeeded\n",
+ __FUNCTION__, debug_name, xattrName);
+ }
+ } else
+ dprintk(EVM_XATTR,
+ "%s: %s(%s) rc = %d no extended attribute\n",
+ __FUNCTION__, debug_name, xattrName, error);
+
+ return error;
+}
+
+static int evm_verify_xattrtype(evm_configtype_t type, void *value,
+ size_t size)
+{
+ int error = 0;
+
+ switch (type) {
+ case EVM_TYPE_SHA1:
+ if (size != SHA1_STR_SIZE + sizeof(time_t))
+ error = -EINVAL;
+ break;
+ case EVM_TYPE_MD5:
+ if (size != MD5_STR_SIZE + sizeof(time_t))
+ error = -EINVAL;
+ break;
+ case EVM_TYPE_ENUM:
+ break;
+ case EVM_TYPE_STRING:
+ if (size < sizeof(time_t))
+ error = -EINVAL;
+ break;
+ case EVM_TYPE_HMAC: /* EVM reserved type */
+ error = -EPERM;
+ break;
+ default:
+ error = -EINVAL;
+ break;
+ }
+ return error;
+}
+
+/*
+ * For a process to modify any "security.evm." extended attributes
+ * included in the "security.evm.hmac", as defined in /etc/evm.config,
+ * the process must have system admin capabilities. The ability to
+ * modify other extended attributes included in the hmac, are assumed
+ * to be controlled by the concerned kernel module's inode_setxattr().
+ * For example, slim permits a process to write the "security.slim.level"
+ * extended attribute up to the process' integrity level.
+
+ * All extended attributes included in the "security.evm.hmac", require
+ * a timestamp. As inode_setxattr() can't change the length of the
+ * attribute (size is not defined as a ptr), we're dependent on the user
+ * application supplying the timestamp. We only verify that it is not
+ * the future.
+
+ * Verify whatever else we can about the value, based on the extended
+ * attribute type.
+ */
+int evm_inode_setxattr(struct dentry *dentry, char *name, void *value,
+ size_t size, int flags)
+{
+ int error = 0;
+ struct evm_xattr_config *config_p;
+
+ struct timespec now;
+ time_t secVal;
+ const unsigned char *debug_name = "???";
+ struct evm_xattr_config *config_data;
+ int config_datasize = 0; /* number of extended attributes */
+ time_t config_version;
+
+ if (!dentry || !name || !value)
+ return -EINVAL;
+
+ now = CURRENT_TIME;
+ if (dentry && dentry->d_name.name)
+ debug_name = dentry->d_name.name;
+ evm_get_config_info(&config_data, &config_datasize,
+ &config_version);
+ for_each_xattr(config_p, config_data, config_datasize) {
+ if (memcmp(name, config_p->xattr_name, strlen(name)) == 0) {
+ secVal = ntohl(*((time_t *) value));
+ if ((!capable(CAP_SYS_ADMIN)) && (evm_install)) {
+ if ((memcmp(name, "security.", 9) != 0))
+ error = -EPERM;
+ } else if ((!capable(CAP_SYS_ADMIN))
+ && (memcmp(name, "security.evm.", 13) ==
+ 0))
+ error = -EPERM;
+ else if (secVal > now.tv_sec + 1) {
+ error = -EINVAL;
+ dprintk(EVM_XATTR, "timestamps fail %x %x\n",
+ (unsigned int)secVal,
+ (unsigned int)now.tv_sec);
+ }
+ if (error == 0)
+ error = evm_verify_xattrtype(config_p->
+ xattr_type, value, size);
+ break;
+ }
+ }
+ dprintk(EVM_XATTR, "%s: %s %s(%d - %s)\n", __FUNCTION__,
+ debug_name, error == 0 ? "succeeded" : "failed",
+ error, name);
+ return error;
+}
+
+
+/*
+ * After updating an extended attribute defined in /etc/evm.config,
+ * calculate and save the new hmac.
+ */
+void evm_inode_post_setxattr(struct dentry *dentry, char *name,
+ void *value, size_t size, int flags)
+{
+ struct evm_xattr_config *config_p;
+ const unsigned char *debug_name = "???";
+ struct evm_xattr_config *config_data;
+ int config_datasize = 0; /* number of extended attributes */
+ time_t config_version;
+
+ if (!dentry || !name || !value)
+ return;
+
+ if (!dentry->d_inode || !dentry->d_inode->i_op
+ || !dentry->d_inode->i_op->setxattr)
+ return;
+
+ if (dentry && dentry->d_name.name)
+ debug_name = dentry->d_name.name;
+
+ evm_get_config_info(&config_data, &config_datasize,
+ &config_version);
+ for_each_xattr(config_p, config_data, config_datasize) {
+ if (memcmp(name, config_p->xattr_name, strlen(name)) == 0) {
+ int rc;
+
+ rc = evm_update_hmac(dentry, flags);
+ dprintk(EVM_XATTR, "%s: %s hmac calculation %s\n",
+ __FUNCTION__, debug_name,
+ rc == 0 ? "succeeded" : "failed");
+ break;
+ }
+ }
+}
+
+/*
+ * Write an extended attribute included in /etc/evm.conf and update
+ * the security.evm.hmac.
+ */
+int evm_setxattr(struct dentry *dentry, char *name, void *value,
+ size_t size, int flags)
+{
+ struct evm_xattr_config *config_p;
+ int rc = -1;
+ struct evm_xattr_config *config_data;
+ int config_datasize = 0; /* number of extended attributes */
+ time_t config_version;
+
+ if (!dentry || !value || !name)
+ return -EINVAL;
+
+ if (!dentry->d_inode || !dentry->d_inode->i_op
+ || !dentry->d_inode->i_op->setxattr)
+ return rc;
+
+ evm_get_config_info(&config_data, &config_datasize, &config_version);
+ for_each_xattr(config_p, config_data, config_datasize) {
+ if (memcmp(name, config_p->xattr_name, strlen(name)) == 0) {
+ rc = dentry->d_inode->i_op->setxattr(dentry, name,
+ value, size, flags);
+ if (!rc)
+ rc = evm_update_hmac(dentry, flags);
+ break;
+ }
+ }
+ return rc;
+}
diff -ruN linux-2.6.14-rc4-git1.orig/security/evm/Kconfig
linux-2.6.14-rc4-git1.new/security/evm/Kconfig
--- linux-2.6.14-rc4-git1.orig/security/evm/Kconfig 1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.14-rc4-git1.new/security/evm/Kconfig 2005-10-13 14:09:05.000000000 -0400
@@ -0,0 +1,27 @@
+
+#menu "Trusted Linux Client"
+
+config SECURITY_EVM
+ tristate "EVM support"
+ depends on SECURITY && SECURITY_STACKER && TCG_TPM
+ default m
+ help
+ The Extended Verification Module implements a mandatory access control
+ based on an extensible set of extended attributes, as defined in
+ /etc/evm.conf, which are HMAC protected against modification
+ using the TPM's KERNEL ROOT KEY. Possible extended attributes
+ include authenticity, integrity, and revision level.
+
+ As a password is required to release the TPM's KERNEL ROOT KEY,
+ choose M here to compile as a module.
+
+config SECURITY_SLIM
+ tristate "SLIM support"
+ depends on SECURITY_EVM
+ help
+ The Simple Linux Integrity Module implements a modified low water-mark
+ mandatory access control integrity model.
+
+source security/evm/slim/ima/Kconfig
+
+#endmenu
diff -ruN linux-2.6.14-rc4-git1.orig/security/evm/Makefile
linux-2.6.14-rc4-git1.new/security/evm/Makefile
--- linux-2.6.14-rc4-git1.orig/security/evm/Makefile 1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.14-rc4-git1.new/security/evm/Makefile 2005-10-13 14:09:05.000000000 -0400
@@ -0,0 +1,7 @@
+#
+# Makefile for kernel Extended Verificaion Module (EVM)
+#
+obj-$(CONFIG_SECURITY_EVM) += evm.o
+obj-$(CONFIG_SECURITY_SLIM) += slim/
+
+evm-objs := evm_init.o evm_config.o evm_crypto.o evm_cache.o evm_xattr.o evm_secfs.o
diff -ruN linux-2.6.14-rc4-git1.orig/security/Kconfig linux-2.6.14-rc4-git1.new/security/Kconfig
--- linux-2.6.14-rc4-git1.orig/security/Kconfig 2005-10-13 13:46:10.000000000 -0400
+++ linux-2.6.14-rc4-git1.new/security/Kconfig 2005-10-13 14:09:05.000000000 -0400
@@ -129,5 +129,7 @@
this number, and this number must always be at least 1.
That one will be the shared slot for all LSMs to share.
+source "security/evm/Kconfig"
+
endmenu
diff -ruN linux-2.6.14-rc4-git1.orig/security/Makefile linux-2.6.14-rc4-git1.new/security/Makefile
--- linux-2.6.14-rc4-git1.orig/security/Makefile 2005-10-13 13:46:10.000000000 -0400
+++ linux-2.6.14-rc4-git1.new/security/Makefile 2005-10-13 14:09:05.000000000 -0400
@@ -4,6 +4,7 @@
obj-$(CONFIG_KEYS) += keys/
obj-$(CONFIG_SECURITY_STACKER) += stacker.o
+obj-$(CONFIG_SECURITY_EVM) += evm/
subdir-$(CONFIG_SECURITY_SELINUX) += selinux
# if we don't select a security model, use the default capabilities
diff -ruN linux-2.6.14-rc4-git1.orig/security/evm/slim/ima/ima_biosmeasurements.c
linux-2.6.14-rc4-git1.new/security/evm/slim/ima/ima_biosmeasurements.c
--- linux-2.6.14-rc4-git1.orig/security/evm/slim/ima/ima_biosmeasurements.c 1969-12-31
19:00:00.000000000 -0500
+++ linux-2.6.14-rc4-git1.new/security/evm/slim/ima/ima_biosmeasurements.c 2005-10-13
14:11:39.000000000 -0400
@@ -0,0 +1,563 @@
+/*
+ * ima_biosmeasurements.c
+ *
+ * Device driver to access the eventlog extended by the TCG BIOS of PC platform
+ *
+ * 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.
+ *
+ *
+ * Revision history:
+ *
+ * 2003/10/29 initial implementation Seiji Munetoh
+ * Adding experimental EventLog support code.
+ * Requirements
+ * - Kernel was configured to support ACPI (CONFIG_ACPI=y)
+ * Other ACPI config options should be 'n'.
+ *
+ * EventLog Initialization
+ * Post-O/S EventLog is stored in the ACPI table. At first we retrive this
+ * table with ACPI driver's help.
+ *
+ * Size of theEventLog
+ * ACPI table size is 0x10000(64K) bytes.
+ * Minimum size of event is 32 bytes, so this hold max 2048 events.
+ * For a while, this is enough size!?
+ * 'tcg_eventlog' holds the copy of ACPI table and new EventLogs.
+ *
+ * Stefan Berger 07/2005
+ * - adapt it to IMA
+ *
+ * Reiner Sailer 08/2005
+ * - major rewrite, move to seq file / security fs
+ *
+ */
+
+#include <linux/config.h>
+
+#ifdef CONFIG_ACPI
+
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/seq_file.h>
+#include <linux/fs.h>
+#include <asm/uaccess.h>
+#include <acpi/acpi.h>
+#include <acpi/actypes.h>
+#include <acpi/actbl.h>
+#include "ima.h"
+
+/*
+
+ TCPA/TCG Eventlog
+
+ EventLog is stored in ACPI table. Current linux ACPI driver
+ does not support the TCPA table under RSDT. and I/F functions
+
+ driver/acpi/tables/tbxfroot.c
+ *****************************************************************************
+ *
+ * FUNCTION: Acpi_get_firmware_table
+ *
+ * PARAMETERS: Signature - Any ACPI table signature
+ * Instance - the non zero instance of the table, allows
+ * support for multiple tables of the same type
+ * Flags - 0: Physical/Virtual support
+ * Ret_buffer - pointer to a structure containing a buffer to
+ * receive the table
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: This function is called to get an ACPI table. The caller
+ * supplies an Out_buffer large enough to contain the entire ACPI
+ * table. Upon completion
+ * the Out_buffer->Length field will indicate the number of bytes
+ * copied into the Out_buffer->Buf_ptr buffer. This table will be
+ * a complete table including the header.
+ *
+ ******************************************************************************/
+
+#define ACPI_TCPA_SIG "TCPA" /* 0x41504354 /'TCPA' */
+
+#define PACK __attribute__ ((__packed__))
+
+struct acpi_tcpa {
+ struct acpi_table_header hdr;
+ u16 reserved;
+ u32 log_max_len PACK;
+ u32 log_start_addr PACK;
+};
+
+struct tcpa_event {
+ u32 PCRIndex;
+ u32 eventType;
+ u8 PCRValue[20]; /* SHA1 */
+ u32 eventSize;
+ /* (eventSize) bytes of event data follows */
+};
+
+/* (Binary) bios measurement buffer */
+static void *tcg_eventlog = NULL;
+static void *tcg_eventlog_addr_limit = NULL; /* MAX */
+
+#define MAX_TEXT_EVENT 1000 /* Max event string length */
+
+static u32 be_decode_u32(u8 * buf)
+{
+ u32 val = buf[3];
+ val = (val << 8) | (u8) buf[2];
+ val = (val << 8) | (u8) buf[1];
+ val = (val << 8) | (u8) buf[0];
+ return val;
+}
+
+/* returns pointer to start of pos. entry of tcg log */
+static void *ima_bios_measurements_start(struct seq_file *m, loff_t * pos)
+{
+ loff_t i;
+ void *addr = tcg_eventlog;
+ struct tcpa_event *event;
+
+ /* read over *pos measurements */
+ for (i = 0; i < *pos; i++) {
+ event = (struct tcpa_event *) addr;
+
+ if ((addr + sizeof(struct tcpa_event)) <
+ tcg_eventlog_addr_limit) {
+ if (event->eventType == 0 && event->eventSize == 0)
+ return NULL;
+ addr += sizeof(struct tcpa_event) + event->eventSize;
+ }
+ }
+
+ /* now check if current entry is valid */
+ if ((addr + sizeof(struct tcpa_event)) >= tcg_eventlog_addr_limit)
+ return NULL;
+
+ event = (struct tcpa_event *) addr;
+
+ if ((event->eventType == 0 && event->eventSize == 0) ||
+ ((addr + sizeof(struct tcpa_event) + event->eventSize) >=
+ tcg_eventlog_addr_limit))
+ return NULL;
+
+ return addr;
+}
+
+static void *ima_bios_measurements_next(struct seq_file *m, void *v,
+ loff_t * pos)
+{
+ struct tcpa_event *event = (struct tcpa_event *) v;
+
+ v += sizeof(struct tcpa_event) + event->eventSize;
+
+ /* now check if current entry is valid */
+ if ((v + sizeof(struct tcpa_event)) >= tcg_eventlog_addr_limit)
+ return NULL;
+
+ event = (struct tcpa_event *) v;
+
+ if (event->eventType == 0 && event->eventSize == 0)
+ return NULL;
+
+ if ((event->eventType == 0 && event->eventSize == 0) ||
+ ((v + sizeof(struct tcpa_event) + event->eventSize) >=
+ tcg_eventlog_addr_limit))
+ return NULL;
+
+ (*pos)++;
+ return v;
+}
+
+static void ima_bios_measurements_stop(struct seq_file *m, void *v)
+{
+}
+
+static int ima_binary_bios_measurements_show(struct seq_file *m, void *v)
+{
+
+ char *eventname;
+ u32 event_id, event_data_size;
+ char data[4];
+ u32 help;
+ int i, len;
+ struct tcpa_event *event = (struct tcpa_event *) v;
+ unsigned char *event_entry =
+ (unsigned char *) (v + sizeof(struct tcpa_event));
+
+ eventname = kmalloc(MAX_TEXT_EVENT, GFP_KERNEL);
+ if (!eventname) {
+ printk(KERN_ERR "%s: ERROR - No Memory for event name\n ",
+ __func__);
+ return -EFAULT;
+ }
+
+ /* 1st: PCR used is in little-endian format (4 bytes) */
+ help = event->PCRIndex;
+ memcpy(data, &help, 4);
+ for (i = 0; i < 4; i++)
+ seq_putc(m, data[i]);
+
+ /* 2nd: SHA1 (20 bytes) */
+ for (i = 0; i < 20; i++)
+ seq_putc(m, event->PCRValue[i]);
+
+ /* 3rd: event type identifier (4 bytes) */
+ help = event->eventType;
+ memcpy(data, &help, 4);
+ for (i = 0; i < 4; i++)
+ seq_putc(m, data[i]);
+
+ len = 0;
+ /* gather event name */
+ if (event->eventType == 0) {
+ len += snprintf(eventname + len, MAX_TEXT_EVENT - len, "[]");
+ } else if (event->eventType == 1) {
+ len += snprintf(eventname + len, MAX_TEXT_EVENT - len, "[]");
+ } else if (event->eventType == 2) {
+ len += snprintf(eventname + len, MAX_TEXT_EVENT - len,
+ "[XML CONFIG]");
+ } else if (event->eventType == 3) {
+ len += snprintf(eventname + len, MAX_TEXT_EVENT - len,
+ "[NO_ACTION]");
+ } else if (event->eventType == 4) {
+ len += snprintf(eventname + len, MAX_TEXT_EVENT - len, "[");
+ if (MAX_TEXT_EVENT - len > event->eventSize) {
+ memcpy(eventname + len, event_entry,
+ event->eventSize);
+ len += event->eventSize;
+ }
+ len += snprintf(eventname + len, MAX_TEXT_EVENT - len, "]");
+ } else if (event->eventType == 5) {
+ len +=
+ snprintf(eventname + len, MAX_TEXT_EVENT - len, "[");
+ if (MAX_TEXT_EVENT - len > event->eventSize) {
+ memcpy(eventname + len, event_entry,
+ event->eventSize);
+ len += event->eventSize;
+ }
+ len += snprintf(eventname + len, MAX_TEXT_EVENT - len, "]");
+ } else if (event->eventType == 6) { /* PC Specific */
+ len += snprintf(eventname + len, MAX_TEXT_EVENT - len, "[");
+
+ event_id = be_decode_u32(event_entry);
+ event_data_size = be_decode_u32(&event_entry[4]);
+
+ /* ToDo Row data -> Base64 */
+
+ switch (event_id) {
+ case 1:
+ len += snprintf(eventname + len, MAX_TEXT_EVENT - len,
+ "SMBIOS");
+ break;
+ case 2:
+ len += snprintf(eventname + len, MAX_TEXT_EVENT - len,
+ "BIS Certificate");
+ break;
+ case 3:
+ len += snprintf(eventname + len, MAX_TEXT_EVENT - len,
+ "POST BIOS ");
+ for (i = 0; i < 20; i++) {
+ len += snprintf(eventname + len,
+ MAX_TEXT_EVENT - len, "%02X",
+ event_entry[8 + i]);
+ }
+ break;
+ case 4:
+ len += snprintf(eventname + len, MAX_TEXT_EVENT - len,
+ "ESCD ");
+ for (i = 0; i < 20; i++) {
+ len +=
+ snprintf(eventname + len,
+ MAX_TEXT_EVENT - len, "%02X",
+ event_entry[8 + i]);
+ }
+ break;
+ case 5:
+ len += snprintf(eventname + len, MAX_TEXT_EVENT - len,
+ "CMOS");
+ break;
+ case 6:
+ len += snprintf(eventname + len, MAX_TEXT_EVENT - len,
+ "NVRAM");
+ break;
+ case 7:
+ len += snprintf(eventname + len, MAX_TEXT_EVENT - len,
+ "Option ROM");
+ break;
+ case 8:
+ len += snprintf(eventname + len, MAX_TEXT_EVENT - len,
+ "Option ROM microcode");
+ break;
+
+ default:
+ len +=
+ snprintf(eventname + len, MAX_TEXT_EVENT - len,
+ "N/A");
+ break;
+ }
+ len += snprintf(eventname + len, MAX_TEXT_EVENT - len, "]");
+ } else {
+ len += snprintf(eventname + len, MAX_TEXT_EVENT - len,
+ "BINARY N/A]");
+ }
+
+ /* 4th: filename <= 255 + \'0' delimiter */
+ if (len > TCG_EVENT_NAME_LEN_MAX)
+ len = TCG_EVENT_NAME_LEN_MAX;
+
+ for (i = 0; i < len; i++)
+ seq_putc(m, eventname[i]);
+
+ /* 5th: delimiter */
+ seq_putc(m, '\0');
+
+ return 0;
+}
+
+static int ima_bios_measurements_release(struct inode *inode,
+ struct file *file)
+{
+ if (tcg_eventlog) {
+ kfree(tcg_eventlog);
+ tcg_eventlog = NULL;
+ }
+ return seq_release(inode, file);
+}
+
+static int ima_ascii_bios_measurements_show(struct seq_file *m, void *v)
+{
+ int len = 0;
+ int i;
+ char *eventname;
+ u32 event_id, event_data_size;
+
+ struct tcpa_event *event = (struct tcpa_event *) v;
+ unsigned char *event_entry =
+ (unsigned char *) (v + sizeof(struct tcpa_event));
+
+ eventname = kmalloc(MAX_TEXT_EVENT, GFP_KERNEL);
+ if (!eventname) {
+ printk(KERN_ERR "%s: ERROR - No Memory for event name\n ",
+ __func__);
+ return -EFAULT;
+ }
+
+ seq_printf(m, "%2d ", event->PCRIndex);
+
+ /* 2nd: SHA1 */
+ for (i = 0; i < 20; i++)
+ seq_printf(m, "%02x", event->PCRValue[i]);
+
+ /* 3rd: event type identifier */
+ seq_printf(m, " %02x", event->eventType);
+
+ if (event->eventType == 0) {
+ len += snprintf(eventname + len, MAX_TEXT_EVENT - len, "[]");
+ } else if (event->eventType == 1) {
+ len += snprintf(eventname + len, MAX_TEXT_EVENT - len, "[]");
+ } else if (event->eventType == 2) {
+ len += snprintf(eventname + len, MAX_TEXT_EVENT - len,
+ "[XML CONFIG]");
+ } else if (event->eventType == 3) {
+ len += snprintf(eventname + len, MAX_TEXT_EVENT - len,
+ "[NO_ACTION]");
+ } else if (event->eventType == 4) {
+ len += snprintf(eventname + len, MAX_TEXT_EVENT - len, "[");
+ if (MAX_TEXT_EVENT - len > event->eventSize) {
+ memcpy(eventname + len, event_entry,
+ event->eventSize);
+ len += event->eventSize;
+ }
+ len += snprintf(eventname + len, MAX_TEXT_EVENT - len, "]");
+ } else if (event->eventType == 5) {
+ len += snprintf(eventname + len, MAX_TEXT_EVENT - len, "[");
+ if (MAX_TEXT_EVENT - len > event->eventSize) {
+ memcpy(eventname + len, event_entry,
+ event->eventSize);
+ len += event->eventSize;
+ }
+ len += snprintf(eventname + len, MAX_TEXT_EVENT - len, "]");
+ } else if (event->eventType == 6) { /* PC Specific */
+ len += snprintf(eventname + len, MAX_TEXT_EVENT - len, "[");
+
+ event_id = be_decode_u32(event_entry);
+ event_data_size = be_decode_u32(&event_entry[4]);
+
+ /* ToDo Row data -> Base64 */
+
+ switch (event_id) {
+ case 1:
+ len += snprintf(eventname + len, MAX_TEXT_EVENT - len,
+ "SMBIOS");
+ break;
+ case 2:
+ len += snprintf(eventname + len, MAX_TEXT_EVENT - len,
+ "BIS Certificate");
+ break;
+ case 3:
+ len += snprintf(eventname + len, MAX_TEXT_EVENT - len,
+ "POST BIOS ");
+ for (i = 0; i < 20; i++) {
+ len += snprintf(eventname + len,
+ MAX_TEXT_EVENT - len, "%02X",
+ event_entry[8 + i]);
+ }
+ break;
+ case 4:
+ len += snprintf(eventname + len, MAX_TEXT_EVENT - len,
+ "ESCD ");
+ for (i = 0; i < 20; i++) {
+ len += snprintf(eventname + len,
+ MAX_TEXT_EVENT - len, "%02X",
+ event_entry[8 + i]);
+ }
+ break;
+ case 5:
+ len += snprintf(eventname + len, MAX_TEXT_EVENT - len,
+ "CMOS");
+ break;
+ case 6:
+ len += snprintf(eventname + len, MAX_TEXT_EVENT - len,
+ "NVRAM");
+ break;
+ case 7:
+ len += snprintf(eventname + len, MAX_TEXT_EVENT - len,
+ "Option ROM");
+ break;
+ case 8:
+ len += snprintf(eventname + len, MAX_TEXT_EVENT - len,
+ "Option ROM microcode");
+ break;
+
+ default:
+ len += snprintf(eventname + len, MAX_TEXT_EVENT - len,
+ "N/A");
+ break;
+ }
+ len += snprintf(eventname + len, MAX_TEXT_EVENT - len, "]");
+ } else {
+ len += snprintf(eventname + len, MAX_TEXT_EVENT - len,
+ "BINARY N/A]");
+ }
+
+ /* 4th: eventname <= max + \'0' delimiter */
+ seq_printf(m, " %s\n", eventname);
+
+ return 0;
+}
+
+
+static struct seq_operations ima_ascii_b_measurments_seqops = {
+ .start = ima_bios_measurements_start,
+ .next = ima_bios_measurements_next,
+ .stop = ima_bios_measurements_stop,
+ .show = ima_ascii_bios_measurements_show
+};
+
+static struct seq_operations ima_binary_b_measurments_seqops = {
+ .start = ima_bios_measurements_start,
+ .next = ima_bios_measurements_next,
+ .stop = ima_bios_measurements_stop,
+ .show = ima_binary_bios_measurements_show
+};
+
+/* read binary bios log */
+static int read_log(void)
+{
+ struct acpi_tcpa *buff;
+ acpi_status status;
+ void *virt;
+
+ if (tcg_eventlog != NULL) {
+ printk(KERN_ERR
+ "%s: ERROR - Eventlog already initialized\n",
+ __func__);
+ return -EFAULT;
+ }
+
+ /* Find TCPA entry in RSDT (ACPI_LOGICAL_ADDRESSING) */
+ status = acpi_get_firmware_table(ACPI_TCPA_SIG, 1,
+ ACPI_LOGICAL_ADDRESSING,
+ (struct acpi_table_header **)
+ &buff);
+
+ if (ACPI_FAILURE(status)) {
+ printk(KERN_ERR "%s: ERROR - Could not get TCPA table\n",
+ __func__);
+ return -EIO;
+ }
+
+ if (buff->log_max_len == 0) {
+ printk(KERN_ERR "%s: ERROR - TCPA log area empty\n",
+ __func__);
+ return -EIO;
+ }
+
+ /* malloc EventLog space */
+ tcg_eventlog = kmalloc(buff->log_max_len, GFP_KERNEL);
+ if (!tcg_eventlog) {
+ printk
+ ("%s: ERROR - Not enough Memory for BIOS measurements\n",
+ __func__);
+ return -EFAULT;
+ }
+
+ tcg_eventlog_addr_limit = tcg_eventlog + buff->log_max_len;
+
+ acpi_os_map_memory(buff->log_start_addr, buff->log_max_len, &virt);
+
+ memcpy((void *) tcg_eventlog, virt, buff->log_max_len);
+
+ acpi_os_unmap_memory(virt, buff->log_max_len);
+ return 0;
+}
+
+static int ima_ascii_bios_measurements_open(struct inode *inode,
+ struct file *file)
+{
+ int err;
+ if ((err = read_log()))
+ return err;
+
+ /* now register seq file */
+ return seq_open(file, &ima_ascii_b_measurments_seqops);
+}
+
+struct file_operations ima_ascii_bios_measurements_ops = {
+ .open = ima_ascii_bios_measurements_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = ima_bios_measurements_release,
+};
+
+static int ima_binary_bios_measurements_open(struct inode *inode,
+ struct file *file)
+{
+ int err;
+
+ if ((err = read_log()))
+ return err;
+
+ /* now register seq file */
+ return seq_open(file, &ima_binary_b_measurments_seqops);
+}
+
+struct file_operations ima_binary_bios_measurements_ops = {
+ .open = ima_binary_bios_measurements_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = ima_bios_measurements_release,
+};
+
+EXPORT_SYMBOL_GPL(ima_ascii_bios_measurements_ops);
+EXPORT_SYMBOL_GPL(ima_binary_bios_measurements_ops);
+
+#endif
+/* CONFIG_ACPI */
diff -ruN linux-2.6.14-rc4-git1.orig/security/evm/slim/ima/ima_fs.c
linux-2.6.14-rc4-git1.new/security/evm/slim/ima/ima_fs.c
--- linux-2.6.14-rc4-git1.orig/security/evm/slim/ima/ima_fs.c 1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.14-rc4-git1.new/security/evm/slim/ima/ima_fs.c 2005-10-13 14:11:39.000000000 -0400
@@ -0,0 +1,315 @@
+/*
+ * Copyright (C) 2005 IBM Corporation
+ *
+ * Authors:
+ * Kylene Hall <kjhall@us.ibm.com>
+ *
+ * Reiner Sailer <sailer@us.ibm.com>
+ * Adaptation to RCU lists 07/15/05
+ * Including ascii runtime and boot measurements 08/15/05
+ *
+ * IBM Integrity Measurement Architecture.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2 of the
+ * License.
+ *
+ * File: ima_fs.c
+ * implemenents imafs for reporting measurement
+ * log and userspace measure requests
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/file.h>
+#include <linux/seq_file.h>
+#include <linux/parser.h>
+#include <linux/device.h>
+#include <asm/uaccess.h>
+
+#include "ima.h"
+
+#ifdef CONFIG_ACPI
+extern struct file_operations ima_ascii_bios_measurements_ops;
+#endif
+
+
+#define TMPBUFLEN 12
+static ssize_t ima_show_htable_value(char __user * buf, size_t count,
+ loff_t * ppos, atomic_t * val)
+{
+ char tmpbuf[TMPBUFLEN];
+ ssize_t len;
+
+ len = scnprintf(tmpbuf, TMPBUFLEN, "%i\n", atomic_read(val));
+ return simple_read_from_buffer(buf, count, ppos, tmpbuf, len);
+}
+
+static ssize_t ima_show_htable_call_count(struct file *filp,
+ char __user * buf,
+ size_t count, loff_t * ppos)
+{
+ return ima_show_htable_value(buf, count, ppos, &htable.call_count);
+}
+static struct file_operations ima_htable_call_count_ops = {
+ .read = ima_show_htable_call_count
+};
+
+static ssize_t ima_show_htable_violations(struct file *filp,
+ char __user * buf,
+ size_t count, loff_t * ppos)
+{
+ return ima_show_htable_value(buf, count, ppos, &htable.violations);
+}
+static struct file_operations ima_htable_violations_ops = {
+ .read = ima_show_htable_violations
+};
+
+static ssize_t ima_show_measurements_count(struct file *filp,
+ char __user * buf,
+ size_t count, loff_t * ppos)
+{
+ return ima_show_htable_value(buf, count, ppos, &htable.len);
+
+}
+static struct file_operations ima_measurements_count_ops = {
+ .read = ima_show_measurements_count
+};
+
+/* returns pointer to hlist_node */
+static void *ima_measurements_start(struct seq_file *m, loff_t * pos)
+{
+ struct list_head *lpos;
+ loff_t l = *pos;
+ /* we need a lock since pos could point beyond last element */
+ rcu_read_lock();
+ list_for_each_rcu(lpos, &ima_measurements) {
+ if (!l--) {
+ rcu_read_unlock();
+ return lpos;
+ }
+ }
+ rcu_read_unlock(); /* can we unlock before printing the contents ? */
+ return NULL;
+}
+
+static void *ima_measurements_next(struct seq_file *m, void *v,
+ loff_t * pos)
+{
+ /* lock protects when reading beyond last element
+ * against concurrent list-extension */
+ struct list_head *lpos = (struct list_head *) v;
+
+ rcu_read_lock();
+ lpos = rcu_dereference(lpos->next);
+ rcu_read_unlock();
+ (*pos)++;
+
+ return (lpos == &ima_measurements) ? NULL : lpos;
+}
+
+static void ima_measurements_stop(struct seq_file *m, void *v)
+{
+}
+
+
+/* print format: 32bit-le=pcr#||char[20]=digest||flags||filename||'\0'
+ * flags bits: 32-16 application flags, 15-3 kernel flags, 2-0 hook */
+static int ima_measurements_show(struct seq_file *m, void *v)
+{
+ /* the list never shrinks, so we don't need a lock here */
+ struct list_head *lpos = v;
+ struct queue_entry *qe;
+ struct measure_entry *e;
+ int filename_len;
+ int i;
+ u32 pcr = CONFIG_IMA_MEASURE_PCR_IDX;
+ char data[4];
+
+ /* get entry */
+ qe = list_entry(lpos, struct queue_entry, later);
+ if (qe == NULL)
+ return -1;
+
+ e = qe->entry;
+ if (e == NULL)
+ return -1;
+
+ /* PCR used is always the same in little-endian format */
+ memcpy(data, &pcr, 4);
+ for (i = 0; i < 4; i++)
+ seq_putc(m, data[i]);
+
+ /* 2nd: SHA1 */
+ for (i = 0; i < 20; i++)
+ seq_putc(m, e->digest[i]);
+
+ /* 3rd: flags */
+ memcpy(data, &e->measure_flags, 4);
+ for (i = 0; i < 4; i++)
+ seq_putc(m, data[i]);
+
+ /* 4th: filename + \'0' delimiter */
+ filename_len = strlen(e->file_name);
+ if (filename_len > TCG_EVENT_NAME_LEN_MAX)
+ filename_len = TCG_EVENT_NAME_LEN_MAX;
+
+ for (i = 0; i < filename_len; i++)
+ seq_putc(m, e->file_name[i]);
+
+ /* 5th: delimiter */
+ seq_putc(m, '\0');
+ return 0;
+}
+
+static struct seq_operations ima_measurments_seqops = {
+ .start = ima_measurements_start,
+ .next = ima_measurements_next,
+ .stop = ima_measurements_stop,
+ .show = ima_measurements_show
+};
+
+static int ima_measurements_open(struct inode *inode, struct file *file)
+{
+ return seq_open(file, &ima_measurments_seqops);
+}
+
+static struct file_operations ima_measurements_ops = {
+ .open = ima_measurements_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release,
+};
+
+
+/* print in ascii */
+static int ima_ascii_measurements_show(struct seq_file *m, void *v)
+{
+ /* the list never shrinks, so we don't need a lock here */
+ struct list_head *lpos = v;
+ struct queue_entry *qe;
+ struct measure_entry *e;
+ int i;
+
+ /* get entry */
+ qe = list_entry(lpos, struct queue_entry, later);
+ if (qe == NULL)
+ return -1;
+
+ e = qe->entry;
+ if (e == NULL)
+ return -1;
+
+ /* 1st: PCR used (config option) */
+ seq_printf(m, "%2d ", CONFIG_IMA_MEASURE_PCR_IDX);
+
+ /* 2nd: SHA1 */
+ for (i = 0; i < 20; i++)
+ seq_printf(m, "%02x", e->digest[i]);
+
+ /* 3th: filename <= max + \'0' delimiter */
+ seq_printf(m, " %s\n", e->file_name);
+
+ return 0;
+}
+
+static struct seq_operations ima_ascii_measurements_seqops = {
+ .start = ima_measurements_start,
+ .next = ima_measurements_next,
+ .stop = ima_measurements_stop,
+ .show = ima_ascii_measurements_show
+};
+
+static int ima_ascii_measurements_open(struct inode *inode,
+ struct file *file)
+{
+ return seq_open(file, &ima_ascii_measurements_seqops);
+}
+
+static struct file_operations ima_ascii_measurements_ops = {
+ .open = ima_ascii_measurements_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release,
+};
+
+
+void ima_fs_init(void)
+{
+ struct dentry *ima_dir, *binary_runtime_measurements,
+ *ascii_runtime_measurements, *runtime_measurements_count,
+ *binary_bios_measurements, *ascii_bios_measurements,
+ *call_count, *violations;
+
+ if ((ima_dir = securityfs_create_dir("ima", NULL)) == NULL)
+ return;
+
+ binary_runtime_measurements =
+ securityfs_create_file("binary_runtime_measurements",
+ S_IRUSR | S_IRGRP, ima_dir, NULL,
+ &ima_measurements_ops);
+ if (binary_runtime_measurements == NULL)
+ goto out6;
+
+ ascii_runtime_measurements =
+ securityfs_create_file("ascii_runtime_measurements",
+ S_IRUSR | S_IRGRP, ima_dir, NULL,
+ &ima_ascii_measurements_ops);
+ if (ascii_runtime_measurements == NULL)
+ goto out5;
+
+ runtime_measurements_count =
+ securityfs_create_file("runtime_measurements_count",
+ S_IRUSR | S_IRGRP, ima_dir, NULL,
+ &ima_measurements_count_ops);
+ if (runtime_measurements_count == NULL)
+ goto out4;
+#ifdef CONFIG_ACPI
+ binary_bios_measurements =
+ securityfs_create_file("binary_bios_measurements",
+ S_IRUSR | S_IRGRP, ima_dir, NULL,
+ &ima_binary_bios_measurements_ops);
+ if (binary_bios_measurements == NULL)
+ goto out3;
+
+ ascii_bios_measurements =
+ securityfs_create_file("ascii_bios_measurements",
+ S_IRUSR | S_IRGRP, ima_dir, NULL,
+ &ima_ascii_bios_measurements_ops);
+ if (ascii_bios_measurements == NULL)
+ goto out2;
+#endif
+ call_count =
+ securityfs_create_file("ima_call_count", S_IRUSR | S_IRGRP,
+ ima_dir, NULL,
+ &ima_htable_call_count_ops);
+ if (call_count == NULL)
+ goto out1;
+
+ violations =
+ securityfs_create_file("violations", S_IRUSR | S_IRGRP,
+ ima_dir, NULL,
+ &ima_htable_violations_ops);
+ if (violations == NULL)
+ goto out;
+ return;
+
+
+ out:
+ securityfs_remove(call_count);
+ out1:
+#ifdef CONFIG_ACPI
+ securityfs_remove(ascii_bios_measurements);
+ out2:
+ securityfs_remove(binary_bios_measurements);
+ out3:
+#endif
+ securityfs_remove(runtime_measurements_count);
+ out4:
+ securityfs_remove(ascii_runtime_measurements);
+ out5:
+ securityfs_remove(binary_runtime_measurements);
+ out6:
+ securityfs_remove(ima_dir);
+}
diff -ruN linux-2.6.14-rc4-git1.orig/security/evm/slim/ima/ima.h
linux-2.6.14-rc4-git1.new/security/evm/slim/ima/ima.h
--- linux-2.6.14-rc4-git1.orig/security/evm/slim/ima/ima.h 1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.14-rc4-git1.new/security/evm/slim/ima/ima.h 2005-10-13 14:11:39.000000000 -0400
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2005 IBM Corporation
+ *
+ * Authors:
+ * Reiner Sailer <sailer@watson.ibm.com>
+ *
+ * Maintained by: Reiner Sailer <sailer@watson.ibm.com>
+ *
+ * LSM IBM Integrity Measurement Architecture.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2 of the
+ * License.
+ *
+ * File: ima.h
+ * defs
+ */
+#ifndef __LINUX_IMA_H
+#define __LINUX_IMA_H
+
+#include <linux/types.h>
+#include <linux/fs.h>
+#include <linux/major.h>
+#include <linux/crypto.h>
+#include <linux/security.h>
+#include <linux/hash.h>
+#include <linux/tpm.h>
+
+#define ima_printk(level, format, arg...) \
+ printk(level "ima (%s): " format ,__func__, ## arg)
+
+#define ima_error(format, arg...) \
+ ima_printk(KERN_ERR, format, ## arg)
+
+#define ima_info(format, arg...) \
+ ima_printk(KERN_INFO, format, ## arg)
+
+/* if you can tolerate panic for the sake of attestation guarantees,
+ * then redefine IMA_PANIC to, panic (see INSTALL documentation) */
+#define IMA_PANIC \
+ ima_error
+
+/* digest size for IMA, fits SHA1 or MD5 */
+#define IMA_DIGEST_SIZE 20
+#define IMA_MEASURE_MODULE_NAME "IMA"
+#define TCG_EVENT_NAME_LEN_MAX 255
+
+/* file systems we won't measure */
+#define IMA_MAGIC 0x9999
+
+#define IMA_HASH_BITS 9
+#define MEASURE_HTABLE_SIZE (1 << IMA_HASH_BITS)
+#define HASH_KEY(digest) (hash_long( \
+ (unsigned long)(*digest), IMA_HASH_BITS));
+
+/* set during registering as lsm */
+extern unsigned char ima_terminating;
+extern int ima_used_chip;
+
+struct measure_entry {
+ u32 measure_flags;
+ u8 digest[IMA_DIGEST_SIZE]; /* sha1 or md5 measurement hash */
+ char file_name[TCG_EVENT_NAME_LEN_MAX+1]; /* name + \0 */
+};
+
+struct queue_entry {
+ struct hlist_node hnext; /* place in hash collision list */
+ struct list_head later; /* place in ima_measurements list */
+ struct measure_entry *entry;
+};
+
+extern struct list_head ima_measurements; /* list of all measurements */
+
+/*
+ * used to protect h_table and sha_table
+ */
+extern spinlock_t ima_queue_lock;
+
+struct h_table {
+ atomic_t len; /* number of stored measurements in the list */
+ atomic_t call_count; /* # measure requests (calls into ima) */
+ atomic_t changed_files; /* times dirty marked entry really changed */
+ atomic_t violations;
+ unsigned int max_htable_size;
+ struct hlist_head queue[MEASURE_HTABLE_SIZE];
+ atomic_t queue_len[MEASURE_HTABLE_SIZE];
+};
+extern struct h_table htable;
+
+extern struct file_operations ima_ascii_bios_measurements_ops;
+extern struct file_operations ima_ascii_bios_measurements_ops;
+
+/* should move into a kconfig somewhen */
+#define CONFIG_IMA_MEASURE_PCR_IDX 10
+
+/* protos */
+void invalidate_pcr(char *);
+int ima_add_measure_entry(struct measure_entry *entry);
+extern void ima_measure(const unsigned char *func, const unsigned char *name,
+ struct inode *inode, int mask,
+ char *hash, int metadata_len, char *metadata);
+extern int _ima_measure(const unsigned char *func, const unsigned char *name,
+ struct inode *inode, int mask, int hash_len,
+ char *hash, int metadata_len, char *metadata);
+extern struct file_operations ima_binary_bios_measurements_ops;
+extern void ima_fs_init(void);
+extern void create_htable(void);
+extern struct queue_entry *ima_lookup_digest_entry(u8 * digest);
+extern void ima_init(void);
+extern int _ima_init(void);
+
+/* TPM "Glue" definitions */
+
+#define IMA_TPM TPM_HW_TYPE<<16|TPM_ANY_NUM
+static inline void ima_extend(const u8* hash)
+{
+ if (!ima_used_chip)
+ return;
+
+ if ( tpm_pcr_extend( IMA_TPM, CONFIG_IMA_MEASURE_PCR_IDX, hash ) != 0 )
+ ima_error("Error Communicating to TPM chip\n");
+}
+
+static inline void ima_pcrread(int idx, u8* pcr, int pcr_size)
+{
+ if (!ima_used_chip)
+ return;
+
+ if ( tpm_pcr_read( IMA_TPM, idx, pcr, pcr_size ) != 0 ) {
+ ima_error("Error Communicating to TPM chip\n");
+ }
+}
+#endif
diff -ruN linux-2.6.14-rc4-git1.orig/security/evm/slim/ima/ima_init.c
linux-2.6.14-rc4-git1.new/security/evm/slim/ima/ima_init.c
--- linux-2.6.14-rc4-git1.orig/security/evm/slim/ima/ima_init.c 1969-12-31 19:00:00.000000000
-0500
+++ linux-2.6.14-rc4-git1.new/security/evm/slim/ima/ima_init.c 2005-10-13 14:11:39.000000000 -0400
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2005 IBM Corporation
+ *
+ * Authors:
+ * Reiner Sailer <sailer@watson.ibm.com>
+ *
+ * Contributions:
+ * Leendert van Doorn <leendert@watson.ibm.com>
+ *
+ * IBM Integrity Measurement Architecture for TLC/EVM/SLIM
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2 of the
+ * License.
+ *
+ * File: ima_init.c
+ * init functions to start up IBM IMA
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/linkage.h>
+#include <linux/time.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <asm/uaccess.h>
+#include <linux/file.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/crypto.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include "ima.h"
+
+/* name for boot aggregate entry */
+static char *boot_aggregate_name = "boot_aggregate";
+
+/* These identify the driver base version and may not be removed. */
+static const char version[] = "v5.5 08/15/2005";
+static const char illegal_pcr[20] =
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+int ima_used_chip = 0;
+
+static void ima_add_boot_aggregate(void)
+{
+ /* cumulative sha1 the first 8 tpm registers */
+ struct measure_entry *entry;
+ size_t count;
+ int err;
+
+ /* create new entry for boot aggregate */
+ entry = (struct measure_entry *)
+ kmalloc(sizeof(struct measure_entry), GFP_KERNEL);
+ if (entry == NULL) {
+ invalidate_pcr("error allocating new measurement entry");
+ return;
+ }
+ entry->measure_flags = 0;
+ memset(entry->digest, 0, 20);
+ if ((count = strlen(boot_aggregate_name)) > TCG_EVENT_NAME_LEN_MAX)
+ count = TCG_EVENT_NAME_LEN_MAX;
+ memcpy(entry->file_name, boot_aggregate_name, count);
+ entry->file_name[count] = '\0'; /* ez-print */
+ if (ima_used_chip) {
+ int i;
+ u8 pcr_i[20];
+ struct crypto_tfm *tfm;
+
+ tfm = crypto_alloc_tfm("sha1", 0);
+ if (tfm == NULL) {
+ kfree(entry);
+ ima_error("Digest init failed ERROR.\n");
+ return;
+ }
+ crypto_digest_init(tfm);
+
+ for (i = 0; i < 8; i++) {
+ ima_pcrread(i, pcr_i, sizeof(pcr_i));
+ /* now accumulate with current aggregate */
+ tfm->__crt_alg->cra_digest.
+ dia_update(crypto_tfm_ctx(tfm), pcr_i, 20);
+ }
+ crypto_digest_final(tfm, entry->digest);
+ crypto_free_tfm(tfm);
+ } else
+ memset(entry->digest, 0xff, 20);
+
+ /* now add measurement; if TPM bypassed, we have a ff..ff entry */
+ err = ima_add_measure_entry(entry);
+ if (err < 0) {
+ kfree(entry);
+ if (err != -EEXIST)
+ invalidate_pcr("error adding boot aggregate");
+ }
+}
+
+
+/* general invalidation function called by the measurement code */
+void invalidate_pcr(char *cause)
+{
+ /* extend pcr with illegal digest (no digest yields 0) */
+ /* extending twice is obviously flagging the exception condition... */
+ ima_error("INVALIDATING PCR AGGREGATE. Cause=%s.\n", cause);
+ ima_extend(illegal_pcr);
+ ima_extend(illegal_pcr);
+ atomic_inc(&htable.violations); /* can overflow into 0; this is an indicator only */
+}
+
+
+int _ima_init(void)
+{
+ printk(KERN_INFO
+ "IBM Integrity Measurement Architecture (IBM IMA %s).\n",
+ version);
+
+ ima_used_chip = 0;
+ if (tpm_pcr_read(IMA_TPM, 0, NULL, 0) == 0)
+ ima_used_chip = 1;
+
+ if (!ima_used_chip)
+ printk(KERN_INFO
+ " IMA (TPM/BYPASS - no TPM chip found)\n");
+
+ create_htable(); /* for measurements */
+
+ /* boot aggregate must be very first entry */
+ ima_add_boot_aggregate();
+
+ ima_fs_init();
+ return 0;
+}
+
+/*
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Reiner Sailer <sailer@watson.ibm.com>");
+MODULE_DESCRIPTION("Run-time LSM-based IBM Integrity Measurement Architecture");
+*/
diff -ruN linux-2.6.14-rc4-git1.orig/security/evm/slim/ima/ima_main.c
linux-2.6.14-rc4-git1.new/security/evm/slim/ima/ima_main.c
--- linux-2.6.14-rc4-git1.orig/security/evm/slim/ima/ima_main.c 1969-12-31 19:00:00.000000000
-0500
+++ linux-2.6.14-rc4-git1.new/security/evm/slim/ima/ima_main.c 2005-10-13 14:11:26.000000000 -0400
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2005 IBM Corporation
+ *
+ * Authors:
+ * Reiner Sailer <sailer@watson.ibm.com>
+ *
+ * EVM/SLIM implementation: Reiner Sailer <sailer@watson.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, version 2 of the
+ * License.
+ *
+ * File: ima_main.c
+ * implements run-time measurements
+ */
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/linkage.h>
+#include <linux/time.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <asm/uaccess.h>
+#include <asm/atomic.h>
+#include <linux/file.h>
+#include <linux/slab.h>
+#include <linux/stat.h>
+
+#include "ima.h"
+
+struct queue_entry *ima_lookup_digest_entry(u8 * digest);
+
+/* Add a file that is scheduled for measurement */
+int
+_ima_measure(const unsigned char *func, const unsigned char *name,
+ struct inode *inode, int mask, int hash_len, char *hash,
+ int metadata_len, char *metadata)
+{
+ struct measure_entry *entry;
+ u8 digest[IMA_DIGEST_SIZE];
+ int err = 0, count;
+
+ atomic_inc(&htable.call_count);
+
+ if (hash_len > IMA_DIGEST_SIZE) {
+ printk("%s: SLIM digest too long. Cutting to %x bytes.\n",
+ __func__, IMA_DIGEST_SIZE);
+ hash_len = IMA_DIGEST_SIZE;
+ }
+
+ memset(digest, 0, IMA_DIGEST_SIZE);
+
+ if (!memcmp(digest, hash, IMA_DIGEST_SIZE))
+ invalidate_pcr("Error, NULL hash value!\n");
+
+ memcpy(digest, hash, hash_len);
+
+ /* check if hash already exists in the measurement list */
+ if (ima_lookup_digest_entry(digest)) {
+ /* printk("%s: Entry for %s already exists. skipping.\n",
+ __func__, name); */
+ goto out;
+ }
+
+ /* create new entry and add to measurement list */
+ entry = kmalloc(sizeof(struct measure_entry), GFP_KERNEL);
+ if (!entry) {
+ invalidate_pcr("error allocating new measurement entry");
+ err = -ENOMEM;
+ goto out; /* invalidate pcr */
+ }
+
+ memset(entry, sizeof(struct measure_entry), 0);
+ entry->measure_flags = 0; //unused
+ if ((count = strlen(name)) > TCG_EVENT_NAME_LEN_MAX)
+ count = TCG_EVENT_NAME_LEN_MAX;
+
+ memcpy(entry->file_name, name, count);
+ entry->file_name[count] = '\0'; /* ez-print */
+ memcpy(entry->digest, digest, IMA_DIGEST_SIZE);
+
+ err = ima_add_measure_entry(entry);
+ if (err < 0) {
+ kfree(entry);
+ if (err != -EEXIST)
+ invalidate_pcr("error adding measurement entry");
+ else {
+ printk("%s: Double entry (%s).\n", __func__, name);
+ err = 0;
+ }
+ goto out;
+ }
+ out:
+ return err;
+}
diff -ruN linux-2.6.14-rc4-git1.orig/security/evm/slim/ima/ima_queue.c
linux-2.6.14-rc4-git1.new/security/evm/slim/ima/ima_queue.c
--- linux-2.6.14-rc4-git1.orig/security/evm/slim/ima/ima_queue.c 1969-12-31 19:00:00.000000000
-0500
+++ linux-2.6.14-rc4-git1.new/security/evm/slim/ima/ima_queue.c 2005-10-13 14:11:26.000000000
-0400
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2005 IBM Corporation
+ *
+ * Authors:
+ * Serge Hallyn <serue@us.ibm.com>
+ * Reiner Sailer <sailer@watson.ibm.com>
+ *
+ * Adapted to SLIM: Reiner Sailer 07/27/05
+ *
+ * Maintained by: Reiner Sailer <sailer@watson.ibm.com>
+ *
+ * IBM Integrity Measurement Architecture.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2 of the
+ * License.
+ *
+ * File: ima_queue.c
+ * implements queues for run-time measurement
+ * functions based on SHA1 (MD5 for SLIM)
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/linkage.h>
+#include <linux/time.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <asm/uaccess.h>
+#include <linux/file.h>
+#include <linux/slab.h>
+#include <linux/crypto.h>
+
+#include "ima.h"
+
+struct list_head ima_measurements; /* list of all measurements */
+
+struct h_table htable; /* key: inode (before secure-hashing a file) */
+
+/* Spinlock protects list for rarely occurring, critical operations
+ * (extend, dirty-flag). For scalability, we use RCU protection during
+ * normal operation (lookup entries). */
+DEFINE_SPINLOCK(ima_queue_lock);
+
+/* mutex protects atomicity of extending measurement list
+ * and extending the TPM PCR aggregate. Since tpm_extend can take
+ * long (and the tpm driver uses a mutex), we can't use the spinlock.
+ */
+static DECLARE_MUTEX(ima_extend_list_mutex);
+
+void create_htable(void)
+{
+ int i;
+
+ spin_lock(&ima_queue_lock);
+ INIT_LIST_HEAD(&ima_measurements);
+ atomic_set(&htable.len, 0);
+ atomic_set(&htable.violations, 0);
+ atomic_set(&htable.call_count, 0);
+
+ htable.max_htable_size = MEASURE_HTABLE_SIZE;
+ for (i = 0; i < htable.max_htable_size; i++) {
+ INIT_HLIST_HEAD(&htable.queue[i]);
+ atomic_set(&htable.queue_len[i], 0);
+ }
+
+ init_MUTEX(&ima_extend_list_mutex);
+ spin_unlock(&ima_queue_lock);
+}
+
+struct queue_entry *ima_lookup_digest_entry(u8 * digest_value)
+{
+ struct queue_entry *qe, *ret = NULL;
+ unsigned int key;
+ struct hlist_node *pos;
+
+ key = HASH_KEY(digest_value);
+ rcu_read_lock();
+ hlist_for_each_entry_rcu(qe, pos, &htable.queue[key], hnext) {
+ if (memcmp(qe->entry->digest, digest_value, 20) == 0) {
+ ret = qe;
+ break;
+ }
+ }
+ rcu_read_unlock();
+ return ret;
+}
+
+/* Called with ima_queue_lock held */
+static int ima_add_digest_entry(struct measure_entry *entry)
+{
+ struct queue_entry *qe;
+ unsigned int key;
+
+ key = HASH_KEY(entry->digest);
+ qe = kmalloc(sizeof(struct queue_entry), GFP_KERNEL);
+ if (qe == NULL) {
+ ima_error("OUT OF MEMORY ERROR creating queue entry.\n");
+ return -ENOMEM;
+ }
+ qe->entry = entry;
+
+ hlist_add_head_rcu(&qe->hnext, &htable.queue[key]);
+ atomic_inc(&htable.queue_len[key]);
+ return 0;
+}
+
+int ima_add_measure_entry(struct measure_entry *entry)
+{
+ struct queue_entry *qe;
+ int error = 0;
+
+ down(&ima_extend_list_mutex);
+ spin_lock(&ima_queue_lock);
+ if (ima_lookup_digest_entry(entry->digest)) {
+ error = -EEXIST;
+ spin_unlock(&ima_queue_lock);
+ goto out;
+ }
+ qe = kmalloc(sizeof(struct queue_entry), GFP_KERNEL);
+ if (qe == NULL) {
+ ima_error("OUT OF MEMORY in %s.\n", __func__);
+ error = -ENOMEM;
+ spin_unlock(&ima_queue_lock);
+ goto out;
+ }
+ qe->entry = entry;
+
+ INIT_LIST_HEAD(&qe->later);
+ list_add_tail_rcu(&qe->later, &ima_measurements);
+ atomic_inc(&htable.len);
+
+ if (ima_add_digest_entry(entry)) {
+ error = -ENOMEM;
+ spin_unlock(&ima_queue_lock);
+ goto out;
+ }
+ spin_unlock(&ima_queue_lock);
+ ima_extend(entry->digest);
+ out:
+ up(&ima_extend_list_mutex);
+ return error;
+}
diff -ruN linux-2.6.14-rc4-git1.orig/security/evm/slim/ima/Kconfig
linux-2.6.14-rc4-git1.new/security/evm/slim/ima/Kconfig
--- linux-2.6.14-rc4-git1.orig/security/evm/slim/ima/Kconfig 1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.14-rc4-git1.new/security/evm/slim/ima/Kconfig 2005-10-13 14:11:26.000000000 -0400
@@ -0,0 +1,25 @@
+#
+# IBM Integrity Measurement Architecture
+#
+
+#menu "TPM-based Integrity Measurement Architecture"
+
+config IMA_MEASURE
+ bool "TCG run-time Integrity Measurement Architecture"
+ depends on SECURITY && (CRYPTO_SHA1=y) && SECURITY_SLIM
+ help
+ To measure executable code running on this
+ system, say Y.
+ If unsure, say N.
+
+config IMA_MEASURE_PCR_IDX
+ int "PCR for Aggregate (8<= Index <= 15)"
+ depends on IMA_MEASURE
+ range 8 15
+ default 10
+ help
+ This determines the PCR index used for aggregating the
+ measurement list into the TPM hardware.
+ If unsure, use the default 10.
+#endmenu
+
diff -ruN linux-2.6.14-rc4-git1.orig/security/evm/slim/ima.h
linux-2.6.14-rc4-git1.new/security/evm/slim/ima.h
--- linux-2.6.14-rc4-git1.orig/security/evm/slim/ima.h 1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.14-rc4-git1.new/security/evm/slim/ima.h 2005-10-13 14:11:39.000000000 -0400
@@ -0,0 +1,13 @@
+/*
+ IMA
+ */
+int ima_init(void);
+void slm_measure(const unsigned char *func,
+ const unsigned char *debug_name, struct nameidata *nd,
+ struct inode *inode, int mask);
+void slm_reset_measure(struct inode *inode, unsigned const char *name);
+void ima_cleanup(void);
+void ima_measure(const unsigned char *func, const unsigned char *name,
+ struct inode *inode, int mask,
+ char *hash, int metadata_len, char *metadata);
+
diff -ruN linux-2.6.14-rc4-git1.orig/security/evm/slim/Makefile
linux-2.6.14-rc4-git1.new/security/evm/slim/Makefile
--- linux-2.6.14-rc4-git1.orig/security/evm/slim/Makefile 1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.14-rc4-git1.new/security/evm/slim/Makefile 2005-10-13 14:11:26.000000000 -0400
@@ -0,0 +1,5 @@
+
+obj-$(CONFIG_SECURITY_SLIM) += slim.o
+slim-y := slm_main.o slm_secfs.o slm_ima.o \
+ ima/ima_fs.o ima/ima_queue.o ima/ima_init.o ima/ima_main.o \
+ ima/ima_biosmeasurements.o
diff -ruN linux-2.6.14-rc4-git1.orig/security/evm/slim/slim.h
linux-2.6.14-rc4-git1.new/security/evm/slim/slim.h
--- linux-2.6.14-rc4-git1.orig/security/evm/slim/slim.h 1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.14-rc4-git1.new/security/evm/slim/slim.h 2005-10-13 14:11:26.000000000 -0400
@@ -0,0 +1,107 @@
+/*
+ * slim.h - simple linux integrity module
+ *
+ * SLIM's specific model is:
+ *
+ * All objects are labeled with exteded attributes to indicate:
+ * Integrity Access Class (IAC)
+ * Secrecy Access Class (SAC)
+ *
+ * All processes inherit from their parents:
+ * Integrity Read Access Class (IRAC)
+ * Integrity Write/Execute Access Class (IWXAC)
+ * Secrecy Write Access Class (SWAC)
+ * Secrecy Read/Execute Access Class (SRXAC)
+ *
+ * SLIM enforces the following Mandatory Access Control Rules:
+ * Read:
+ * IRAC(process) <= IAC(object)
+ * SRXAC(process) >= SAC(object)
+ * Write:
+ * IWXAC(process) >= IAC(object)
+ * SWAC(process) <= SAC(process)
+ * Execute:
+ * IWXAC(process) <= IAC(object)
+ * SRXAC(process) >= SAC(object)
+*/
+
+#include <linux/security.h>
+#include <linux/version.h>
+#include <linux/security-stack.h>
+
+enum slm_iac_level { // integrity access class
+ SLM_IAC_EXEMPT= -1, SLM_IAC_NOTDEFINED= 0, SLM_IAC_UNTRUSTED,
+ SLM_IAC_USER, SLM_IAC_SYSTEM, SLM_IAC_HIGHEST
+};
+// integrity access class
+//char *slm_iac_str[] = { "0", "UNTRUSTED", "USER", "SYSTEM" };
+extern char *slm_iac_str[];
+
+enum slm_sac_level { // secrecy access class
+ SLM_SAC_EXEMPT=-1, SLM_SAC_NOTDEFINED= 0, SLM_SAC_PUBLIC, SLM_SAC_USER,
+ SLM_SAC_USER_SENSITIVE, SLM_SAC_SYSTEM_SENSITIVE, SLM_SAC_HIGHEST
+};
+
+// secrecy access class
+//char *slm_sac_str[] = { "0", "PUBLIC", "USER", "USER-SENSITIVE",
+// "SYSTEM-SENSITIVE"
+//};
+extern char *slm_sac_str[];
+
+struct slm_tsec_data { // task security data (process info)
+ struct security_list lsm_list;
+ enum slm_iac_level iac_r; //Guard program ability to read low integrity files
+ enum slm_iac_level iac_wx;// but retain ability to write/execute higher ones
+ enum slm_sac_level sac_w; // Guard program alibity to write low secrecy files
+ enum slm_sac_level sac_rx;// after having read/executed higher secrecy files
+ int unlimited; // guard process not bounded to current process limit
+};
+
+struct slm_file_xattr { // file extended attributes
+ enum slm_iac_level iacLevel; // for integrity
+ enum slm_sac_level sacLevel; // for secrecy
+ struct slm_tsec_data guard; // guard process information
+};
+
+extern struct slm_xattr_config *slm_parse_config(char *data,
+ unsigned long datalen, int *datasize);
+
+extern int slm_init_config(void);
+
+extern void slm_init_secfs(void);
+void slm_cleanup_secfs(void);
+
+extern unsigned int slm_debug;
+enum slm_debug_level {
+ SLM_BASE = 1, SLM_INTEGRITY = 2, SLM_SECRECY = 4,
+ SLM_VERBOSE = 8,
+};
+
+extern unsigned int slm_ima;
+#undef dprintk
+#define dprintk(level, format, a...) \
+ if (slm_debug & level) \
+ printk(KERN_INFO format, ##a)
+
+extern int slm_idx;
+#define SLM_LSM_ID 0x99999999
+#define slm_get_task(head) \
+ security_get_value_type(head, SLM_LSM_ID, struct slm_tsec_data,slm_idx);
+#define slm_set_task(head, task) \
+ security_set_value_type(head, SLM_LSM_ID, task, slm_idx);
+#define slm_del_task(head) \
+ security_del_value_type(head, SLM_LSM_ID, struct slm_tsec_data,slm_idx);
+
+struct slm_isec_data { /* task security data (process info) */
+ struct security_list lsm_list;
+ int measured;
+ struct slm_file_xattr level;
+};
+#define slm_get_isec(head) \
+ security_get_value_type(head, SLM_LSM_ID, struct slm_isec_data,slm_idx);
+#define slm_set_isec(head, isec) \
+ security_set_value_type(head, SLM_LSM_ID, isec, slm_idx);
+#define slm_add_isec(head, isec) \
+ security_add_value_type(head, SLM_LSM_ID, isec, slm_idx);
+#define slm_del_isec(head) \
+ security_del_value_type(head, SLM_LSM_ID, struct slm_isec_data,slm_idx);
diff -ruN linux-2.6.14-rc4-git1.orig/security/evm/slim/slm_ima.c
linux-2.6.14-rc4-git1.new/security/evm/slim/slm_ima.c
--- linux-2.6.14-rc4-git1.orig/security/evm/slim/slm_ima.c 1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.14-rc4-git1.new/security/evm/slim/slm_ima.c 2005-10-13 14:11:26.000000000 -0400
@@ -0,0 +1,142 @@
+/*
+ * IMA Integrity Measurement/Attestation interface from SLIM
+ *
+ * Copyright (C) 2005 IBM Corporation
+ * Authors:
+ * Reiner Sailer <sailer@us.ibm.com>
+ * Mimi Zohar <zohar@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 <asm-generic/errno-base.h>
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/proc_fs.h>
+#include <linux/devfs_fs_kernel.h>
+#include <linux/namei.h>
+#include <linux/evm.h>
+#include "../evm.h"
+#include "slim.h"
+#include "ima.h"
+
+#define MD5_STR_SIZE 32 /* String value */
+#define MD5_DIGEST_SIZE 16 /* MD5 is 128-bits */
+
+void _ima_init(void);
+int _ima_measure(const unsigned char *func, const unsigned char *name,
+ struct inode *inode, int mask, int hash_len,
+ char *hash, int metadata_len, char *metadata);
+
+void ima_measure(const unsigned char *func, const unsigned char *name,
+ struct inode *inode, int mask,
+ char *hash, int metadata_len, char *metadata)
+{
+ char hashStr[SHA1_STR_SIZE + 1];
+ int len, err;
+
+ if (!hash)
+ return;
+
+ memset(hashStr, 0, sizeof hashStr);
+ len = mem2hex(hash, hashStr, SHA1_DIGEST_SIZE);
+
+ if ((err = _ima_measure(func, name, inode, mask, SHA1_DIGEST_SIZE, hash,
+ metadata_len, metadata)))
+ printk(KERN_INFO "%s: _ima_measure failed (%d).\n",
+ __func__, err);
+}
+
+/* what could we exclude
+ * - non-executable/non-library files ?
+ * - /proc /dev ?
+ */
+static int skip_measurement(struct inode *inode)
+{
+#define SYSFS_MAGIC 0x62656572
+
+ if ((inode->i_sb->s_magic == DEVFS_SUPER_MAGIC) ||
+ (inode->i_sb->s_magic == PROC_SUPER_MAGIC) ||
+ (inode->i_sb->s_magic == SYSFS_MAGIC)) {
+ return 1; /*can't measure */
+ }
+ if (S_ISLNK(inode->i_mode) ||
+ S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode) ||
+ S_ISFIFO(inode->i_mode) || S_ISSOCK(inode->i_mode))
+ return 1; /* don't measure */
+
+ return 0; /* measure */
+}
+
+void slm_measure(const unsigned char *func,
+ const unsigned char *name, struct nameidata *nd,
+ struct inode *inode, int mask)
+{
+ struct slm_isec_data *slm_isec = NULL;
+ struct evm_isec_cache *evm_isec = NULL;
+ char *metadata;
+
+ if (skip_measurement(inode))
+ return;
+
+ /* Only measure files opened for read-only or execute. */
+ if (S_ISREG(inode->i_mode)
+ && ((mask == MAY_READ) || (mask == MAY_EXEC))
+ && (mask != MAY_WRITE) && (mask != MAY_APPEND)) {
+ slm_isec = slm_get_isec(inode->i_security);
+ if (!slm_isec) {
+ slm_isec = kmalloc(sizeof (struct slm_isec_data),
+ GFP_KERNEL);
+ if (!slm_isec);
+ return;
+ memset(slm_isec, 0, sizeof (struct slm_isec_data));
+ }
+ if (!slm_isec->measured) {
+ evm_isec = evm_get_isec(inode->i_security);
+ if (evm_isec) {
+ int metadata_len = 0;
+ char hash[SHA1_DIGEST_SIZE +1];
+
+ metadata = NULL;
+ memset(hash, 0, SHA1_DIGEST_SIZE +1);
+ if (memcmp(evm_isec->hash, hash,
+ SHA1_DIGEST_SIZE) == 0)
+ evm_calc_dhash(nd, evm_isec->hash,
+ EVM_TYPE_SHA1);
+ ima_measure(func, name, inode, mask,
+ evm_isec->hash, metadata_len, metadata);
+ kfree(metadata);
+ slm_isec->measured = 1;
+ slm_set_isec(inode->i_security, slm_isec);
+ }
+ }
+ }
+}
+
+void slm_reset_measure(struct inode *inode, const unsigned char *name)
+{
+ if (S_ISREG(inode->i_mode)) {
+ struct slm_isec_data *slm_isec;
+
+ slm_isec = slm_del_isec(inode->i_security);
+ if (slm_isec && slm_isec->measured) {
+ /* printk(KERN_INFO "%s: %s measure reset\n",
+ __FUNCTION__, name); */
+ slm_isec->measured = 0;
+ }
+ }
+}
+
+int ima_init(void)
+{
+ _ima_init();
+ return 0;
+}
+
+void ima_cleanup(void)
+{
+}
diff -ruN linux-2.6.14-rc4-git1.orig/security/evm/slim/slm_main.c
linux-2.6.14-rc4-git1.new/security/evm/slim/slm_main.c
--- linux-2.6.14-rc4-git1.orig/security/evm/slim/slm_main.c 1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.14-rc4-git1.new/security/evm/slim/slm_main.c 2005-10-13 14:11:26.000000000 -0400
@@ -0,0 +1,1384 @@
+/*
+ * SLIM - Simple Linux Integrity Module
+ *
+ * Copyright (C) 2005 IBM Corporation
+ * Author: Mimi Zohar <zohar@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 <asm-generic/errno-base.h>
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/proc_fs.h>
+#include <linux/devfs_fs_kernel.h>
+#include <linux/socket.h>
+#include <linux/fs.h>
+#include <linux/namei.h>
+#include <linux/evm.h>
+#include "slim.h"
+
+int slm_idx;
+static int slm_inode_permission(struct inode *inode, int mask,
+ struct nameidata *nd);
+static int slm_file_permission(struct file *file, int mask);
+static void slm_getr_xattr(struct dentry *, struct slm_file_xattr *,
+ const unsigned char *);
+static int slm_get_xattr(struct dentry *, struct slm_file_xattr *);
+static int slm_set_taskperm(int mask, struct slm_file_xattr *level,
+ const unsigned char *name, void *caller);
+static int slm_set_xattr(struct slm_file_xattr *level,
+ char **name, void **value, size_t *value_len);
+static int slm_socket_create (int family, int type, int protocol, int kern);
+
+static int slm_inode_unlink(struct inode *, struct dentry *);
+static void slm_inode_post_create (struct inode *, struct dentry *, int mask);
+static int slm_inode_setxattr (struct dentry *, char *name, void *value,
+ size_t size, int flags);
+void slm_inode_free_security(struct inode *inode);
+
+static int slm_task_post_setuid (uid_t, uid_t, uid_t, int flags);
+static int slm_task_alloc_security(struct task_struct *);
+static int slm_alloc_security(struct task_struct *);
+extern void slm_measure(const unsigned char *func,
+ const unsigned char *debug_name, struct nameidata *nd,
+ struct inode *inode, int mask);
+extern void slm_reset_measure(struct inode *inode, const unsigned char *name);
+extern void ima_init(void);
+extern void ima_cleanup(void);
+
+unsigned int slm_debug = 1;
+static char *slm_config = "/etc/slim.conf";
+static char *slm_xattrName = "security.slim.level";
+static int slm_enable_ima = 0;
+static int secondary = 0;
+
+char *slm_iac_str[] = { "0", "UNTRUSTED", "USER", "SYSTEM" };
+char *slm_sac_str[] = { "0", "PUBLIC", "USER", "USER-SENSITIVE",
+ "SYSTEM-SENSITIVE"};
+
+static char *get_token(char *bufStart, char *bufEnd, char delimiter,
+ int *token_len)
+{
+ char *bufp = bufStart;
+ char *token = NULL;
+
+ while (!token && (bufp < bufEnd)) { /* Get start of token */
+ switch(*bufp) {
+ case ' ':
+ case '\n':
+ case '\t':
+ bufp++;
+ break;
+ case '#':
+ while ((*bufp != '\n') && (bufp++ < bufEnd));
+ bufp++;
+ break;
+ default:
+ token = bufp;
+ break;
+ }
+ }
+ if (!token)
+ return NULL;
+
+ *token_len = 0;
+ while ((*token_len == 0) && (bufp <= bufEnd)) {
+ if ((*bufp == delimiter) || (*bufp == '\n') )
+ *token_len = bufp - token;
+ if (bufp == bufEnd)
+ *token_len = bufp - token;
+ bufp++;
+
+ }
+ if (*token_len == 0)
+ token = NULL;
+ return token;
+}
+
+static int is_guardIntegrity(struct slm_file_xattr *level)
+{
+ int rc = 0;
+
+ if ((level->guard.iac_r != SLM_IAC_NOTDEFINED)
+ || (level->guard.iac_wx != SLM_IAC_NOTDEFINED))
+ rc = 1;
+ return rc;
+}
+
+static int is_guardSecrecy(struct slm_file_xattr *level)
+{
+ int rc = 0;
+
+ if ((level->guard.sac_rx != SLM_SAC_NOTDEFINED)
+ || (level->guard.sac_w != SLM_IAC_NOTDEFINED))
+ rc = 1;
+ return rc;
+}
+
+/*
+ * Enforce process integrity & secrecy levels.
+ * - update integrity process level of integrity guard program
+ * - update secrecy process level of secrecy guard program
+ */
+static int slm_bprm_check_security (struct linux_binprm *bprm)
+{
+ struct nameidata nd, *n = &nd;
+ struct dentry *dentry;
+ int rc;
+ evm_status status;
+ struct inode *inode = NULL;
+
+ struct task_struct *cur_tsk = current, *parent_tsk;
+ struct slm_tsec_data *cur_tsec = NULL, *parent_tsec;
+ struct slm_file_xattr level; // access control level
+
+ cur_tsec = slm_get_task(cur_tsk->security);
+ if (!cur_tsec) {
+ if ((rc = slm_alloc_security(cur_tsk)) < 0)
+ return rc;
+ cur_tsec = slm_get_task(cur_tsk->security);
+ if (!cur_tsec) {
+ printk(KERN_INFO "bprm: NULL cur_tsec\n");
+ return 0;
+ }
+ }
+
+ rc = path_lookup(bprm->filename, LOOKUP_FOLLOW, &nd);
+ if (rc){
+ dprintk(SLM_BASE, "%s: path_lookup for %s failed\n",
+ __FUNCTION__, bprm->filename);
+ return rc;
+ }
+
+ if (!n || !n->dentry || !n->dentry->d_inode)
+ return 0;
+ else
+ dentry = n->dentry;
+
+ inode = dentry->d_inode;
+
+ /* slm_inode_permission measured all SYSTEM level integrity objects */
+ if ((level.iacLevel != SLM_IAC_SYSTEM) && slm_enable_ima)
+ slm_measure(__FUNCTION__, bprm->filename, &nd, inode, MAY_EXEC);
+
+ /* Recursively traverse path (dentry->parent) until a level is found */
+ memset(&level, 0, sizeof(struct slm_file_xattr));
+ slm_getr_xattr(dentry, &level, bprm->filename);/* returns something */
+
+ /* Possible return codes:
+ EVM_PERMIT, EVM_DENY, EVM_SANDBOX, EVM_NOLABEL */
+ if ((status = evm_analyze_cacheinfo(dentry)) < 0) {
+ if (status == EVM_DENY) { /* Somethings very wrong */
+ dprintk(SLM_BASE, "%s: EVM_DENY requested for %s\n",
+ __FUNCTION__, bprm->filename);
+ return -EACCES;
+ } else if (status == EVM_NOLABEL) {
+ dprintk(SLM_BASE, "%s: EVM_NOLABEL for %s\n",
+ __FUNCTION__, bprm->filename);
+ } else {/* sandbox by making the iaclevel untrusted */
+ dprintk(SLM_BASE, "%s: EVM_SANDBOX requested for %s\n",
+ __FUNCTION__, bprm->filename);
+ level.iacLevel = SLM_IAC_UNTRUSTED;
+ }
+ }
+
+ /*
+ * enforce: IWXAC(process) <= IAC(object)
+ * Permit process to execute file of equal or greater integrity
+ */
+ parent_tsk = cur_tsk->parent;
+ parent_tsec = slm_get_task(parent_tsk->security);
+ if ((cur_tsec->iac_wx <= level.iacLevel) || (level.iacLevel == -1)) {
+ dprintk(SLM_INTEGRITY, "%s: ppid %d(%s %d-%s) pid %d(%s %d-%s)"
+ " %s executing\n", __FUNCTION__, parent_tsk->pid,
+ parent_tsk->comm, parent_tsec->iac_wx,
+ (parent_tsec->iac_wx != parent_tsec->iac_r)
+ ? "guard" : slm_iac_str[parent_tsec->iac_wx],
+ cur_tsk->pid, cur_tsk->comm, cur_tsec->iac_wx,
+ (cur_tsec->iac_wx != cur_tsec->iac_r)
+ ? "guard" : slm_iac_str[cur_tsec->iac_wx],
+ bprm->filename);
+ /* Being a guard process is not inherited */
+ cur_tsec->iac_r = cur_tsec->iac_wx;
+ } else {
+ dprintk(SLM_BASE,"%s: ppid %d(%s %d-%s) pid %d(%s %d-%s)"
+ " %s executing, demoting integrity to iac=%d-%s\n",
+ __FUNCTION__, parent_tsk->pid,
+ parent_tsk->comm, parent_tsec->iac_wx,
+ (parent_tsec->iac_wx != parent_tsec->iac_r)
+ ? "guard" : slm_iac_str[parent_tsec->iac_wx],
+ cur_tsk->pid, cur_tsk->comm, cur_tsec->iac_wx,
+ (cur_tsec->iac_wx != cur_tsec->iac_r)
+ ? "guard" : slm_iac_str[cur_tsec->iac_wx],
+ bprm->filename, level.iacLevel,
+ slm_iac_str[level.iacLevel]);
+ cur_tsec->iac_r = level.iacLevel;
+ cur_tsec->iac_wx = level.iacLevel;
+ }
+
+ if (is_guardIntegrity(&level)) {
+ dprintk(SLM_INTEGRITY, "%s: ppid %d pid %d %s (integrity guard)"
+ "cur: r %s wx %s new: r %s wx %s %s\n",
+ __FUNCTION__,
+ parent_tsk->pid, cur_tsk->pid, bprm->filename,
+ slm_iac_str[cur_tsec->iac_r],
+ slm_iac_str[cur_tsec->iac_wx],
+ slm_iac_str[level.guard.iac_r],
+ slm_iac_str[level.guard.iac_wx],
+ (level.guard.unlimited ? "unlimited" :
+ "limited"));
+ if (level.guard.unlimited) {
+ cur_tsec->iac_r = level.guard.iac_r;
+ cur_tsec->iac_wx = level.guard.iac_wx;
+ } else {
+ cur_tsec->iac_r = cur_tsec->iac_r < level.guard.iac_r
+ ? cur_tsec->iac_r : level.guard.iac_r;
+ cur_tsec->iac_wx = cur_tsec->iac_wx < level.guard.iac_wx
+ ? cur_tsec->iac_wx : level.guard.iac_wx;
+ }
+ }
+
+ /*
+ * enforce: SRXAC(process) >= SAC(object)
+ * Permit process to execute file of equal or lesser secrecy
+ */
+ if ((cur_tsec->sac_rx >= level.sacLevel) || (level.sacLevel == -1)){
+ /* Being a guard process is not inherited */
+ cur_tsec->sac_w = cur_tsec->sac_rx;
+ } else {
+ dprintk(SLM_SECRECY, "%s: ppid %d(%s %d-%s) pid %d(%s %d-%s) %s"
+ "executing, promoting secrecy to sac=%d-%s\n",
+ __FUNCTION__, parent_tsk->pid, parent_tsk->comm,
+ parent_tsec->sac_rx,
+ (parent_tsec->sac_w != parent_tsec->sac_rx)
+ ? "guard" : slm_sac_str[parent_tsec->sac_rx],
+ cur_tsk->pid, cur_tsk->comm, cur_tsec->sac_rx,
+ (cur_tsec->sac_w != cur_tsec->sac_rx)
+ ? "guard" : slm_sac_str[cur_tsec->sac_rx],
+ bprm->filename, level.sacLevel,
+ slm_sac_str[level.sacLevel]);
+ cur_tsec->sac_rx = level.sacLevel;
+ cur_tsec->sac_w = level.sacLevel;
+ }
+
+ /* Executing a secrecy guard program? Update the secrecy levels. */
+ if (is_guardSecrecy(&level)) {
+ dprintk(SLM_SECRECY, "%s: ppid %d pid %d %s (secrecy guard)"
+ "cur: rx %s w %s new: rx %s w %s\n", __FUNCTION__,
+ parent_tsk->pid,cur_tsk->pid, bprm->filename,
+ slm_sac_str[cur_tsec->sac_rx],
+ slm_sac_str[cur_tsec->sac_w],
+ slm_sac_str[level.guard.sac_rx],
+ slm_sac_str[level.guard.sac_w]);
+ /*
+ * set low write secrecy range,
+ * not less than current value, prevent leaking data
+ */
+ cur_tsec->sac_w = cur_tsec->sac_w > level.guard.sac_w
+ ? cur_tsec->sac_w : level.guard.sac_w;
+ /* limit secrecy range, never demote secrecy */
+ cur_tsec->sac_rx = cur_tsec->sac_rx > level.guard.sac_rx
+ ? cur_tsec->sac_rx : level.guard.sac_rx;
+ }
+ return 0;
+}
+
+/*
+ * Recursively traverse path (dentry->parent) until level info is found
+ */
+static void slm_getr_xattr(struct dentry *dentry, struct slm_file_xattr *level,
+ const unsigned char *fn)
+{
+ struct dentry *parent_dentry;
+ struct slm_isec_data *slm_isec = NULL;
+ struct inode *parent_inode = NULL;
+ int rc;
+
+ if (!dentry || !level)
+ return;
+
+ parent_inode = dentry->d_inode;
+ slm_isec = slm_get_isec(parent_inode->i_security);
+ if (slm_isec && (slm_isec->level.iacLevel != SLM_IAC_NOTDEFINED)) {
+ memcpy(level, &slm_isec->level,
+ sizeof (struct slm_file_xattr));
+ dprintk(SLM_VERBOSE, "%s: %s level %d start\n",__FUNCTION__,
+ dentry->d_name.name, level->iacLevel);
+ return;
+ }
+
+ parent_dentry = dentry;
+ if ((parent_inode->i_sb->s_magic == PROC_SUPER_MAGIC)
+ || S_ISCHR(parent_inode->i_mode)
+ || S_ISBLK(parent_inode->i_mode)) {
+ level->iacLevel = -1; /* EXEMPT */
+ level->sacLevel = -1;
+ } else if (S_ISSOCK(parent_inode->i_mode)) {
+ /* retain current levels, only demote in socket_create*/
+ struct task_struct *cur_tsk = current;
+ struct slm_tsec_data *cur_tsec;
+
+ if (!cur_tsk)
+ return;
+ cur_tsec = slm_get_task(cur_tsk->security);
+ if (!cur_tsec)
+ return;
+ level->iacLevel = cur_tsec->iac_r;
+ level->sacLevel = cur_tsec->sac_rx;
+ } else { /* Get the slim extended attributes */
+ while ((parent_dentry)
+ && ((rc = slm_get_xattr(parent_dentry, level) < 0))){
+ level->iacLevel = SLM_IAC_UNTRUSTED;
+ level->sacLevel = SLM_SAC_PUBLIC;
+
+ if (strncmp(parent_dentry->d_name.name, "/",
+ parent_dentry->d_name.len) == 0) {
+ dprintk(SLM_VERBOSE, "%s: root not labeled\n",
+ __FUNCTION__);
+ level->iacLevel = SLM_IAC_SYSTEM;
+ level->sacLevel = SLM_SAC_PUBLIC;
+ }
+
+ if (parent_dentry == parent_dentry->d_parent)
+ break;
+ else
+ parent_dentry = parent_dentry->d_parent;
+ }
+ }
+ if (!slm_isec) {
+ slm_isec = kmalloc(sizeof (struct slm_isec_data), GFP_KERNEL);
+ memset(slm_isec, 0, sizeof (struct slm_isec_data));
+ memcpy(&slm_isec->level, level,
+ sizeof (struct slm_file_xattr));
+ slm_set_isec(parent_inode->i_security, slm_isec);
+ } else if (slm_isec->level.iacLevel != SLM_IAC_NOTDEFINED) {
+ printk(KERN_INFO "%s: %s level %d reset\n",
+ __FUNCTION__, dentry->d_name.name, level->iacLevel);
+ slm_add_isec(parent_inode->i_security, slm_isec);
+ }
+ return;
+}
+
+static int set_iac(char *token, enum slm_iac_level *level)
+{
+ int rc = -1;
+ int iac;
+
+ if (memcmp(token, "EXEMPT", 6) == 0) {
+ *level = -1;
+ rc = 0;
+ } else {
+ for (iac = 0; iac < sizeof(slm_iac_str)/sizeof(char *); iac++){
+ if (memcmp(token, slm_iac_str[iac],
+ strlen(slm_iac_str[iac])) == 0) {
+ *level = iac;
+ rc = 0;
+ break;
+ }
+ }
+ }
+ return rc;
+}
+
+static int set_sac(char *token, enum slm_sac_level *level)
+{
+ int rc = -1;
+ int sac;
+
+ if (memcmp(token, "EXEMPT", 6) == 0) {
+ *level = -1;
+ rc = 0;
+ } else {
+ for (sac = 0; sac < sizeof(slm_sac_str)/sizeof(char *); sac++){
+ if (memcmp(token, slm_sac_str[sac],
+ strlen(slm_sac_str[sac])) == 0) {
+ *level = sac;
+ rc = 0;
+ break;
+ }
+ }
+ }
+ return rc;
+}
+
+static int set_bounds(char *token, int *unlimited)
+{
+ *unlimited = (memcmp(token, "UNLIMITED", 9) == 0) ? 1 : 0;
+ return 0;
+}
+
+static int slm_get_xattr(struct dentry *dentry, struct slm_file_xattr *level)
+{
+ int rc = -1;
+ char *token;
+ int token_len, xattr_len;
+ char *xattr_value;
+ char *buf, *buf_end;
+ int fieldno = 0;
+
+ if (!dentry || !dentry->d_inode || !dentry->d_inode->i_op
+ || !dentry->d_inode->i_op->getxattr)
+ return rc;
+
+ memset(level, 0, sizeof(struct slm_file_xattr));
+ if ((xattr_len = dentry->d_inode->i_op->getxattr(dentry,
+ slm_xattrName, NULL, 0)) < 0) {
+ if (xattr_len == -EOPNOTSUPP) {
+ level->iacLevel = -1;
+ level->sacLevel = -1;
+ rc = 0;
+ return rc;
+ } else {
+ dprintk(SLM_VERBOSE, "%s: %d %s getxattr failed %s\n",
+ __FUNCTION__, xattr_len, dentry->d_name.name,
+ slm_xattrName);
+ return xattr_len;
+ }
+ }
+
+ xattr_value = kmalloc(xattr_len+1, GFP_KERNEL);
+ if (!xattr_value)
+ return -ENOMEM;
+
+ memset(xattr_value, 0, xattr_len +1);
+ xattr_len = dentry->d_inode->i_op->getxattr(dentry, slm_xattrName,
+ xattr_value, xattr_len);
+
+ /* Get the 7 access class levels from the extended attribute */
+ buf = xattr_value + sizeof(time_t);
+ if (*buf == 0x20)
+ buf++; /* skip blank after timestamp */
+ buf_end = xattr_value + xattr_len;
+
+ while ((token = get_token(buf, buf_end, ' ', &token_len)) != NULL) {
+ buf = token + token_len;
+ switch(++fieldno) {
+ case 1:
+ if (set_iac(token, &level->iacLevel) == 0)
+ rc = 0;
+ break;
+ case 2:
+ set_sac(token, &level->sacLevel);
+ break;
+ case 3:
+ set_iac(token, &level->guard.iac_r);
+ break;
+ case 4:
+ set_iac(token, &level->guard.iac_wx);
+ break;
+ case 5:
+ set_bounds(token, &level->guard.unlimited);
+ set_sac(token, &level->guard.sac_w);
+ break;
+ case 6:
+ set_sac(token, &level->guard.sac_rx);
+ break;
+ case 7:
+ set_bounds(token, &level->guard.unlimited);
+ default:
+ break;
+ }
+ }
+ if (xattr_value)
+ kfree(xattr_value);
+ return rc;
+}
+
+/*
+ * slm_set_taskperm
+ */
+static int slm_set_taskperm(int mask, struct slm_file_xattr *level,
+ const unsigned char *name, void *caller)
+{
+ struct task_struct *cur_tsk = current, *parent_tsk;
+ struct slm_tsec_data *cur_tsec = NULL, *parent_tsec = NULL;
+ int rc;
+
+ if (!cur_tsk)
+ return 0;
+ cur_tsec = slm_get_task(cur_tsk->security);
+
+ parent_tsk = cur_tsk->parent;
+ if (!parent_tsk)
+ return 0;
+ parent_tsec = slm_get_task(parent_tsk->security);
+
+ if ((!cur_tsec) || (!parent_tsec)) {
+ if ((rc = slm_alloc_security(cur_tsk)) < 0)
+ return rc;
+ }
+ cur_tsec = slm_get_task(cur_tsk->security);
+ parent_tsec = slm_get_task(parent_tsk->security);
+ if ((!cur_tsec) || (!parent_tsec)) {
+ printk(KERN_INFO "set_taskperm: NULL cur_tsec/parent_tsec\n");
+ return 0;
+ }
+
+ if (!level)
+ return 0;
+
+ if (mask & MAY_READ) {
+ /*
+ * enforce: IRAC(process) <= IAC(object)
+ * Permit process to read file of equal or greater integrity
+ * otherwise, demote the process.
+ */
+ if ((cur_tsec->iac_r <= level->iacLevel)
+ || (level->iacLevel == -1))
+ {
+ ;
+ } else { /* Reading lower integrity, demote process */
+ dprintk(SLM_BASE, "ppid %d(%s p=%d-%s) "
+ " pid %d(%s p=%d-%s) demoting integrity to"
+ " iac=%d-%s(%s)\n",
+ parent_tsk->pid, parent_tsk->comm,
+ parent_tsec->iac_r,
+ (parent_tsec->iac_wx != parent_tsec->iac_r)
+ ? "guard" : slm_iac_str[parent_tsec->iac_r],
+ cur_tsk->pid, cur_tsk->comm, cur_tsec->iac_r,
+ (cur_tsec->iac_wx != cur_tsec->iac_r)
+ ? "guard" : slm_iac_str[cur_tsec->iac_r],
+ level->iacLevel, slm_iac_str[level->iacLevel],
+ name);
+
+ /* Even in the case of a integrity guard process. */
+ cur_tsec->iac_r = level->iacLevel;
+ cur_tsec->iac_wx = level->iacLevel;
+ }
+
+ /*
+ * enforce: SRXAC(process) >= SAC(object)
+ * Permit process to read file of equal or lesser secrecy;
+ * otherwise, promote the process.
+ */
+ if ((cur_tsec->sac_rx >= level->sacLevel)
+ || (level->sacLevel == -1)){
+ ;
+ } else { /* Reading higher secrecy, promote process */
+ dprintk(SLM_BASE, "ppid %d(%s p=%d-%s) "
+ "pid %d(%s p=%d-%s) promoting secrecy to "
+ "p=%d-%s(%s)\n", parent_tsk->pid,
+ parent_tsk->comm, parent_tsec->sac_rx,
+ (parent_tsec->sac_w != parent_tsec->sac_rx)
+ ? "guard" : slm_sac_str[parent_tsec->sac_rx],
+ cur_tsk->pid, cur_tsk->comm, cur_tsec->sac_rx,
+ (cur_tsec->sac_w != cur_tsec->sac_rx)
+ ? "guard" : slm_sac_str[cur_tsec->sac_rx],
+ level->sacLevel, slm_sac_str[level->sacLevel],
+ name);
+ /* Even in the case of a secrecy guard process. */
+ cur_tsec->sac_rx = level->sacLevel;
+ cur_tsec->sac_w = level->sacLevel;
+ }
+ }
+
+ if ((mask & MAY_WRITE) || (mask & MAY_APPEND)) {
+ /*
+ * enforce: IWXAC(process) >= IAC(object)
+ * Permit process to write a file of equal or lesser integrity.
+ */
+ if ((cur_tsec->iac_wx >= level->iacLevel)
+ || (level->iacLevel == -1)) {
+ ;
+ } else { /* can't write higher integrity */
+ dprintk(SLM_BASE, "ppid %d(%s p=%d-%s) "
+ "pid %d(%s p=%d-%s) can't write higher "
+ "integrity iac=%d-%s(%s)\n",
+ parent_tsk->pid, parent_tsk->comm,
+ parent_tsec->iac_wx,
+ (parent_tsec->iac_wx != parent_tsec->iac_r)
+ ? "guard" : slm_iac_str[parent_tsec->iac_wx],
+ cur_tsk->pid, cur_tsk->comm, cur_tsec->iac_wx,
+ (cur_tsec->iac_wx != cur_tsec->iac_r)
+ ? "guard" : slm_iac_str[cur_tsec->iac_wx],
+ level->iacLevel, slm_iac_str[level->iacLevel],
+ name);
+ return -EACCES;
+ }
+
+ /*
+ * enforce: SWAC(process) <= SAC(process)
+ * Permit process to write a file of equal or greater secrecy
+ */
+ if ((cur_tsec->sac_w <= level->sacLevel)
+ || (level->sacLevel == -1)) {
+ ;
+ } else { /* can't write lower secrecy */
+ dprintk(SLM_BASE, "ppid %d(%s p=%d-%s) "
+ "pid %d(%s p=%d-%s) can't write lower "
+ "secrecy sac=%d-%s(%s)\n",
+ parent_tsk->pid, parent_tsk->comm,
+ parent_tsec->sac_w,
+ (parent_tsec->sac_w != parent_tsec->sac_rx)
+ ? "guard" : slm_iac_str[parent_tsec->sac_w],
+ cur_tsk->pid, cur_tsk->comm, cur_tsec->sac_w,
+ (cur_tsec->sac_w != cur_tsec->sac_rx)
+ ? "guard" : slm_sac_str[cur_tsec->sac_w],
+ level->sacLevel, slm_sac_str[level->sacLevel],
+ name);
+ return -EACCES;
+ }
+ }
+ return 0;
+}
+
+/*
+ * Premise:
+ * Can't write or execute higher integrity, can't read lower integrity
+ * Can't read or execute higher secrecy, can't write lower secrecy
+ */
+static int slm_inode_permission(struct inode *inode, int mask,
+ struct nameidata *nd)
+{
+ char *path = (char *)__get_free_page(GFP_KERNEL);
+ const unsigned char *fn;
+ struct dentry *dentry = NULL;
+ struct slm_file_xattr level;
+ int rc = 0;
+ const unsigned char *debug_name;
+
+ if (!nd || !nd->dentry){
+ if (inode)
+ dentry = d_find_alias(inode);
+ } else
+ dentry = nd->dentry;
+
+ if (!dentry || !dentry->d_name.name) {
+ if (path)
+ free_page((unsigned long)path);
+ return 0; // Hm, we'll be called again.
+ }
+
+ if (!path || !nd || !nd->dentry || !nd->mnt)
+ debug_name = dentry->d_name.name; /* short name */
+ else {
+ fn = d_path(nd->dentry, nd->mnt, path, PAGE_SIZE);
+ debug_name = (!fn) ? dentry->d_name.name : fn; /* full name */
+ }
+
+ /* Recursively traverse path (dentry->parent) until level info found */
+ memset(&level, 0, sizeof(struct slm_file_xattr));
+ slm_getr_xattr(dentry, &level, debug_name);/* always returns something*/
+
+ /* measure all SYSTEM level integrity objects */
+ if ((level.iacLevel == SLM_IAC_SYSTEM) && slm_enable_ima)
+ slm_measure(__FUNCTION__, debug_name, nd, inode, mask);
+
+ rc = slm_set_taskperm(mask, &level, debug_name, (void *)__FUNCTION__);
+ dprintk(SLM_VERBOSE, "%s: %s - %s\n",
+ __FUNCTION__, dentry->d_name.name, debug_name);
+ if (path)
+ free_page((unsigned long)path);
+
+
+ if (S_ISDIR(inode->i_mode) && (mask & MAY_WRITE))
+ return 0;
+ else
+ return rc;
+}
+
+static int slm_inode_unlink(struct inode *dir, struct dentry *dentry)
+{
+ struct slm_file_xattr level;
+ int mask = MAY_WRITE;
+ int rc = 0;
+
+ if (!dentry || !dentry->d_name.name)
+ return 0;
+
+ memset(&level, 0, sizeof(struct slm_file_xattr));
+ slm_getr_xattr(dentry, &level, NULL); /* always returns something */
+
+ rc = slm_set_taskperm(mask, &level, dentry->d_name.name,
+ (void *) __FUNCTION__);
+ return rc;
+}
+
+void slm_inode_free_security(struct inode *inode)
+{
+ struct slm_isec_data *slm_isec;
+
+ slm_isec = slm_del_isec(inode->i_security);
+ if (slm_isec)
+ kfree(slm_isec);
+}
+
+static int slm_file_permission(struct file *file, int mask)
+{
+ char *path = (char *)__get_free_page(GFP_KERNEL);
+ const unsigned char *fn = NULL;
+ struct dentry *dentry;
+ struct slm_file_xattr level; /* access control levels */
+ const unsigned char *debug_name;
+ int rc = 0;
+ struct inode *inode = NULL;
+
+ if (!file || !file->f_dentry || !file->f_dentry->d_name.name){
+ if (path)
+ free_page((unsigned long)path);
+ return 0;
+ }
+
+ dentry = file->f_dentry;
+ inode = dentry->d_inode;
+
+ if (S_ISREG(inode->i_mode)) {
+ if (!path || !file || !file->f_dentry || !file->f_vfsmnt)
+ debug_name = dentry->d_name.name; /* short name */
+ else {
+ fn = d_path(file->f_dentry, file->f_vfsmnt, path,
+ PAGE_SIZE);
+ debug_name = (!fn)
+ ? dentry->d_name.name : fn;
+ }
+
+ /* Will need to measure again. */
+ if ((mask & MAY_WRITE) || (mask & MAY_APPEND)) {
+ struct inode *inode = NULL;
+
+ inode = dentry->d_inode;
+ if (slm_enable_ima && inode)
+ slm_reset_measure(inode, debug_name);
+ }
+
+ /*
+ * Recursively traverse path (dentry->parent) until level
+ * info is found.
+ */
+ memset(&level, 0, sizeof(struct slm_file_xattr));
+ slm_getr_xattr(dentry, &level, debug_name);
+ rc = slm_set_taskperm(mask, &level, debug_name,
+ (void *) __FUNCTION__);
+ }
+ if (path)
+ free_page((unsigned long)path);
+ return rc;
+}
+
+static int slm_alloc_security(struct task_struct *tsk)
+{
+ struct slm_tsec_data *tsec = NULL, *cur_tsec = NULL,
+ *parent_tsec = NULL;
+ struct task_struct *cur_tsk = current, *parent_tsk;
+
+
+ /* allocate parent_tsk->security, if needed */
+ parent_tsk = cur_tsk->parent;
+ if (parent_tsk)
+ parent_tsec = slm_get_task(parent_tsk->security);
+ if (parent_tsk && !parent_tsec){
+ dprintk(SLM_VERBOSE, "%s: alloc_security pid %d "
+ "** parent_tsk->pid %d **\n", __FUNCTION__,
+ tsk->pid, parent_tsk->pid);
+ parent_tsec = (struct slm_tsec_data *)
+ kmalloc(sizeof (struct slm_tsec_data), GFP_KERNEL);
+ if (!parent_tsec)
+ return -ENOMEM;
+
+ memset(parent_tsec, 0, sizeof(struct slm_tsec_data));
+ slm_set_task(parent_tsk->security, parent_tsec);
+ parent_tsec->iac_r = SLM_IAC_HIGHEST - 1;
+ parent_tsec->iac_wx = SLM_IAC_HIGHEST - 1;
+ parent_tsec->sac_w = SLM_SAC_NOTDEFINED + 1;
+ parent_tsec->sac_rx = SLM_SAC_NOTDEFINED + 1;
+ }
+
+ /* allocate current->security, if needed */
+ cur_tsec = slm_get_task(cur_tsk->security);
+ if (!cur_tsec) {
+ dprintk(SLM_VERBOSE, "%s: alloc_security pid %d ** "
+ "cur_tsk->pid %d" " ** parent_tsk->pid %d\n",
+ __FUNCTION__,
+ tsk->pid, cur_tsk->pid, parent_tsk->pid);
+
+ /* initial process, set to highest integrity and secrecy */
+ cur_tsec = (struct slm_tsec_data *)
+ kmalloc(sizeof (struct slm_tsec_data), GFP_KERNEL);
+ if (!cur_tsec)
+ return -ENOMEM;
+
+ memset(cur_tsec, 0, sizeof(struct slm_tsec_data));
+ slm_set_task(cur_tsk->security, cur_tsec);
+ cur_tsec->iac_r = SLM_IAC_HIGHEST - 1;
+ cur_tsec->iac_wx = SLM_IAC_HIGHEST - 1;
+ cur_tsec->sac_w = SLM_SAC_NOTDEFINED + 1;
+ cur_tsec->sac_rx = SLM_SAC_NOTDEFINED + 1;
+ }
+
+
+ /* new tsk->security inherits from current->security */
+ tsec = slm_get_task(tsk->security);
+ if (!tsec) {
+ tsec = (struct slm_tsec_data *)
+ kmalloc(sizeof (struct slm_tsec_data), GFP_KERNEL);
+ if (!tsec)
+ return -ENOMEM;
+ memcpy(tsec, cur_tsec, sizeof (struct slm_tsec_data));
+ slm_set_task(tsk->security, tsec);
+ } else {
+ dprintk(SLM_VERBOSE, "%s: tsk->pid %d cur_tsk->pid %d\n",
+ __FUNCTION__, tsk->pid, cur_tsk->pid);
+ }
+ return 0;
+}
+
+/*
+ * Check integrity permission to create a regular file.
+ */
+static int slm_inode_create (struct inode *inode,
+ struct dentry *dentry, int mask)
+{
+ struct slm_tsec_data *cur_tsec = NULL;
+ struct task_struct *cur_tsk = current;
+ struct dentry *parent_dentry;
+ struct slm_file_xattr parent_level;
+ const unsigned char *debug_name = "???";
+
+ memset(&parent_level, 0, sizeof(struct slm_file_xattr));
+ cur_tsec = slm_get_task(cur_tsk->security);
+
+ if (!dentry)
+ return 0;
+ else
+ parent_dentry = dentry->d_parent;
+
+ if (!parent_dentry || !parent_dentry->d_name.name)
+ debug_name = parent_dentry->d_name.name;
+
+ /* get parent integrity info */
+ slm_getr_xattr(parent_dentry, &parent_level, debug_name);
+
+ /*
+ * enforce: IWXAC(process) >= IAC(object)
+ * Permit process to write a file of equal or lesser integrity.
+ */
+ if ((cur_tsec->iac_wx >= parent_level.iacLevel)
+ || (parent_level.iacLevel == -1)) {
+ ;
+ } else { /* can't write higher integrity */
+ dprintk(SLM_INTEGRITY, "%s: %s - prohibit low integrity "
+ " process writing into higher level directory\n",
+ __FUNCTION__, debug_name);
+ return -EPERM;
+ }
+ return 0;
+}
+
+static int slm_set_xattr(struct slm_file_xattr *level,
+ char **name, void **value, size_t *value_len)
+{
+ int len;
+ int xattrLen;
+ char buf[76]; /* buffer for extended attribute */
+ char *bufp = buf;
+ char *xattrVal = buf;
+ struct timespec now;
+ time_t nl_time;
+
+ if (!level)
+ return 0;
+
+ memset(buf, 0, sizeof buf);
+
+ now = CURRENT_TIME;
+ len = sizeof now.tv_sec;
+ nl_time = htonl(now.tv_sec);
+ memcpy(bufp, &nl_time, len);
+ bufp += len;
+ *bufp++ = ' ';
+
+ if (level->iacLevel == -1) {
+ memcpy(bufp, "EXEMPT",6);
+ bufp += 6;
+ } else if (level->iacLevel == 0) {
+ dprintk(SLM_VERBOSE, "%s: iaclevel is 0\n", __FUNCTION__);
+ level->iacLevel = 1;
+ len = strlen(slm_iac_str[level->iacLevel]);
+ memcpy(bufp, slm_iac_str[level->iacLevel], len);
+ bufp += len;
+ } else {
+ len = strlen(slm_iac_str[level->iacLevel]);
+ memcpy(bufp, slm_iac_str[level->iacLevel], len);
+ bufp += len;
+ }
+ *bufp++ = ' ';
+ if (level->sacLevel == -1) {
+ memcpy(bufp, "EXEMPT",6);
+ bufp += 6;
+ } else {
+ len = strlen(slm_sac_str[level->sacLevel]);
+ memcpy(bufp, slm_sac_str[level->sacLevel], len);
+ bufp += len;
+ }
+ xattrLen = bufp - buf;
+
+ *name = kmalloc(sizeof("slim.level") + 1, GFP_KERNEL);
+ if (!*name)
+ return -ENOMEM;
+ memcpy(*name, "slim.level", sizeof("slim.level"));
+ *value = kmalloc(xattrLen + 1, GFP_KERNEL);
+ if (!*value)
+ return -ENOMEM;
+ memcpy(*value, xattrVal, xattrLen);
+ *value_len = xattrLen;
+ return 0;
+}
+
+/* Create the security.slim.level extended attribute */
+static int slm_inode_init_security(struct inode *inode, struct inode *dir,
+ struct list_head *head)
+{
+ struct slm_isec_data *slm_isec = NULL;
+ struct slm_tsec_data *cur_tsec = NULL;
+ struct task_struct *cur_tsk = current;
+ struct slm_file_xattr level;
+ struct xattr_data *data;
+
+ if (!head)
+ return 0;
+
+ cur_tsec = slm_get_task(cur_tsk->security);
+
+ /* get parent level info */
+ memset(&level, 0, sizeof (struct slm_file_xattr));
+ slm_isec = slm_get_isec(dir->i_security);
+ if (!slm_isec) {
+ printk(KERN_INFO "%s: slm_isec is NULL \n",__FUNCTION__);
+ return (-EOPNOTSUPP);
+ }
+
+ if (slm_isec && (slm_isec->level.iacLevel != SLM_IAC_NOTDEFINED)) {
+ memcpy(&level, &slm_isec->level,
+ sizeof (struct slm_file_xattr));
+ dprintk(SLM_VERBOSE, "%s: level %d\n", __FUNCTION__,
+ slm_isec->level.iacLevel);
+ }
+
+ /* low integrity process wrote into a higher level directory */
+ if (cur_tsec->iac_wx < level.iacLevel) {
+ level.iacLevel = cur_tsec->iac_wx;
+ level.sacLevel = cur_tsec->sac_w;
+ }
+ /* if directory is exempt, then use process level */
+ if (level.iacLevel == -1){
+ level.iacLevel = cur_tsec->iac_wx;
+ level.sacLevel = cur_tsec->sac_w;
+ }
+
+ data = kmalloc(sizeof(struct xattr_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ /* set levels, based on parent */
+ slm_set_xattr(&level, &data->name, &data->value, &data->len);
+ INIT_LIST_HEAD(&data->list);
+ list_add_tail(&data->list, head);
+
+ return 0;
+}
+
+
+/*
+ * Update the security.evm.hmac on a newly created regular file.
+ */
+static void slm_inode_post_create (struct inode *inode,
+ struct dentry *dentry, int mask)
+{
+ int rc;
+
+ if (!dentry) {
+ printk(KERN_INFO "%s: dentry is null\n", __FUNCTION__);
+ return;
+ }
+ rc = evm_update_hmac(dentry, 0);
+ if (dentry->d_name.name)
+ dprintk(SLM_VERBOSE, "%s: %s hmac calculation %s\n",
+ __FUNCTION__, dentry->d_name.name,
+ rc == 0 ? "succeeded" : "failed");
+}
+
+/*
+ * Check permissions to create a new directory in the existing directory
+ * associated with inode strcture @dir.
+ */
+static int slm_inode_mkdir (struct inode *inode,
+ struct dentry *dentry, int mask)
+{
+ struct slm_tsec_data *cur_tsec = NULL;
+ struct slm_isec_data *slm_isec = NULL;
+ struct task_struct *cur_tsk = current;
+ struct dentry *parent_dentry;
+ struct slm_file_xattr parent_level;
+ const unsigned char *debug_name = "???";
+ int rc = 0;
+
+ memset(&parent_level, 0, sizeof(struct slm_file_xattr));
+ cur_tsec = slm_get_task(cur_tsk->security);
+
+ if (!dentry)
+ return 0;
+ else
+ parent_dentry = dentry->d_parent;
+
+ if (!parent_dentry || !parent_dentry->d_name.name)
+ debug_name = parent_dentry->d_name.name;
+
+ /* get parent level info */
+ while ((parent_dentry) && ((rc = slm_get_xattr(parent_dentry,
+ &parent_level)) < 0)){
+ parent_level.iacLevel = SLM_IAC_SYSTEM;
+ parent_level.sacLevel = SLM_SAC_PUBLIC;
+ if (parent_dentry == parent_dentry->d_parent)
+ break;
+ else
+ parent_dentry = parent_dentry->d_parent; /* try again */
+ }
+ if (cur_tsec->iac_wx < parent_level.iacLevel) {
+ if (parent_level.iacLevel == SLM_IAC_SYSTEM)
+ return -EACCES;
+ else {
+ dprintk(SLM_VERBOSE, "%s:%s - creating lower integrity"
+ " directory, than parent\n",
+ __FUNCTION__, debug_name);
+ return 0;
+ }
+ }
+ slm_isec = slm_get_isec(inode->i_security);
+ if (!slm_isec) {
+ slm_isec = kmalloc(sizeof (struct slm_isec_data), GFP_KERNEL);
+ if (!slm_isec)
+ return -ENOMEM;
+ memset(slm_isec, 0, sizeof (struct slm_isec_data));
+ }
+ if (!slm_isec->level.iacLevel) {
+ memcpy(&slm_isec->level, &parent_level,
+ sizeof (struct slm_file_xattr));
+ }
+ return 0;
+}
+
+/*
+ * Update the security.evm.hmac on a newly created directory.
+ */
+static void slm_inode_post_mkdir (struct inode *inode,
+ struct dentry *dentry, int mask)
+{
+ int rc;
+
+ if (!dentry)
+ return;
+ rc = evm_update_hmac(dentry, 0);
+ if (dentry->d_name.name)
+ dprintk(SLM_VERBOSE, "%s: %s hmac calculation %s\n",
+ __FUNCTION__, dentry->d_name.name,
+ rc == 0 ? "succeeded" : "failed");
+}
+
+static int slm_inode_rename (struct inode *old_dir, struct dentry *old_dentry,
+ struct inode *new_dir, struct dentry *new_dentry)
+{
+ struct slm_file_xattr old_level, parent_level;
+ struct dentry *parent_dentry;
+ const unsigned char *debug_name;
+
+ memset(&old_level, 0, sizeof(struct slm_file_xattr));
+ debug_name = old_dentry->d_name.name; /* short name */
+ slm_getr_xattr(old_dentry, &old_level, debug_name);
+ parent_dentry = new_dentry->d_parent;
+
+ memset(&parent_level, 0, sizeof(struct slm_file_xattr));
+ debug_name = parent_dentry->d_name.name; /* short name */
+ slm_getr_xattr(parent_dentry, &parent_level, debug_name);
+ if (old_level.iacLevel < parent_level.iacLevel) {
+ dprintk(SLM_BASE, "%s: prohibit rename of %s (low"
+ " integrity) into %s (higher level directory)\n",
+ __FUNCTION__, old_dentry->d_name.name,
+ parent_dentry->d_name.name);
+ return -EPERM;
+ }
+ return 0;
+}
+
+/*
+ * Limit the integrity value of an object to be no greater than that
+ * of the current process. This is especially important for objects
+ * being promoted.
+*/
+int slm_inode_setxattr (struct dentry *dentry, char *name, void *value,
+ size_t size, int flags)
+{
+ struct task_struct *cur_tsk = current;
+ struct slm_tsec_data *cur_tsec = NULL;
+
+ cur_tsec = slm_get_task(cur_tsk->security);
+ if (!cur_tsec)
+ return 0;
+
+ if (!value)
+ return -EINVAL;
+
+ dprintk(SLM_VERBOSE,"%s: name %s value %s process:iac_r %s iac_wx %s\n",
+ __FUNCTION__, name, (char *)value,
+ slm_iac_str[cur_tsec->iac_r], slm_iac_str[cur_tsec->iac_wx]);
+
+ if (memcmp(name, slm_xattrName, strlen(slm_xattrName)) != 0)
+ return 0;
+
+ switch(cur_tsec->iac_wx){
+ case SLM_IAC_USER:
+ if ((memcmp(value + sizeof(time_t) + 1, "USER", 4) != 0)
+ && (memcmp(value + sizeof(time_t) + 1,
+ "UNTRUSTED", 9) != 0))
+ return -EPERM;
+ break;
+ case SLM_IAC_SYSTEM:
+ if ((memcmp(value + sizeof(time_t) + 1, "SYSTEM", 6) != 0)
+ && (memcmp(value + sizeof(time_t) + 1, "USER", 4) != 0)
+ && (memcmp(value + sizeof(time_t) + 1, "UNTRUSTED", 9)
+ != 0)
+ && (memcmp(value + sizeof(time_t) + 1, "EXEMPT", 6)
+ != 0))
+ return -EPERM;
+ break;
+ default:
+ return -EPERM;
+ }
+ return 0;
+}
+
+/*
+ * Opening a socket demotes the integrity of a process to untrusted.
+ */
+int slm_socket_create (int family, int type, int protocol, int kern)
+{
+ struct task_struct *cur_tsk = current, *parent_tsk;
+ struct slm_tsec_data *cur_tsec = NULL;
+ int rc;
+
+ cur_tsec = slm_get_task(cur_tsk->security);
+ if (!cur_tsec) {
+ if ((rc = slm_alloc_security(cur_tsk)) < 0)
+ return rc;
+ cur_tsec = slm_get_task(cur_tsk->security);
+ if (!cur_tsec)
+ return 0;
+ }
+ /* Todo: make sure this process and the local/unix process
+ * are running with the same iac and sac.
+ */
+ if ((family != AF_UNIX) && (family != AF_LOCAL) &&
+ (family != AF_NETLINK)) {
+ if (cur_tsec->iac_r > SLM_IAC_UNTRUSTED) {
+ parent_tsk = cur_tsk->parent;
+ dprintk(SLM_INTEGRITY, "%s: ppid %d pid %d demoting "
+ "family %d type %d protocol %d kern %d"
+ " to untrusted.\n", __FUNCTION__,
+ parent_tsk->pid, cur_tsk->pid,
+ family, type, protocol, kern);
+ cur_tsec->iac_r = SLM_IAC_UNTRUSTED;
+ cur_tsec->iac_wx = SLM_IAC_UNTRUSTED;
+ }
+ }
+ return 0;
+}
+
+/*
+ * When a task gets allocated, it inherits the current IAC and SAC.
+ * Set the values and store them in p->security.
+ */
+static int slm_task_alloc_security(struct task_struct *tsk)
+{
+ int rc;
+ struct slm_tsec_data *tsec = NULL;
+
+ tsec = slm_get_task(tsk->security);
+ if (!tsec) {
+ if ((rc = slm_alloc_security(tsk)) < 0)
+ return rc;
+ };
+ return 0;
+}
+
+static void slm_task_free_security(struct task_struct *tsk)
+{
+ struct slm_tsec_data *tsec;
+
+ if (!tsk)
+ return;
+
+ tsec = slm_del_task(tsk->security);
+ if (tsec)
+ kfree(tsec);
+ return;
+}
+
+static int slm_task_post_setuid (uid_t old_ruid, uid_t old_euid,
+ uid_t old_suid, int flags)
+{
+ struct task_struct *cur_tsk = current, *parent_tsk;
+ struct slm_tsec_data *cur_tsec, *parent_tsec;
+
+ cur_tsec = slm_get_task(cur_tsk->security);
+ if (!cur_tsec)
+ ;
+ else if (flags == LSM_SETID_ID) {
+ /*set process to USER level integrity for everything but root*/
+ dprintk(SLM_VERBOSE, "ruid %d euid %d suid %d "
+ "cur: uid %d euid %d suid %d\n",
+ old_ruid, old_euid, old_suid,
+ current->uid, current->euid, current->suid);
+ if (cur_tsec->iac_r == SLM_IAC_UNTRUSTED) {
+ dprintk(SLM_VERBOSE, "Integrity: pid %d iac_r %d "
+ " iac_wx %d remains UNTRUSTED\n",
+ cur_tsk->pid, cur_tsec->iac_r,cur_tsec->iac_wx);
+ } else if (current->uid != 0) {
+ dprintk(SLM_VERBOSE, "setting: pid %d iac_r %d "
+ " iac_wx %d to USER\n",
+ cur_tsk->pid, cur_tsec->iac_r,cur_tsec->iac_wx);
+ cur_tsec->iac_r = SLM_IAC_USER;
+ cur_tsec->iac_wx = SLM_IAC_USER;
+ } else if ((current->uid == 0) && (old_ruid != 0)) {
+ dprintk(SLM_VERBOSE, "setting: pid %d iac_r %d "
+ " iac_wx %d to SYSTEM\n",
+ cur_tsk->pid, cur_tsec->iac_r,cur_tsec->iac_wx);
+ cur_tsec->iac_r = SLM_IAC_SYSTEM;
+ cur_tsec->iac_wx = SLM_IAC_SYSTEM;
+ }
+ } else {
+ parent_tsk = cur_tsk->parent;
+ if (!parent_tsk) {
+ parent_tsec = slm_get_task(parent_tsk->security);
+ dprintk(SLM_VERBOSE, "ppid %d(%s %d-%s) "
+ " pid %d(%s)iac_r %d iac_wx %d\n",
+ parent_tsk->pid, parent_tsk->comm,
+ parent_tsec->iac_wx,
+ (parent_tsec->iac_wx != parent_tsec->iac_r)
+ ? "guard" : slm_iac_str[parent_tsec->iac_wx],
+ cur_tsk->pid, cur_tsk->comm,
+ cur_tsec->iac_r, cur_tsec->iac_wx);
+ }
+ }
+ return 0;
+}
+
+static inline int slm_setprocattr(struct task_struct *tsk,
+ char *name, void *value, size_t size)
+{
+ dprintk(SLM_BASE, "%s: %s \n", __FUNCTION__, name);
+ return -EACCES;
+
+}
+
+
+static inline int slm_getprocattr(struct task_struct *tsk,
+ char *name, void *value, size_t size)
+{
+ struct slm_tsec_data *tsec = NULL;
+ size_t len = 0;
+
+ tsec = slm_get_task(tsk->security);
+ if (!tsec)
+ len = snprintf(value, size, "unknown");
+ else {
+ if (tsec->iac_wx != tsec->iac_r)
+ len = snprintf(value, size, "guard wx:%s r:%s",
+ slm_iac_str[tsec->iac_wx],
+ slm_iac_str[tsec->iac_r]);
+ else
+ len = snprintf(value, size, "%s",
+ slm_iac_str[tsec->iac_wx]);
+ }
+ return len > size ? size : len;
+}
+
+static struct security_operations slm_security_ops = {
+ .bprm_check_security = slm_bprm_check_security,
+ .inode_permission = slm_inode_permission,
+ .inode_unlink = slm_inode_unlink,
+ .inode_create = slm_inode_create,
+ .inode_post_create = slm_inode_post_create,
+ .inode_mkdir = slm_inode_mkdir,
+ .inode_post_mkdir = slm_inode_post_mkdir,
+ .inode_rename = slm_inode_rename,
+ .inode_setxattr = slm_inode_setxattr,
+ .inode_free_security = slm_inode_free_security,
+ .inode_init_security = slm_inode_init_security,
+ .file_permission = slm_file_permission,
+ .socket_create = slm_socket_create,
+ .task_alloc_security = slm_task_alloc_security,
+ .task_free_security = slm_task_free_security,
+ .task_post_setuid = slm_task_post_setuid,
+ .getprocattr = slm_getprocattr,
+ .setprocattr = slm_setprocattr
+};
+
+static int __init init_slm (void)
+{
+ int error = 0;
+
+#ifdef CONFIG_IMA_MEASURE
+ slm_enable_ima = 1;
+ ima_init();
+#endif
+
+ slm_init_secfs();
+
+ slm_idx = 1;
+ if (register_security(&slm_security_ops, &slm_idx)) {
+ if (mod_reg_security ("slim", &slm_security_ops, &slm_idx)) {
+ dprintk(SLM_BASE, "%s: security hooks registration "
+ "failed\n", __FUNCTION__);
+ return -EINVAL;
+ }
+ secondary = 1;
+ }
+ dprintk(SLM_BASE, "%s: registered security hooks (idx = %d)\n",
+ __FUNCTION__, slm_idx);
+
+ sandbox_avail = 1;
+ return error;
+}
+
+static void __exit cleanup_slm (void)
+{
+ sandbox_avail = 0;
+
+ slm_cleanup_secfs();
+ if (secondary) {
+ if (mod_unreg_security ("slim", &slm_security_ops))
+ dprintk(SLM_BASE, "%s: failure unregistering module "
+ "with primary module.\n", __FUNCTION__);
+ } else if (unregister_security (&slm_security_ops)) {
+ dprintk(SLM_BASE, "%s: failure unregistering module "
+ "with the kernel\n", __FUNCTION__);
+ }
+#ifdef IMA_MEASURE
+ ima_cleanup();
+ slm_enable_ima = 0;
+#endif
+
+ dprintk(SLM_BASE, "%s: completed \n", __FUNCTION__);
+}
+
+module_init(init_slm);
+module_exit (cleanup_slm);
+
+module_param(slm_config, charp, 0444);
+MODULE_PARM_DESC(slm_config, "SLIM configuration file");
+
+module_param(slm_xattrName, charp, 0444);
+MODULE_PARM_DESC(slm_config, "SLIM extended attribute");
+
+module_param(slm_enable_ima, bool, 1);
+MODULE_PARM_DESC(slm_enable_ima, "SLIM enabling IMA");
+
+module_param(slm_debug, uint, 1);
+MODULE_DESCRIPTION("Simple Linux Integrity Module");
+
+MODULE_LICENSE("GPL");
diff -ruN linux-2.6.14-rc4-git1.orig/security/evm/slim/slm_secfs.c
linux-2.6.14-rc4-git1.new/security/evm/slim/slm_secfs.c
--- linux-2.6.14-rc4-git1.orig/security/evm/slim/slm_secfs.c 1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.14-rc4-git1.new/security/evm/slim/slm_secfs.c 2005-10-13 14:11:39.000000000 -0400
@@ -0,0 +1,146 @@
+/*
+ * SLIM securityfs support: debuggin control files
+ *
+ * Copyright (C) 2005 IBM Corporation
+ * Author: Mimi Zohar <zohar@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 <asm/uaccess.h>
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/security.h>
+#include "slim.h"
+
+static struct dentry *slim_dir, *slim_level;
+static struct dentry *slim_debug_dir, *slim_integrity, *slim_secrecy,
+ *slim_verbose;
+
+static ssize_t slm_read_level(struct file *file, char __user *buf,
+ size_t buflen, loff_t *ppos)
+{
+ struct task_struct *cur_tsk = current;
+ struct slm_tsec_data *cur_tsec = NULL;
+ ssize_t len = 0;
+ char *page;
+
+ page = (char *)__get_free_page(GFP_KERNEL);
+ if (!page)
+ return -ENOMEM;
+
+ cur_tsec = slm_get_task(cur_tsk->security);
+ if (!cur_tsec)
+ len = sprintf(page, "level: unknown\n");
+ else {
+ if (cur_tsec->iac_wx != cur_tsec->iac_r)
+ len = sprintf(page, "level: guard wx:%s r:%s\n",
+ slm_iac_str[cur_tsec->iac_wx],
+ slm_iac_str[cur_tsec->iac_r]);
+ else
+ len = sprintf(page, "level: %s\n",
+ slm_iac_str[cur_tsec->iac_wx]);
+ }
+ len = simple_read_from_buffer(buf, buflen, ppos, page, len);
+ free_page((unsigned long)page);
+ return len;
+}
+
+static int slm_open_debug(struct inode *inode, struct file *file)
+{
+ if (inode->u.generic_ip)
+ file->private_data = inode->u.generic_ip;
+ return 0;
+}
+
+static ssize_t slm_read_debug(struct file *file, char __user *buf,
+ size_t buflen, loff_t *ppos)
+{
+ ssize_t len = 0;
+ char *page;
+
+ page = (char *)__get_free_page(GFP_KERNEL);
+ if (!page)
+ return -ENOMEM;
+
+ if (strcmp(file->private_data,"integrity") == 0)
+ len = sprintf(page, "slm_debug: integrity %s\n",
+ ((slm_debug & SLM_INTEGRITY) == SLM_INTEGRITY)
+ ? "ON" : "OFF");
+ else if (strcmp(file->private_data,"secrecy") == 0)
+ len = sprintf(page, "slm_debug: secrecy %s\n",
+ ((slm_debug & SLM_SECRECY) == SLM_SECRECY)
+ ? "ON" : "OFF");
+ else if (strcmp(file->private_data,"verbose") == 0)
+ len = sprintf(page, "evm_debug: verbose %s\n",
+ ((slm_debug & SLM_VERBOSE) == SLM_VERBOSE)
+ ? "ON" : "OFF");
+ len = simple_read_from_buffer(buf, buflen, ppos, page, len);
+ free_page((unsigned long)page);
+ return len;
+}
+
+static ssize_t slm_write_debug(struct file *file, const char __user *buf,
+ size_t buflen, loff_t *ppos)
+{
+ char flag;
+
+ if (copy_from_user(&flag, buf, 1))
+ return -EFAULT;
+
+ if (strcmp(file->private_data,"integrity") == 0)
+ slm_debug = (flag == '0') ? slm_debug & ~SLM_INTEGRITY :
+ slm_debug | SLM_INTEGRITY;
+ else if (strcmp(file->private_data,"xattr") == 0)
+ slm_debug = (flag == '0') ? slm_debug & ~SLM_SECRECY :
+ slm_debug | SLM_SECRECY;
+ else if (strcmp(file->private_data,"crypto") == 0)
+ slm_debug = (flag == '0') ? slm_debug & ~SLM_VERBOSE :
+ slm_debug | SLM_VERBOSE;
+ return buflen;
+}
+
+static struct file_operations slm_level_ops = {
+ .read = slm_read_level,
+};
+
+static struct file_operations slm_debug_ops = {
+ .read = slm_read_debug,
+ .write = slm_write_debug,
+ .open = slm_open_debug,
+};
+
+void slm_init_secfs(void)
+{
+ if ((slim_dir = securityfs_create_dir("slim", NULL)) == NULL )
+ return;
+ slim_level = securityfs_create_file("level", S_IRUGO,
+ slim_dir, NULL, &slm_level_ops);
+
+ if ((slim_debug_dir = securityfs_create_dir("debug", slim_dir)) == NULL)
+ return;
+ slim_integrity = securityfs_create_file("integrity", S_IRUSR | S_IRGRP,
+ slim_debug_dir, "integrity", &slm_debug_ops);
+ slim_secrecy = securityfs_create_file("secrecy", S_IRUSR | S_IRGRP,
+ slim_debug_dir, "secrecy", &slm_debug_ops);
+ slim_verbose = securityfs_create_file("verbose", S_IRUSR | S_IRGRP,
+ slim_debug_dir, "verbose", &slm_debug_ops);
+ return;
+}
+
+
+void slm_cleanup_secfs(void)
+{
+ securityfs_remove(slim_integrity);
+ securityfs_remove(slim_secrecy);
+ securityfs_remove(slim_verbose);
+ securityfs_remove(slim_debug_dir);
+
+ securityfs_remove(slim_level);
+ securityfs_remove(slim_dir);
+}
+