| From: |
| Casey Schaufler <casey@schaufler-ca.com> |
| To: |
| LSM <linux-security-module@vger.kernel.org> |
| Subject: |
| [PATCH][RFC v1] Datastate: LSM supporting file content based access
controls |
| Date: |
| Sat, 07 Aug 2010 12:03:22 -0700 |
| Message-ID: |
| <4C5DADFA.3060104@schaufler-ca.com> |
| Cc: |
| Casey Schaufler <casey@schaufler-ca.com> |
| Archive-link: |
| Article, Thread
|
From: Casey Schaufler <casey@schaufler-ca.com>
This is the initial RFC version of the Datastate LSM.
The datastate LSM provides three functions in
support of file content based access control.
It identifies files that are modified with an
extended attribute DATASTATE. The pathname of
the modified file is announced in the pseudo
file /datastate/changed. Finally, file opens
are controlled by subject/object/access rules
that can be specified by the administrator.
This is an early RFC version. There are known
issues and a couple of places where feedback
from a larger audience than has been available
to date is going to influence the project
direction.
Applications providing additional components
of the system are available from
http://schaufler-ca.com/datastate
More information will be presented at LinuxCon
and with subsequent RFC versions.
Signed-off-by: Casey Schaufler <casey@schaufler-ca.com>
---
include/linux/lsm_audit.h | 9
security/Kconfig | 8
security/Makefile | 2
security/datastate/Kconfig | 9
security/datastate/Makefile | 7
security/datastate/datastate.h | 256 +++++
security/datastate/datastate_access.c | 352 +++++++
security/datastate/datastate_lsm.c | 1060 ++++++++++++++++++++++++
security/datastate/datastatefs.c | 589 +++++++++++++
9 files changed, 2291 insertions(+), 1 deletion(-)
diff -uprN -X linux-2.6/Documentation/dontdiff linux-2.6/include/linux/lsm_audit.h linux-0807/include/linux/lsm_audit.h
--- linux-2.6/include/linux/lsm_audit.h 2010-08-07 09:15:03.000000000 -0700
+++ linux-0807/include/linux/lsm_audit.h 2010-08-07 09:35:21.000000000 -0700
@@ -71,6 +71,15 @@ struct common_audit_data {
} u;
/* this union contains LSM specific data */
union {
+#ifdef CONFIG_SECURITY_DATASTATE
+ /* DataState data */
+ struct datastate_audit_data {
+ const char *function;
+ char *subject;
+ char *object;
+ int result;
+ } datastate_audit_data;
+#endif
#ifdef CONFIG_SECURITY_SMACK
/* SMACK data */
struct smack_audit_data {
diff -uprN -X linux-2.6/Documentation/dontdiff linux-2.6/security/datastate/datastate_access.c linux-0807/security/datastate/datastate_access.c
--- linux-2.6/security/datastate/datastate_access.c 1969-12-31 16:00:00.000000000 -0800
+++ linux-0807/security/datastate/datastate_access.c 2010-08-07 09:35:21.000000000 -0700
@@ -0,0 +1,352 @@
+/*
+ * Copyright (C) 2010 Casey Schaufler <casey@schaufler-ca.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.
+ *
+ * Author:
+ * Casey Schaufler <casey@schaufler-ca.com>
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/sched.h>
+#include "datastate.h"
+
+struct datastate_known datastate_known_system = {
+ .ds_known = "-system",
+ .ds_secid = 2,
+};
+
+struct datastate_known datastate_known_user = {
+ .ds_known = "-user",
+ .ds_secid = 3,
+};
+
+struct datastate_known datastate_known_open = {
+ .ds_known = "-open",
+ .ds_secid = 4,
+};
+
+struct datastate_known datastate_known_closed = {
+ .ds_known = "-closed",
+ .ds_secid = 5,
+};
+
+struct datastate_known datastate_known_inherent = {
+ .ds_known = "-inherent",
+ .ds_secid = 6,
+};
+
+struct datastate_known datastate_known_new = {
+ .ds_known = "-new",
+ .ds_secid = 7,
+};
+
+LIST_HEAD(datastate_known_list);
+
+/*
+ * The initial value needs to be bigger than any of the
+ * known values above.
+ */
+static u32 datastate_next_secid = 10;
+
+/*
+ * what events do we log
+ * can be overwritten at run-time by /datastate/logging
+ */
+int log_policy = DATASTATE_AUDIT_DENIED;
+
+/**
+ * datastate_access - determine if a subject has a specific access to an object
+ * @subject_label: a pointer to the subject's Datastate label
+ * @object_label: a pointer to the object's Datastate label
+ * @request: the access requested, in "MAY" format
+ * @a : a pointer to the audit data
+ *
+ * This function looks up the subject/object pair in the
+ * access rule list and returns 0 if the access is permitted,
+ * non zero otherwise.
+ */
+int datastate_access(char *subject_label, char *object_label, int request,
+ struct datastate_audit_info *a)
+{
+ struct datastate_rule *srp;
+ int found = 0;
+ int rc = 0;
+
+ /*
+ * Hardcoded comparisons.
+ *
+ * A system subject can access any object.
+ */
+ if (subject_label == datastate_known_system.ds_known)
+ goto out_audit;
+
+ /*
+ * An user subject can access any object.
+ */
+ if (subject_label == datastate_known_user.ds_known)
+ goto out_audit;
+ /*
+ * Beyond here an explicit relationship is required.
+ * If the requested access is contained in the available
+ * access (e.g. read is included in readwrite) it's
+ * good.
+ */
+ rcu_read_lock();
+ list_for_each_entry_rcu(srp, &datastate_rule_list, list) {
+ if (srp->ds_subject == subject_label) {
+ if (srp->ds_object == object_label) {
+ found = 1;
+ if (srp->ds_access == DATASTATE_MAY_NOT)
+ rc = -EACCES;
+ break;
+ }
+ }
+ }
+ rcu_read_unlock();
+
+ /*
+ * In the case where there is no explicit relationship.
+ */
+ if (!found && object_label == datastate_known_open.ds_known)
+ rc = -EACCES;
+
+out_audit:
+#ifdef CONFIG_AUDIT
+ if (a)
+ datastate_log(subject_label, object_label, request, rc, a);
+#endif
+ return rc;
+}
+
+/**
+ * datastate_curacc - determine if current has a specific access to an object
+ * @obj_label: a pointer to the object's Datastate label
+ * @mode: the access requested, in "MAY" format
+ * @a : common audit data
+ *
+ * This function checks the current subject label/object label pair
+ * in the access rule list and returns 0 if the access is permitted,
+ * non zero otherwise. It allows that current may have the capability
+ * to override the rules.
+ */
+int datastate_curacc(char *obj_label, u32 mode, struct datastate_audit_info *a)
+{
+ int rc;
+ char *sp = current_security();
+
+ rc = datastate_access(sp, obj_label, mode, NULL);
+ if (rc == 0)
+ goto out_audit;
+
+ if (capable(CAP_MAC_OVERRIDE))
+ return 0;
+
+out_audit:
+#ifdef CONFIG_AUDIT
+ if (a)
+ datastate_log(sp, obj_label, mode, rc, a);
+#endif
+ return rc;
+}
+
+#ifdef CONFIG_AUDIT
+/**
+ * datastate_log_callback - DATASTATE specific information
+ * will be called by generic audit code
+ * @ab : the audit_buffer
+ * @a : audit_data
+ *
+ */
+static void datastate_log_callback(struct audit_buffer *ab, void *a)
+{
+ struct common_audit_data *ad = a;
+ struct datastate_audit_data *sad = &ad->datastate_audit_data;
+ audit_log_format(ab, "lsm=DATASTATE fn=%s action=%s",
+ ad->datastate_audit_data.function,
+ sad->result ? "denied" : "granted");
+ audit_log_format(ab, " subject=");
+ audit_log_untrustedstring(ab, sad->subject);
+ audit_log_format(ab, " object=");
+ audit_log_untrustedstring(ab, sad->object);
+}
+
+/**
+ * datastate_log - Audit the granting or denial of permissions.
+ * @subject_label : datastate label of the requester
+ * @object_label : datastate label of the object being accessed
+ * @request: requested permissions
+ * @result: result from datastate_access
+ * @a: auxiliary audit data
+ *
+ * Audit the granting or denial of permissions in accordance
+ * with the policy.
+ */
+void datastate_log(char *subject_label, char *object_label, int request,
+ int result, struct datastate_audit_info *ad)
+{
+ struct datastate_audit_data *sad;
+ struct common_audit_data *a = &ad->a;
+
+ /* check if we have to log the current event */
+ if (result != 0 && (log_policy & DATASTATE_AUDIT_DENIED) == 0)
+ return;
+ if (result == 0 && (log_policy & DATASTATE_AUDIT_ACCEPT) == 0)
+ return;
+
+ if (a->datastate_audit_data.function == NULL)
+ a->datastate_audit_data.function = "unknown";
+
+ /* end preparing the audit data */
+ sad = &a->datastate_audit_data;
+ sad->subject = subject_label;
+ sad->object = object_label;
+ sad->result = result;
+ a->lsm_pre_audit = datastate_log_callback;
+
+ common_lsm_audit(a);
+}
+#else /* #ifdef CONFIG_AUDIT */
+void datastate_log(char *subject_label, char *object_label, int request,
+ int result, struct datastate_audit_info *ad)
+{
+}
+#endif
+
+static DEFINE_MUTEX(datastate_known_lock);
+
+/**
+ * datastate_import_entry - import a label, return the list entry
+ * @string: a text string that might be a Datastate label
+ * @len: the maximum size, or zero if it is NULL terminated.
+ *
+ * Returns a pointer to the entry in the label list that
+ * matches the passed string, adding it if necessary.
+ */
+struct datastate_known *datastate_import_entry(const char *string, int len)
+{
+ struct datastate_known *skp;
+ char datastate[DATASTATE_LABELLEN];
+ int found;
+ int i;
+
+ if (len <= 0 || len > DATASTATE_MAXLEN)
+ len = DATASTATE_MAXLEN;
+
+ for (i = 0, found = 0; i < DATASTATE_LABELLEN; i++) {
+ if (found)
+ datastate[i] = '\0';
+ else if (i >= len || string[i] > '~' || string[i] <= ' ' ||
+ string[i] == '/' || string[i] == '"' ||
+ string[i] == '\\' || string[i] == '\'') {
+ datastate[i] = '\0';
+ found = 1;
+ } else
+ datastate[i] = string[i];
+ }
+
+ if (datastate[0] == '\0')
+ return NULL;
+
+ mutex_lock(&datastate_known_lock);
+
+ found = 0;
+ list_for_each_entry_rcu(skp, &datastate_known_list, list) {
+ if (strncmp(skp->ds_known, datastate, DATASTATE_MAXLEN) == 0) {
+ found = 1;
+ break;
+ }
+ }
+
+ if (found == 0) {
+ skp = kzalloc(sizeof(struct datastate_known), GFP_KERNEL);
+ if (skp != NULL) {
+ strncpy(skp->ds_known, datastate, DATASTATE_MAXLEN);
+ skp->ds_secid = datastate_next_secid++;
+ /*
+ * Make sure that the entry is actually
+ * filled before putting it on the list.
+ */
+ list_add_rcu(&skp->list, &datastate_known_list);
+ }
+ }
+
+ mutex_unlock(&datastate_known_lock);
+
+ return skp;
+}
+
+/**
+ * datastate_import - import a datastate label
+ * @string: a text string that might be a Datastate label
+ * @len: the maximum size, or zero if it is NULL terminated.
+ *
+ * Returns a pointer to the label in the label list that
+ * matches the passed string, adding it if necessary.
+ */
+char *datastate_import(const char *string, int len)
+{
+ struct datastate_known *skp;
+
+ /* labels cannot begin with a '-' */
+ if (string[0] == '-')
+ return NULL;
+ skp = datastate_import_entry(string, len);
+ if (skp == NULL)
+ return NULL;
+ return skp->ds_known;
+}
+
+/**
+ * datastate_from_secid - find the Datastate label associated with a secid
+ * @secid: an integer that might be associated with a Datastate label
+ *
+ * Returns a pointer to the appropraite Datastate label if there is one,
+ * otherwise a pointer to the invalid Datastate label.
+ */
+char *datastate_from_secid(const u32 secid)
+{
+ struct datastate_known *skp;
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(skp, &datastate_known_list, list) {
+ if (skp->ds_secid == secid) {
+ rcu_read_unlock();
+ return skp->ds_known;
+ }
+ }
+
+ /*
+ * If we got this far someone asked for the translation
+ * of a secid that is not on the list.
+ */
+ rcu_read_unlock();
+ return datastate_known_inherent.ds_known;
+}
+
+/**
+ * datastate_to_secid - find the secid associated with a Datastate label
+ * @datastate: the Datastate label
+ *
+ * Returns the appropriate secid if there is one,
+ * otherwise 0
+ */
+u32 datastate_to_secid(const char *datastate)
+{
+ struct datastate_known *skp;
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(skp, &datastate_known_list, list) {
+ if (strncmp(skp->ds_known, datastate, DATASTATE_MAXLEN) == 0) {
+ rcu_read_unlock();
+ return skp->ds_secid;
+ }
+ }
+ rcu_read_unlock();
+ return 0;
+}
diff -uprN -X linux-2.6/Documentation/dontdiff linux-2.6/security/datastate/datastatefs.c linux-0807/security/datastate/datastatefs.c
--- linux-2.6/security/datastate/datastatefs.c 1969-12-31 16:00:00.000000000 -0800
+++ linux-0807/security/datastate/datastatefs.c 2010-08-07 09:35:21.000000000 -0700
@@ -0,0 +1,589 @@
+/*
+ * Copyright (C) 2010 Casey Schaufler <casey@schaufler-ca.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.
+ *
+ * Authors:
+ * Casey Schaufler <casey@schaufler-ca.com>
+ */
+
+#include <linux/kernel.h>
+#include <linux/vmalloc.h>
+#include <linux/security.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+/*
+#include <net/net_namespace.h>
+#include <net/netlabel.h>
+*/
+#include <linux/seq_file.h>
+#include <linux/ctype.h>
+#include <linux/audit.h>
+#include "datastate.h"
+
+/*
+ * datastatefs pseudo filesystem.
+ */
+
+enum datastate_inos {
+ DATASTATE_ROOT_INO = 2,
+ DATASTATE_LOAD = 3, /* load policy */
+ DATASTATE_LOGGING = 4, /* logging */
+ DATASTATE_CHANGED = 5, /* changed files to scan */
+};
+
+/*
+ * List locks
+ */
+static DEFINE_MUTEX(datastate_list_lock);
+static DEFINE_MUTEX(datastate_changed_lock);
+
+LIST_HEAD(datastate_rule_list);
+LIST_HEAD(datastate_changed_list);
+
+#define SEQ_READ_FINISHED 1
+
+
+/*
+ * Values for parsing MAC rules
+ * DATASTATE_ACCESSLEN: Length for a rule access field
+ * DATASTATE_LOADLEN: Datastate rule length
+ */
+#define DATASTATE_ACCESSLEN 2
+#define DATASTATE_LOADLEN (DATASTATE_LABELLEN + DATASTATE_LABELLEN + \
+ DATASTATE_ACCESSLEN)
+
+/*
+ * Seq_file read operations for /datastate/load
+ */
+
+static void *load_seq_start(struct seq_file *s, loff_t *pos)
+{
+ if (*pos == SEQ_READ_FINISHED)
+ return NULL;
+ if (list_empty(&datastate_rule_list))
+ return NULL;
+ return datastate_rule_list.next;
+}
+
+static void *load_seq_next(struct seq_file *s, void *v, loff_t *pos)
+{
+ struct list_head *list = v;
+
+ if (list_is_last(list, &datastate_rule_list)) {
+ *pos = SEQ_READ_FINISHED;
+ return NULL;
+ }
+ return list->next;
+}
+
+static int load_seq_show(struct seq_file *s, void *v)
+{
+ struct list_head *list = v;
+ struct datastate_rule *srp =
+ list_entry(list, struct datastate_rule, list);
+
+ seq_printf(s, "%s %s ", (char *)srp->ds_subject,
+ (char *)srp->ds_object);
+
+ if (srp->ds_access == DATASTATE_MAY_NOT)
+ seq_printf(s, "N\n");
+ else
+ seq_printf(s, "Y\n");
+
+ return 0;
+}
+
+static void load_seq_stop(struct seq_file *s, void *v)
+{
+ /* No-op */
+}
+
+static const struct seq_operations load_seq_ops = {
+ .start = load_seq_start,
+ .next = load_seq_next,
+ .show = load_seq_show,
+ .stop = load_seq_stop,
+};
+
+/**
+ * datastate_open_load - open() for /datastate/load
+ * @inode: inode structure representing file
+ * @file: "load" file pointer
+ *
+ * For reading, use load_seq_* seq_file reading operations.
+ */
+static int datastate_open_load(struct inode *inode, struct file *file)
+{
+ return seq_open(file, &load_seq_ops);
+}
+
+/**
+ * datastate_set_access - add a rule to the rule list
+ * @srp: the new rule to add
+ *
+ * Looks through the current subject/object/access list for
+ * the subject/object pair and replaces the access that was
+ * there. If the pair isn't found add it with the specified
+ * access.
+ *
+ * Returns 0 if nothing goes wrong or -ENOMEM if it fails
+ * during the allocation of the new pair to add.
+ */
+static int datastate_set_access(struct datastate_rule *srp)
+{
+ struct datastate_rule *sp;
+ int ret = 0;
+ int found;
+ mutex_lock(&datastate_list_lock);
+
+ found = 0;
+ list_for_each_entry_rcu(sp, &datastate_rule_list, list) {
+ if (sp->ds_subject == srp->ds_subject &&
+ sp->ds_object == srp->ds_object) {
+ found = 1;
+ sp->ds_access = srp->ds_access;
+ break;
+ }
+ }
+ if (found == 0)
+ list_add_rcu(&srp->list, &datastate_rule_list);
+
+ mutex_unlock(&datastate_list_lock);
+
+ return ret;
+}
+
+/**
+ * datastate_write_load - write() for /datastate/load
+ * @file: file pointer, not actually used
+ * @buf: where to get the data from
+ * @count: bytes sent
+ * @ppos: where to start - must be 0
+ *
+ * Get one datastate access rule from above.
+ * The format is exactly:
+ * char subject[DATASTATE_LABELLEN]
+ * char object[DATASTATE_LABELLEN]
+ * char access[DATASTATE_ACCESSLEN]
+ *
+ * writes must be
+ * DATASTATE_LABELLEN+DATASTATE_LABELLEN+DATASTATE_ACCESSLEN
+ * bytes.
+ */
+static ssize_t datastate_write_load(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct datastate_rule *rule;
+ char *data;
+ int rc = -EINVAL;
+
+ /*
+ * Must have privilege.
+ * No partial writes.
+ * Enough data must be present.
+ */
+ if (!capable(CAP_MAC_ADMIN))
+ return -EPERM;
+
+ if (*ppos != 0 || count != DATASTATE_LOADLEN)
+ return -EINVAL;
+
+ data = kzalloc(count, GFP_KERNEL);
+ if (data == NULL)
+ return -ENOMEM;
+
+ if (copy_from_user(data, buf, count) != 0) {
+ rc = -EFAULT;
+ goto out;
+ }
+
+ rule = kzalloc(sizeof(*rule), GFP_KERNEL);
+ if (rule == NULL) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ rule->ds_subject = datastate_import(data, 0);
+ if (rule->ds_subject == NULL)
+ goto out_free_rule;
+
+ rule->ds_object = datastate_import(data + DATASTATE_LABELLEN, 0);
+ if (rule->ds_object == NULL)
+ goto out_free_rule;
+
+ switch (data[DATASTATE_LABELLEN + DATASTATE_LABELLEN]) {
+ case 'n':
+ case 'N':
+ rule->ds_access = DATASTATE_MAY_NOT;
+ break;
+ case 'y':
+ case 'Y':
+ rule->ds_access = DATASTATE_MAY;
+ break;
+ default:
+ goto out_free_rule;
+ }
+
+ rc = datastate_set_access(rule);
+
+ if (rc != 0)
+ rc = count;
+ goto out;
+
+out_free_rule:
+ kfree(rule);
+out:
+ kfree(data);
+ return rc;
+}
+
+static const struct file_operations datastate_load_ops = {
+ .open = datastate_open_load,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .write = datastate_write_load,
+ .release = seq_release,
+};
+/**
+ * datastate_read_logging - read() for /datastate/logging
+ * @filp: file pointer, not actually used
+ * @buf: where to put the result
+ * @cn: maximum to send along
+ * @ppos: where to start
+ *
+ * Returns number of bytes read or error code, as appropriate
+ */
+static ssize_t datastate_read_logging(struct file *filp, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ char temp[32];
+ ssize_t rc;
+
+ if (*ppos != 0)
+ return 0;
+
+ sprintf(temp, "%d\n", log_policy);
+ rc = simple_read_from_buffer(buf, count, ppos, temp, strlen(temp));
+ return rc;
+}
+
+/**
+ * datastate_write_logging - write() for /datastate/logging
+ * @file: file pointer, not actually used
+ * @buf: where to get the data from
+ * @count: bytes sent
+ * @ppos: where to start
+ *
+ * Returns number of bytes written or error code, as appropriate
+ */
+static ssize_t datastate_write_logging(struct file *file,
+ const char __user *buf, size_t count,
+ loff_t *ppos)
+{
+ char temp[32];
+ int i;
+
+ if (!capable(CAP_MAC_ADMIN))
+ return -EPERM;
+
+ if (count >= sizeof(temp) || count == 0)
+ return -EINVAL;
+
+ if (copy_from_user(temp, buf, count) != 0)
+ return -EFAULT;
+
+ temp[count] = '\0';
+
+ if (sscanf(temp, "%d", &i) != 1)
+ return -EINVAL;
+ if (i < 0 || i > 3)
+ return -EINVAL;
+ log_policy = i;
+ return count;
+}
+
+
+
+static const struct file_operations datastate_logging_ops = {
+ .read = datastate_read_logging,
+ .write = datastate_write_logging,
+};
+
+
+/*
+ * Seq_file read operations for /datastate/changed
+ */
+
+static void *changed_seq_start(struct seq_file *s, loff_t *pos)
+{
+ if (*pos == SEQ_READ_FINISHED)
+ return NULL;
+
+ if (list_empty(&datastate_changed_list))
+ return NULL;
+
+ return datastate_changed_list.next;
+}
+
+static void *changed_seq_next(struct seq_file *s, void *v, loff_t *pos)
+{
+ struct list_head *list = v;
+ struct list_head *next = NULL;
+ struct datastate_changed *dcp;
+
+ /*
+ * This is an non-obvious if/else
+ * On the "if" side next should be set to NULL, but that is
+ * done above in the declaration.
+ */
+ if (list_is_last(list, &datastate_changed_list))
+ *pos = SEQ_READ_FINISHED;
+ else
+ next = list->next;
+
+ dcp = list_entry(list, struct datastate_changed, list);
+ datastate_del_changed(dcp);
+
+ return next;
+}
+
+static int changed_seq_show(struct seq_file *s, void *v)
+{
+ struct list_head *list = v;
+ struct datastate_changed *dcp =
+ list_entry(list, struct datastate_changed, list);
+
+ seq_printf(s, "%s\n", dcp->ds_changed);
+
+ return 0;
+}
+
+static void changed_seq_stop(struct seq_file *s, void *v)
+{
+ /* No-op */
+}
+
+static const struct seq_operations changed_seq_ops = {
+ .start = changed_seq_start,
+ .next = changed_seq_next,
+ .show = changed_seq_show,
+ .stop = changed_seq_stop,
+};
+
+/**
+ * datastate_add_changed - add an entry to the changed file list
+ * @filename: name of the file changed in an allocated buffer.
+ *
+ */
+void datastate_add_changed(char *filename)
+{
+ struct datastate_changed *dcp;
+ int namelen;
+
+ namelen = strlen(filename);
+ if (namelen < 1 || namelen > PATH_MAX)
+ return;
+
+ dcp = kzalloc(sizeof(*dcp), GFP_KERNEL);
+ if (dcp == NULL)
+ return;
+
+ dcp->ds_changed = kzalloc(namelen + 1, GFP_KERNEL);
+ if (dcp->ds_changed == NULL) {
+ kfree(dcp);
+ return;
+ }
+
+ strcpy(dcp->ds_changed, filename);
+
+ mutex_lock(&datastate_list_lock);
+
+ list_add_tail_rcu(&dcp->list, &datastate_changed_list);
+
+ mutex_unlock(&datastate_list_lock);
+}
+
+/**
+ * datastate_del_changed - remove an entry from the changed list
+ * @dcp: the entry to remove and delete
+ *
+ */
+void datastate_del_changed(struct datastate_changed *dcp)
+{
+ mutex_lock(&datastate_list_lock);
+
+ list_del_rcu(&dcp->list);
+
+ mutex_unlock(&datastate_list_lock);
+
+ kfree(dcp->ds_changed);
+ kfree(dcp);
+}
+
+/**
+ * datastate_open_changed - open() for /datastate/changed
+ * @inode: inode structure representing file
+ * @file: "changed" file pointer
+ *
+ * For reading, use changed_seq_* seq_file reading operations.
+ */
+static int datastate_open_changed(struct inode *inode, struct file *file)
+{
+ return seq_open(file, &changed_seq_ops);
+}
+
+/**
+ * datastate_write_changed - write() for /datastate/changed
+ * @file: file pointer, not actually used
+ * @buf: where to get the data from
+ * @count: bytes sent
+ * @ppos: where to start - must be 0
+ *
+ * Get one datastate access rule from above.
+ * The format is exactly:
+ * char subject[DATASTATE_LABELLEN]
+ * char object[DATASTATE_LABELLEN]
+ * char access[DATASTATE_ACCESSLEN]
+ *
+ * write size must be:
+ * DATASTATE_LABELLEN+DATASTATE_LABELLEN+DATASTATE_ACCESSLEN
+ */
+static ssize_t datastate_write_changed(struct file *file,
+ const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ char *filename;
+
+ /*
+ * Must have privilege.
+ * No partial writes.
+ */
+ if (!capable(CAP_MAC_ADMIN))
+ return -EPERM;
+
+ if (*ppos != 0 || count > PATH_MAX || count < 1)
+ return -EINVAL;
+
+ filename = kzalloc(count + 1, GFP_KERNEL);
+ if (filename == NULL)
+ return -ENOMEM;
+
+ if (copy_from_user(filename, buf, count) != 0) {
+ kfree(filename);
+ return -EFAULT;
+ }
+
+ datastate_add_changed(filename);
+
+ return count;
+}
+
+static const struct file_operations datastate_changed_ops = {
+ .open = datastate_open_changed,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .write = datastate_write_changed,
+ .release = seq_release,
+};
+
+/**
+ * datastate_fill_super - fill the /datastatefs superblock
+ * @sb: the empty superblock
+ * @data: unused
+ * @silent: unused
+ *
+ * Fill in the well known entries for /datastate
+ *
+ * Returns 0 on success, an error code on failure
+ */
+static int datastate_fill_super(struct super_block *sb, void *data, int silent)
+{
+ int rc;
+ struct inode *root_inode;
+
+ static struct tree_descr datastate_files[] = {
+ [DATASTATE_LOAD] = {
+ "load", &datastate_load_ops, S_IRUGO|S_IWUSR},
+ [DATASTATE_LOGGING] = {
+ "logging", &datastate_logging_ops, S_IRUGO|S_IWUSR},
+ [DATASTATE_CHANGED] = {
+ "changed", &datastate_changed_ops, S_IRUGO|S_IWUSR},
+ /* last one */ {""}
+ };
+
+ rc = simple_fill_super(sb, DATASTATE_MAGIC, datastate_files);
+ if (rc != 0) {
+ printk(KERN_ERR "%s failed %d while creating inodes\n",
+ __func__, rc);
+ return rc;
+ }
+
+ root_inode = sb->s_root->d_inode;
+
+ return 0;
+}
+
+/**
+ * datastate_get_sb - get the datastatefs superblock
+ * @fs_type: passed along without comment
+ * @flags: passed along without comment
+ * @dev_name: passed along without comment
+ * @data: passed along without comment
+ * @mnt: passed along without comment
+ *
+ * Just passes everything along.
+ *
+ * Returns what the lower level code does.
+ */
+static int datastate_get_sb(struct file_system_type *fs_type,
+ int flags, const char *dev_name, void *data,
+ struct vfsmount *mnt)
+{
+ return get_sb_single(fs_type, flags, data, datastate_fill_super, mnt);
+}
+
+static struct file_system_type datastate_fs_type = {
+ .name = "datastatefs",
+ .get_sb = datastate_get_sb,
+ .kill_sb = kill_litter_super,
+};
+
+static struct vfsmount *datastatefs_mount;
+
+/**
+ * init_datastate_fs - get the datastatefs superblock
+ *
+ * register the datastatefs
+ *
+ * Do not register datastatefs if Datastate wasn't enabled
+ * on boot. We can not put this method normally under the
+ * datastate_init() code path since the security subsystem get
+ * initialized before the vfs caches.
+ *
+ * Returns true if we were not chosen on boot or if
+ * we were chosen and filesystem registration succeeded.
+ */
+static int __init init_datastate_fs(void)
+{
+ int err;
+
+ if (!security_module_enable(&datastate_ops))
+ return 0;
+
+ err = register_filesystem(&datastate_fs_type);
+ if (!err) {
+ datastatefs_mount = kern_mount(&datastate_fs_type);
+ if (IS_ERR(datastatefs_mount)) {
+ printk(KERN_ERR "datastatefs: could not mount!\n");
+ err = PTR_ERR(datastatefs_mount);
+ datastatefs_mount = NULL;
+ }
+ }
+
+ return err;
+}
+
+device_initcall(init_datastate_fs);
diff -uprN -X linux-2.6/Documentation/dontdiff linux-2.6/security/datastate/datastate.h linux-0807/security/datastate/datastate.h
--- linux-2.6/security/datastate/datastate.h 1969-12-31 16:00:00.000000000 -0800
+++ linux-0807/security/datastate/datastate.h 2010-08-07 09:35:21.000000000 -0700
@@ -0,0 +1,256 @@
+/*
+ * Copyright (C) 2010 Casey Schaufler <casey@schaufler-ca.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.
+ *
+ * Author:
+ * Casey Schaufler <casey@schaufler-ca.com>
+ *
+ */
+
+#ifndef _SECURITY_DATASTATE_H
+#define _SECURITY_DATASTATE_H
+
+#include <linux/capability.h>
+/*
+#include <linux/spinlock.h>
+*/
+#include <linux/security.h>
+/*
+#include <linux/in.h>
+#include <net/netlabel.h>
+*/
+#include <linux/list.h>
+#include <linux/rculist.h>
+#include <linux/lsm_audit.h>
+
+/*
+ * Why 23? It's arbitrary really. Smack uses 23, and why
+ * rock the boat?
+ */
+#define DATASTATE_MAXLEN 23
+#define DATASTATE_LABELLEN (DATASTATE_MAXLEN+1)
+
+/*
+ * Inode datastate data
+ */
+struct inode_datastate {
+ char *ds_inode; /* label of the fso */
+ struct mutex ds_lock; /* initialization lock */
+};
+
+/*
+ * A label access rule.
+ */
+struct datastate_rule {
+ struct list_head list;
+ char *ds_subject;
+ char *ds_object;
+ int ds_access;
+};
+
+/*
+ * A changed file list entry.
+ */
+struct datastate_changed {
+ struct list_head list;
+ char *ds_changed;
+};
+
+/*
+ * This is the repository for labels seen so that it is
+ * not necessary to keep allocating tiny chuncks of memory
+ * and so that they can be shared.
+ *
+ * Labels are never modified in place. Anytime a label
+ * is imported (e.g. xattrset on a file) the list is checked
+ * for it and it is added if it doesn't exist. The address
+ * is passed out in either case. Entries are added, but
+ * never deleted.
+ *
+ * Since labels are hanging around anyway it doesn't
+ * hurt to maintain a secid for those awkward situations
+ * where kernel components that ought to use LSM independent
+ * interfaces don't. The secid should go away when all of
+ * these components have been repaired.
+ */
+struct datastate_known {
+ struct list_head list;
+ char ds_known[DATASTATE_LABELLEN];
+ u32 ds_secid;
+};
+
+/*
+ * xattr names
+ */
+#define XATTR_DATASTATE_SUFFIX "DATASTATE"
+#define XATTR_NAME_DATASTATE XATTR_SECURITY_PREFIX XATTR_DATASTATE_SUFFIX
+
+/*
+ * datastatefs magic number
+ * datastatefs macic number
+ */
+#define DATASTATE_MAGIC 0x41544144 /* "DATA" */
+
+/*
+ * A limit on the number of entries in the lists
+ * makes some of the list administration easier.
+ */
+#define DATASTATE_LIST_MAX 10000
+
+#define DATASTATE_MAY_NOT 0
+#define DATASTATE_MAY 1
+
+/*
+ * Datastate audit data; is empty if CONFIG_AUDIT not set
+ * to save some stack
+ */
+struct datastate_audit_info {
+#ifdef CONFIG_AUDIT
+ struct common_audit_data a;
+#endif
+};
+/*
+ * These functions are in datastate_lsm.c
+ */
+struct inode_datastate *new_inode_datastate(char *);
+
+/*
+ * These functions are in datastate_access.c
+ */
+int datastate_access(char *, char *, int, struct datastate_audit_info *);
+int datastate_curacc(char *, u32, struct datastate_audit_info *);
+char *datastate_from_secid(const u32);
+char *datastate_import(const char *, int);
+struct datastate_known *datastate_import_entry(const char *, int);
+u32 datastate_to_secid(const char *);
+void datastate_add_changed(char *);
+void datastate_del_changed(struct datastate_changed *);
+
+/*
+ * Shared data.
+ */
+extern struct datastate_known datastate_known_system;
+extern struct datastate_known datastate_known_user;
+extern struct datastate_known datastate_known_new;
+extern struct datastate_known datastate_known_inherent;
+extern struct datastate_known datastate_known_open;
+extern struct datastate_known datastate_known_closed;
+
+extern struct list_head datastate_known_list;
+extern struct list_head datastate_rule_list;
+
+extern struct security_operations datastate_ops;
+
+/*
+ * Present a pointer to the datastate label in an inode blob.
+ */
+static inline char *datastate_of_inode(const struct inode *isp)
+{
+ struct inode_datastate *sip = isp->i_security;
+ return sip->ds_inode;
+}
+
+/*
+ * logging functions
+ */
+#define DATASTATE_AUDIT_DENIED 0x1
+#define DATASTATE_AUDIT_ACCEPT 0x2
+extern int log_policy;
+
+void datastate_log(char *subject_label, char *object_label, int request,
+ int result, struct datastate_audit_info *auditdata);
+
+#ifdef CONFIG_AUDIT
+
+/*
+ * some inline functions to set up audit data
+ * they do nothing if CONFIG_AUDIT is not set
+ *
+ */
+static inline void datastate_ad_init(struct datastate_audit_info *a,
+ const char *func, char type)
+{
+ memset(a, 0, sizeof(*a));
+ a->a.type = type;
+ a->a.datastate_audit_data.function = func;
+}
+
+static inline void datastate_ad_setfield_u_tsk(
+ struct datastate_audit_info *a,
+ struct task_struct *t)
+{
+ a->a.u.tsk = t;
+}
+static inline void datastate_ad_setfield_u_fs_path_dentry(
+ struct datastate_audit_info *a,
+ struct dentry *d)
+{
+ a->a.u.fs.path.dentry = d;
+}
+static inline void datastate_ad_setfield_u_fs_path_mnt(
+ struct datastate_audit_info *a,
+ struct vfsmount *m)
+{
+ a->a.u.fs.path.mnt = m;
+}
+static inline void datastate_ad_setfield_u_fs_inode(
+ struct datastate_audit_info *a,
+ struct inode *i)
+{
+ a->a.u.fs.inode = i;
+}
+static inline void datastate_ad_setfield_u_fs_path(
+ struct datastate_audit_info *a,
+ struct path p)
+{
+ a->a.u.fs.path = p;
+}
+static inline void datastate_ad_setfield_u_net_sk(
+ struct datastate_audit_info *a,
+ struct sock *sk)
+{
+ a->a.u.net.sk = sk;
+}
+
+#else /* no AUDIT */
+
+static inline void datastate_ad_init(struct datastate_audit_info *a,
+ const char *func, char type)
+{
+}
+static inline void datastate_ad_setfield_u_tsk(
+ struct datastate_audit_info *a,
+ struct task_struct *t)
+{
+}
+static inline void datastate_ad_setfield_u_fs_path_dentry(
+ struct datastate_audit_info *a,
+ struct dentry *d)
+{
+}
+static inline void datastate_ad_setfield_u_fs_path_mnt(
+ struct datastate_audit_info *a,
+ struct vfsmount *m)
+{
+}
+static inline void datastate_ad_setfield_u_fs_inode(
+ struct datastate_audit_info *a,
+ struct inode *i)
+{
+}
+static inline void datastate_ad_setfield_u_fs_path(
+ struct datastate_audit_info *a,
+ struct path p)
+{
+}
+static inline void datastate_ad_setfield_u_net_sk(
+ struct datastate_audit_info *a,
+ struct sock *sk)
+{
+}
+#endif
+
+#endif /* _SECURITY_DATASTATE_H */
diff -uprN -X linux-2.6/Documentation/dontdiff linux-2.6/security/datastate/datastate_lsm.c linux-0807/security/datastate/datastate_lsm.c
--- linux-2.6/security/datastate/datastate_lsm.c 1969-12-31 16:00:00.000000000 -0800
+++ linux-0807/security/datastate/datastate_lsm.c 2010-08-07 09:35:21.000000000 -0700
@@ -0,0 +1,1060 @@
+/*
+ * Datastate security module
+ *
+ * This file contains the datastate hook function implementations.
+ *
+ * Author:
+ * Casey Schaufler <casey@schaufler-ca.com>
+ *
+ * Copyright (C) 2010 Casey Schaufler <casey@schaufler-ca.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ */
+
+#include <linux/xattr.h>
+#include <linux/pagemap.h>
+#include <linux/mount.h>
+#include <linux/stat.h>
+#include <linux/ext2_fs.h>
+#include <linux/kd.h>
+#include <asm/ioctls.h>
+#include <linux/ip.h>
+#include <linux/tcp.h>
+#include <linux/udp.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/pipe_fs_i.h>
+#include <linux/audit.h>
+#include <linux/magic.h>
+#include <linux/fsnotify.h>
+#include <linux/proc_fs.h>
+#include "datastate.h"
+
+#define task_security(task) (task_cred_xxx((task), security))
+
+/*
+ * LSM hooks.
+ *
+ * Inode hooks
+ */
+
+/**
+ * datastate_inode_alloc_security - allocate an inode blob
+ * @inode: the inode in need of a blob
+ *
+ * Returns 0 if it gets a blob, -ENOMEM otherwise
+ */
+static int datastate_inode_alloc_security(struct inode *inode)
+{
+ struct inode_datastate *isp;
+
+ inode->i_security = kzalloc(sizeof(struct inode_datastate), GFP_KERNEL);
+ if (inode->i_security == NULL)
+ return -ENOMEM;
+
+ isp = inode->i_security;
+ mutex_init(&isp->ds_lock);
+ isp->ds_inode = datastate_known_new.ds_known;
+
+ return 0;
+}
+
+/**
+ * datastate_inode_free_security - free an inode blob
+ * @inode: the inode with a blob
+ *
+ * Clears the blob pointer in inode
+ */
+static void datastate_inode_free_security(struct inode *inode)
+{
+ kfree(inode->i_security);
+ inode->i_security = NULL;
+}
+
+/**
+ * datastate_inode_init_security - copy out the datastate from an inode
+ * @inode: the inode
+ * @dir: unused
+ * @name: where to put the attribute name
+ * @value: where to put the attribute value
+ * @len: where to put the length of the attribute
+ *
+ * Returns 0 if it all works out, -ENOMEM if there's no memory
+ */
+static int datastate_inode_init_security(struct inode *inode,
+ struct inode *dir, char **name,
+ void **value, size_t *len)
+{
+ char *isp;
+
+ /*
+ * Only supply labels to regular files
+ */
+ if (S_ISREG(inode->i_mode) == 0)
+ return -EOPNOTSUPP;
+
+ if (name) {
+ *name = kstrdup(XATTR_DATASTATE_SUFFIX, GFP_KERNEL);
+ if (*name == NULL)
+ return -ENOMEM;
+ }
+
+ isp = datastate_of_inode(inode);
+
+ if (value) {
+ *value = kstrdup(isp, GFP_KERNEL);
+ if (*value == NULL)
+ return -ENOMEM;
+ }
+
+ if (len)
+ *len = strlen(isp) + 1;
+
+ return 0;
+}
+
+/**
+ * datastate_inode_permission - Datastate version of permission()
+ * @inode: the inode in question
+ * @mask: the access requested
+ *
+ * This is the important Datastate hook.
+ *
+ * Returns 0 if access is permitted, -EACCES otherwise
+ */
+static int datastate_inode_permission(struct inode *inode, int mask)
+{
+ struct datastate_audit_info ad;
+ struct inode_datastate *isp = inode->i_security;
+
+ /*
+ * No permission to check. Existence test. Yup, it's there.
+ */
+ if (mask == 0)
+ return 0;
+
+ datastate_ad_init(&ad, __func__, LSM_AUDIT_DATA_FS);
+ datastate_ad_setfield_u_fs_inode(&ad, inode);
+
+ return datastate_curacc(isp->ds_inode, mask, &ad);
+}
+
+/**
+ * datastate_inode_setxattr - Datastate check for setting xattrs
+ * @dentry: the object
+ * @name: name of the attribute
+ * @value: unused
+ * @size: unused
+ * @flags: unused
+ *
+ * This protects the Datastate attribute explicitly.
+ *
+ * Returns 0 if access is permitted, an error code otherwise
+ */
+static int datastate_inode_setxattr(struct dentry *dentry, const char *name,
+ const void *value, size_t size, int flags)
+{
+ struct inode *inode = dentry->d_inode;
+ struct datastate_audit_info ad;
+ int rc = 0;
+
+ if (strcmp(name, XATTR_NAME_DATASTATE) == 0) {
+ if (!capable(CAP_MAC_ADMIN))
+ rc = -EPERM;
+ if (S_ISREG(inode->i_mode) == 0)
+ return -EOPNOTSUPP;
+ /*
+ * check label validity here so import wont fail on
+ * post_setxattr
+ */
+ if (size == 0 || size >= DATASTATE_LABELLEN ||
+ datastate_import(value, size) == NULL)
+ rc = -EINVAL;
+ } else
+ rc = cap_inode_setxattr(dentry, name, value, size, flags);
+
+ datastate_ad_init(&ad, __func__, LSM_AUDIT_DATA_FS);
+ datastate_ad_setfield_u_fs_path_dentry(&ad, dentry);
+
+ if (rc == 0)
+ rc = datastate_curacc(datastate_of_inode(inode),
+ MAY_WRITE, &ad);
+
+ return rc;
+}
+
+/**
+ * datastate_inode_post_setxattr - Apply the Datastate update approved above
+ * @dentry: object
+ * @name: attribute name
+ * @value: attribute value
+ * @size: attribute size
+ * @flags: unused
+ *
+ * Set the pointer in the inode blob to the entry found
+ * in the master label list.
+ */
+static void datastate_inode_post_setxattr(struct dentry *dentry,
+ const char *name, const void *value,
+ size_t size, int flags)
+{
+ struct inode_datastate *isp;
+
+ /*
+ * Not DATASTATE
+ */
+ if (strcmp(name, XATTR_NAME_DATASTATE))
+ return;
+
+ isp = dentry->d_inode->i_security;
+
+ /*
+ * No locking is done here. This is a pointer assignment.
+ * It won't fail because value has already been checked in
+ * datastate_inode_setxattr().
+ */
+ isp->ds_inode = datastate_import(value, size);
+
+ return;
+}
+
+/*
+ * datastate_inode_getxattr - Datastate check on getxattr
+ * @dentry: the object
+ * @name: unused
+ *
+ * Returns 0 if access is permitted, an error code otherwise
+ */
+static int datastate_inode_getxattr(struct dentry *dentry, const char *name)
+{
+ struct datastate_audit_info ad;
+
+ datastate_ad_init(&ad, __func__, LSM_AUDIT_DATA_FS);
+ datastate_ad_setfield_u_fs_path_dentry(&ad, dentry);
+
+ return datastate_curacc(datastate_of_inode(dentry->d_inode), MAY_READ,
+ &ad);
+}
+
+/*
+ * datastate_inode_removexattr - Datastate check on removexattr
+ * @dentry: the object
+ * @name: name of the attribute
+ *
+ * Removing the Datastate attribute requires CAP_MAC_ADMIN
+ *
+ * Returns 0 if access is permitted, an error code otherwise
+ */
+static int datastate_inode_removexattr(struct dentry *dentry, const char *name)
+{
+ struct datastate_audit_info ad;
+ int rc = 0;
+
+ if (strcmp(name, XATTR_NAME_DATASTATE) == 0) {
+ if (!capable(CAP_MAC_ADMIN))
+ rc = -EPERM;
+ } else
+ rc = cap_inode_removexattr(dentry, name);
+
+ datastate_ad_init(&ad, __func__, LSM_AUDIT_DATA_FS);
+ datastate_ad_setfield_u_fs_path_dentry(&ad, dentry);
+ if (rc == 0)
+ rc = datastate_curacc(datastate_of_inode(dentry->d_inode),
+ MAY_WRITE, &ad);
+
+ return rc;
+}
+
+/**
+ * datastate_inode_getsecurity - get datastate xattrs
+ * @inode: the object
+ * @name: attribute name
+ * @buffer: where to put the result
+ * @alloc: unused
+ *
+ * Returns the size of the attribute or an error code
+ */
+static int datastate_inode_getsecurity(const struct inode *inode,
+ const char *name, void **buffer,
+ bool alloc)
+{
+ struct inode_datastate *isp = inode->i_security;
+ char *dp;
+ int ilen;
+
+ if (strcmp(name, XATTR_DATASTATE_SUFFIX) != 0)
+ return -EOPNOTSUPP;
+ /*
+ * Only support the datastate attribute on regular files
+ */
+ if (S_ISREG(inode->i_mode) == 0)
+ return -EOPNOTSUPP;
+
+ dp = isp->ds_inode;
+
+ ilen = strlen(dp) + 1;
+ *buffer = dp;
+ return ilen;
+}
+
+/**
+ * datastate_inode_listsecurity - list the Datastate attributes
+ * @inode: the object
+ * @buffer: where they go
+ * @buffer_size: size of buffer
+ *
+ * Returns 0 on success, -EINVAL otherwise
+ */
+static int datastate_inode_listsecurity(struct inode *inode, char *buffer,
+ size_t buffer_size)
+{
+ int len;
+
+ if (S_ISREG(inode->i_mode) == 0)
+ return -EINVAL;
+
+ len = strlen(XATTR_NAME_DATASTATE);
+ if (buffer != NULL && len <= buffer_size) {
+ memcpy(buffer, XATTR_NAME_DATASTATE, len);
+ return len;
+ }
+ return -EINVAL;
+}
+
+/**
+ * datastate_inode_getsecid - Extract inode's security id
+ * @inode: inode to extract the info from
+ * @secid: where result will be saved
+ */
+static void datastate_inode_getsecid(const struct inode *inode, u32 *secid)
+{
+ struct inode_datastate *isp = inode->i_security;
+
+ *secid = datastate_to_secid(isp->ds_inode);
+}
+
+/**
+ * datastate_file_changed - Handle a file that has changed
+ * @file: the changed object
+ *
+ */
+static void datastate_file_changed(struct file *file)
+{
+ struct path *path = &file->f_path;
+ struct dentry *dentry = path->dentry;
+ struct inode *inode = dentry->d_inode;
+ struct inode_datastate *isp = inode->i_security;
+ int rc;
+ char *buffer;
+ char *name;
+
+ /*
+ * Quick exit if the inode is already marked.
+ */
+ if (isp->ds_inode == datastate_known_open.ds_known)
+ return;
+ /*
+ * Only interested in regular files.
+ */
+ if (S_ISREG(inode->i_mode) == 0)
+ return;
+
+ /*
+ * Leave files with '+' labels alone. A Plus label
+ * indicates that the "system" recognizes this as a file
+ * that is updated "safely". Think /var/log/messages.
+ */
+ if (isp->ds_inode[0] == '+')
+ return;
+
+ /*
+ * This file is getting modified. It does not really much
+ * matter what kind of modification it is, because there
+ * are all sorts of sneaky ways to do damage.
+ */
+ isp->ds_inode = datastate_known_open.ds_known;
+
+ mutex_lock(&inode->i_mutex);
+
+ if (inode->i_op->setxattr != NULL)
+ rc = inode->i_op->setxattr(dentry, XATTR_NAME_DATASTATE,
+ isp->ds_inode, strlen(isp->ds_inode) + 1, 0);
+
+ fsnotify_xattr(dentry);
+
+ mutex_unlock(&inode->i_mutex);
+
+ buffer = kzalloc(PATH_MAX, GFP_KERNEL);
+ name = d_path(path, buffer, PATH_MAX);
+ if (name != NULL)
+ datastate_add_changed(name);
+
+ kfree(buffer);
+
+ return;
+}
+
+/**
+ * datastate_dentry_open - open
+ * @file: file being opened
+ * @cred: who is opening the file
+ *
+ * Returns 0
+ */
+static int datastate_dentry_open(struct file *file, const struct cred *cred)
+{
+ /*
+ * An open for write is a good clue that the file is going
+ * to change. There is some chance this will be a false alarm.
+ */
+ if ((file->f_mode & FMODE_WRITE) != 0)
+ datastate_file_changed(file);
+
+ return 0;
+}
+
+/*
+ * File Hooks
+ */
+
+/**
+ * datastate_file_permission - Datastate check on file operations
+ * @file: the open object
+ * @mask: the requested access
+ *
+ * Returns 0
+ *
+ * Should access checks be done on each read or write?
+ * UNICOS and SELinux say yes.
+ * Trusted Solaris, Trusted Irix, Smack and just about everyone else says no.
+ *
+ * Datastate is not checking the access, rather if this is a
+ * write operation the file may now contain naughty bits.
+ */
+static int datastate_file_permission(struct file *file, int mask)
+{
+
+ /*
+ * Only interested in write operations.
+ * If this is a write operation process further.
+ */
+ if ((mask & MAY_WRITE) != 0)
+ datastate_file_changed(file);
+
+ return 0;
+}
+
+/**
+ * datastate_file_ioctl - Datastate check on ioctls
+ * @file: the object
+ * @cmd: what to do
+ * @arg: unused
+ *
+ * Relies heavily on the correct use of the ioctl command conventions.
+ *
+ * Returns 0 if allowed, error code otherwise
+ */
+static int datastate_file_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ /*
+ * Treat all write ioctls as data writes. Because the only
+ * case that matters is regular files this might be better ignored.
+ */
+ if (_IOC_DIR(cmd) & _IOC_WRITE)
+ datastate_file_changed(file);
+
+ return 0;
+}
+
+/*
+ * Task hooks
+ */
+
+/**
+ * datastate_cred_alloc_blank - "allocate" task-level security credentials
+ * @new: the new credentials
+ * @gfp: the atomicity of any memory allocations
+ *
+ * Prepare a blank set of credentials for modification. This must allocate all
+ * the memory the LSM module might require such that cred_transfer() can
+ * complete without error.
+ */
+static int datastate_cred_alloc_blank(struct cred *cred, gfp_t gfp)
+{
+ cred->security = NULL;
+ return 0;
+}
+
+
+/**
+ * datastate_cred_free - "free" task-level security credentials
+ * @cred: the credentials in question
+ *
+ * Datastate isn't using copies of blobs. Everyone
+ * points to an immutable list. The blobs never go away.
+ * There is no leak here.
+ */
+static void datastate_cred_free(struct cred *cred)
+{
+ cred->security = NULL;
+}
+
+/**
+ * datastate_cred_prepare - prepare new set of credentials for modification
+ * @new: the new credentials
+ * @old: the original credentials
+ * @gfp: the atomicity of any memory allocations
+ *
+ * Prepare a new set of credentials for modification.
+ */
+static int datastate_cred_prepare(struct cred *new, const struct cred *old,
+ gfp_t gfp)
+{
+ new->security = old->security;
+ return 0;
+}
+
+/**
+ * datastate_cred_transfer - Transfer the old credentials to the new
+ * @new: the new credentials
+ * @old: the original credentials
+ *
+ * Fill in a set of blank credentials from another set of credentials.
+ */
+static void datastate_cred_transfer(struct cred *new, const struct cred *old)
+{
+ new->security = old->security;
+}
+
+/**
+ * datastate_kernel_act_as - Set the subjective context in a set of credentials
+ * @new: points to the set of credentials to be modified.
+ * @secid: specifies the security ID to be set
+ *
+ * Set the security data for a kernel service.
+ */
+static int datastate_kernel_act_as(struct cred *new, u32 secid)
+{
+ char *datastate = datastate_from_secid(secid);
+
+ if (datastate == NULL)
+ return -EINVAL;
+
+ new->security = datastate;
+ return 0;
+}
+
+/**
+ * datastate_kernel_create_files_as - Set the file creation label in creds
+ * @new: points to the set of credentials to be modified
+ * @inode: points to the inode to use as a reference
+ *
+ * Set the file creation context in a set of credentials to the same
+ * as the objective context of the specified inode
+ */
+static int datastate_kernel_create_files_as(struct cred *new,
+ struct inode *inode)
+{
+ struct inode_datastate *isp = inode->i_security;
+
+ new->security = isp->ds_inode;
+ return 0;
+}
+
+/**
+ * datastate_task_getsecid - get the secid of the task
+ * @p: the object task
+ * @secid: where to put the result
+ *
+ * Sets the secid to contain a u32 version of the datastate label.
+ */
+static void datastate_task_getsecid(struct task_struct *p, u32 *secid)
+{
+ *secid = datastate_to_secid(task_security(p));
+}
+
+/**
+ * datastate_task_to_inode - copy task datastate into the inode blob
+ * @p: task to copy from
+ * @inode: inode to copy to
+ *
+ * Sets the datastate pointer in the inode security blob
+ */
+static void datastate_task_to_inode(struct task_struct *p, struct inode *inode)
+{
+ struct inode_datastate *isp = inode->i_security;
+ isp->ds_inode = task_security(p);
+}
+
+/**
+ * datastate_inode_setsecurity - set datastate xattrs
+ * @inode: the object
+ * @name: attribute name
+ * @value: attribute value
+ * @size: size of the attribute
+ * @flags: unused
+ *
+ * Sets the named attribute in the appropriate blob
+ *
+ * Returns 0 on success, or an error code
+ */
+static int datastate_inode_setsecurity(struct inode *inode, const char *name,
+ const void *value, size_t size,
+ int flags)
+{
+ char *sp;
+ struct inode_datastate *nsp = inode->i_security;
+
+ if (value == NULL || size > DATASTATE_LABELLEN || size == 0)
+ return -EACCES;
+
+ if (strcmp(name, XATTR_DATASTATE_SUFFIX) != 0)
+ return -EOPNOTSUPP;
+ /*
+ * Only support the datastate attribute on regular files
+ */
+ if (S_ISREG(inode->i_mode) == 0)
+ return -EINVAL;
+
+ sp = datastate_import(value, size);
+ if (sp == NULL)
+ return -EINVAL;
+
+ nsp->ds_inode = sp;
+ return 0;
+}
+
+/**
+ * datastate_d_instantiate - Make sure the blob is correct on an inode
+ * @opt_dentry: unused
+ * @inode: the object
+ *
+ * Set the inode's security blob if it hasn't been done already.
+ */
+static void datastate_d_instantiate(struct dentry *opt_dentry,
+ struct inode *inode)
+{
+ struct inode_datastate *isp;
+ struct dentry *dp;
+ char in[DATASTATE_LABELLEN];
+ char *fetch;
+ int rc;
+
+ if (inode == NULL)
+ return;
+
+ isp = inode->i_security;
+
+ mutex_lock(&isp->ds_lock);
+ /*
+ * If the inode is already instantiated take the quick way out
+ */
+ if (isp->ds_inode != datastate_known_new.ds_known)
+ goto unlockandout;
+
+ /*
+ * Use this if there is no label attribute on the file.
+ */
+ isp->ds_inode = datastate_known_inherent.ds_known;
+
+ /*
+ * If this is the root inode the superblock
+ * may be in the process of initialization.
+ */
+ if (opt_dentry->d_parent == opt_dentry)
+ goto unlockandout;
+
+ /*
+ * If the filesystem supports xattrs look for one.
+ */
+ if (inode->i_op->getxattr == NULL)
+ goto unlockandout;
+
+ dp = dget(opt_dentry);
+ if (dp == NULL)
+ goto unlockandout;
+
+ rc = inode->i_op->getxattr(dp, XATTR_NAME_DATASTATE, in,
+ DATASTATE_LABELLEN);
+ if (rc < 0)
+ goto dputunlockandout;
+
+ fetch = datastate_import(in, rc);
+ if (fetch == NULL)
+ goto dputunlockandout;
+
+ isp->ds_inode = fetch;
+
+dputunlockandout:
+ dput(dp);
+
+unlockandout:
+ mutex_unlock(&isp->ds_lock);
+ return;
+}
+
+/**
+ * datastate_getprocattr - Datastate process attribute access
+ * @p: the object task
+ * @name: the name of the attribute in /proc/.../attr
+ * @value: where to put the result
+ *
+ * Places a copy of the task Datastate into value
+ *
+ * Returns the length of the datastate label or an error code
+ */
+static int datastate_getprocattr(struct task_struct *p, char *name,
+ char **value)
+{
+ char *cp;
+ int slen;
+
+ if (strcmp(name, "current") != 0)
+ return -EINVAL;
+
+ cp = kstrdup(task_security(p), GFP_KERNEL);
+ if (cp == NULL)
+ return -ENOMEM;
+
+ slen = strlen(cp);
+ *value = cp;
+ return slen;
+}
+
+/**
+ * datastate_setprocattr - Datastate process attribute setting
+ * @p: the object task
+ * @name: the name of the attribute in /proc/.../attr
+ * @value: the value to set
+ * @size: the size of the value
+ *
+ * Sets the Datastate value of the task. Only setting self
+ * is permitted and only with privilege
+ *
+ * Returns the length of the datastate label or an error code
+ */
+static int datastate_setprocattr(struct task_struct *p, char *name,
+ void *value, size_t size)
+{
+ struct cred *new;
+ char *newdatastate;
+
+ /*
+ * Changing another process' Datastate value is too dangerous
+ * and supports no sane use case.
+ */
+ if (p != current)
+ return -EPERM;
+
+ if (!capable(CAP_MAC_ADMIN))
+ return -EPERM;
+
+ if (value == NULL || size == 0 || size >= DATASTATE_LABELLEN)
+ return -EINVAL;
+
+ if (strcmp(name, "current") != 0)
+ return -EINVAL;
+
+ newdatastate = datastate_import(value, size);
+ if (newdatastate == NULL)
+ return -EINVAL;
+
+ new = prepare_creds();
+ if (new == NULL)
+ return -ENOMEM;
+ new->security = newdatastate;
+ commit_creds(new);
+ return size;
+}
+
+/*
+ * Datastate Audit hooks
+ *
+ * Audit requires a unique representation of each Datastate specific
+ * rule. This unique representation is used to distinguish the
+ * object to be audited from remaining kernel objects and also
+ * works as a glue between the audit hooks.
+ *
+ * Since repository entries are added but never deleted, we'll use
+ * the datastate_known label address related to the given audit rule as
+ * the needed unique representation. This also better fits the datastate
+ * model where nearly everything is a label.
+ */
+#ifdef CONFIG_AUDIT
+
+/**
+ * datastate_audit_rule_init - Initialize a datastate audit rule
+ * @field: audit rule fields given from user-space (audit.h)
+ * @op: required testing operator (=, !=, >, <, ...)
+ * @rulestr: datastate label to be audited
+ * @vrule: pointer to save our own audit rule representation
+ *
+ * Prepare to audit cases where (@field @op @rulestr) is true.
+ * The label to be audited is created if necessay.
+ */
+static int datastate_audit_rule_init(u32 field, u32 op, char *rulestr,
+ void **vrule)
+{
+ char **rule = (char **)vrule;
+ *rule = NULL;
+
+ if (field != AUDIT_SUBJ_USER && field != AUDIT_OBJ_USER)
+ return -EINVAL;
+
+ if (op != Audit_equal && op != Audit_not_equal)
+ return -EINVAL;
+
+ *rule = datastate_import(rulestr, 0);
+
+ return 0;
+}
+
+/**
+ * datastate_audit_rule_known - Distinguish Datastate audit rules
+ * @krule: rule of interest, in Audit kernel representation format
+ *
+ * This is used to filter Datastate rules from remaining Audit ones.
+ * If it's proved that this rule belongs to us, the
+ * audit_rule_match hook will be called to do the final judgement.
+ */
+static int datastate_audit_rule_known(struct audit_krule *krule)
+{
+ struct audit_field *f;
+ int i;
+
+ for (i = 0; i < krule->field_count; i++) {
+ f = &krule->fields[i];
+
+ if (f->type == AUDIT_SUBJ_USER || f->type == AUDIT_OBJ_USER)
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * datastate_audit_rule_match - Audit given object ?
+ * @secid: security id for identifying the object to test
+ * @field: audit rule flags given from user-space
+ * @op: required testing operator
+ * @vrule: datastate internal rule presentation
+ * @actx: audit context associated with the check
+ *
+ * The core Audit hook. It's used to take the decision of
+ * whether to audit or not to audit a given object.
+ */
+static int datastate_audit_rule_match(u32 secid, u32 field, u32 op,
+ void *vrule, struct audit_context *actx)
+{
+ char *datastate;
+ char *rule = vrule;
+
+ if (!rule) {
+ audit_log(actx, GFP_KERNEL, AUDIT_SELINUX_ERR,
+ "Datastate: missing rule\n");
+ return -ENOENT;
+ }
+
+ if (field != AUDIT_SUBJ_USER && field != AUDIT_OBJ_USER)
+ return 0;
+
+ datastate = datastate_from_secid(secid);
+
+ /*
+ * No need to do string comparisons. If a match occurs,
+ * both pointers will point to the same datastate_known
+ * label.
+ */
+ if (op == Audit_equal)
+ return (rule == datastate);
+ if (op == Audit_not_equal)
+ return (rule != datastate);
+
+ return 0;
+}
+
+/**
+ * datastate_audit_rule_free - free datastate rule representation
+ * @vrule: rule to be freed.
+ *
+ * No memory was allocated.
+ */
+static void datastate_audit_rule_free(void *vrule)
+{
+ /* No-op */
+}
+
+#endif /* CONFIG_AUDIT */
+
+/**
+ * datastate_secid_to_secctx - return the datastate label for a secid
+ * @secid: incoming integer
+ * @secdata: destination
+ * @seclen: how long it is
+ *
+ * Exists for networking code.
+ */
+static int datastate_secid_to_secctx(u32 secid, char **secdata, u32 *seclen)
+{
+ char *sp = datastate_from_secid(secid);
+
+ *secdata = sp;
+ *seclen = strlen(sp);
+ return 0;
+}
+
+/**
+ * datastate_secctx_to_secid - return the secid for a datastate label
+ * @secdata: datastate label
+ * @seclen: how long result is
+ * @secid: outgoing integer
+ *
+ * Exists for audit and networking code.
+ */
+static int datastate_secctx_to_secid(const char *secdata, u32 seclen,
+ u32 *secid)
+{
+ *secid = datastate_to_secid(secdata);
+ return 0;
+}
+
+/**
+ * datastate_release_secctx - don't do anything.
+ * @secdata: unused
+ * @seclen: unused
+ *
+ * Exists to make sure nothing gets done, and properly
+ */
+static void datastate_release_secctx(char *secdata, u32 seclen)
+{
+}
+
+static int datastate_inode_notifysecctx(struct inode *inode, void *ctx,
+ u32 ctxlen)
+{
+ return datastate_inode_setsecurity(inode, XATTR_DATASTATE_SUFFIX,
+ ctx, ctxlen, 0);
+}
+
+static int datastate_inode_setsecctx(struct dentry *dentry, void *ctx,
+ u32 ctxlen)
+{
+ return __vfs_setxattr_noperm(dentry, XATTR_NAME_DATASTATE, ctx,
+ ctxlen, 0);
+}
+
+static int datastate_inode_getsecctx(struct inode *inode, void **ctx,
+ u32 *ctxlen)
+{
+ int len = 0;
+ len = datastate_inode_getsecurity(inode, XATTR_DATASTATE_SUFFIX,
+ ctx, true);
+
+ if (len < 0)
+ return len;
+ *ctxlen = len;
+ return 0;
+}
+
+struct security_operations datastate_ops = {
+ .name = "datastate",
+
+ .inode_alloc_security = datastate_inode_alloc_security,
+ .inode_free_security = datastate_inode_free_security,
+ .inode_init_security = datastate_inode_init_security,
+ .inode_permission = datastate_inode_permission,
+ .inode_setxattr = datastate_inode_setxattr,
+ .inode_post_setxattr = datastate_inode_post_setxattr,
+ .inode_getxattr = datastate_inode_getxattr,
+ .inode_removexattr = datastate_inode_removexattr,
+ .inode_getsecurity = datastate_inode_getsecurity,
+ .inode_setsecurity = datastate_inode_setsecurity,
+ .inode_listsecurity = datastate_inode_listsecurity,
+ .inode_getsecid = datastate_inode_getsecid,
+
+ .file_permission = datastate_file_permission,
+ .file_ioctl = datastate_file_ioctl,
+
+ .dentry_open = datastate_dentry_open,
+
+ .cred_alloc_blank = datastate_cred_alloc_blank,
+ .cred_free = datastate_cred_free,
+ .cred_prepare = datastate_cred_prepare,
+ .cred_transfer = datastate_cred_transfer,
+ .kernel_act_as = datastate_kernel_act_as,
+ .kernel_create_files_as = datastate_kernel_create_files_as,
+ .task_getsecid = datastate_task_getsecid,
+ .task_to_inode = datastate_task_to_inode,
+
+ .d_instantiate = datastate_d_instantiate,
+
+ .getprocattr = datastate_getprocattr,
+ .setprocattr = datastate_setprocattr,
+
+ /* Audit hooks */
+#ifdef CONFIG_AUDIT
+ .audit_rule_init = datastate_audit_rule_init,
+ .audit_rule_known = datastate_audit_rule_known,
+ .audit_rule_match = datastate_audit_rule_match,
+ .audit_rule_free = datastate_audit_rule_free,
+#endif /* CONFIG_AUDIT */
+
+ .secid_to_secctx = datastate_secid_to_secctx,
+ .secctx_to_secid = datastate_secctx_to_secid,
+ .release_secctx = datastate_release_secctx,
+ .inode_notifysecctx = datastate_inode_notifysecctx,
+ .inode_setsecctx = datastate_inode_setsecctx,
+ .inode_getsecctx = datastate_inode_getsecctx,
+};
+
+
+static __init void init_datastate_know_list(void)
+{
+ list_add(&datastate_known_system.list, &datastate_known_list);
+ list_add(&datastate_known_user.list, &datastate_known_list);
+ list_add(&datastate_known_open.list, &datastate_known_list);
+ list_add(&datastate_known_closed.list, &datastate_known_list);
+ list_add(&datastate_known_inherent.list, &datastate_known_list);
+ list_add(&datastate_known_new.list, &datastate_known_list);
+}
+
+/**
+ * datastate_init - initialize the datastate system
+ *
+ * Returns 0
+ */
+static __init int datastate_init(void)
+{
+ struct cred *cred;
+
+ if (!security_module_enable(&datastate_ops))
+ return 0;
+
+ printk(KERN_INFO "Datastate: Initializing.\n");
+
+ /*
+ * Set the security state for the initial task.
+ */
+ cred = (struct cred *) current->cred;
+ cred->security = &datastate_known_system.ds_known;
+
+ /* initilize the datastate_know_list */
+ init_datastate_know_list();
+
+ /*
+ * Register with LSM
+ */
+ if (register_security(&datastate_ops))
+ panic("datastate: Unable to register with kernel.\n");
+
+ return 0;
+}
+
+/*
+ * Datastate requires early initialization in order to label
+ * all processes and objects when they are created.
+ */
+security_initcall(datastate_init);
diff -uprN -X linux-2.6/Documentation/dontdiff linux-2.6/security/datastate/Kconfig linux-0807/security/datastate/Kconfig
--- linux-2.6/security/datastate/Kconfig 1969-12-31 16:00:00.000000000 -0800
+++ linux-0807/security/datastate/Kconfig 2010-08-07 09:35:21.000000000 -0700
@@ -0,0 +1,9 @@
+config SECURITY_DATASTATE
+ bool "Data State Manager Kernel Support"
+ default n
+ help
+ This selects the data state manager kernel component.
+ This is used to control access based on the data in
+ select containers.
+ If you are unsure how to answer this question, answer N.
+
diff -uprN -X linux-2.6/Documentation/dontdiff linux-2.6/security/datastate/Makefile linux-0807/security/datastate/Makefile
--- linux-2.6/security/datastate/Makefile 1969-12-31 16:00:00.000000000 -0800
+++ linux-0807/security/datastate/Makefile 2010-08-07 09:35:21.000000000 -0700
@@ -0,0 +1,7 @@
+#
+# Makefile for the DataState LSM
+#
+
+obj-$(CONFIG_SECURITY_DATASTATE) := datastate.o
+
+datastate-y := datastate_lsm.o datastate_access.o datastatefs.o
diff -uprN -X linux-2.6/Documentation/dontdiff linux-2.6/security/Kconfig linux-0807/security/Kconfig
--- linux-2.6/security/Kconfig 2010-08-07 09:15:08.000000000 -0700
+++ linux-0807/security/Kconfig 2010-08-07 09:35:21.000000000 -0700
@@ -56,7 +56,7 @@ config SECURITYFS
help
This will build the securityfs filesystem. It is currently used by
the TPM bios character driver and IMA, an integrity provider. It is
- not used by SELinux or SMACK.
+ not used by DataState, SELinux or SMACK.
If you are unsure how to answer this question, answer N.
@@ -137,6 +137,7 @@ config LSM_MMAP_MIN_ADDR
this low address space will need the permission specific to the
systems running LSM.
+source security/datastate/Kconfig
source security/selinux/Kconfig
source security/smack/Kconfig
source security/tomoyo/Kconfig
@@ -146,6 +147,7 @@ source security/integrity/ima/Kconfig
choice
prompt "Default security module"
+ default DEFAULT_SECURITY_DATASTATE if SECURITY_DATASTATE
default DEFAULT_SECURITY_SELINUX if SECURITY_SELINUX
default DEFAULT_SECURITY_SMACK if SECURITY_SMACK
default DEFAULT_SECURITY_TOMOYO if SECURITY_TOMOYO
@@ -156,6 +158,9 @@ choice
Select the security module that will be used by default if the
kernel parameter security= is not specified.
+ config DEFAULT_SECURITY_DATASTATE
+ bool "Data State Manager" if SECURITY_DATASTATE=y
+
config DEFAULT_SECURITY_SELINUX
bool "SELinux" if SECURITY_SELINUX=y
@@ -175,6 +180,7 @@ endchoice
config DEFAULT_SECURITY
string
+ default "datastate" if DEFAULT_SECURITY_DATASTATE
default "selinux" if DEFAULT_SECURITY_SELINUX
default "smack" if DEFAULT_SECURITY_SMACK
default "tomoyo" if DEFAULT_SECURITY_TOMOYO
diff -uprN -X linux-2.6/Documentation/dontdiff linux-2.6/security/Makefile linux-0807/security/Makefile
--- linux-2.6/security/Makefile 2010-08-07 09:15:08.000000000 -0700
+++ linux-0807/security/Makefile 2010-08-07 09:35:21.000000000 -0700
@@ -3,6 +3,7 @@
#
obj-$(CONFIG_KEYS) += keys/
+subdir-$(CONFIG_SECURITY_DATASTATE) += datastate
subdir-$(CONFIG_SECURITY_SELINUX) += selinux
subdir-$(CONFIG_SECURITY_SMACK) += smack
subdir-$(CONFIG_SECURITY_TOMOYO) += tomoyo
@@ -16,6 +17,7 @@ obj-$(CONFIG_MMU) += min_addr.o
obj-$(CONFIG_SECURITY) += security.o capability.o
obj-$(CONFIG_SECURITYFS) += inode.o
# Must precede capability.o in order to stack properly.
+obj-$(CONFIG_SECURITY_DATASTATE) += datastate/built-in.o
obj-$(CONFIG_SECURITY_SELINUX) += selinux/built-in.o
obj-$(CONFIG_SECURITY_SMACK) += smack/built-in.o
obj-$(CONFIG_AUDIT) += lsm_audit.o
--
To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html