2007-08-11 17:58:15

by Casey Schaufler

[permalink] [raw]
Subject: [PATCH] Smack: Simplified Mandatory Access Control Kernel

From: Casey Schaufler <[email protected]>

Smack is the Simplified Mandatory Access Control Kernel.

Smack implements mandatory access control (MAC) using labels
attached to tasks and data containers, including files, SVIPC,
and other tasks. Smack is a kernel based scheme that requires
an absolute minimum of application support and a very small
amount of configuration data.

Smack is implemented as a clean LSM. It requires no external
code changes and the patch modifies only the Kconfig and Makefile
in the security directory. Smack uses extended attributes and
provides a set of general mount options, borrowing technics used
elsewhere. Smack uses netlabel for CIPSO labeling. Smack provides
a pseudo-filesystem smackfs that is used for manipulation of
system Smack attributes.

Smack provides mandatory access controls based on the label attached
to a task and the label attached to the object it is attempting to
access. Smack labels are deliberately short (1-7 characters) text
strings. Single character labels using special characters are reserved
for system use. The only operation applied to Smack labels is equality
comparison. No wildcards or expressions, regular or otherwise, are
used.

A file always gets the Smack label of the task that created it.

Smack defines and uses these labels:

"*" - pronounced "star"
"_" - pronounced "floor"
"^" - pronounced "hat"
"?" - pronounced "huh"

The access rules enforced by Smack are, in order:

1. Any access requested by a task labeled "*" is denied.
2. A read or execute access requested by a task labeled "^"
is permitted.
3. A read or execute access requested on an object labeled "_"
is permitted.
4. Any access requested on an object labeled "*" is permitted.
5. Any access requested by a task on an object with the same
label is permitted.
6. Any access requested that is explicitly defined in the loaded
rule set is permitted.
7. Any other access is denied.

Rules may be explicitly defined by writing subject,object,access
triples to /smack/load.

Smack rule sets can be easily defined that describe Bell&LaPadula
sensitivity, Biba integrity, and a variety of interesting
configurations. Smack rule sets can be modified on the fly to
accomodate changes in the operating environment or even the time
of day.

Some practical use cases:

Hierarchical levels. The less common of the two usual uses
for MLS systems is to define hierarchical levels, often
unclassified, confidential, secret, and so on. To set up smack
to support this, these rules could be defined:

C Unclass rx
S C rx
S Unclass rx
TS S rx
TS C rx
TS Unclass rx

A TS process can read S, C, and Unclass data, but cannot write it.
An S process can read C and Unclass. Note that specifying that
TS can read S and S can read C does not imply TS can read C, it
has to be explicitly stated.

Non-hierarchical categories. This is the more common of the
usual uses for an MLS system. Since the default rule is that a
subject cannot access an object with a different label no
access rules are required to implement compartmentalization.

A case that the Bell & LaPadula policy does not allow is demonstrated
with this Smack access rule:

A case that Bell&LaPadula does not allow that Smack does:

ESPN ABC r
ABC ESPN r

On my portable video device I have two applications, one that
shows ABC programming and the other ESPN programming. ESPN wants
to show me sport stories that show up as news, and ABC will
only provide minimal information about a sports story if ESPN
is covering it. Each side can look at the other's info, neither
can change the other. Neither can see what FOX is up to, which
is just as well all things considered.

Another case that I especially like:

SatData Guard w
Guard Publish w

A program running with the Guard label opens a UDP socket and
accepts messages sent by a program running with a SatData label.
The Guard program inspects the message to ensure it is wholesome
and if it is sends it to a program running with the Publish label.
This program then puts the information passed in an appropriate
place. Note that the Guard program cannot write to a Publish
file system object because file system semanitic require read as
well as write.

The four cases (categories, levels, mutual read, guardbox) here
are all quite real, and problems I've been asked to solve over
the years. The first two are easy to do with traditonal MLS systems
while the last two you can't without invoking privilege, at least
for a while.


Signed-off-by: Casey Schaufler <[email protected]>
---
security/Kconfig | 1
security/Makefile | 2
security/smack/Kconfig | 10
security/smack/Makefile | 8
security/smack/smack.h | 246 +++++
security/smack/smack_access.c | 113 ++
security/smack/smack_lsm.c | 1989 ++++++++++++++++++++++++++++++++++++++++++
security/smack/smackfs.c | 994 ++++++++++++++++++++
8 files changed, 3363 insertions(+)

diff -uprN -X linux-2.6.22-base/Documentation/dontdiff linux-2.6.22-base/security/Kconfig linux-2.6.22/security/Kconfig
--- linux-2.6.22-base/security/Kconfig 2007-07-08 16:32:17.000000000 -0700
+++ linux-2.6.22/security/Kconfig 2007-07-10 01:08:05.000000000 -0700
@@ -94,6 +94,7 @@ config SECURITY_ROOTPLUG
If you are unsure how to answer this question, answer N.

source security/selinux/Kconfig
+source security/smack/Kconfig

endmenu

diff -uprN -X linux-2.6.22-base/Documentation/dontdiff linux-2.6.22-base/security/Makefile linux-2.6.22/security/Makefile
--- linux-2.6.22-base/security/Makefile 2007-07-08 16:32:17.000000000 -0700
+++ linux-2.6.22/security/Makefile 2007-07-10 01:08:05.000000000 -0700
@@ -4,6 +4,7 @@

obj-$(CONFIG_KEYS) += keys/
subdir-$(CONFIG_SECURITY_SELINUX) += selinux
+subdir-$(CONFIG_SECURITY_SMACK) += smack

# if we don't select a security model, use the default capabilities
ifneq ($(CONFIG_SECURITY),y)
@@ -14,5 +15,6 @@ endif
obj-$(CONFIG_SECURITY) += security.o dummy.o inode.o
# Must precede capability.o in order to stack properly.
obj-$(CONFIG_SECURITY_SELINUX) += selinux/built-in.o
+obj-$(CONFIG_SECURITY_SMACK) += commoncap.o smack/built-in.o
obj-$(CONFIG_SECURITY_CAPABILITIES) += commoncap.o capability.o
obj-$(CONFIG_SECURITY_ROOTPLUG) += commoncap.o root_plug.o
diff -uprN -X linux-2.6.22-base/Documentation/dontdiff linux-2.6.22-base/security/smack/Kconfig linux-2.6.22/security/smack/Kconfig
--- linux-2.6.22-base/security/smack/Kconfig 1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.22/security/smack/Kconfig 2007-07-10 01:08:05.000000000 -0700
@@ -0,0 +1,10 @@
+config SECURITY_SMACK
+ bool "Simplified Mandatory Access Control Kernel Support"
+ depends on NETLABEL && SECURITY_NETWORK
+ default n
+ help
+ This selects the Simplified Mandatory Access Control Kernel.
+ SMACK is useful for sensitivity, integrity, and a variety
+ of other madatory security schemes.
+ If you are unsure how to answer this question, answer N.
+
diff -uprN -X linux-2.6.22-base/Documentation/dontdiff linux-2.6.22-base/security/smack/Makefile linux-2.6.22/security/smack/Makefile
--- linux-2.6.22-base/security/smack/Makefile 1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.22/security/smack/Makefile 2007-07-10 01:08:05.000000000 -0700
@@ -0,0 +1,8 @@
+#
+# Makefile for the SMACK LSM
+#
+
+obj-$(CONFIG_SECURITY_SMACK) := smack.o
+
+smack-y := smack_lsm.o smack_access.o smackfs.o
+
diff -uprN -X linux-2.6.22-base/Documentation/dontdiff linux-2.6.22-base/security/smack/smack_access.c linux-2.6.22/security/smack/smack_access.c
--- linux-2.6.22-base/security/smack/smack_access.c 1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.22/security/smack/smack_access.c 2007-07-24 15:36:18.000000000 -0700
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2007 Casey Schaufler <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2.
+ *
+ * Author:
+ * Casey Schaufler <[email protected]>
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/fs.h>
+#include <linux/sched.h>
+#include "smack.h"
+
+extern struct smk_list_entry *smack_list;
+
+static int smk_get_access(smack_t sub, smack_t obj)
+{
+ struct smk_list_entry *sp = smack_list;
+
+ for (; sp != NULL; sp = sp->smk_next)
+ if (sp->smk_rule.smk_subject == sub &&
+ sp->smk_rule.smk_object == obj)
+ return sp->smk_rule.smk_access;
+ /*
+ * No access is explicitly defined for this pair.
+ */
+ return MAY_NOT;
+}
+
+int smk_access(smack_t *sub_label, smack_t *obj_label, int request)
+{
+ smack_t sub = *sub_label;
+ smack_t obj = *obj_label;
+ /*
+ * Hardcoded comparisons.
+ *
+ * A star subject can't access any object.
+ */
+ if (sub == SMK_STAR)
+ return -EACCES;
+ /*
+ * A star object can be accessed by any subject.
+ */
+ if (obj == SMK_STAR)
+ return 0;
+ /*
+ * An object can be accessed in any way by a subject
+ * with the same label.
+ */
+ if (sub == obj)
+ return 0;
+ /*
+ * A hat subject can read any object.
+ * A floor object can be read by any subject.
+ */
+ if (sub == SMK_HAT && ((request & MAY_ANYREAD) == request))
+ return 0;
+
+ if (obj == SMK_FLOOR && ((request & MAY_ANYREAD) == request))
+ return 0;
+ /*
+ * 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.
+ *
+ * Yes, that is supposed to be a single ampersand.
+ */
+ if ((request & smk_get_access(sub, obj)) == request)
+ return 0;
+
+ return -EACCES;
+}
+
+int smk_curacc(smack_t *obj_label, u32 mode)
+{
+ struct task_smack *tsp = current->security;
+ int rc;
+
+ rc = smk_access(&tsp->smk_task, obj_label, mode);
+ if (rc == 0)
+ return 0;
+
+ if (capable(CAP_MAC_OVERRIDE))
+ return 0;
+
+ return rc;
+}
+
+/*
+ * The value that this adds is that everything after any
+ * character that's not allowed in a smack will be null
+ */
+smack_t smk_from_string(char *str)
+{
+ smack_t smack = 0LL;
+ char *cp;
+ int i;
+
+ for (cp = (char *)&smack, i = 0; i < sizeof(smack_t); str++,cp++,i++) {
+ if (*str <= ' ' || *str > '~')
+ return smack;
+ *cp = *str;
+ }
+ /*
+ * Too long.
+ */
+ return SMK_INVALID;
+}
diff -uprN -X linux-2.6.22-base/Documentation/dontdiff linux-2.6.22-base/security/smack/smackfs.c linux-2.6.22/security/smack/smackfs.c
--- linux-2.6.22-base/security/smack/smackfs.c 1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.22/security/smack/smackfs.c 2007-07-24 21:51:30.000000000 -0700
@@ -0,0 +1,994 @@
+/*
+ * Copyright (C) 2007 Casey Schaufler <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2.
+ *
+ * Author:
+ * Casey Schaufler <[email protected]>
+ *
+ * Special thanks to the authors of selinuxfs. They had a good idea.
+ *
+ * Karl MacMillan <[email protected]>
+ * James Morris <[email protected]>
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/vmalloc.h>
+#include <linux/security.h>
+#include <linux/mutex.h>
+#include <net/netlabel.h>
+#include "../../net/netlabel/netlabel_domainhash.h"
+#include <net/cipso_ipv4.h>
+#include "smack.h"
+
+/*
+ * smackfs pseudo filesystem.
+ */
+
+
+enum smk_inos {
+ SMK_ROOT_INO = 2,
+ SMK_LOAD = 3, /* load policy */
+ SMK_LINKS = 4, /* symlinks */
+ SMK_CIPSO = 5, /* load label -> CIPSO mapping */
+ SMK_DOI = 6, /* CIPSO DOI */
+ SMK_DIRECT = 7, /* CIPSO level indicating direct label */
+ SMK_AMBIENT = 8, /* internet ambient label */
+ SMK_NLTYPE = 9, /* label scheme to use by default */
+ SMK_TMP = 100, /* MUST BE LAST! /smack/tmp */
+};
+
+/*
+ * This is the "ambient" label for network traffic.
+ * If it isn't somehow marked, use this.
+ * It can be reset via smackfs/ambient
+ */
+smack_t smack_net_ambient = SMK_FLOOR;
+
+/*
+ * This is the default packet marking scheme for network traffic.
+ * It can be reset via smackfs/nltype
+ */
+int smack_net_nltype = NETLBL_NLTYPE_CIPSOV4;
+
+/*
+ * This is the level in a CIPSO header that indicates a
+ * smack label is contained directly in the category set.
+ * It can be reset via smackfs/direct
+ */
+int smack_cipso_direct = SMACK_CIPSO_DIRECT_DEFAULT;
+static int smk_cipso_doi_value = SMACK_CIPSO_DOI_DEFAULT;
+
+static struct smk_cipso_entry smack_cipso_floor = {
+ .smk_next = NULL,
+ .smk_smack = SMK_FLOOR,
+ .smk_level = 0,
+ .smk_catset = 0LL,
+};
+struct smk_cipso_entry *smack_cipso = &smack_cipso_floor;
+static DEFINE_MUTEX(smack_cipso_lock);
+
+static int smack_list_count;
+struct smk_list_entry *smack_list;
+static DEFINE_MUTEX(smack_list_lock);
+
+/*
+ * 'ssssssss oooooooo mmmm\n\0'
+ */
+#define SMACK_RULE_LINE_SIZE (2 * (sizeof(smack_t) + 1) + 6)
+
+static ssize_t smk_read_load(struct file *filp, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ ssize_t bytes;
+ struct smk_list_entry *slp = smack_list;
+ struct smack_rule *srp;
+ char *result;
+ char *cp;
+ int realbytes = 0;
+
+ bytes = SMACK_RULE_LINE_SIZE * smack_list_count;
+ if (bytes == 0)
+ return 0;
+
+ result = kzalloc(bytes, GFP_KERNEL);
+ if (result == NULL)
+ return -ENOMEM;
+
+ for (cp = result; slp != NULL; slp = slp->smk_next) {
+ srp = &slp->smk_rule;
+ sprintf(cp, "%-8s %-8s",
+ (char *)&srp->smk_subject, (char *)&srp->smk_object);
+ cp += strlen(cp);
+ if (srp->smk_access != 0)
+ *cp++ = ' ';
+ if ((srp->smk_access & MAY_READ) != 0)
+ *cp++ = 'r';
+ if ((srp->smk_access & MAY_WRITE) != 0)
+ *cp++ = 'w';
+ if ((srp->smk_access & MAY_EXEC) != 0)
+ *cp++ = 'x';
+ if ((srp->smk_access & MAY_APPEND) != 0)
+ *cp++ = 'a';
+ *cp++ = '\n';
+ }
+ *cp++ = '\0';
+ realbytes = strlen(result);
+
+ bytes = simple_read_from_buffer(buf, count, ppos, result, realbytes);
+
+ kfree(result);
+
+ return bytes;
+}
+
+/*
+ * For purposes of SMACK:
+ * append is a form of write
+ * exec is a form of read
+ * This isn't reflected here, but I thought I should mention it.
+ */
+
+static void smk_set_access(struct smack_rule *srp)
+{
+ struct smk_list_entry *sp;
+ struct smk_list_entry *newp;
+
+ mutex_lock(&smack_list_lock);
+
+ for (sp = smack_list; sp != NULL; sp = sp->smk_next)
+ if (sp->smk_rule.smk_subject == srp->smk_subject &&
+ sp->smk_rule.smk_object == srp->smk_object) {
+ sp->smk_rule.smk_access = srp->smk_access;
+ break;
+ }
+
+ if (sp == NULL) {
+ newp = kzalloc(sizeof(struct smk_list_entry), GFP_KERNEL);
+ newp->smk_rule = *srp;
+ newp->smk_next = smack_list;
+ smack_list = newp;
+ smack_list_count++;
+ }
+
+ mutex_unlock(&smack_list_lock);
+
+ return;
+}
+
+
+static ssize_t smk_write_load(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct smack_rule rule;
+ ssize_t rc = count;
+ char *data = NULL;
+ char modestr[8];
+ char *cp;
+
+
+ if (!capable(CAP_MAC_OVERRIDE))
+ return -EPERM;
+ /*
+ * No partial writes.
+ */
+ if (*ppos != 0)
+ return -EINVAL;
+
+ /*
+ * 80 characters per line ought to be enough.
+ */
+ if (count > SMACK_LIST_MAX * 80)
+ return -ENOMEM;
+
+ data = kzalloc(count + 1, GFP_KERNEL);
+ if (data == NULL)
+ return -ENOMEM;
+
+ if (copy_from_user(data, buf, count) != 0) {
+ kfree(data);
+ return -EFAULT;
+ }
+
+ *(data + count) = '\0';
+
+ for (cp = data - 1; cp != NULL; cp = strchr(cp + 1, '\n')) {
+ if (*++cp == '\0')
+ break;
+ if (sscanf(cp, "%7s %7s %7s\n",
+ (char *)&rule.smk_subject,
+ (char *)&rule.smk_object, modestr) != 3) {
+ printk("%s:%d bad scan\n",
+ __FUNCTION__, __LINE__);
+ break;
+ }
+ rule.smk_subject =
+ smk_from_string((char *)&rule.smk_subject);
+ if (rule.smk_subject == SMK_INVALID)
+ break;
+ rule.smk_object =
+ smk_from_string((char *)&rule.smk_object);
+ if (rule.smk_object == SMK_INVALID)
+ break;
+ rule.smk_access = 0;
+ if (strchr(modestr, 'r') || strchr(modestr, 'R'))
+ rule.smk_access |= MAY_READ;
+ if (strchr(modestr, 'w') || strchr(modestr, 'W'))
+ rule.smk_access |= MAY_WRITE;
+ if (strchr(modestr, 'x') || strchr(modestr, 'X'))
+ rule.smk_access |= MAY_EXEC;
+ if (strchr(modestr, 'a') || strchr(modestr, 'A'))
+ rule.smk_access |= MAY_APPEND;
+ smk_set_access(&rule);
+ printk("%s:%d rule %s %s 0x%x\n",
+ __FUNCTION__, __LINE__,
+ (char *)&rule.smk_subject,
+ (char *)&rule.smk_object,
+ rule.smk_access);
+ }
+
+ kfree(data);
+ return rc;
+}
+
+static struct file_operations smk_load_ops = {
+ .read = smk_read_load,
+ .write = smk_write_load,
+};
+
+static char *smk_digit(char *cp)
+{
+ for (; *cp != '\0'; cp++)
+ if (*cp >= '0' && *cp <= '9')
+ return cp;
+
+ return NULL;
+}
+
+static int smk_cipso_doied;
+static int smk_cipso_written;
+
+/*
+ * This code reaches too deeply into netlabel internals
+ * for comfort, however there is no netlabel KAPI that
+ * allows for kernel based initialization of a CIPSO DOI.
+ * Until Paul and Casey can work out an appropriate
+ * interface Smack will do it this way.
+ */
+static void smk_cipso_doi(void)
+{
+ int rc;
+ struct cipso_v4_doi *doip;
+ struct netlbl_dom_map *ndmp;
+ struct netlbl_audit audit_info;
+
+ if (smk_cipso_doied != 0)
+ return;
+ smk_cipso_doied = 1;
+
+ doip = kmalloc(sizeof(struct cipso_v4_doi), GFP_KERNEL);
+ if (doip == NULL)
+ panic("smack: Failed to initialize cipso DOI.\n");
+ doip->map.std = NULL;
+
+ ndmp = kmalloc(sizeof(struct netlbl_dom_map), GFP_KERNEL);
+ if (ndmp == NULL)
+ panic("smack: Failed to initialize cipso ndmp.\n");
+
+ doip->doi = smk_cipso_doi_value;
+ doip->type = CIPSO_V4_MAP_PASS;
+ doip->tags[0] = CIPSO_V4_TAG_RBITMAP;
+ for (rc = 1; rc < CIPSO_V4_TAG_MAXCNT; rc++)
+ doip->tags[rc] = CIPSO_V4_TAG_INVALID;
+
+ rc = cipso_v4_doi_add(doip);
+ if (rc != 0)
+ printk("%s:%d add doi rc = %d\n", __FUNCTION__, __LINE__, rc);
+
+ ndmp->domain = NULL;
+ ndmp->type = NETLBL_NLTYPE_CIPSOV4;
+ ndmp->type_def.cipsov4 = doip;
+
+ rc = netlbl_domhsh_remove_default(&audit_info);
+ if (rc != 0)
+ printk("%s:%d remove rc = %d\n", __FUNCTION__, __LINE__, rc);
+
+ rc = netlbl_domhsh_add_default(ndmp, &audit_info);
+ if (rc != 0)
+ printk("%s:%d add rc = %d\n", __FUNCTION__, __LINE__, rc);
+}
+
+/*
+ * label level[/cat[,cat]]
+ */
+static ssize_t smk_read_cipso(struct file *filp, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ ssize_t bytes;
+ struct smk_cipso_entry *slp = smack_cipso;
+ char sep;
+ char *result;
+ char *cp;
+ char *cbp;
+ int realbytes = 0;
+ int cat = -1;
+ unsigned char m;
+
+ smk_cipso_doi();
+
+ result = kzalloc(smk_cipso_written, GFP_KERNEL);
+ if (result == NULL)
+ return -ENOMEM;
+ cp = result;
+
+ for (slp = smack_cipso; slp != NULL; slp = slp->smk_next) {
+ sprintf(cp, "%-8s %3d", (char *)&slp->smk_smack,slp->smk_level);
+ cp += strlen(cp);
+ cat = 1;
+ sep = '/';
+ for (cbp = (char *)&slp->smk_catset; *cbp != 0; cbp++) {
+ for (m = 0x80; m != 0; m >>= 1) {
+ if ((m & *cbp) != 0) {
+ sprintf(cp, "%c%d", sep, cat);
+ cp += strlen(cp);
+ sep = ',';
+ }
+ cat++;
+ }
+ }
+ *cp++ = '\n';
+ }
+ *cp++ = '\0';
+ realbytes = strlen(result);
+
+ bytes = simple_read_from_buffer(buf, count, ppos, result, realbytes);
+
+ kfree(result);
+
+ return bytes;
+}
+
+static ssize_t smk_write_cipso(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct smk_cipso_entry *scp;
+ smack_t mapsmack;
+ smack_t mapcatset;
+ int maplevel;
+ ssize_t rc = count;
+ char *data = NULL;
+ char *cp;
+ char *eolp;
+ char *linep;
+ int cat;
+ int i;
+
+ smk_cipso_doi();
+
+ if (!capable(CAP_MAC_OVERRIDE))
+ return -EPERM;
+ /*
+ * No partial writes.
+ */
+ if (*ppos != 0)
+ return -EINVAL;
+ /*
+ * 80 characters per line ought to be enough.
+ */
+ if (count > SMACK_LIST_MAX * 80)
+ return -ENOMEM;
+
+ data = kzalloc(count + 1, GFP_KERNEL);
+ if (data == NULL)
+ return -ENOMEM;
+
+ if (copy_from_user(data, buf, count) != 0) {
+ kfree(data);
+ return -EFAULT;
+ }
+
+ *(data + count) = '\0';
+ smk_cipso_written += count;
+
+ for (eolp = strchr(data, '\n'), linep = data;
+ eolp != NULL && rc >= 0;
+ linep = eolp + 1, eolp = strchr(linep, '\n')) {
+
+ if (eolp == linep)
+ continue;
+ *eolp = '\0';
+
+ mapsmack = smk_from_string(linep);
+ mapcatset = 0ll;
+
+ if (mapsmack == SMK_INVALID)
+ continue;
+
+ cp = smk_digit(linep + strlen((char *)&mapsmack));
+ if (cp == NULL)
+ continue;
+
+ i = sscanf(cp, "%d", &maplevel);
+ if (i != 1)
+ continue;
+
+ cp = strchr(cp, '/');
+ if (cp != NULL) {
+ cp = smk_digit(cp);
+ if (cp == NULL)
+ continue;
+
+ do {
+ i = sscanf(cp, "%d", &cat);
+ if (i != 1)
+ break;
+ if (cat > SMACK_CIPSO_MAXCAT) {
+ i = 0;
+ break;
+ }
+ smack_catset_bit(cat, &mapcatset);
+
+ cp = strchr(cp, ',');
+ if (cp != NULL)
+ cp = smk_digit(cp);
+ } while (cp != NULL);
+ }
+
+ if (i != 1)
+ continue;
+
+ mutex_lock(&smack_cipso_lock);
+
+ for (scp = smack_cipso; scp != NULL; scp = scp->smk_next)
+ if (mapsmack == scp->smk_smack)
+ break;
+
+ if (scp == NULL) {
+ scp = kzalloc(sizeof(struct smk_cipso_entry),
+ GFP_KERNEL);
+ if (scp == NULL)
+ rc = -ENOMEM;
+ else {
+ scp->smk_next = smack_cipso;
+ scp->smk_smack = mapsmack;
+ scp->smk_level = maplevel;
+ scp->smk_catset = mapcatset;
+ smack_cipso = scp;
+ /*
+ * Add this to ensure that there are
+ * enough bytes for the regurgitation
+ */
+ smk_cipso_written += sizeof(smack_t);
+ }
+ }
+
+ mutex_unlock(&smack_cipso_lock);
+ }
+
+ kfree(data);
+ return rc;
+}
+
+static struct file_operations smk_cipso_ops = {
+ .read = smk_read_cipso,
+ .write = smk_write_cipso,
+};
+
+static ssize_t smk_read_doi(struct file *filp, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ char temp[80];
+ ssize_t rc;
+
+ if (*ppos != 0)
+ return 0;
+
+ sprintf(temp, "%d", smk_cipso_doi_value);
+ rc = simple_read_from_buffer(buf, count, ppos, temp, strlen(temp));
+
+ return rc;
+}
+
+static ssize_t smk_write_doi(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ char temp[80];
+ int i;
+
+ if (!capable(CAP_MAC_OVERRIDE))
+ return -EPERM;
+
+ if (count > sizeof(temp))
+ return -EINVAL;
+
+ if (copy_from_user(temp, buf, count) != 0)
+ return -EFAULT;
+
+ if (sscanf(temp, "%d", &i) != 1)
+ return -EINVAL;
+
+ smk_cipso_doi_value = i;
+
+ return count;
+}
+
+static struct file_operations smk_doi_ops = {
+ .read = smk_read_doi,
+ .write = smk_write_doi,
+};
+
+static ssize_t smk_read_direct(struct file *filp, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ char temp[80];
+ ssize_t rc;
+
+ if (*ppos != 0)
+ return 0;
+
+ sprintf(temp, "%d", smack_cipso_direct);
+ rc = simple_read_from_buffer(buf, count, ppos, temp, strlen(temp));
+
+ return rc;
+}
+
+static ssize_t smk_write_direct(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ char temp[80];
+ int i;
+
+ if (!capable(CAP_MAC_OVERRIDE))
+ return -EPERM;
+
+ if (count > sizeof(temp))
+ return -EINVAL;
+
+ if (copy_from_user(temp, buf, count) != 0)
+ return -EFAULT;
+
+ if (sscanf(temp, "%d", &i) != 1)
+ return -EINVAL;
+
+ smack_cipso_direct = i;
+
+ return count;
+}
+
+static struct file_operations smk_direct_ops = {
+ .read = smk_read_direct,
+ .write = smk_write_direct,
+};
+
+static ssize_t smk_read_ambient(struct file *filp, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ ssize_t rc;
+
+ if (count < sizeof(smack_t))
+ return -EINVAL;
+
+ if (*ppos != 0)
+ return 0;
+
+ rc = simple_read_from_buffer(buf, count, ppos, &smack_net_ambient,
+ sizeof(smack_t));
+
+ return rc;
+}
+
+static ssize_t smk_write_ambient(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ smack_t smack;
+
+ if (!capable(CAP_MAC_OVERRIDE))
+ return -EPERM;
+
+ if (count > sizeof(smack_t))
+ return -EINVAL;
+
+ if (copy_from_user(&smack, buf, count) != 0)
+ return -EFAULT;
+
+ smack = smk_from_buffer(&smack, count);
+ if (smack == SMK_INVALID)
+ return -EINVAL;
+ /*
+ * Better check to be sure this is OK.
+ */
+ smack_net_ambient = smack;
+
+ return count;
+}
+
+static struct file_operations smk_ambient_ops = {
+ .read = smk_read_ambient,
+ .write = smk_write_ambient,
+};
+
+struct option_names {
+ int o_number;
+ char *o_name;
+ char *o_alias;
+};
+
+static struct option_names netlbl_choices[] = {
+ { NETLBL_NLTYPE_RIPSO,
+ NETLBL_NLTYPE_RIPSO_NAME, "ripso" },
+ { NETLBL_NLTYPE_CIPSOV4,
+ NETLBL_NLTYPE_CIPSOV4_NAME, "cipsov4" },
+ { NETLBL_NLTYPE_CIPSOV4,
+ NETLBL_NLTYPE_CIPSOV4_NAME, "cipso" },
+ { NETLBL_NLTYPE_CIPSOV6,
+ NETLBL_NLTYPE_CIPSOV6_NAME, "cipsov6" },
+ { NETLBL_NLTYPE_UNLABELED,
+ NETLBL_NLTYPE_UNLABELED_NAME, "unlabeled" },
+};
+#define NCHOICES (sizeof(netlbl_choices) / sizeof(struct option_names))
+
+static ssize_t smk_read_nltype(struct file *filp, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ char bound[40];
+ ssize_t rc;
+ int i;
+
+ if (count < sizeof(smack_t))
+ return -EINVAL;
+
+ if (*ppos != 0)
+ return 0;
+
+ sprintf(bound, "unknown");
+
+ for (i = 0; i < NCHOICES; i++)
+ if (smack_net_nltype == netlbl_choices[i].o_number) {
+ sprintf(bound, "%s", netlbl_choices[i].o_name);
+ break;
+ }
+
+ rc = simple_read_from_buffer(buf, count, ppos, bound, strlen(bound));
+
+ return rc;
+}
+
+static ssize_t smk_write_nltype(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ char bound[40];
+ char *cp;
+ int i;
+
+ if (!capable(CAP_MAC_OVERRIDE))
+ return -EPERM;
+
+ if (count >= 40)
+ return -EINVAL;
+
+ if (copy_from_user(bound, buf, count) != 0)
+ return -EFAULT;
+
+ bound[count] = '\0';
+ cp = strchr(bound, ' ');
+ if (cp != NULL)
+ *cp = '\0';
+ cp = strchr(bound, '\n');
+ if (cp != NULL)
+ *cp = '\0';
+
+ for (i = 0; i < NCHOICES; i++)
+ if ((strcmp(bound, netlbl_choices[i].o_name) == 0) ||
+ (strcmp(bound, netlbl_choices[i].o_alias) == 0)) {
+ smack_net_nltype = netlbl_choices[i].o_number;
+ return count;
+ }
+ /*
+ * Not a valid choice.
+ */
+ return -EINVAL;
+}
+
+static struct file_operations smk_nltype_ops = {
+ .read = smk_read_nltype,
+ .write = smk_write_nltype,
+};
+
+/*
+ * mapping for symlinks
+ */
+#define SMK_TMPPATH_SIZE 32
+#define SMK_TMPPATH_ROOT "/moldy/"
+
+struct smk_link {
+ struct smk_link *sl_next;
+ int sl_inum;
+ char sl_name[SMK_TMPPATH_SIZE];
+ char sl_target[SMK_TMPPATH_SIZE];
+};
+
+static struct super_block *smk_sb = NULL;
+static struct smk_link *smk_links = NULL;
+static int smk_links_count = 0;
+
+static void *smackfs_follow_link(struct dentry *dentry, struct nameidata *nd)
+{
+ struct task_smack *tsp = current->security;
+ smack_t *sp = &tsp->smk_task;
+ char *cp;
+ int inum = dentry->d_inode->i_ino;
+ struct smk_link *slp;
+
+ for (slp = smk_links; slp != NULL; slp = slp->sl_next)
+ if (slp->sl_inum == inum)
+ break;
+
+ if (slp == NULL) {
+ printk("%s:%d failed\n", __FUNCTION__, __LINE__);
+ return NULL;
+ }
+ cp = kzalloc(SMK_TMPPATH_SIZE, GFP_KERNEL);
+ if (cp == NULL)
+ return NULL;
+
+ strcpy(cp, slp->sl_target);
+ strcat(cp, (char *)sp);
+ nd_set_link(nd, cp);
+ /*
+ * Unlike the relink below, hang on to the memory allocated
+ * because nd_set_link passes it along.
+ */
+ return NULL;
+}
+
+static int smackfs_readlink(struct dentry *dentry, char __user *buffer, int buflen)
+{
+ smack_t *csp = smk_of_task(current);
+ char *cp;
+ int len;
+ int inum = dentry->d_inode->i_ino;
+ struct smk_link *slp;
+
+ for (slp = smk_links; slp != NULL; slp = slp->sl_next)
+ if (slp->sl_inum == inum)
+ break;
+
+ if (slp == NULL) {
+ printk("%s:%d failed\n", __FUNCTION__, __LINE__);
+ return -EACCES;
+ }
+
+ cp = kzalloc(SMK_TMPPATH_SIZE, GFP_KERNEL);
+ if (cp == NULL)
+ return -ENOMEM;
+
+ strcpy(cp, slp->sl_target);
+ strcat(cp, (char *)csp);
+ len = strlen(cp);
+ len = (len > buflen) ? buflen : len;
+
+ if (copy_to_user(buffer, cp, len) != 0)
+ len = -EFAULT;
+
+ kfree(cp);
+ return len;
+}
+
+static void smackfs_put_link(struct dentry *dentry, struct nameidata *nd, void *ptr)
+{
+ kfree(nd_get_link(nd));
+}
+
+static struct inode_operations smackfs_symlink_inode_operations = {
+ .readlink = smackfs_readlink,
+ .follow_link = smackfs_follow_link,
+ .put_link = smackfs_put_link,
+};
+
+static void smk_add_symlink(char *name, char *target)
+{
+ static int inum = SMK_TMP;
+ struct inode *inode;
+ struct dentry *dentry;
+ struct smk_link *slp;
+
+ for (slp = smk_links; slp != NULL; slp = slp->sl_next) {
+ if (strcmp(slp->sl_name, name) != 0)
+ continue;
+ strcpy(slp->sl_target, target);
+ return;
+ }
+
+ slp = kzalloc(sizeof(struct smk_link), GFP_KERNEL);
+ if (slp == NULL)
+ return;
+
+ dentry = d_alloc_name(smk_sb->s_root, name);
+ if (dentry == NULL) {
+ printk("%s:%d link dentry failed\n", __FUNCTION__, __LINE__);
+ return;
+ }
+
+ inode = new_inode(smk_sb);
+ if (inode == NULL) {
+ printk("%s:%d link inode failed\n", __FUNCTION__, __LINE__);
+ return;
+ }
+
+ inode->i_mode = S_IFLNK | S_IRWXUGO;
+ inode->i_uid = 0;
+ inode->i_gid = 0;
+ inode->i_blocks = 0;
+ inode->i_atime = CURRENT_TIME;
+ inode->i_mtime = inode->i_atime;
+ inode->i_ctime = inode->i_atime;
+ inode->i_ino = inum++;
+ inode->i_op = &smackfs_symlink_inode_operations;
+ d_add(dentry, inode);
+
+ strcpy(slp->sl_name, name);
+ strcpy(slp->sl_target, target);
+ slp->sl_inum = inode->i_ino;
+ slp->sl_next = smk_links;
+ smk_links = slp;
+ smk_links_count++;
+
+ return;
+}
+
+static ssize_t smk_read_links(struct file *filp, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ ssize_t bytes = sizeof(struct smk_link) * smk_links_count;
+ struct smk_link *slp;
+ char *result;
+ char *cp;
+
+
+ result = kzalloc(bytes, GFP_KERNEL);
+ if (result == NULL)
+ return -ENOMEM;
+ *result = '\0';
+
+ for (slp = smk_links, cp = result; slp != NULL; slp = slp->sl_next) {
+ sprintf(cp, "%s %s\n", slp->sl_name, slp->sl_target);
+ cp += strlen(slp->sl_name) + strlen(slp->sl_target);
+ }
+
+ bytes = simple_read_from_buffer(buf,count,ppos,result,strlen(result));
+
+ kfree(result);
+
+ return bytes;
+}
+
+static ssize_t smk_write_links(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ ssize_t rc = count;
+ char *data;
+ char *cp;
+ char name[SMK_TMPPATH_SIZE];
+ char target[SMK_TMPPATH_SIZE];
+
+ /*
+ * No partial writes.
+ */
+ if (*ppos != 0)
+ return -EINVAL;
+ /*
+ * 80 characters per line ought to be enough.
+ */
+ if (count > SMACK_LIST_MAX * 80)
+ return -ENOMEM;
+
+ data = kzalloc(count + 1, GFP_KERNEL);
+ if (data == NULL)
+ return -ENOMEM;
+
+ if (copy_from_user(data, buf, count) != 0) {
+ kfree(data);
+ return -EFAULT;
+ }
+
+ *(data + count) = '\0';
+
+ for (cp = data - 1; cp != NULL; cp = strchr(cp + 1, '\n')) {
+ if (*++cp == '\0')
+ break;
+ if (sscanf(cp, "%14s %30s\n", name, target) != 2) {
+ printk("%s:%d bad scan\n",
+ __FUNCTION__, __LINE__);
+ break;
+ }
+ smk_add_symlink(name, target);
+ printk("%s:%d add %s -> %s\n",
+ __FUNCTION__, __LINE__, name, target);
+ }
+
+ kfree(data);
+ return rc;
+}
+
+static struct file_operations smk_links_ops = {
+ .read = smk_read_links,
+ .write = smk_write_links,
+};
+
+static int smk_fill_super(struct super_block *sb, void * data, int silent)
+{
+ int rc;
+ struct inode *root_inode;
+
+ static struct tree_descr smack_files[] = {
+ [SMK_LOAD] = {"load", &smk_load_ops, S_IRUGO|S_IWUSR},
+ [SMK_LINKS] = {"links", &smk_links_ops, S_IRUGO|S_IWUSR},
+ [SMK_CIPSO] = {"cipso", &smk_cipso_ops, S_IRUGO|S_IWUSR},
+ [SMK_DOI] = {"doi", &smk_doi_ops, S_IRUGO|S_IWUSR},
+ [SMK_DIRECT] = {"direct", &smk_direct_ops, S_IRUGO|S_IWUSR},
+ [SMK_AMBIENT] = {"ambient", &smk_ambient_ops, S_IRUGO|S_IWUSR},
+ [SMK_NLTYPE] = {"nltype", &smk_nltype_ops, S_IRUGO|S_IWUSR},
+ /* last one */ {""}
+ };
+
+ /*
+ * There will be only one smackfs. Casey says so.
+ */
+ smk_sb = sb;
+
+ rc = simple_fill_super(sb, SMACK_MAGIC, smack_files);
+ if (rc != 0) {
+ printk(KERN_ERR "%s failed %d while creating inodes\n",
+ __FUNCTION__, rc);
+ return rc;
+ }
+
+ root_inode = sb->s_root->d_inode;
+ root_inode->i_security = new_inode_smack(SMK_FLOOR);
+
+ /*
+ * Create a directory for /smack/tmp
+ */
+ smk_add_symlink("tmp", SMK_TMPPATH_ROOT);
+
+ return 0;
+}
+
+static int smk_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, smk_fill_super, mnt);
+}
+
+static struct file_system_type smk_fs_type = {
+ .name = "smackfs",
+ .get_sb = smk_get_sb,
+ .kill_sb = kill_litter_super,
+};
+
+static struct vfsmount *smackfs_mount;
+
+static int __init init_smk_fs(void)
+{
+ int err;
+
+ err = register_filesystem(&smk_fs_type);
+ if (!err) {
+ smackfs_mount = kern_mount(&smk_fs_type);
+ if (IS_ERR(smackfs_mount)) {
+ printk(KERN_ERR "smackfs: could not mount!\n");
+ err = PTR_ERR(smackfs_mount);
+ smackfs_mount = NULL;
+ }
+ }
+
+ mutex_init(&smack_list_lock);
+ mutex_init(&smack_cipso_lock);
+ return err;
+}
+
+__initcall(init_smk_fs);
diff -uprN -X linux-2.6.22-base/Documentation/dontdiff linux-2.6.22-base/security/smack/smack.h linux-2.6.22/security/smack/smack.h
--- linux-2.6.22-base/security/smack/smack.h 1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.22/security/smack/smack.h 2007-07-24 16:16:27.000000000 -0700
@@ -0,0 +1,246 @@
+/*
+ * Copyright (C) 2007 Casey Schaufler <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2.
+ *
+ * Author:
+ * Casey Schaufler <[email protected]>
+ *
+ */
+
+#ifndef _SECURITY_SMACK_H
+#define _SECURITY_SMACK_H
+
+#include <linux/capability.h>
+#include <net/netlabel.h>
+
+/*
+ * A smack_t contains an 8 byte string, 7 characters
+ * and a terminating NULL. Yes, it's short and 12.5%
+ * wasted.
+ *
+ * There is likely to be experimentation with alternative
+ * representations, perhaps a u32 so that it's the same
+ * as what's used in Other LSM MAC schemes, or an array
+ * of 20 chars so that the names aren't so tightly constrained.
+ */
+typedef u64 smack_t;
+
+struct superblock_smack {
+ smack_t smk_root;
+ smack_t smk_floor;
+ smack_t smk_hat;
+ smack_t smk_default;
+ int smk_initialized;
+};
+
+/*
+ * Task smack data
+ */
+struct task_smack {
+ smack_t smk_task; /* label of the task */
+};
+
+/*
+ * Socket smack data
+ */
+struct socket_smack {
+ smack_t smk_out; /* label to associate with outbound packets */
+ smack_t smk_in; /* label to check on incoming packets */
+ smack_t smk_packet; /* label of incoming packet on request */
+};
+
+/*
+ * Inode smack data
+ */
+struct inode_smack {
+ smack_t smk_inode; /* label of the fso */
+ struct mutex smk_lock; /* initialization lock */
+ int smk_flags; /* smack inode flags */
+};
+
+#define SMK_INODE_INSTANT 0x01 /* inode is instantiated */
+
+/*
+ * A label access rule.
+ */
+struct smack_rule {
+ smack_t smk_subject;
+ smack_t smk_object;
+ int smk_access;
+};
+
+/*
+ * An entry in the table of permitted label accesses.
+ */
+struct smk_list_entry {
+ struct smk_list_entry *smk_next;
+ struct smack_rule smk_rule;
+};
+
+/*
+ * An entry in the table mapping smack values to
+ * CIPSO level/category-set values.
+ */
+struct smk_cipso_entry {
+ struct smk_cipso_entry *smk_next;
+ int smk_level; /* for CIPSO */
+ smack_t smk_smack;
+ smack_t smk_catset;
+};
+
+/*
+ * Mount options
+ */
+#define SMK_FSDEFAULT "smackfsdef="
+#define SMK_FSFLOOR "smackfsfloor="
+#define SMK_FSHAT "smackfshat="
+#define SMK_FSROOT "smackfsroot="
+
+/*
+ * xattr names
+ */
+#define XATTR_SMACK_SUFFIX "SMACK64"
+#define XATTR_SMACK_IPIN "SMACK64IPIN"
+#define XATTR_SMACK_IPOUT "SMACK64IPOUT"
+#define XATTR_SMACK_PACKET "SMACK64PACKET"
+#define XATTR_NAME_SMACK XATTR_SECURITY_PREFIX XATTR_SMACK_SUFFIX
+
+/*
+ * smackfs macic number
+ */
+#define SMACK_MAGIC 0x43415d53 /* "SMAC" */
+
+/*
+ * A limit on the number of entries in the lists
+ * makes some of the list administration easier.
+ */
+#define SMACK_LIST_MAX 10000
+
+/*
+ * CIPSO defaults.
+ */
+#define SMACK_CIPSO_DOI_DEFAULT 3 /* Historical */
+#define SMACK_CIPSO_DIRECT_DEFAULT 250 /* Arbitrary */
+#define SMACK_CIPSO_MAXCAT 63 /* Bigger gets harder */
+
+/*
+ * Pre-defined smack label values.
+ */
+#define SMK_INVALID 0x00LL /* NULLSTRING */
+#define SMK_HAT 0x5ELL /* "^" */
+#define SMK_FLOOR 0x5FLL /* "_" */
+#define SMK_STAR 0x2ALL /* "*" */
+#define SMK_HUH 0x3FLL /* "?" */
+#define SMK_UNSET 0x5445534e55LL /* "UNSET" */
+/*
+ * There's a place in the CIPSO initialization that
+ * wants this.
+ */
+#define SMK32_FLOOR 0x5F /* "_" */
+
+#define SMK_MAXLEN (sizeof(smack_t) - 1) /* NULL terminated */
+/*
+ * Just to make the common cases easier to deal with
+ */
+#define MAY_ANY (MAY_READ | MAY_WRITE | MAY_APPEND | MAY_EXEC)
+#define MAY_ANYREAD (MAY_READ | MAY_EXEC)
+#define MAY_ANYWRITE (MAY_WRITE | MAY_APPEND)
+#define MAY_READWRITE (MAY_READ | MAY_WRITE)
+#define MAY_NOT 0
+
+/*
+ * There are not enough CAP bits available to make this
+ * real, so Casey borrowed the capability that looks to
+ * him like it has the best balance of similarity amd
+ * low use.
+ */
+#define CAP_MAC_OVERRIDE CAP_LINUX_IMMUTABLE
+
+/*
+ * These functions are in smack_lsm.c
+ */
+struct inode_smack *new_inode_smack(smack_t);
+
+/*
+ * These functions are in smack_access.c
+ */
+int smk_access(smack_t *, smack_t *, int);
+int smk_curacc(smack_t *, u32);
+smack_t smk_from_string(char *);
+
+/*
+ * Stricly for CIPSO level manipulation.
+ * Set the category bit number in a smack_t.
+ */
+static inline void smack_catset_bit(int cat, smack_t *catsetp)
+{
+ char *cp = (char *)catsetp;
+
+ if (cat > sizeof(smack_t) * 8)
+ return;
+
+ cp[(cat - 1) / 8] |= 0x80 >> ((cat - 1) % 8);
+}
+
+/*
+ * Present a pointer to the smack label in a task blob.
+ */
+static inline smack_t *smk_of_task(const struct task_struct *tsp)
+{
+ struct task_smack *stp = tsp->security;
+ return &stp->smk_task;
+}
+
+/*
+ * Present a pointer to the smack label in an inode blob.
+ */
+static inline smack_t *smk_of_inode(const struct inode *isp)
+{
+ struct inode_smack *sip = isp->i_security;
+ return &sip->smk_inode;
+}
+
+/*
+ * allocate a new smack label.
+ */
+static inline smack_t *new_smack_t(smack_t value)
+{
+ smack_t *bp;
+
+ bp = kzalloc(sizeof(smack_t), GFP_KERNEL);
+ if (bp != NULL)
+ *bp = value;
+
+ return bp;
+}
+
+/*
+ * Copy a smack label from the buffer. The smack label may
+ * actually be shorter than size. In any case, null pad
+ * beyond the end.
+ */
+static inline smack_t smk_from_buffer(const void *value, int size)
+{
+ smack_t smack;
+ char *from = (char *)value;
+ char *to = (char *)&smack;
+ int found;
+ int i;
+
+ for (i = 0, found = 0; i < sizeof(smack_t); i++, to++) {
+ if (found)
+ *to = '\0';
+ else if (i >= size || *from > '~' || *from <= ' ') {
+ *to = '\0';
+ found = 1;
+ }
+ else
+ *to = *from++;
+ }
+ return smack;
+}
+
+#endif /* _SECURITY_SMACK_H */
diff -uprN -X linux-2.6.22-base/Documentation/dontdiff linux-2.6.22-base/security/smack/smack_lsm.c linux-2.6.22/security/smack/smack_lsm.c
--- linux-2.6.22-base/security/smack/smack_lsm.c 1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.22/security/smack/smack_lsm.c 2007-07-24 15:02:16.000000000 -0700
@@ -0,0 +1,1989 @@
+/*
+ * Simplified MAC Kernel (smack) security module
+ *
+ * This file contains the smack hook function implementations.
+ *
+ * Author:
+ * Casey Schaufler <[email protected]>
+ *
+ * Copyright (C) 2007 Casey Schaufler <[email protected]>
+ *
+ * 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/tcp.h>
+#include <linux/udp.h>
+#include <linux/mutex.h>
+#include <net/netlabel.h>
+#include <net/cipso_ipv4.h>
+
+#include "smack.h"
+
+/*
+ * I hope these are the hokeyist lines of code in the module. Casey.
+ */
+#define DEVPTS_SUPER_MAGIC 0x1cd1
+#define SOCKFS_MAGIC 0x534F434B
+#define PIPEFS_MAGIC 0x50495045
+#define TMPFS_MAGIC 0x01021994
+
+/*
+ * These are maintained in smackfs
+ */
+extern smack_t smack_net_ambient;
+extern int smack_net_nltype;
+extern int smack_cipso_direct;
+extern struct smk_cipso_entry *smack_cipso;
+
+
+/*
+ * Fetch the smack label from a file.
+ */
+static int smk_fetch(struct inode *ip, struct dentry *dp, smack_t *isp)
+{
+ int rc;
+ smack_t smack;
+
+ if (ip->i_op->getxattr == NULL)
+ return -EOPNOTSUPP;
+
+ rc = ip->i_op->getxattr(dp, XATTR_NAME_SMACK, &smack, sizeof(smack_t));
+ if (rc > 0)
+ *isp = smk_from_buffer(&smack, rc);
+
+ return rc;
+}
+
+static smack_t *free_smack_t(smack_t *sp)
+{
+ kfree(sp);
+ return NULL;
+}
+
+struct inode_smack *new_inode_smack(smack_t smack)
+{
+ struct inode_smack *isp;
+
+ isp = kzalloc(sizeof(struct inode_smack), GFP_KERNEL);
+ if (isp == NULL)
+ return NULL;
+
+ isp->smk_inode = smack;
+ isp->smk_flags = 0;
+ mutex_init(&isp->smk_lock);
+
+ return isp;
+}
+
+/*
+ * LSM hooks.
+ * We he, that is fun!
+ */
+static int smack_ptrace(struct task_struct *ptp, struct task_struct *ctp)
+{
+ smack_t *psp = smk_of_task(ptp);
+ smack_t *csp = smk_of_task(ctp);
+ int rc;
+
+ rc = cap_ptrace(ptp, ctp);
+ if (rc != 0)
+ return rc;
+
+ rc = smk_access(psp, csp, MAY_READWRITE);
+ if (rc != 0 && __capable(ptp, CAP_MAC_OVERRIDE))
+ return 0;
+
+ return rc;
+}
+
+static int smack_syslog(int type)
+{
+ int rc;
+ smack_t *sp = smk_of_task(current);
+
+ rc = cap_syslog(type);
+ if (rc == 0)
+ if (*sp != SMK_FLOOR)
+ rc = -EACCES;
+
+ return rc;
+}
+
+static int smack_task_alloc_security(struct task_struct *tsk)
+{
+ struct task_smack *tsp;
+ struct task_smack *ctsp = current->security;
+
+ tsp = kzalloc(sizeof(struct task_smack), GFP_KERNEL);
+ if (tsp == NULL)
+ return -ENOMEM;
+
+ *tsp = *ctsp;
+ tsk->security = tsp;
+
+ return 0;
+}
+
+static void smack_task_free_security(struct task_struct *task)
+{
+ kfree(task->security);
+ task->security = NULL;
+}
+
+static int smack_task_setpgid(struct task_struct *p, pid_t pgid)
+{
+ int rc;
+
+ rc = smk_curacc(smk_of_task(p), MAY_WRITE);
+ return rc;
+}
+
+static int smack_task_setnice(struct task_struct *p, int nice)
+{
+ int rc;
+
+ rc = smk_curacc(smk_of_task(p), MAY_WRITE);
+ return rc;
+}
+
+static int smack_task_setioprio(struct task_struct *p, int ioprio)
+{
+ int rc;
+
+ rc = smk_curacc(smk_of_task(p), MAY_WRITE);
+ return rc;
+}
+
+static int smack_task_getioprio(struct task_struct *p)
+{
+ int rc;
+
+ rc = smk_curacc(smk_of_task(p), MAY_READ);
+ return rc;
+}
+
+static int smack_task_setscheduler(struct task_struct *p, int policy,
+ struct sched_param *lp)
+{
+ int rc;
+
+ rc = smk_curacc(smk_of_task(p), MAY_WRITE);
+ return rc;
+}
+
+static int smack_task_getscheduler(struct task_struct *p)
+{
+ int rc;
+
+ rc = smk_curacc(smk_of_task(p), MAY_READ);
+ return rc;
+}
+
+static int smack_task_movememory(struct task_struct *p)
+{
+ int rc;
+
+ rc = smk_curacc(smk_of_task(p), MAY_WRITE);
+ return rc;
+}
+
+static int smack_task_kill(struct task_struct *p, struct siginfo *info,
+ int sig, u32 secid)
+{
+ smack_t *tsp = smk_of_task(p);
+ int rc;
+
+ /*
+ * Sending a signal requires that the sender
+ * can write the receiver.
+ */
+ rc = smk_curacc(tsp, MAY_WRITE);
+
+ return rc;
+}
+
+/*
+ * Superblock Hooks.
+ */
+static int smack_sb_alloc_security(struct super_block *sb)
+{
+ struct superblock_smack *sbsp;
+
+ sbsp = kzalloc(sizeof(struct superblock_smack), GFP_KERNEL);
+
+ if (sbsp == NULL)
+ return -ENOMEM;
+
+ sbsp->smk_root = SMK_FLOOR;
+ sbsp->smk_default = SMK_FLOOR;
+ sbsp->smk_floor = SMK_FLOOR;
+ sbsp->smk_hat = SMK_HAT;
+ sbsp->smk_initialized = 0;
+
+ sb->s_security = sbsp;
+
+ return 0;
+}
+
+static void smack_sb_free_security(struct super_block *sb)
+{
+ kfree(sb->s_security);
+ sb->s_security = NULL;
+}
+
+static int smack_sb_copy_data(struct file_system_type *type, void *orig,
+ void *smackopts)
+{
+ char *cp, *commap, *otheropts, *dp;
+
+ /* Binary mount data: just copy */
+ if (type->fs_flags & FS_BINARY_MOUNTDATA) {
+ copy_page(smackopts, orig);
+ return 0;
+ }
+
+ otheropts = (char *)get_zeroed_page(GFP_KERNEL);
+ if (otheropts == NULL)
+ return -ENOMEM;
+
+ for (cp = orig, commap = orig; commap != NULL; cp = commap + 1) {
+ if (strstr(cp, SMK_FSDEFAULT) == cp)
+ dp = smackopts;
+ else if (strstr(cp, SMK_FSFLOOR) == cp)
+ dp = smackopts;
+ else if (strstr(cp, SMK_FSHAT) == cp)
+ dp = smackopts;
+ else if (strstr(cp, SMK_FSROOT) == cp)
+ dp = smackopts;
+ else
+ dp = otheropts;
+
+ commap = strchr(cp, ',');
+ if (commap != NULL)
+ *commap = '\0';
+
+ if (*dp != '\0')
+ strcat(dp, ",");
+ strcat(dp, cp);
+ }
+
+ strcpy(orig, otheropts);
+ free_page((unsigned long)otheropts);
+
+ return 0;
+}
+
+static int smack_sb_kern_mount(struct super_block *sb, void *data)
+{
+ int rc;
+ struct dentry *root = sb->s_root;
+ struct inode *inode = root->d_inode;
+ struct superblock_smack *sp = sb->s_security;
+ struct inode_smack *isp;
+ char *op;
+ char *commap;
+
+ if (sp == NULL) {
+ rc = smack_sb_alloc_security(sb);
+ if (rc != 0)
+ return rc;
+ }
+ if (sp->smk_initialized != 0)
+ return 0;
+ if (inode == NULL)
+ return 0;
+
+ sp->smk_initialized = 1;
+
+ for (op = data; op != NULL; op = commap) {
+ commap = strchr(op, ',');
+ if (commap != NULL)
+ *commap++ = '\0';
+
+ if (strncmp(op, SMK_FSHAT, strlen(SMK_FSHAT)) == 0) {
+ op += strlen(SMK_FSHAT);
+ if (strlen(op) <= SMK_MAXLEN)
+ sp->smk_hat = smk_from_string(op);
+ } else if (strncmp(op, SMK_FSFLOOR, strlen(SMK_FSFLOOR)) == 0) {
+ op += strlen(SMK_FSFLOOR);
+ if (strlen(op) <= SMK_MAXLEN)
+ sp->smk_floor = smk_from_string(op);
+ } else if (strncmp(op,SMK_FSDEFAULT,strlen(SMK_FSDEFAULT))==0) {
+ op += strlen(SMK_FSDEFAULT);
+ if (strlen(op) <= SMK_MAXLEN)
+ sp->smk_default = smk_from_string(op);
+ } else if (strncmp(op, SMK_FSROOT, strlen(SMK_FSROOT)) == 0) {
+ op += strlen(SMK_FSROOT);
+ if (strlen(op) <= SMK_MAXLEN)
+ sp->smk_root = smk_from_string(op);
+ }
+ }
+
+ /*
+ * Initialize the root inode.
+ */
+ isp = inode->i_security;
+ if (isp == NULL)
+ inode->i_security = new_inode_smack(sp->smk_root);
+ else
+ isp->smk_inode = sp->smk_root;
+
+ return 0;
+}
+
+static int smack_sb_statfs(struct dentry *dentry)
+{
+ struct superblock_smack *sbp;
+
+ if (dentry == NULL || dentry->d_sb == NULL ||
+ dentry->d_sb->s_security == NULL)
+ return 0;
+
+ sbp = dentry->d_sb->s_security;
+
+ return smk_curacc(&sbp->smk_floor, MAY_READ);
+}
+
+static int smack_sb_mount(char *dev_name, struct nameidata *nd,
+ char *type, unsigned long flags, void *data)
+{
+ struct superblock_smack *sbp;
+ int rc;
+
+ if (nd == NULL || nd->mnt == NULL || nd->mnt->mnt_sb == NULL ||
+ nd->mnt->mnt_sb->s_security == NULL)
+ return 0;
+
+ sbp = nd->mnt->mnt_sb->s_security;
+
+ rc = smk_curacc(&sbp->smk_floor, MAY_WRITE);
+ return rc;
+}
+
+static int smack_sb_umount(struct vfsmount *mnt, int flags)
+{
+ struct superblock_smack *sbp;
+ int rc;
+
+ sbp = mnt->mnt_sb->s_security;
+
+ rc = smk_curacc(&sbp->smk_floor, MAY_WRITE);
+ return rc;
+}
+
+/*
+ * Inode hooks
+ */
+static int smack_inode_alloc_security(struct inode *inode)
+{
+ smack_t *csp = smk_of_task(current);
+
+ inode->i_security = new_inode_smack(*csp);
+ if (inode->i_security == NULL)
+ return -ENOMEM;
+ return 0;
+}
+
+static void smack_inode_free_security(struct inode *inode)
+{
+ kfree(inode->i_security);
+ inode->i_security = NULL;
+}
+
+static int smack_inode_init_security(struct inode *inode, struct inode *dir,
+ char **name, void **value, size_t *len)
+{
+ smack_t *isp = smk_of_inode(inode);
+
+ if (name && (*name = kstrdup(XATTR_SMACK_SUFFIX, GFP_KERNEL)) == NULL)
+ return -ENOMEM;
+
+ if (value && (*value = kstrdup((char *)isp, GFP_KERNEL)) == NULL)
+ return -ENOMEM;
+
+ if (len)
+ *len = strlen((char *)isp) + 1;
+
+ return 0;
+}
+
+static int smack_inode_link(struct dentry *old_dentry, struct inode *dir,
+ struct dentry *new_dentry)
+{
+ int rc;
+
+ rc = smk_curacc(smk_of_inode(dir), MAY_WRITE);
+ return rc;
+}
+
+static int smack_inode_symlink(struct inode *dir, struct dentry *dentry,
+ const char *name)
+{
+ int rc;
+
+ rc = smk_curacc(smk_of_inode(dir), MAY_WRITE);
+ return rc;
+}
+
+static int smack_inode_mknod(struct inode *dir, struct dentry *dentry,
+ int mode, dev_t dev)
+{
+ int rc;
+
+ rc = smk_curacc(smk_of_inode(dir), MAY_WRITE);
+ return rc;
+}
+
+static int smack_inode_rename(struct inode *old_inode,
+ struct dentry *old_dentry,
+ struct inode *new_inode,
+ struct dentry *new_dentry)
+{
+ int rc;
+
+ rc = smk_curacc(smk_of_inode(old_inode), MAY_READWRITE);
+ if (rc == 0)
+ rc = smk_curacc(smk_of_inode(new_inode), MAY_READWRITE);
+ return rc;
+}
+
+static int smack_inode_readlink(struct dentry *dentry)
+{
+ int rc;
+
+ rc = smk_curacc(smk_of_inode(dentry->d_inode), MAY_READ);
+ return rc;
+}
+
+static int smack_inode_follow_link(struct dentry *dentry,
+ struct nameidata *nameidata)
+{
+ int rc;
+
+ rc = smk_curacc(smk_of_inode(dentry->d_inode), MAY_READ);
+ return rc;
+}
+
+static int smack_inode_permission(struct inode *inode, int mask,
+ struct nameidata *nd)
+{
+ int rc;
+
+ /*
+ * No permission to check. Existence test. Yup, it's there.
+ */
+ if (mask == 0)
+ return 0;
+
+ rc = smk_curacc(smk_of_inode(inode), mask);
+ return rc;
+}
+
+static int smack_inode_setattr(struct dentry *dentry, struct iattr *iattr)
+{
+ int rc;
+
+ rc = smk_curacc(smk_of_inode(dentry->d_inode), MAY_WRITE);
+ return rc;
+}
+
+static int smack_inode_getattr(struct vfsmount *mnt, struct dentry *dentry)
+{
+ int rc = smk_curacc(smk_of_inode(dentry->d_inode), MAY_READ);
+
+ return rc;
+}
+
+static int smack_inode_setxattr(struct dentry *dentry, char *name,
+ void *value, size_t size, int flags)
+{
+ int rc;
+
+ if (strcmp(name, XATTR_NAME_SMACK) == 0 &&
+ !__capable(current, CAP_MAC_OVERRIDE))
+ return -EPERM;
+
+ rc = cap_inode_setxattr(dentry, name, value, size, flags);
+ if (rc == 0)
+ rc = smk_curacc(smk_of_inode(dentry->d_inode), MAY_WRITE);
+
+ return rc;
+}
+
+static void smack_inode_post_setxattr(struct dentry *dentry, char *name,
+ void *value, size_t size, int flags)
+{
+ int i;
+ smack_t *vsp;
+ smack_t *isp;
+ char *nuller;
+
+ /*
+ * Not SMACK
+ */
+ if (strcmp(name, XATTR_NAME_SMACK))
+ return;
+
+ if (size >= sizeof(smack_t))
+ return;
+
+ vsp = (smack_t *)value;
+ isp = smk_of_inode(dentry->d_inode);
+ nuller = (char *)isp;
+
+ *isp = *vsp;
+
+ for (i = size; i < sizeof(smack_t); i++)
+ nuller[i] = '\0';
+
+ return;
+}
+
+static int smack_inode_getxattr(struct dentry *dentry, char *name)
+{
+ int rc;
+
+ rc = smk_curacc(smk_of_inode(dentry->d_inode), MAY_READ);
+ return rc;
+}
+
+static int smack_inode_listxattr(struct dentry *dentry)
+{
+ int rc;
+
+ rc = smk_curacc(smk_of_inode(dentry->d_inode), MAY_READ);
+ return rc;
+}
+
+static int smack_inode_removexattr(struct dentry *dentry, char *name)
+{
+ int rc;
+
+ if (strcmp(name, XATTR_NAME_SMACK) == 0 &&
+ !__capable(current, CAP_MAC_OVERRIDE))
+ return -EPERM;
+
+ rc = cap_inode_removexattr(dentry, name);
+ if (rc == 0)
+ rc = smk_curacc(smk_of_inode(dentry->d_inode), MAY_WRITE);
+
+ return rc;
+}
+
+static const char *smack_inode_xattr_getsuffix(void)
+{
+ return XATTR_SMACK_SUFFIX;
+}
+
+static int smack_inode_getsecurity(const struct inode *inode, const char *name, void *buffer, size_t size, int err)
+{
+ struct socket_smack *ssp;
+ struct socket *sock;
+ struct super_block *sbp;
+ struct inode *ip = (struct inode *)inode;
+ smack_t *bsp = buffer;
+ smack_t *isp;
+
+ if (size < sizeof(smack_t) || name == NULL || bsp == NULL ||
+ inode == NULL || inode->i_security == NULL)
+ return 0;
+
+ if (strcmp(name, XATTR_SMACK_SUFFIX) == 0) {
+ isp = smk_of_inode(inode);
+ *bsp = *isp;
+ return strlen((char *)isp) + 1;
+ }
+
+ /*
+ * The rest of the Smack xattrs are only on sockets.
+ */
+ sbp = ip->i_sb;
+ if (sbp->s_magic != SOCKFS_MAGIC)
+ return -EOPNOTSUPP;
+
+ sock = SOCKET_I(ip);
+ if (sock == NULL)
+ return -EOPNOTSUPP;
+
+ ssp = sock->sk->sk_security;
+
+ if (strcmp(name, XATTR_SMACK_PACKET) == 0)
+ *bsp = ssp->smk_packet;
+ else if (strcmp(name, XATTR_SMACK_IPIN) == 0)
+ *bsp = ssp->smk_in;
+ else if (strcmp(name, XATTR_SMACK_IPOUT) == 0)
+ *bsp = ssp->smk_out;
+ else
+ return -EOPNOTSUPP;
+
+ return strlen((char *)bsp) + 1;
+}
+
+static int smack_inode_setsecurity(struct inode *inode, const char *name,
+ const void *value, size_t size, int flags)
+{
+ smack_t smack;
+ smack_t *isp = smk_of_inode(inode);
+ struct socket_smack *ssp;
+ struct socket *sock;
+ struct super_block *sbp;
+ struct inode *ip = (struct inode *)inode;
+
+ if (value == NULL || size > sizeof(smack_t))
+ return -EACCES;
+
+ smack = smk_from_buffer(value, size);
+ if (smack == SMK_INVALID)
+ return -EINVAL;
+
+ if (strcmp(name, XATTR_SMACK_SUFFIX) == 0) {
+ *isp = smack;
+ return 0;
+ }
+ /*
+ * The rest of the Smack xattrs are only on sockets.
+ */
+ sbp = ip->i_sb;
+ if (sbp->s_magic != SOCKFS_MAGIC)
+ return -EOPNOTSUPP;
+
+ sock = SOCKET_I(ip);
+ if (sock == NULL)
+ return -EOPNOTSUPP;
+
+ ssp = sock->sk->sk_security;
+
+ if (strcmp(name, XATTR_SMACK_PACKET) == 0)
+ ssp->smk_packet = smack;
+ else if (strcmp(name, XATTR_SMACK_IPIN) == 0)
+ ssp->smk_in = smack;
+ else if (strcmp(name, XATTR_SMACK_IPOUT) == 0)
+ ssp->smk_out = smack;
+ else
+ return -EOPNOTSUPP;
+ return 0;
+}
+
+static int smack_inode_listsecurity(struct inode *inode, char *buffer,
+ size_t buffer_size)
+{
+ int len = strlen(XATTR_NAME_SMACK);
+
+ if (buffer != NULL && len <= buffer_size) {
+ memcpy(buffer, XATTR_NAME_SMACK, len);
+ return len;
+ }
+ return -EINVAL;
+}
+
+static void smack_d_instantiate (struct dentry *opt_dentry, struct inode *inode)
+{
+ struct super_block *sbp;
+ struct superblock_smack *sbsp;
+ struct inode_smack *isp;
+ smack_t *csp = smk_of_task(current);
+ smack_t sbs;
+ smack_t final = SMK_UNSET;
+ struct dentry *dp;
+ int rc;
+
+ if (inode == NULL)
+ return;
+
+ if (inode->i_security == NULL)
+ inode->i_security = new_inode_smack(SMK_UNSET);
+
+ isp = inode->i_security;
+
+ mutex_lock(&isp->smk_lock);
+ /*
+ * If the inode is already instantiated
+ * take the quick way out
+ */
+ if (isp->smk_flags & SMK_INODE_INSTANT)
+ goto unlockandout;
+
+ sbp = inode->i_sb;
+ sbsp = sbp->s_security;
+ /*
+ * We're going to use the superblock default label
+ * if there's no label on the file.
+ */
+ sbs = sbsp->smk_default;
+
+ /*
+ * This is pretty hackish.
+ * Casey says that we shouldn't have to do
+ * file system specific code, but it does help
+ * with keeping it simple.
+ */
+ switch (sbp->s_magic) {
+ case SMACK_MAGIC:
+ /*
+ * Casey says that it's a little embarassing
+ * that the smack file system doesn't do
+ * extended attributes.
+ */
+ final = SMK_STAR;
+ break;
+ case PIPEFS_MAGIC:
+ /*
+ * Casey says pipes are easy (?)
+ */
+ final = SMK_STAR;
+ break;
+ case DEVPTS_SUPER_MAGIC:
+ /*
+ * devpts seems content with the label of the task.
+ * Programs that change smack have to treat the
+ * pty with respect.
+ */
+ final = *csp;
+ break;
+ case SOCKFS_MAGIC:
+ /*
+ * Casey says sockets get the smack of the task.
+ */
+ final = *csp;
+ break;
+ case PROC_SUPER_MAGIC:
+ /*
+ * Casey says procfs appears not to care.
+ */
+ final = sbs;
+ break;
+ case TMPFS_MAGIC:
+ /*
+ * Device labels should come from the filesystem,
+ * but watch out, because they're volitile,
+ * getting recreated on every reboot.
+ */
+ sbs = SMK_STAR;
+ /*
+ * No break.
+ *
+ * If a smack value has been set we want to use it,
+ * but since tmpfs isn't giving us the opportunity
+ * to set mount options simulate setting the
+ * superblock default.
+ */
+ default:
+ /*
+ * This isn't an understood special case.
+ * Get the value from the xattr.
+ *
+ * No xattr support means, alas, no SMACK label.
+ * Use the aforeapplied default.
+ * It would be curious if the label of the task
+ * does not match that assigned.
+ */
+ if (inode->i_op->getxattr == NULL) {
+ final = sbs;
+ break;
+ }
+ /*
+ * Get the dentry for xattr.
+ */
+ if (opt_dentry == NULL) {
+ dp = d_find_alias(inode);
+ if (dp == NULL) {
+ final = sbs;
+ break;
+ }
+ } else {
+ dp = dget(opt_dentry);
+ if (dp == NULL) {
+ final = sbs;
+ break;
+ }
+ }
+
+ rc = smk_fetch(inode, dp, &final);
+ if (rc < 0)
+ final = sbs;
+
+ dput(dp);
+ break;
+ }
+
+ if (final == SMK_UNSET) {
+ printk("%s:%d unset? Investigate!\n", __FUNCTION__, __LINE__);
+ final = *csp;
+ }
+ isp->smk_inode = final;
+ isp->smk_flags |= SMK_INODE_INSTANT;
+
+unlockandout:
+ mutex_unlock(&isp->smk_lock);
+ return;
+}
+
+/*
+ * File Hooks
+ */
+
+static int smack_file_alloc_security(struct file *file)
+{
+ smack_t *csp = smk_of_task(current);
+
+ file->f_security = new_smack_t(*csp);
+ if (file->f_security == NULL)
+ return -ENOMEM;
+ return 0;
+}
+
+static void smack_file_free_security(struct file *file)
+{
+ file->f_security = free_smack_t(file->f_security);
+}
+
+/*
+ * Should access checks be done on each read or write?
+ * UNICOS and SELinux say yes.
+ * Trusted Solaris, Trusted Irix, and just about everyone else says no.
+ *
+ * I'll say no for now. Smack does not do the frequent
+ * label changing that SELinux does.
+ */
+static int smack_file_permission(struct file *file, int mask)
+{
+ return 0;
+}
+
+static int smack_file_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ int rc = 0;
+
+ if (_IOC_DIR(cmd) & _IOC_WRITE)
+ rc = smk_curacc(file->f_security, MAY_WRITE);
+
+ if (rc == 0 && (_IOC_DIR(cmd) & _IOC_READ))
+ rc = smk_curacc(file->f_security, MAY_READ);
+
+ return rc;
+}
+
+static int smack_file_lock(struct file *file, unsigned int cmd)
+{
+ int rc;
+
+ rc = smk_curacc(file->f_security, MAY_WRITE);
+ return rc;
+}
+
+static int smack_file_fcntl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ int rc;
+
+ switch (cmd) {
+ case F_DUPFD:
+ case F_GETFD:
+ case F_GETFL:
+ case F_GETLK:
+ case F_GETOWN:
+ case F_GETSIG:
+ rc = smk_curacc(file->f_security, MAY_READ);
+ break;
+ case F_SETFD:
+ case F_SETFL:
+ case F_SETLK:
+ case F_SETLKW:
+ case F_SETOWN:
+ case F_SETSIG:
+ rc = smk_curacc(file->f_security, MAY_WRITE);
+ break;
+ default:
+ rc = smk_curacc(file->f_security, MAY_READWRITE);
+ }
+
+ return rc;
+}
+
+static int smack_file_send_sigiotask(struct task_struct *tsk,
+ struct fown_struct *fown, int signum)
+{
+ struct file *file;
+ int rc;
+
+ /*
+ * struct fown_struct is never outside the context of a struct file
+ */
+ file = (struct file *)((long)fown - offsetof(struct file,f_owner));
+ rc = smk_access(file->f_security, tsk->security, MAY_WRITE);
+ if (rc != 0 && __capable(tsk, CAP_MAC_OVERRIDE))
+ return 0;
+ return rc;
+}
+
+static inline int file_may(struct file *file)
+{
+ if (file->f_mode & FMODE_READ)
+ return (file->f_mode & FMODE_WRITE) ? MAY_READWRITE : MAY_READ;
+
+ return (file->f_mode & FMODE_WRITE) ? MAY_WRITE : 0;
+}
+
+static int smack_file_receive(struct file *file)
+{
+ int may = 0;
+ int rc;
+
+ /*
+ * This code relies on bitmasks.
+ */
+ if (file->f_mode & FMODE_READ)
+ may = MAY_READ;
+ if (file->f_mode & FMODE_WRITE)
+ may |= MAY_WRITE;
+
+ rc = smk_curacc(file->f_security, may);
+ return rc;
+}
+
+/*
+ * Socket hooks.
+ */
+
+/*
+ * Initialize the socket blob from the associated task.
+ */
+static int smack_sk_alloc_security(struct sock *sk, int family, gfp_t priority)
+{
+ smack_t *csp = smk_of_task(current);
+ struct socket_smack *ssp;
+
+ ssp = kzalloc(sizeof(struct socket_smack), priority);
+ if (ssp == NULL)
+ return -ENOMEM;
+
+ ssp->smk_in = *csp;
+ ssp->smk_out = *csp;
+ ssp->smk_packet = SMK_INVALID;
+
+ sk->sk_security = ssp;
+
+ return 0;
+}
+
+/*
+ * Free the blob.
+ */
+static void smack_sk_free_security(struct sock *sk)
+{
+ kfree(sk->sk_security);
+ sk->sk_security = NULL;
+}
+
+static void smack_set_catset(smack_t catset, struct netlbl_lsm_secattr *sap)
+{
+ unsigned char *cp;
+ unsigned char m;
+ int cat;
+ int rc;
+
+ if (catset == 0LL)
+ return;
+
+ sap->flags |= NETLBL_SECATTR_MLS_CAT;
+ sap->mls_cat = netlbl_secattr_catmap_alloc(GFP_ATOMIC);
+ sap->mls_cat->startbit = 0;
+
+ for (cat = 1, cp = (unsigned char *)&catset; *cp != 0; cp++)
+ for (m = 0x80; m != 0; m >>= 1, cat++) {
+ if ((m & *cp) == 0)
+ continue;
+ rc = netlbl_secattr_catmap_setbit(sap->mls_cat, cat,
+ GFP_ATOMIC);
+ }
+}
+
+/*
+ * Casey says that CIPSO is good enough for now.
+ * It can be used to effect.
+ * It can also be abused to effect when necessary.
+ * Appologies to the TSIG group in general and GW in particular.
+ */
+static void smack_to_secattr(smack_t smack, struct netlbl_lsm_secattr *nlsp)
+{
+ struct smk_cipso_entry *scp;
+
+ switch (smack_net_nltype) {
+ case NETLBL_NLTYPE_CIPSOV4:
+ nlsp->domain = NULL;
+ nlsp->flags = NETLBL_SECATTR_DOMAIN;
+ nlsp->flags |= NETLBL_SECATTR_MLS_LVL;
+
+ for (scp = smack_cipso; scp != NULL; scp = scp->smk_next)
+ if (scp->smk_smack == smack)
+ break;
+
+ if (scp != NULL) {
+ nlsp->mls_lvl = scp->smk_level;
+ smack_set_catset(scp->smk_catset, nlsp);
+ } else {
+ nlsp->mls_lvl = smack_cipso_direct;
+ smack_set_catset(smack, nlsp);
+ }
+ break;
+ case NETLBL_NLTYPE_NONE:
+ case NETLBL_NLTYPE_UNLABELED:
+ break;
+ default:
+ break;
+ }
+}
+
+static int smack_netlabel(struct sock *sk)
+{
+ struct socket_smack *ssp = sk->sk_security;
+ struct netlbl_lsm_secattr secattr;
+ int rc;
+
+ netlbl_secattr_init(&secattr);
+ smack_to_secattr(ssp->smk_out, &secattr);
+ if (secattr.flags != NETLBL_SECATTR_NONE)
+ rc = netlbl_sock_setattr(sk, &secattr);
+ else
+ rc = -EINVAL;
+
+ netlbl_secattr_destroy(&secattr);
+ return rc;
+}
+
+static int smack_socket_post_create(struct socket *sock, int family,
+ int type, int protocol, int kern)
+{
+ struct inode_smack *isp;
+ smack_t *csp;
+ int rc = 0;
+
+ isp = SOCK_INODE(sock)->i_security;
+
+ if (isp == NULL) {
+ if (kern)
+ isp = new_inode_smack(SMK_FLOOR);
+ else {
+ csp = smk_of_task(current);
+ isp = new_inode_smack(*csp);
+ }
+ SOCK_INODE(sock)->i_security = isp;
+ }
+
+ if (family != PF_INET)
+ return 0;
+
+ /*
+ * Set the outbound netlbl.
+ */
+ rc = smack_netlabel(sock->sk);
+
+ return rc;
+}
+
+static int smack_inode_create(struct inode *dir, struct dentry *dentry,
+ int mode)
+{
+ int rc;
+
+ rc = smk_curacc(smk_of_inode(dir), MAY_WRITE);
+ return rc;
+}
+
+static int smack_inode_unlink(struct inode *dir, struct dentry *dentry)
+{
+ struct inode *ip = dentry->d_inode;
+ int rc;
+
+ /*
+ * You need write access to the thing you're unlinking
+ */
+ rc = smk_curacc(smk_of_inode(ip), MAY_WRITE);
+ if (rc == 0)
+ /*
+ * You also need write access to the containing directory
+ */
+ rc = smk_curacc(smk_of_inode(dir), MAY_WRITE);
+
+ return rc;
+}
+
+static int smack_inode_mkdir(struct inode *dir, struct dentry *dentry, int mode)
+{
+ int rc;
+
+ rc = smk_curacc(smk_of_inode(dir), MAY_WRITE);
+ return rc;
+}
+
+static int smack_inode_rmdir(struct inode *dir, struct dentry *dentry)
+{
+ struct inode *ip = dentry->d_inode;
+ int rc;
+
+ /*
+ * You need write access to the thing you're removing
+ */
+ rc = smk_curacc(smk_of_inode(ip), MAY_WRITE);
+ if (rc == 0)
+ /*
+ * You also need write access to the containing directory
+ */
+ rc = smk_curacc(smk_of_inode(dir), MAY_WRITE);
+
+ return rc;
+}
+
+static int smack_file_set_fowner(struct file *file)
+{
+ smack_t *fsp = file->f_security;
+ smack_t *csp = smk_of_task(current);
+
+ *fsp = *csp;
+
+ return 0;
+}
+
+static int smack_task_getpgid(struct task_struct *p)
+{
+ int rc;
+
+ rc = smk_curacc(smk_of_task(p), MAY_READ);
+ return rc;
+}
+
+static int smack_task_getsid(struct task_struct *p)
+{
+ int rc;
+
+ rc = smk_curacc(smk_of_task(p), MAY_READ);
+ return rc;
+}
+
+static void smack_task_getsecid(struct task_struct *p, u32 *secid)
+{
+ /*
+ * This is supposed to be called once, at boot,
+ * by the netlabel system.
+ */
+ *secid = SMK32_FLOOR;
+}
+
+static int smack_flags_to_may(int flags)
+{
+ int may = 0;
+
+ if (flags & S_IRUGO)
+ may |= MAY_READ;
+ if (flags & S_IWUGO)
+ may |= MAY_WRITE;
+ if (flags & S_IXUGO)
+ may |= MAY_EXEC;
+
+ return may;
+}
+
+static int smack_msg_msg_alloc_security(struct msg_msg *msg)
+{
+ smack_t *csp = smk_of_task(current);
+
+ msg->security = new_smack_t(*csp);
+ if (msg->security == NULL)
+ return -ENOMEM;
+ return 0;
+}
+
+static void smack_msg_msg_free_security(struct msg_msg *msg)
+{
+ msg->security = free_smack_t(msg->security);
+}
+
+
+static smack_t *smack_of_shm(struct shmid_kernel *shp)
+{
+ if (shp == NULL)
+ return NULL;
+
+ return (smack_t *)shp->shm_perm.security;
+}
+
+static int smack_shm_alloc_security(struct shmid_kernel *shp)
+{
+ smack_t *csp = smk_of_task(current);
+ struct kern_ipc_perm *isp = &shp->shm_perm;
+
+ isp->security = new_smack_t(*csp);
+ if (isp->security == NULL)
+ return -ENOMEM;
+ return 0;
+}
+
+static void smack_shm_free_security(struct shmid_kernel *shp)
+{
+ struct kern_ipc_perm *isp = &shp->shm_perm;
+
+ isp->security = free_smack_t(isp->security);
+}
+
+static int smack_shm_associate(struct shmid_kernel *shp, int shmflg)
+{
+ smack_t *ssp = smack_of_shm(shp);
+ int rc;
+
+ if (ssp == NULL)
+ return 0;
+
+ rc = smack_flags_to_may(shmflg);
+ rc = smk_curacc(ssp, rc);
+ return rc;
+}
+
+static int smack_shm_shmctl(struct shmid_kernel *shp, int cmd)
+{
+ smack_t *ssp = smack_of_shm(shp);
+ int rc;
+
+ if (ssp == NULL)
+ return 0;
+
+ switch(cmd) {
+ case IPC_STAT:
+ case SHM_STAT:
+ rc = MAY_READ;
+ break;
+ case IPC_SET:
+ case SHM_LOCK:
+ case SHM_UNLOCK:
+ case IPC_RMID:
+ rc = MAY_READWRITE;
+ break;
+ case IPC_INFO:
+ case SHM_INFO:
+ /*
+ * System level information.
+ */
+ return 0;
+ default:
+ return -EINVAL;
+ }
+
+ rc = smk_curacc(ssp, rc);
+ return rc;
+}
+
+static int smack_shm_shmat(struct shmid_kernel *shp, char __user *shmaddr, int shmflg)
+{
+ smack_t *ssp = smack_of_shm(shp);
+ int rc;
+
+ if (ssp == NULL)
+ return 0;
+
+ rc = smack_flags_to_may(shmflg);
+ rc = smk_curacc(ssp, rc);
+ return rc;
+}
+
+static smack_t *smack_of_sem(struct sem_array *sma)
+{
+ if (sma == NULL)
+ return NULL;
+
+ return (smack_t *)sma->sem_perm.security;
+}
+
+static int smack_sem_alloc_security(struct sem_array *sma)
+{
+ smack_t *csp = smk_of_task(current);
+ struct kern_ipc_perm *isp = &sma->sem_perm;
+
+ isp->security = new_smack_t(*csp);
+ if (isp->security == NULL)
+ return -ENOMEM;
+ return 0;
+}
+
+static void smack_sem_free_security(struct sem_array *sma)
+{
+ struct kern_ipc_perm *isp = &sma->sem_perm;
+
+ isp->security = free_smack_t(isp->security);
+}
+
+
+static int smack_sem_associate(struct sem_array *sma, int semflg)
+{
+ smack_t *ssp = smack_of_sem(sma);
+ int rc;
+
+ if (ssp == NULL)
+ return 0;
+
+ rc = smack_flags_to_may(semflg);
+ rc = smk_curacc(ssp, rc);
+ return rc;
+}
+
+static int smack_sem_semctl(struct sem_array *sma, int cmd)
+{
+ smack_t *ssp = smack_of_sem(sma);
+ int rc;
+
+ if (ssp == NULL)
+ return 0;
+
+ switch(cmd) {
+ case GETPID:
+ case GETNCNT:
+ case GETZCNT:
+ case GETVAL:
+ case GETALL:
+ case IPC_STAT:
+ case SEM_STAT:
+ rc = MAY_READ;
+ break;
+ case SETVAL:
+ case SETALL:
+ case IPC_RMID:
+ case IPC_SET:
+ rc = MAY_READWRITE;
+ break;
+ case IPC_INFO:
+ case SEM_INFO:
+ /*
+ * System level information
+ */
+ return 0;
+ default:
+ return -EINVAL;
+ }
+
+ rc = smk_curacc(ssp, rc);
+ return rc;
+}
+
+static int smack_sem_semop(struct sem_array *sma, struct sembuf *sops, unsigned nsops, int alter)
+{
+ smack_t *ssp = smack_of_sem(sma);
+ int rc;
+
+ if (ssp == NULL)
+ return 0;
+
+ rc = smk_curacc(ssp, MAY_READWRITE);
+ return rc;
+}
+
+static int smack_msg_queue_alloc_security(struct msg_queue *msq)
+{
+ smack_t *csp = smk_of_task(current);
+ struct kern_ipc_perm *kisp = &msq->q_perm;
+
+ kisp->security = new_smack_t(*csp);
+ if (kisp->security == NULL)
+ return -ENOMEM;
+ return 0;
+}
+
+static void smack_msg_queue_free_security(struct msg_queue *msq)
+{
+ struct kern_ipc_perm *kisp = &msq->q_perm;
+
+ kisp->security = free_smack_t(kisp->security);
+}
+
+static smack_t *smack_of_msq(struct msg_queue *msq)
+{
+ if (msq == NULL)
+ return NULL;
+
+ return (smack_t *)msq->q_perm.security;
+}
+
+static int smack_msg_queue_associate(struct msg_queue *msq, int msqflg)
+{
+ smack_t *msp = smack_of_msq(msq);
+ int rc;
+
+ if (msp == NULL)
+ return 0;
+
+ rc = smack_flags_to_may(msqflg);
+ rc = smk_curacc(msp, rc);
+ return rc;
+}
+
+static int smack_msg_queue_msgctl(struct msg_queue *msq, int cmd)
+{
+ smack_t *msp = smack_of_msq(msq);
+ int rc;
+
+ if (msp == NULL)
+ return 0;
+
+ switch(cmd) {
+ case IPC_STAT:
+ case MSG_STAT:
+ rc = MAY_READ;
+ break;
+ case IPC_SET:
+ case IPC_RMID:
+ rc = MAY_READWRITE;
+ break;
+ case IPC_INFO:
+ case MSG_INFO:
+ /*
+ * System level information
+ */
+ return 0;
+ default:
+ return -EINVAL;
+ }
+
+ rc = smk_curacc(msp, rc);
+ return rc;
+}
+
+static int smack_msg_queue_msgsnd(struct msg_queue *msq, struct msg_msg *msg, int msqflg)
+{
+ smack_t *msp = smack_of_msq(msq);
+ int rc;
+
+ if (msp == NULL)
+ return 0;
+
+ rc = smack_flags_to_may(msqflg);
+ rc = smk_curacc(msp, rc);
+ return rc;
+}
+
+static int smack_msg_queue_msgrcv(struct msg_queue *msq, struct msg_msg *msg, struct task_struct *target, long type, int mode)
+{
+ smack_t *msp = smack_of_msq(msq);
+ int rc;
+
+ if (msp == NULL)
+ return 0;
+
+ rc = smk_curacc(msp, MAY_READWRITE);
+ return rc;
+}
+
+static int smack_ipc_permission(struct kern_ipc_perm *ipp, short flag)
+{
+ smack_t *isp = ipp->security;
+ int rc;
+
+ rc = smack_flags_to_may(flag);
+ rc = smk_curacc(isp, rc);
+
+ return rc;
+}
+
+static void smack_task_to_inode(struct task_struct *p, struct inode *inode)
+{
+ smack_t *tsp = smk_of_task(p);
+ smack_t *isp = smk_of_inode(inode);
+
+ *isp = *tsp;
+}
+
+static int smack_task_wait(struct task_struct *p)
+{
+ smack_t *tsp = smk_of_task(p);
+ smack_t *csp = smk_of_task(current);
+ int rc;
+
+ rc = smk_access(csp, tsp, MAY_WRITE);
+ if (rc == 0)
+ return 0;
+
+ /*
+ * Allow the operation to succeed if either task
+ * has privilege to perform operations that might
+ * account for the smack labels having gotten to
+ * be different in the first place.
+ *
+ * This breaks the strict subjet/object access
+ * control ideal, taking the object's privilege
+ * state into account in the decision as well as
+ * the smack value.
+ */
+ if (__capable(current, CAP_MAC_OVERRIDE) ||
+ __capable(p, CAP_MAC_OVERRIDE))
+ return 0;
+
+ return rc;
+}
+
+static int smack_getprocattr(struct task_struct *p, char *name, char **value)
+{
+ smack_t *sp = smk_of_task(p);
+ int slen = strlen((char *)sp);
+
+ if (strcmp(name, "current") == 0) {
+ *value = (char *)new_smack_t(*sp);
+ return slen;
+ }
+
+ return -EINVAL;
+}
+
+static int smack_setprocattr(struct task_struct *p, char *name,
+ void *value, size_t size)
+{
+ smack_t *psp = smk_of_task(p);
+ smack_t newsmack = 0LL;
+
+ if (!__capable(p, CAP_MAC_OVERRIDE))
+ return -EPERM;
+
+ if (value == NULL || size == 0 || size >= sizeof(smack_t))
+ return -EINVAL;
+
+ if (strcmp(name, "current") == 0) {
+ newsmack = smk_from_buffer(value, size);
+ if (newsmack == SMK_INVALID)
+ return -EINVAL;
+ *psp = newsmack;
+ return size;
+ }
+ return -EINVAL;
+}
+
+static int smack_unix_stream_connect(struct socket *sock,
+ struct socket *other, struct sock *newsk)
+{
+ struct inode *sp = SOCK_INODE(sock);
+ struct inode *op = SOCK_INODE(other);
+ int rc;
+
+ rc = smk_access(smk_of_inode(sp), smk_of_inode(op), MAY_READWRITE);
+ return rc;
+}
+
+static int smack_unix_may_send(struct socket *sock, struct socket *other)
+{
+ struct inode *sp = SOCK_INODE(sock);
+ struct inode *op = SOCK_INODE(other);
+ int rc;
+
+ rc = smk_access(smk_of_inode(sp), smk_of_inode(op), MAY_WRITE);
+ return rc;
+}
+
+/*
+ * Convert a netlabel mls_lvl/mls_cat pair into a smack value.
+ */
+
+static smack_t smack_from_secattr(struct netlbl_lsm_secattr *sap)
+{
+ struct smk_cipso_entry *scp;
+ smack_t smack = 0LL;
+ int pcat;
+
+ if ((sap->flags & NETLBL_SECATTR_MLS_LVL) == 0) {
+ /*
+ * If there are flags but no level netlabel isn't
+ * behaving the way we expect it to.
+ *
+ * Without guidance regarding the smack value
+ * for the packet fall back on the network
+ * ambient value.
+ */
+ return smack_net_ambient;
+ }
+ /*
+ * Get the categories, if any
+ */
+ if ((sap->flags & NETLBL_SECATTR_MLS_CAT) != 0)
+ for (pcat = -1;;) {
+ pcat = netlbl_secattr_catmap_walk(sap->mls_cat, pcat+1);
+ if (pcat < 0)
+ break;
+ smack_catset_bit(pcat, &smack);
+ }
+ /*
+ * If it is CIPSO using smack direct mapping
+ * we are already done. WeeHee.
+ */
+ if (sap->mls_lvl == smack_cipso_direct)
+ return smack;
+
+ /*
+ * Look it up in the supplied table if it is not a direct mapping.
+ */
+ for (scp = smack_cipso; scp != NULL; scp = scp->smk_next)
+ if (scp->smk_level == sap->mls_lvl && scp->smk_catset == smack)
+ return scp->smk_smack;
+ /*
+ * It is CIPSO, but not one we know.
+ */
+
+ return SMK_HUH;
+}
+
+static int smack_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
+{
+ struct netlbl_lsm_secattr secattr;
+ struct socket_smack *ssp = sk->sk_security;
+ smack_t si;
+ int rc;
+
+ if (sk->sk_family != PF_INET && sk->sk_family != PF_INET6)
+ return 0;
+
+ /*
+ * Translate what netlabel gave us.
+ */
+ netlbl_secattr_init(&secattr);
+ rc = netlbl_skbuff_getattr(skb, &secattr);
+ if (rc == 0)
+ si = smack_from_secattr(&secattr);
+ else
+ si = smack_net_ambient;
+ netlbl_secattr_destroy(&secattr);
+ /*
+ * Receiving a packet requires that the other end
+ * be able to write here. Read access is not required.
+ * This is the simplist possible security model
+ * for networking.
+ */
+ rc = smk_access(&si, &ssp->smk_in, MAY_WRITE);
+
+ /*
+ * Set the receive packet on success.
+ */
+ if (rc == 0)
+ ssp->smk_packet = si;
+
+ return rc;
+}
+
+static void smack_sock_graft(struct sock *sk, struct socket *parent)
+{
+ struct socket_smack *ssp;
+ struct netlbl_lsm_secattr secattr;
+ smack_t ssi;
+ int rc;
+
+ if (sk == NULL || parent == NULL || parent->sk == NULL)
+ return;
+
+ if (sk->sk_family != PF_INET && sk->sk_family != PF_INET6)
+ return;
+
+ ssp = parent->sk->sk_security;
+
+ netlbl_secattr_init(&secattr);
+ rc = netlbl_sock_getattr(sk, &secattr);
+ if (rc == 0)
+ ssi = smack_from_secattr(&secattr);
+ else
+ ssi = SMK_HUH;
+ netlbl_secattr_destroy(&secattr);
+
+ netlbl_secattr_init(&secattr);
+
+ smack_to_secattr(ssi, &secattr);
+ if (secattr.flags != NETLBL_SECATTR_NONE)
+ rc = netlbl_sock_setattr(parent->sk, &secattr);
+ netlbl_secattr_destroy(&secattr);
+}
+
+static int smack_inet_conn_request(struct sock *sk, struct sk_buff *skb,
+ struct request_sock *req)
+{
+ struct netlbl_lsm_secattr skb_secattr;
+ struct socket_smack *ssp = sk->sk_security;
+ smack_t sb;
+ int rc;
+
+ if (skb == NULL)
+ return -EACCES;
+
+ netlbl_secattr_init(&skb_secattr);
+ rc = netlbl_skbuff_getattr(skb, &skb_secattr);
+ if (rc == 0)
+ sb = smack_from_secattr(&skb_secattr);
+ else
+ sb = SMK_HUH;
+ netlbl_secattr_destroy(&skb_secattr);
+ /*
+ * Receiving a packet requires that the other end
+ * be able to write here. Read access is not required.
+ */
+ rc = smk_access(&sb, &ssp->smk_in, MAY_WRITE);
+ return rc;
+}
+
+/*
+ * Key management security hooks
+ *
+ * Casey has not tested key support very heavily.
+ * The permission check is most likely too restrictive.
+ * If you care about keys please have a look.
+ */
+#ifdef CONFIG_KEYS
+static int smack_key_alloc(struct key *key, struct task_struct *tsk,
+ unsigned long flags)
+{
+ key->security = new_smack_t(smack_of_task(tsk));
+ if (key->security == NULL)
+ return -ENOMEM;
+ return 0;
+}
+
+static void smack_key_free(struct key *key)
+{
+ key->security = free_smack_t(key->security);
+}
+
+/*
+ * Casey says that until he understands the key permissions
+ * better the task is only going to have access to the key
+ * if it has read and write access.
+ */
+static int smack_key_permission(key_ref_t key_ref,
+ struct task_struct *context, key_perm_t perm)
+{
+ struct key *keyp;
+ smack_t *ksp;
+ smack_t *tsp;
+ int rc;
+
+ keyp = key_ref_to_ptr(key_ref);
+ if (keyp == NULL)
+ return -EINVAL;
+
+ ksp = keyp->security;
+ tsp = smk_of_task(context);
+
+ rc = smk_access(tsp, ksp, MAY_READWRITE);
+
+ return rc;
+}
+#endif /* CONFIG_KEYS */
+
+static struct security_operations smack_ops = {
+ .ptrace = smack_ptrace,
+ .capget = cap_capget,
+ .capset_check = cap_capset_check,
+ .capset_set = cap_capset_set,
+ .capable = cap_capable,
+ /* .acct No hook required */
+ /* .sysctl No hook required */
+ /* .quotactl No hook required */
+ /* .quota_on No hook required */
+ .syslog = smack_syslog,
+ .settime = cap_settime,
+ .vm_enough_memory = cap_vm_enough_memory,
+
+ /* .bprm_alloc_security No hook required */
+ /* .bprm_free_security No hook required */
+ .bprm_apply_creds = cap_bprm_apply_creds,
+ /* .bprm_post_apply_creds No hook required */
+ .bprm_set_security = cap_bprm_set_security,
+ /* .bprm_check_security No hook required */
+ .bprm_secureexec = cap_bprm_secureexec,
+
+ .sb_alloc_security = smack_sb_alloc_security,
+ .sb_free_security = smack_sb_free_security,
+ .sb_copy_data = smack_sb_copy_data,
+ .sb_kern_mount = smack_sb_kern_mount,
+ .sb_statfs = smack_sb_statfs,
+ .sb_mount = smack_sb_mount,
+ /* .sb_check_sb No hook required */
+ .sb_umount = smack_sb_umount,
+ /* .sb_umount_close No hook required */
+ /* .sb_umount_busy No hook required */
+ /* .sb_post_remount No hook required */
+ /* .sb_post_mountroot No hook required */
+ /* .sb_post_addmount No hook required */
+ /* .sb_pivotroot No hook required */
+ /* .sb_post_pivotroot No hook required */
+
+ .inode_alloc_security = smack_inode_alloc_security,
+ .inode_free_security = smack_inode_free_security,
+ .inode_init_security = smack_inode_init_security,
+ .inode_create = smack_inode_create,
+ .inode_link = smack_inode_link,
+ .inode_unlink = smack_inode_unlink,
+ .inode_symlink = smack_inode_symlink,
+ .inode_mkdir = smack_inode_mkdir,
+ .inode_rmdir = smack_inode_rmdir,
+ .inode_mknod = smack_inode_mknod,
+ .inode_rename = smack_inode_rename,
+ .inode_readlink = smack_inode_readlink,
+ .inode_follow_link = smack_inode_follow_link,
+ .inode_permission = smack_inode_permission,
+ .inode_setattr = smack_inode_setattr,
+ .inode_getattr = smack_inode_getattr,
+ /* .inode_delete No hook required */
+ .inode_setxattr = smack_inode_setxattr,
+ .inode_post_setxattr = smack_inode_post_setxattr,
+ .inode_getxattr = smack_inode_getxattr,
+ .inode_listxattr = smack_inode_listxattr,
+ .inode_removexattr = smack_inode_removexattr,
+ .inode_xattr_getsuffix = smack_inode_xattr_getsuffix,
+ .inode_getsecurity = smack_inode_getsecurity,
+ .inode_setsecurity = smack_inode_setsecurity,
+ .inode_listsecurity = smack_inode_listsecurity,
+
+ .file_permission = smack_file_permission,
+ .file_alloc_security = smack_file_alloc_security,
+ .file_free_security = smack_file_free_security,
+ .file_ioctl = smack_file_ioctl,
+ /* .file_mmap No hook required */
+ /* .file_mprotect No hook required */
+ .file_lock = smack_file_lock,
+ .file_fcntl = smack_file_fcntl,
+ .file_set_fowner = smack_file_set_fowner,
+ .file_send_sigiotask = smack_file_send_sigiotask,
+ .file_receive = smack_file_receive,
+
+ /* .task_create No hook required */
+ .task_alloc_security = smack_task_alloc_security,
+ .task_free_security = smack_task_free_security,
+ /* .task_setuid No hook required */
+ .task_post_setuid = cap_task_post_setuid,
+ /* .task_setgid No hook required */
+ .task_setpgid = smack_task_setpgid,
+ .task_getpgid = smack_task_getpgid,
+ .task_getsid = smack_task_getsid,
+ .task_getsecid = smack_task_getsecid,
+ /* .task_setgroups No hook required */
+ .task_setnice = smack_task_setnice,
+ .task_setioprio = smack_task_setioprio,
+ .task_getioprio = smack_task_getioprio,
+ /* .task_setrlimit No hook required */
+ .task_setscheduler = smack_task_setscheduler,
+ .task_getscheduler = smack_task_getscheduler,
+ .task_movememory = smack_task_movememory,
+ .task_kill = smack_task_kill,
+ .task_wait = smack_task_wait,
+ /* .task_prctl No hook required */
+ .task_reparent_to_init = cap_task_reparent_to_init,
+ .task_to_inode = smack_task_to_inode,
+
+ .ipc_permission = smack_ipc_permission,
+
+ .msg_msg_alloc_security = smack_msg_msg_alloc_security,
+ .msg_msg_free_security = smack_msg_msg_free_security,
+
+ .msg_queue_alloc_security = smack_msg_queue_alloc_security,
+ .msg_queue_free_security = smack_msg_queue_free_security,
+ .msg_queue_associate = smack_msg_queue_associate,
+ .msg_queue_msgctl = smack_msg_queue_msgctl,
+ .msg_queue_msgsnd = smack_msg_queue_msgsnd,
+ .msg_queue_msgrcv = smack_msg_queue_msgrcv,
+
+ .shm_alloc_security = smack_shm_alloc_security,
+ .shm_free_security = smack_shm_free_security,
+ .shm_associate = smack_shm_associate,
+ .shm_shmctl = smack_shm_shmctl,
+ .shm_shmat = smack_shm_shmat,
+
+ .sem_alloc_security = smack_sem_alloc_security,
+ .sem_free_security = smack_sem_free_security,
+ .sem_associate = smack_sem_associate,
+ .sem_semctl = smack_sem_semctl,
+ .sem_semop = smack_sem_semop,
+
+ .netlink_send = cap_netlink_send,
+ .netlink_recv = cap_netlink_recv,
+
+ /* .register_security No hook required */
+ /* .unregister_security No hook required */
+
+ .d_instantiate = smack_d_instantiate,
+
+ .getprocattr = smack_getprocattr,
+ .setprocattr = smack_setprocattr,
+ /* .secid_to_secctx No hook required */
+ /* .release_secctx No hook required */
+
+ .unix_stream_connect = smack_unix_stream_connect,
+ .unix_may_send = smack_unix_may_send,
+
+ /* .socket_create No hook required */
+ .socket_post_create = smack_socket_post_create,
+ /* .socket_bind No hook required */
+ /* .socket_connect No hook required */
+ /* .socket_listen No hook required */
+ /* .socket_accept No hook required */
+ /* .socket_post_accept No hook required */
+ /* .socket_sendmsg No hook required */
+ /* .socket_recvmsg No hook required */
+ /* .socket_getsockname No hook required */
+ /* .socket_getpeername No hook required */
+ /* .socket_getsockopt No hook required */
+ /* .socket_setsockopt No hook required */
+ /* .socket_shutdown No hook required */
+ .socket_sock_rcv_skb = smack_socket_sock_rcv_skb,
+ /* .socket_getpeersec_stream No hook required */
+ /* .socket_getpeersec_dgram No hook required */
+ .sk_alloc_security = smack_sk_alloc_security,
+ .sk_free_security = smack_sk_free_security,
+ /* .sk_clone_security No hook required */
+ /* .sk_getsecid No hook required */
+ .sock_graft = smack_sock_graft,
+ .inet_conn_request = smack_inet_conn_request,
+ /* .inet_csk_clone No hook required */
+ /* .inet_conn_established No hook required */
+
+ /* .req_classify_flow No hook required */
+ /* .xfrm_policy_alloc_security no xfrm for smack */
+ /* .xfrm_policy_clone_security no xfrm for smack */
+ /* .xfrm_policy_free_security no xfrm for smack */
+ /* .xfrm_policy_delete_security no xfrm for smack */
+ /* .xfrm_state_alloc_security no xfrm for smack */
+ /* .xfrm_state_free_security no xfrm for smack */
+ /* .xfrm_state_delete_security no xfrm for smack */
+ /* .xfrm_policy_lookup no xfrm for smack */
+ /* .xfrm_state_pol_flow_match no xfrm for smack */
+ /* .xfrm_decode_session no xfrm for smack */
+
+ /* key management security hooks */
+#ifdef CONFIG_KEYS
+ .key_alloc = smack_key_alloc,
+ .key_free = smack_key_free,
+ .key_permission = smack_key_permission,
+#endif /* CONFIG_KEYS */
+
+};
+
+static __init int smack_init(void)
+{
+ struct task_smack *tsp;
+ printk(KERN_INFO "Smack: Initializing.\n");
+
+ /* Set the security state for the initial task. */
+
+ tsp = kzalloc(sizeof(struct task_smack), GFP_KERNEL);
+ if (tsp == NULL)
+ panic("smack: Failed to initialize initial task.\n");
+
+ tsp->smk_task = SMK_FLOOR;
+ current->security = tsp;
+
+ if (register_security(&smack_ops))
+ panic("smack: Unable to register with kernel.\n");
+
+ return 0;
+}
+
+/* smack requires early initialization in order to label
+ all processes and objects when they are created. */
+security_initcall(smack_init);
+




2007-08-11 19:16:12

by Arjan van de Ven

[permalink] [raw]
Subject: Re: [PATCH] Smack: Simplified Mandatory Access Control Kernel

> +extern struct smk_list_entry *smack_list;

any reason to invent your own list rather than just using list.h?



> +
> +#include <linux/kernel.h>
> +#include <linux/vmalloc.h>
> +#include <linux/security.h>
> +#include <linux/mutex.h>
> +#include <net/netlabel.h>
> +#include "../../net/netlabel/netlabel_domainhash.h"

can't you move this header to include/ instead?



> +
> +static struct file_operations smk_load_ops = {
> + .read = smk_read_load,
> + .write = smk_write_load,
> +};

make that a const please

> +
> + doip = kmalloc(sizeof(struct cipso_v4_doi), GFP_KERNEL);
> + if (doip == NULL)
> + panic("smack: Failed to initialize cipso DOI.\n");
> + doip->map.std = NULL;
> +
> + ndmp = kmalloc(sizeof(struct netlbl_dom_map), GFP_KERNEL);
> + if (ndmp == NULL)
> + panic("smack: Failed to initialize cipso ndmp.\n");


is panic() really the right thing here? It's usually considered quite
rude ;)


> +static struct file_operations smk_cipso_ops = {
> + .read = smk_read_cipso,
> + .write = smk_write_cipso,
> +};

another candidate for const


> +static void *smackfs_follow_link(struct dentry *dentry, struct nameidata *nd)
> +{


this one deserves a comment; are you implementing magic symlinks here?



2007-08-11 19:18:41

by Kyle Moffett

[permalink] [raw]
Subject: Re: [PATCH] Smack: Simplified Mandatory Access Control Kernel

On Aug 11, 2007, at 13:57:31, Casey Schaufler wrote:
> Smack implements mandatory access control (MAC) using labels
> attached to tasks and data containers, including files, SVIPC, and
> other tasks. Smack is a kernel based scheme that requires an
> absolute minimum of application support and a very small amount of
> configuration data.

For starters, we would appear to already have a very capable
labelling system which can do *all* of this, completely and without
exceptions, in a much more flexible way. Admittedly it's more
complicated, but one does have to have complexity in order to handle
everything from CD burning in X, to Apache daemons, to only allowing
Top-Secret-level logins over the IPsec tunnel on the Top Secret
network, etc. Do you see any particular reason this couldn't be
implemented as a very *very* simplified SELinux wrapper? For
example, this set of rules basically defines your described "read-vs-
write-vs-exec" policy as best I can figure out:

user uu roles rr;

role rr types { star floor hat huh };

define(`r',`
allow $1 $2:file { read getattr };
allow $1 $2:socket { read getattr getopt recvfrom recv_msg };
allow $1 $2:ipc { getattr read associate unix_read };
## List of more "read" allow rules for different types of objects... ##
')

define(`w',`
allow $1 $2:file { ioctl write create setattr lock append unlink link
rename swapon quotaon mounton };
allow $1 $2:socket { ioctl write create setattr lock append bind
connect listen accept setopt shutdown sendto send_msg name_bind };
allow $1 $2:ipc { create destroy setattr write unix_write };
## List of more "write" allow rules for different types of objects... ##
')

define(`x',`
allow $1 $2:file { execute };
allow $1 $2:dir { search };
## List of more "execute" allow rules for different types of
objects... ##
')


And now to describe these rules:

> Smack defines and uses these labels:
> "*" - pronounced "star"
> "_" - pronounced "floor"
> "^" - pronounced "hat"
> "?" - pronounced "huh"
>
> The access rules enforced by Smack are, in order:
>
> 1. Any access requested by a task labeled "*" is denied.
> 2. A read or execute access requested by a task labeled "^"
> is permitted.
> 3. A read or execute access requested on an object labeled "_"
> is permitted.
> 4. Any access requested on an object labeled "*" is permitted.
> 5. Any access requested by a task on an object with the same
> label is permitted.
> 6. Any access requested that is explicitly defined in the loaded
> rule set is permitted.
> 7. Any other access is denied.

## These are calls to the above macros which plug in the necessary
arguments
r(hat, {*})
x(hat, {*})
r(~{star}, floor)
x(~{star}, floor)
r(~{star}, star)
w(~{star}, star)
x(~{star}, star)
r(~{star}, self)
w(~{star}, self)
x(~{star}, self)
## Include your "loaded rule set" here ##


> Rules may be explicitly defined by writing subject,object,access
> triples to /smack/load.

Maybe worth a little utility to convert a file full of
"subject,object,access" triples to an appropriate SELinux policy
would be appropriate?


> Smack rule sets can be easily defined that describe Bell&LaPadula
> sensitivity, Biba integrity, and a variety of interesting
> configurations. Smack rule sets can be modified on the fly to
> accomodate changes in the operating environment or even the time of
> day.

SELinux can do this as well. It even includes support for
conditional policy:

bool foo_can_do_logging true;
if (foo_can_do_logging) {
allow foo_t foo_log_t:file { create read getattr append };
}

The SELinux tools also have support for policy modules, so you can
extend the policy without modifying the base system. Plus the stuff
has been very heavily tested and even supports X (as soon as the beta
X code gets improved and merged in the upstream X.org codebase).


> Some practical use cases:
>
> Hierarchical levels. The less common of the two usual uses for MLS
> systems is to define hierarchical levels, often unclassified,
> confidential, secret, and so on. To set up smack to support this,
> these rules could be defined:
>
> C Unclass rx
> S C rx
> S Unclass rx
> TS S rx
> TS C rx
> TS Unclass rx
>
> A TS process can read S, C, and Unclass data, but cannot write it.
> An S process can read C and Unclass. Note that specifying that
> TS can read S and S can read C does not imply TS can read C, it
> has to be explicitly stated.

The big problem here is the duplication. Say you have a locked-down
Apache configuration and you want to run 2 apache processes, one at
Secret and one at Top-Secret. Under your model you have to copy-
paste the policy and make sure to apply fixes/changes to both
places. Under SELinux, you can have processes as:
system_u:system_r:httpd_t:Secret:UFOSightings,AlienDissection
system_u:system_r:httpd_t:TopSecret:NukeTests

They can only read and write objects for which multiple conditions
are true: First, the object must match the Bell-Padula model with
respect to the sensitivities and categories
("Secret:UFOSightings,AlienDissection", etc) *and* it must also be
objects that httpd_t is allowed to read/write. That also includes
network traffic, and your firewall can appropriately label data over
a given IPsec link as one classification level while data over the
raw network is labeled as another. Oh, and you can tweak the MLS
constraints of the Bell-Padula model as necessary too.


> Another case that I especially like:
>
> SatData Guard w
> Guard Publish w
>
> A program running with the Guard label opens a UDP socket and
> accepts messages sent by a program running with a SatData label.
> The Guard program inspects the message to ensure it is wholesome
> and if it is sends it to a program running with the Publish label.
> This program then puts the information passed in an appropriate
> place. Note that the Guard program cannot write to a Publish file
> system object because file system semanitic require read aswell as
> write.

This is exactly what my company does, but the ability to restrict
*exactly* what mechanisms are used is important. You restrict
against file-system access implicitly, whereas SELinux does it
explicitly:
allow foo_t foo_sock_t:socket { .... };
versus:
allow foo_t foo_log_t:file { ... };

With SELinux you can also allow your program to create files labelled
as "log files" in one directory in one type and "transfer files" in
another directory in a completely different type.


> The four cases (categories, levels, mutual read, guardbox) here are
> all quite real, and problems I've been asked to solve over the
> years. The first two are easy to do with traditonal MLS systems
> while the last two you can't without invoking privilege, at least
> for a while.

I'm proud to say that the software of the company I work for does not
need any extra privilege at all (aside from binding to ports <1024)
to run under SELinux with strict MLS. Under Trusted Solaris and such
we needed all sorts of dirty privilege hacks to relabel the files
consistently, but under SELinux the policy does all the relabeling
for us, we don't need to do a thing.

Cheers,
Kyle Moffett

2007-08-11 19:56:53

by Casey Schaufler

[permalink] [raw]
Subject: Re: [PATCH] Smack: Simplified Mandatory Access Control Kernel


--- Arjan van de Ven <[email protected]> wrote:

> > +extern struct smk_list_entry *smack_list;
>
> any reason to invent your own list rather than just using list.h?

The list.h mechanisms are fine, but heavier than I require.
I'm willing to give in on it, but I don't see an advantage.

> > +
> > +#include <linux/kernel.h>
> > +#include <linux/vmalloc.h>
> > +#include <linux/security.h>
> > +#include <linux/mutex.h>
> > +#include <net/netlabel.h>
> > +#include "../../net/netlabel/netlabel_domainhash.h"
>
> can't you move this header to include/ instead?

Paul Moore, the developer of netlabel, promised to work out
the right solution for this with me at a future date. He
doesn't want to move the header, and I respect that.

> > +
> > +static struct file_operations smk_load_ops = {
> > + .read = smk_read_load,
> > + .write = smk_write_load,
> > +};
>
> make that a const please

Will do.

> > +
> > + doip = kmalloc(sizeof(struct cipso_v4_doi), GFP_KERNEL);
> > + if (doip == NULL)
> > + panic("smack: Failed to initialize cipso DOI.\n");
> > + doip->map.std = NULL;
> > +
> > + ndmp = kmalloc(sizeof(struct netlbl_dom_map), GFP_KERNEL);
> > + if (ndmp == NULL)
> > + panic("smack: Failed to initialize cipso ndmp.\n");
>
>
> is panic() really the right thing here? It's usually considered quite
> rude ;)

It's really early in start-up and if you're out of memory at that
point you are not going very far into the future.

> > +static struct file_operations smk_cipso_ops = {
> > + .read = smk_read_cipso,
> > + .write = smk_write_cipso,
> > +};
>
> another candidate for const

Will do that, too.

> > +static void *smackfs_follow_link(struct dentry *dentry, struct nameidata
> *nd)
> > +{
>
>
> this one deserves a comment; are you implementing magic symlinks here?

Yup.

Casey Schaufler
[email protected]

2007-08-11 20:26:43

by Jan Engelhardt

[permalink] [raw]
Subject: Re: [PATCH] Smack: Simplified Mandatory Access Control Kernel


On Aug 11 2007 10:57, Casey Schaufler wrote:
>
> "*" - pronounced "star"
wall
> "_" - pronounced "floor"
floor
> "^" - pronounced "hat"
roof
> "?" - pronounced "huh"
it's dark in here :)

>+config SECURITY_SMACK
>+ bool "Simplified Mandatory Access Control Kernel Support"
>+ depends on NETLABEL && SECURITY_NETWORK

Is this a hard dependency, or can this work without network sec labels?

>+ default n
>+ help
>+ This selects the Simplified Mandatory Access Control Kernel.
>+ SMACK is useful for sensitivity, integrity, and a variety
>+ of other madatory security schemes.
>+ If you are unsure how to answer this question, answer N.

I smell broken tabs.

>+++ linux-2.6.22/security/smack/Makefile 2007-07-10 01:08:05.000000000 -0700
>@@ -0,0 +1,8 @@
>+#
>+# Makefile for the SMACK LSM
>+#
>+
>+obj-$(CONFIG_SECURITY_SMACK) := smack.o
>+
>+smack-y := smack_lsm.o smack_access.o smackfs.o

smack-objs :=

>+++ linux-2.6.22/security/smack/smack_access.c 2007-07-24 15:36:18.000000000 -0700
>@@ -0,0 +1,113 @@
>+extern struct smk_list_entry *smack_list;
>+
>+static int smk_get_access(smack_t sub, smack_t obj)
>+{
>+ struct smk_list_entry *sp = smack_list;

proposes const struct. should be used elsewhere too.

>+ if (sub == SMK_STAR)

I just remember MechWarrior2 (game from Activison), where SMK stood for their
movie format... it's also a nice abbreviation for Seamonkey (browser suite).

>+ if (sub == SMK_HAT && ((request & MAY_ANYREAD) == request))
>+ return 0;
>+
>+ if (obj == SMK_FLOOR && ((request & MAY_ANYREAD) == request))
>+ return 0;

Redundant parentheses, be gone.

Never was it easier to say what ^ is called in your language :)

if (sub == '^' && ...)
return 0;
if (obj == '_' && ...)


>+/*
>+ * The value that this adds is that everything after any
>+ * character that's not allowed in a smack will be null
>+ */
>+smack_t smk_from_string(char *str)
>+{
>+ smack_t smack = 0LL;

"smack_t smack = 0;" is enough here.

>+ char *cp;
>+ int i;
>+
>+ for (cp = (char *)&smack, i = 0; i < sizeof(smack_t); str++,cp++,i++) {

Whatever it tries to do, this is not endian-safe. Except if @str
actually points to another smack_t. Yuck.

>+ if (*str <= ' ' || *str > '~')
>+ return smack;
>+ *cp = *str;
>+ }
>+ /*
>+ * Too long.
>+ */
>+ return SMK_INVALID;
>+}
>diff -uprN -X linux-2.6.22-base/Documentation/dontdiff linux-2.6.22-base/security/smack/smackfs.c linux-2.6.22/security/smack/smackfs.c
>--- linux-2.6.22-base/security/smack/smackfs.c 1969-12-31 16:00:00.000000000 -0800
>+++ linux-2.6.22/security/smack/smackfs.c 2007-07-24 21:51:30.000000000 -0700

Can't securityfs and/or sysfs be used?

>+enum smk_inos {
>+ SMK_ROOT_INO = 2,
>+ SMK_LOAD = 3, /* load policy */
>+ SMK_LINKS = 4, /* symlinks */
>+ SMK_CIPSO = 5, /* load label -> CIPSO mapping */
>+ SMK_DOI = 6, /* CIPSO DOI */
>+ SMK_DIRECT = 7, /* CIPSO level indicating direct label */
>+ SMK_AMBIENT = 8, /* internet ambient label */
>+ SMK_NLTYPE = 9, /* label scheme to use by default */
>+ SMK_TMP = 100, /* MUST BE LAST! /smack/tmp */
>+};

Generally, =s are aligned too.

>+static struct smk_cipso_entry smack_cipso_floor = {
>+ .smk_next = NULL,
>+ .smk_smack = SMK_FLOOR,
>+ .smk_level = 0,
>+ .smk_catset = 0LL,
>+};

const me.

>+/*
>+ * 'ssssssss oooooooo mmmm\n\0'
>+ */

I wonder why it's limited to 8 characters? Ah right.. sizeof(smack_t).
uhm.. are you trying to tell me that smack_t [typedef'ed to u64]
are actually meant as a char[8]? (/me scrathces head)

>+ for (cp = result; slp != NULL; slp = slp->smk_next) {
>+ srp = &slp->smk_rule;
>+ sprintf(cp, "%-8s %-8s",
>+ (char *)&srp->smk_subject, (char *)&srp->smk_object);

I like (const char *).

>+ printk("%s:%d bad scan\n",
>+ __FUNCTION__, __LINE__);

__FUNCTION__ is a GNU extension, C99 uses __func__.
Not sure what they've got for __LINE__.

>+ if (strchr(modestr, 'r') || strchr(modestr, 'R'))
>+ rule.smk_access |= MAY_READ;
>+ if (strchr(modestr, 'w') || strchr(modestr, 'W'))
>+ rule.smk_access |= MAY_WRITE;
>+ if (strchr(modestr, 'x') || strchr(modestr, 'X'))
>+ rule.smk_access |= MAY_EXEC;
>+ if (strchr(modestr, 'a') || strchr(modestr, 'A'))
>+ rule.smk_access |= MAY_APPEND;

There's strpbrk() available for you.

>+static char *smk_digit(char *cp)
>+{
>+ for (; *cp != '\0'; cp++)
>+ if (*cp >= '0' && *cp <= '9')
>+ return cp;
>+
>+ return NULL;
>+}

strpbrk again.

>+static int smk_cipso_doied;
>+static int smk_cipso_written;

Votes for unsigned int if it {can never be, is not intended to be} negative.

>+ for (slp = smack_cipso; slp != NULL; slp = slp->smk_next) {
>+ sprintf(cp, "%-8s %3d", (char *)&slp->smk_smack,slp->smk_level);
>+ cp += strlen(cp);

sprintf returns the number of bytes it has written, so
cp += sprintf(cp, ...)
might be used. [Can sprintf even return -1 here?]

>+ *(data + count) = '\0';

data[count] = '\0';

>+ char temp[80];
>+ ssize_t rc;
>+
>+ if (*ppos != 0)
>+ return 0;
>+
>+ sprintf(temp, "%d", smk_cipso_doi_value);
>+ rc = simple_read_from_buffer(buf, count, ppos, temp, strlen(temp));

80 is plenty for a 11 char string.

Look, they've got funny ideas! :)
net/ipv4/netfilter/nf_nat_irc.c:char buffer[sizeof("4294967296 65635")];

>+static struct option_names netlbl_choices[] = {
>+ { NETLBL_NLTYPE_RIPSO,
>+ NETLBL_NLTYPE_RIPSO_NAME, "ripso" },
>+ { NETLBL_NLTYPE_CIPSOV4,
>+ NETLBL_NLTYPE_CIPSOV4_NAME, "cipsov4" },
>+ { NETLBL_NLTYPE_CIPSOV4,
>+ NETLBL_NLTYPE_CIPSOV4_NAME, "cipso" },
>+ { NETLBL_NLTYPE_CIPSOV6,
>+ NETLBL_NLTYPE_CIPSOV6_NAME, "cipsov6" },
>+ { NETLBL_NLTYPE_UNLABELED,
>+ NETLBL_NLTYPE_UNLABELED_NAME, "unlabeled" },
>+};
>+#define NCHOICES (sizeof(netlbl_choices) / sizeof(struct option_names))

ARRAY_SIZE()..

>+ for (i = 0; i < NCHOICES; i++)
>+ if (smack_net_nltype == netlbl_choices[i].o_number) {
>+ sprintf(bound, "%s", netlbl_choices[i].o_name);

looks dangerous; to overflow.

>+ for (i = 0; i < NCHOICES; i++)
>+ if ((strcmp(bound, netlbl_choices[i].o_name) == 0) ||
>+ (strcmp(bound, netlbl_choices[i].o_alias) == 0)) {

redundant ()

>+#define SMK_TMPPATH_SIZE 32
>+#define SMK_TMPPATH_ROOT "/moldy/"

What is going to be mold?

>+ if (slp == NULL) {
>+ printk("%s:%d failed\n", __FUNCTION__, __LINE__);

KERN_<level> is missing. (Check other places too)

>+#define SMK_MAXLEN (sizeof(smack_t) - 1) /* NULL terminated */

>+
>+/*
>+ * I hope these are the hokeyist lines of code in the module. Casey.
>+ */
>+#define DEVPTS_SUPER_MAGIC 0x1cd1
>+#define SOCKFS_MAGIC 0x534F434B
>+#define PIPEFS_MAGIC 0x50495045
>+#define TMPFS_MAGIC 0x01021994

Try moving them to linux/magic.h.

>+extern int smack_net_nltype;
>+extern int smack_cipso_direct;
>+extern struct smk_cipso_entry *smack_cipso;

for consistency reasons, add extern to the other vars too...

>+struct inode_smack *new_inode_smack(smack_t smack)
>+{
>+ struct inode_smack *isp;
>+
>+ isp = kzalloc(sizeof(struct inode_smack), GFP_KERNEL);
>+ if (isp == NULL)
>+ return NULL;
>+
>+ isp->smk_inode = smack;
>+ isp->smk_flags = 0;
>+ mutex_init(&isp->smk_lock);
>+
>+ return isp;
>+}

Tabs or so.

>+static int smack_task_movememory(struct task_struct *p)
>+{
>+ int rc;
>+
>+ rc = smk_curacc(smk_of_task(p), MAY_WRITE);
>+ return rc;
>+}

Uh...

{
return smk_curacc(smk_of_task(p), MAY_WRITE);
}

(also others)

>+
>+static int smack_task_kill(struct task_struct *p, struct siginfo *info,
>+ int sig, u32 secid)
>+{
>+ smack_t *tsp = smk_of_task(p);
>+ int rc;
>+
>+ /*
>+ * Sending a signal requires that the sender
>+ * can write the receiver.
>+ */
>+ rc = smk_curacc(tsp, MAY_WRITE);
>+
>+ return rc;
>+}
>+
>+static int smack_sb_statfs(struct dentry *dentry)
>+{
>+ struct superblock_smack *sbp;
>+
>+ if (dentry == NULL || dentry->d_sb == NULL ||
>+ dentry->d_sb->s_security == NULL)
>+ return 0;
>+
>+ sbp = dentry->d_sb->s_security;
>+
>+ return smk_curacc(&sbp->smk_floor, MAY_READ);
>+}

>+/*
>+ * Inode hooks
>+ */
>+static int smack_inode_alloc_security(struct inode *inode)
>+{
>+ smack_t *csp = smk_of_task(current);
>+
>+ inode->i_security = new_inode_smack(*csp);
>+ if (inode->i_security == NULL)
>+ return -ENOMEM;
>+ return 0;
>+}

Tabz.

>+static int smack_inode_init_security(struct inode *inode, struct inode *dir,
>+ char **name, void **value, size_t *len)
>+{
>+ smack_t *isp = smk_of_inode(inode);
>+
>+ if (name && (*name = kstrdup(XATTR_SMACK_SUFFIX, GFP_KERNEL)) == NULL)
>+ return -ENOMEM;
>+
>+ if (value && (*value = kstrdup((char *)isp, GFP_KERNEL)) == NULL)
>+ return -ENOMEM;

Try not to do big assignments in if.

>+static const char *smack_inode_xattr_getsuffix(void)
>+{
>+ return XATTR_SMACK_SUFFIX;
>+}

inline it, or opencode.

>+static int smack_inode_setsecurity(struct inode *inode, const char *name,
>+ const void *value, size_t size, int flags)
>+{
>+ smack_t smack;
>+ smack_t *isp = smk_of_inode(inode);
>+ struct socket_smack *ssp;
>+ struct socket *sock;
>+ struct super_block *sbp;
>+ struct inode *ip = (struct inode *)inode;

This cast is really pointless.

>+static int smack_inode_listsecurity(struct inode *inode, char *buffer,
>+ size_t buffer_size)

Whitespace broken.


[attention span ran out]

Nice.




Jan
--

2007-08-11 21:01:22

by Casey Schaufler

[permalink] [raw]
Subject: Re: [PATCH] Smack: Simplified Mandatory Access Control Kernel


--- Kyle Moffett <[email protected]> wrote:

> On Aug 11, 2007, at 13:57:31, Casey Schaufler wrote:
> > Smack implements mandatory access control (MAC) using labels
> > attached to tasks and data containers, including files, SVIPC, and
> > other tasks. Smack is a kernel based scheme that requires an
> > absolute minimum of application support and a very small amount of
> > configuration data.
>
> For starters, we would appear to already have a very capable
> labelling system

I am not going to argue that SELinux is not a capable
labelling system. SELinux is a fine implementation of
of Type Enforcement.

> which can do *all* of this, completely and without
> exceptions,

That's quite a strong assertion.

> in a much more flexible way.

Indeed, SELinux provides tremendous flexibility.

> Admittedly it's more complicated,

Yes, it is.

> but one does have to have complexity in order to handle
> everything from CD burning in X, to Apache daemons, to only allowing
> Top-Secret-level logins over the IPsec tunnel on the Top Secret
> network, etc.

I do not agree with you. The MLS systems from the 1990's could do all
that (except the IPsec tunnel, the function of which was preceeded by
TSIG interfaces and protocols) without the complexity required by
SELinux policy.

> Do you see any particular reason this couldn't be
> implemented as a very *very* simplified SELinux wrapper?

I would be interested to see the attempt made, but I have no interest
in doing so myself. I think that some of the reasons will come out
as we progress through your examples.

> For
> example, this set of rules basically defines your described "read-vs-
> write-vs-exec" policy as best I can figure out:
>
> user uu roles rr;
>
> role rr types { star floor hat huh };
>
> define(`r',`
> allow $1 $2:file { read getattr };
> allow $1 $2:socket { read getattr getopt recvfrom recv_msg };
> allow $1 $2:ipc { getattr read associate unix_read };
> ## List of more "read" allow rules for different types of objects... ##
> ')

It would be instructive for those who are not well versed in the
nuances of SELinux policy if you actually spelled out the whole
thing, rather than using "## and more ##". Part of the point of
Smack is the makeup of the full list that would be required here.

> define(`w',`
> allow $1 $2:file { ioctl write create setattr lock append unlink link
> rename swapon quotaon mounton };
> allow $1 $2:socket { ioctl write create setattr lock append bind
> connect listen accept setopt shutdown sendto send_msg name_bind };
> allow $1 $2:ipc { create destroy setattr write unix_write };
> ## List of more "write" allow rules for different types of objects... ##
> ')

Again, I suggest you present the complete list.

> define(`x',`
> allow $1 $2:file { execute };
> allow $1 $2:dir { search };
> ## List of more "execute" allow rules for different types of
> objects... ##
> ')

Here too.

> And now to describe these rules:
>
> > Smack defines and uses these labels:
> > "*" - pronounced "star"
> > "_" - pronounced "floor"
> > "^" - pronounced "hat"
> > "?" - pronounced "huh"
> >
> > The access rules enforced by Smack are, in order:
> >
> > 1. Any access requested by a task labeled "*" is denied.
> > 2. A read or execute access requested by a task labeled "^"
> > is permitted.
> > 3. A read or execute access requested on an object labeled "_"
> > is permitted.
> > 4. Any access requested on an object labeled "*" is permitted.
> > 5. Any access requested by a task on an object with the same
> > label is permitted.
> > 6. Any access requested that is explicitly defined in the loaded
> > rule set is permitted.
> > 7. Any other access is denied.
>
> ## These are calls to the above macros which plug in the necessary
> arguments
> r(hat, {*})
> x(hat, {*})
> r(~{star}, floor)
> x(~{star}, floor)
> r(~{star}, star)
> w(~{star}, star)
> x(~{star}, star)
> r(~{star}, self)
> w(~{star}, self)
> x(~{star}, self)
> ## Include your "loaded rule set" here ##

What would that look like?


> > Rules may be explicitly defined by writing subject,object,access
> > triples to /smack/load.
>
> Maybe worth a little utility to convert a file full of
> "subject,object,access" triples to an appropriate SELinux policy
> would be appropriate?

Maybe. It would at least be educational for comparisons by those
looking to choose between the schemes.

> > Smack rule sets can be easily defined that describe Bell&LaPadula
> > sensitivity, Biba integrity, and a variety of interesting
> > configurations. Smack rule sets can be modified on the fly to
> > accomodate changes in the operating environment or even the time of
> > day.
>
> SELinux can do this as well. It even includes support for
> conditional policy:
>
> bool foo_can_do_logging true;
> if (foo_can_do_logging) {
> allow foo_t foo_log_t:file { create read getattr append };
> }

You have to build the booleans into the policy in advance.

> The SELinux tools also have support for policy modules, so you can
> extend the policy without modifying the base system.

And that's a good thing, but you still have to compile and reload
your policy, and maybe relabel you filesystem when you do so.

> Plus the stuff has been very heavily tested

It does have the advantage of maturity.

> and even supports X (as soon as the beta
> X code gets improved and merged in the upstream X.org codebase).

The MLS systems of the 1990s did that, and having done MLS X
myself I can assure you that although it has an interesting
set of issues to address (object definition in particular)
and a programming model that is very different from the kernel
it's not that big a deal.

> > Some practical use cases:
> >
> > Hierarchical levels. The less common of the two usual uses for MLS
> > systems is to define hierarchical levels, often unclassified,
> > confidential, secret, and so on. To set up smack to support this,
> > these rules could be defined:
> >
> > C Unclass rx
> > S C rx
> > S Unclass rx
> > TS S rx
> > TS C rx
> > TS Unclass rx
> >
> > A TS process can read S, C, and Unclass data, but cannot write it.
> > An S process can read C and Unclass. Note that specifying that
> > TS can read S and S can read C does not imply TS can read C, it
> > has to be explicitly stated.
>
> The big problem here is the duplication. Say you have a locked-down
> Apache configuration and you want to run 2 apache processes, one at
> Secret and one at Top-Secret. Under your model you have to copy-
> paste the policy and make sure to apply fixes/changes to both
> places.

I'm sorry, but I don't understand your point at all.

> Under SELinux, you can have processes as:
> system_u:system_r:httpd_t:Secret:UFOSightings,AlienDissection
> system_u:system_r:httpd_t:TopSecret:NukeTests
>
> They can only read and write objects for which multiple conditions
> are true: First, the object must match the Bell-Padula model with
> respect to the sensitivities and categories
> ("Secret:UFOSightings,AlienDissection", etc) *and* it must also be
> objects that httpd_t is allowed to read/write. That also includes
> network traffic, and your firewall can appropriately label data over
> a given IPsec link as one classification level while data over the
> raw network is labeled as another. Oh, and you can tweak the MLS
> constraints of the Bell-Padula model as necessary too.

Ok.

> > Another case that I especially like:
> >
> > SatData Guard w
> > Guard Publish w
> >
> > A program running with the Guard label opens a UDP socket and
> > accepts messages sent by a program running with a SatData label.
> > The Guard program inspects the message to ensure it is wholesome
> > and if it is sends it to a program running with the Publish label.
> > This program then puts the information passed in an appropriate
> > place. Note that the Guard program cannot write to a Publish file
> > system object because file system semanitic require read aswell as
> > write.
>
> This is exactly what my company does, but the ability to restrict
> *exactly* what mechanisms are used is important. You restrict
> against file-system access implicitly, whereas SELinux does it
> explicitly:
> allow foo_t foo_sock_t:socket { .... };
> versus:
> allow foo_t foo_log_t:file { ... };
>
> With SELinux you can also allow your program to create files labelled
> as "log files" in one directory in one type and "transfer files" in
> another directory in a completely different type.

I would be very interested to see the policy that your guard box uses.

> > The four cases (categories, levels, mutual read, guardbox) here are
> > all quite real, and problems I've been asked to solve over the
> > years. The first two are easy to do with traditonal MLS systems
> > while the last two you can't without invoking privilege, at least
> > for a while.
>
> I'm proud to say that the software of the company I work for does not
> need any extra privilege at all (aside from binding to ports <1024)
> to run under SELinux with strict MLS.

That is an aspect of SELinux that has its dark side. While you
are not explicitly violating a policy (e.g. requiring a capability)
you are doing things that some of us would argue ought to require
a capability. SELinux integrates privilege into the policy mechanism.

Smack does not. Smack is designed to use traditional Linux
mechanisms for privilege.

> Under Trusted Solaris and such
> we needed all sorts of dirty privilege hacks to relabel the files
> consistently, but under SELinux the policy does all the relabeling
> for us, we don't need to do a thing.

Yeah. Well, you won't like Smack then. Implicit relabeling of
processes and files does not happen. This is a major philisophical
difference.


Casey Schaufler
[email protected]

2007-08-11 21:47:36

by Kyle Moffett

[permalink] [raw]
Subject: Re: [PATCH] Smack: Simplified Mandatory Access Control Kernel

On Aug 11, 2007, at 17:01:09, Casey Schaufler wrote:
>> [SELinux...] which can do *all* of this, completely and without
>> exceptions,
>
> That's quite a strong assertion.

It is, but I stand by it. If anyone can point out some portion of
this which *cannot* be implemented as SELinux policy I will humbly
step completely out of this discussion.


>> but one does have to have complexity in order to handle everything
>> from CD burning in X, to Apache daemons, to only allowing Top-
>> Secret-level logins over the IPsec tunnel on the Top Secret
>> network, etc.
>
> I do not agree with you. The MLS systems from the 1990's could do
> all that (except the IPsec tunnel, the function of which was
> preceeded by TSIG interfaces and protocols) without the complexity
> required by SELinux policy.

No, most/all of the MLS systems from the 1990s did not integrate Bell-
LaPadula, Type Enforcement, and Role-Based-Access-Control together.
In addition, none of the MLS systems from the 1990s had modifiable
constraints the way SELinux does (for example I can modify the
fundamental policy to allow a process whose domain has the
"i_am_special" attribute to override the BLP model for certain target
types in certain special situations, all depending on how I apply
that attribute and those types).

>> For example, this set of rules basically defines your described
>> "read-vs-write-vs-exec" policy as best I can figure out:
>>
>> user uu roles rr;
>>
>> role rr types { star floor hat huh };
>>
>> define(`r',`
>> allow $1 $2:file { read getattr };
>> allow $1 $2:socket { read getattr getopt recvfrom recv_msg };
>> allow $1 $2:ipc { getattr read associate unix_read };
>> ## List of more "read" allow rules for different types of
>> objects... ##
>> ')
>
> It would be instructive for those who are not well versed in the
> nuances of SELinux policy if you actually spelled out the whole
> thing, rather than using "## and more ##". Part of the point of
> Smack is the makeup of the full list that would be required here.

Well, yes, but how exactly do you define "read", "write", and
"execute" for the full list of SELinux object classes found here:
http://www.tresys.com/selinux/obj_perms_help.html

I was considering compiling the complete list, but such an exercise
would take me at least an hour to do properly and which categories
individual permissions should be placed in could be argued for
weeks. The SELinux reference policy doesn't even completely qualify
all of those things with MLS restrictions, and they're the ones who
got the hooks implemented originally.

Specifically, do you classify the bind() syscall as a "read" or a
"write" operation. How would you classify accept()? In order to
support the full range of security controls that Linux has hooks for,
you would need to identify a list of how to define "read", "write",
and "execute" for every object-class in that massive list.
Furthermore you would need to ensure that the definitions work for
every process. From the variations between different portions of the
SELinux ref policy, I contend that there is no single definition of
"read", or "write" which works for all usages of a given object-class.

>> And now to describe these rules:
>>
>>> Smack defines and uses these labels:
>>> "*" - pronounced "star"
>>> "_" - pronounced "floor"
>>> "^" - pronounced "hat"
>>> "?" - pronounced "huh"
>>>
>>> The access rules enforced by Smack are, in order:
>>>
>>> 1. Any access requested by a task labeled "*" is denied.
>>> 2. A read or execute access requested by a task labeled "^"
>>> is permitted.
>>> 3. A read or execute access requested on an object labeled "_"
>>> is permitted.
>>> 4. Any access requested on an object labeled "*" is permitted.
>>> 5. Any access requested by a task on an object with the same
>>> label is permitted.
>>> 6. Any access requested that is explicitly defined in the loaded
>>> rule set is permitted.
>>> 7. Any other access is denied.
>>
>> ## These are calls to the above macros which plug in the necessary
>> arguments
>> r(hat, {*})
>> x(hat, {*})
>> r(~{star}, floor)
>> x(~{star}, floor)
>> r(~{star}, star)
>> w(~{star}, star)
>> x(~{star}, star)
>> r(~{star}, self)
>> w(~{star}, self)
>> x(~{star}, self)
>> ## Include your "loaded rule set" here ##
>
> What would that look like?

The same kind of thing as the r, x, and w above, with occasional
"type my_type_t; role rr types my_type_t;" to declare a type before
use. If you wanted to add support for attributes which apply to
multiple types, you could put those after a comma after the "type
my_type_t" portion of the type declaration.

>>> Smack rule sets can be easily defined that describe Bell&LaPadula
>>> sensitivity, Biba integrity, and a variety of interesting
>>> configurations. Smack rule sets can be modified on the fly to
>>> accomodate changes in the operating environment or even the time
>>> of day.
>>
>> SELinux can do this as well. It even includes support for
>> conditional policy:
>>
>> bool foo_can_do_logging true;
>> if (foo_can_do_logging) {
>> allow foo_t foo_log_t:file { create read getattr append };
>> }
>
> You have to build the booleans into the policy in advance.

Well, yes, but a policy which completely ignores


>> The SELinux tools also have support for policy modules, so you can
>> extend the policy without modifying the base system.
>
> And that's a good thing, but you still have to compile and reload
> your policy, and maybe relabel you filesystem when you do so.

What exactly prevents you from having to "reload" and "relabel the
filesystem" when using your system? When you change the meaning of
new types, you clearly would need to first reload the policy then
relaunch programs or relabel files to take advantage of the new
types, otherwise they would be categorically ignored by the system.

The only difference is the "compile" step, which converts from a
reasonably modifiable language into a binary format which the kernel
can efficiently parse. I suspect you'd get shot down instantly if
you tried to stuff the policy compiler in the kernel too.

>>> Some practical use cases:
>>>
>>> Hierarchical levels. The less common of the two usual uses for
>>> MLS systems is to define hierarchical levels, often
>>> unclassified, confidential, secret, and so on. To set up smack to
>>> support this, these rules could be defined:
>>>
>>> C Unclass rx
>>> S C rx
>>> S Unclass rx
>>> TS S rx
>>> TS C rx
>>> TS Unclass rx
>>>
>>> A TS process can read S, C, and Unclass data, but cannot write
>>> it. An S process can read C and Unclass. Note that specifying
>>> that TS can read S and S can read C does not imply TS can read C,
>>> it has to be explicitly stated.
>>
>> The big problem here is the duplication. Say you have a locked-
>> down Apache configuration and you want to run 2 apache processes,
>> one at Secret and one at Top-Secret. Under your model you have to
>> copy-paste the policy and make sure to apply fixes/changes to both
>> places.
>
> I'm sorry, but I don't understand your point at all.

Ok, let's say you want to use your model to restrict Apache and MySQL
running at the same classification level. You have to define
combined labels, such as:
S_Apache S_MySQL rx
[...]

Now if you want to run an apache/mysql pair at TS, you have to copy
all those rules and substitute "S_" with "TS_". With SELinux, you
don't have to do anything other than define new labels for the secret-
web-docs and the top-secret-web-docs folders, because the Apache
*type-enforcement* policy is completely independent from the MLS
levels. You can write a 5-line policy to label 2 slightly different
initscripts with different range-transitions. The only difference
between them will be the paths inside and the labels on them. The
rest of apache's policy stays the same.

>> This is exactly what my company does, but the ability to restrict
>> *exactly* what mechanisms are used is important. You restrict
>> against file-system access implicitly, whereas SELinux does it
>> explicitly:
>> allow foo_t foo_sock_t:socket { .... };
>> versus:
>> allow foo_t foo_log_t:file { ... };
>>
>> With SELinux you can also allow your program to create files
>> labelled as "log files" in one directory in one type and "transfer
>> files" in another directory in a completely different type.
>
> I would be very interested to see the policy that your guard box uses.

Unfortunately that's still considered company-proprietary information
at the moment, although that's likely to change at some point in the
future. We're not all that different from the httpd policies and
similar stuff found in the SELinux "refpolicy" available from tresys'
website.

>> I'm proud to say that the software of the company I work for does
>> not need any extra privilege at all (aside from binding to ports
>> <1024) to run under SELinux with strict MLS.
>
> That is an aspect of SELinux that has its dark side. While you are
> not explicitly violating a policy (e.g. requiring a capability) you
> are doing things that some of us would argue ought to require a
> capability. SELinux integrates privilege into the policy mechanism.

How does "moving data around a system" require root privileges? Why
should it? Admittedly, moving data from "TopSecret" to "Secret"
requires a process labeled with a range including at least "S-TS"
*and* with the MLS "read-to-clearance" attribute, but you can be as
stingy with that attribute as you like. That attribute is explicitly
given its magic abilities in the mlsconstrain rules defined in the
policy file itself, so it's also possible to do information flow
analysis on it.

> Smack does not. Smack is designed to use traditional Linux
> mechanisms for privilege.
>
>> Under Trusted Solaris and such we needed all sorts of dirty
>> privilege hacks to relabel the files consistently, but under
>> SELinux the policy does all the relabeling for us, we don't need
>> to do a thing.
>
> Yeah. Well, you won't like Smack then. Implicit relabeling of
> processes and files does not happen. This is a major philisophical
> difference.

Here's an argument for why implicit labeling is good and explicit
labeling is bad: If your process wants to label things explicitly it
needs special privileges to do so, and it can abuse those
privileges. Under SELinux the labeling is implicit *AND* the
transitions are entirely within the policy. This means that you can
do information flow analysis (as I described above) and it *ALSO*
means that the program cannot possibly "abuse" its privileges because
it has none. When httpd creates a file in its log directory that
file is created with httpd_log_t and it is limited to being able to
append to the file *only*. So when your apache gets compromised, the
attacker has NO WAY to modify the logs, and you can prove this with a
60-second study of the policy. In fact, on my companies productions
systems I can *PROVE* that the only way to get audit data out of the
system is via a local TTY by interacting with
system_u:object_r:local_login_t:SystemLow-SystemHigh to log in with a
process of someluser:auditreview_r:auditreview_t:SystemHigh, which
may read the audit files, or
otherluser:auditadmin_r:auditadmin_t:SystemHigh, which may read and
delete the audit files. I can even prove that no process on the
system is allowed to modify the files after they are created, barring
a kernel-level vulnerability.

So the implicit labeling of files actually restricts every process
*FURTHER*. They no longer have even an inkling of a choice about
file labeling, and that's a GOOD thing. The only remaining extension
we need to really thoroughly lock down our systems is to allow last-
path-component matching in type-transitions and allow rules (EG:
When "vipw" creates a file named "passwd" in an "etc_t" directory, it
is automatically labeled "etc_passwd_t") From what I understand
Stephen Smalley and others are thinking that over even now. I'll do
it myself as soon as I get time at work beyond prepping systems for
shipping to clients if they haven't finished it by them.

Cheers,
Kyle Moffett

2007-08-11 22:20:26

by Andi Kleen

[permalink] [raw]
Subject: Re: [PATCH] Smack: Simplified Mandatory Access Control Kernel

Casey Schaufler <[email protected]> writes:

> Smack is the Simplified Mandatory Access Control Kernel.

I like the simplified part.

> +static int smk_get_access(smack_t sub, smack_t obj)
> +{
> + struct smk_list_entry *sp = smack_list;
> +
> + for (; sp != NULL; sp = sp->smk_next)
> + if (sp->smk_rule.smk_subject == sub &&
> + sp->smk_rule.smk_object == obj)
> + return sp->smk_rule.smk_access;

Do I miss something, or is there really no locking for the reader side
of the list? That looks dangerous. Of course a global lock for readers
would be likely a scaling disaster. You could use RCU.

Or if you assume rules are changed only very infrequently it might
be more cache friendly to compile all the rules into a linear buffer
and then just replace the whole buffer atomically with a RCU
grace period on cahnges.

It doesn't look like it would scale to larger numbers of rules though.
Is that intended? Would caching of decisions fit into the design?

Also in general code style would need some improvements;
e.g. no externs in .c; no ../.. include hacks etc.
You also seem weak on the Documentation front.
Other than that it looks reasonably clean (haven't read all of it)

-Andi

2007-08-11 23:22:37

by Casey Schaufler

[permalink] [raw]
Subject: Re: [PATCH] Smack: Simplified Mandatory Access Control Kernel


--- Jan Engelhardt <[email protected]> wrote:

>
> On Aug 11 2007 10:57, Casey Schaufler wrote:
> >
> > "*" - pronounced "star"
> wall
> > "_" - pronounced "floor"
> floor
> > "^" - pronounced "hat"
> roof
> > "?" - pronounced "huh"
> it's dark in here :)

It's almost worth considering the change for the joke. Almost.

> >+config SECURITY_SMACK
> >+ bool "Simplified Mandatory Access Control Kernel Support"
> >+ depends on NETLABEL && SECURITY_NETWORK
>
> Is this a hard dependency, or can this work without network sec labels?

Smack uses netlabel to communicate label information even locally.
Without this only processes running with the ambient label would
be able to use sockets.

> >+ default n
> >+ help
> >+ This selects the Simplified Mandatory Access Control Kernel.
> >+ SMACK is useful for sensitivity, integrity, and a variety
> >+ of other madatory security schemes.
> >+ If you are unsure how to answer this question, answer N.
>
> I smell broken tabs.

Yup. Fixed.

> >+++ linux-2.6.22/security/smack/Makefile 2007-07-10 01:08:05.000000000 -0700
> >@@ -0,0 +1,8 @@
> >+#
> >+# Makefile for the SMACK LSM
> >+#
> >+
> >+obj-$(CONFIG_SECURITY_SMACK) := smack.o
> >+
> >+smack-y := smack_lsm.o smack_access.o smackfs.o
>
> smack-objs :=

Added.

> >+++ linux-2.6.22/security/smack/smack_access.c 2007-07-24 15:36:18.000000000
> -0700
> >@@ -0,0 +1,113 @@
> >+extern struct smk_list_entry *smack_list;
> >+
> >+static int smk_get_access(smack_t sub, smack_t obj)
> >+{
> >+ struct smk_list_entry *sp = smack_list;
>
> proposes const struct. should be used elsewhere too.

I've hit a few places.

> >+ if (sub == SMK_STAR)
>
> I just remember MechWarrior2 (game from Activison), where SMK stood for their
> movie format... it's also a nice abbreviation for Seamonkey (browser suite).

Err, yeah. Okay.

> >+ if (sub == SMK_HAT && ((request & MAY_ANYREAD) == request))
> >+ return 0;
> >+
> >+ if (obj == SMK_FLOOR && ((request & MAY_ANYREAD) == request))
> >+ return 0;
>
> Redundant parentheses, be gone.

Done.

> Never was it easier to say what ^ is called in your language :)
>
> if (sub == '^' && ...)
> return 0;
> if (obj == '_' && ...)
>
> >+/*
> >+ * The value that this adds is that everything after any
> >+ * character that's not allowed in a smack will be null
> >+ */
> >+smack_t smk_from_string(char *str)
> >+{
> >+ smack_t smack = 0LL;
>
> "smack_t smack = 0;" is enough here.

Ok.

> >+ char *cp;
> >+ int i;
> >+
> >+ for (cp = (char *)&smack, i = 0; i < sizeof(smack_t); str++,cp++,i++) {
>
> Whatever it tries to do, this is not endian-safe. Except if @str
> actually points to another smack_t. Yuck.

Small string stuck in a large integer. The only operation is
comparison. Big or little endian the string will come out right,
and that's what's important.

> >+ if (*str <= ' ' || *str > '~')
> >+ return smack;
> >+ *cp = *str;
> >+ }
> >+ /*
> >+ * Too long.
> >+ */
> >+ return SMK_INVALID;
> >+}
> >diff -uprN -X linux-2.6.22-base/Documentation/dontdiff
> linux-2.6.22-base/security/smack/smackfs.c
> linux-2.6.22/security/smack/smackfs.c
> >--- linux-2.6.22-base/security/smack/smackfs.c 1969-12-31 16:00:00.000000000
> -0800
> >+++ linux-2.6.22/security/smack/smackfs.c 2007-07-24 21:51:30.000000000
> -0700
>
> Can't securityfs and/or sysfs be used?

I have to look into that for the configuration values. I don't
think I can do it for the symlinks.

> >+enum smk_inos {
> >+ SMK_ROOT_INO = 2,
> >+ SMK_LOAD = 3, /* load policy */
> >+ SMK_LINKS = 4, /* symlinks */
> >+ SMK_CIPSO = 5, /* load label -> CIPSO mapping */
> >+ SMK_DOI = 6, /* CIPSO DOI */
> >+ SMK_DIRECT = 7, /* CIPSO level indicating direct label */
> >+ SMK_AMBIENT = 8, /* internet ambient label */
> >+ SMK_NLTYPE = 9, /* label scheme to use by default */
> >+ SMK_TMP = 100, /* MUST BE LAST! /smack/tmp */
> >+};
>
> Generally, =s are aligned too.

It's pretty inconsistant, but that's what I prefer, too. I'll do it.

> >+static struct smk_cipso_entry smack_cipso_floor = {
> >+ .smk_next = NULL,
> >+ .smk_smack = SMK_FLOOR,
> >+ .smk_level = 0,
> >+ .smk_catset = 0LL,
> >+};
>
> const me.

This is the correct tinitial value, but could be changed.

> >+/*
> >+ * 'ssssssss oooooooo mmmm\n\0'
> >+ */
>
> I wonder why it's limited to 8 characters? Ah right.. sizeof(smack_t).
> uhm.. are you trying to tell me that smack_t [typedef'ed to u64]
> are actually meant as a char[8]? (/me scrathces head)

Yes. "s == o" vs "strcmp(s,o) == 0".

> >+ for (cp = result; slp != NULL; slp = slp->smk_next) {
> >+ srp = &slp->smk_rule;
> >+ sprintf(cp, "%-8s %-8s",
> >+ (char *)&srp->smk_subject, (char *)&srp->smk_object);
>
> I like (const char *).
>
> >+ printk("%s:%d bad scan\n",
> >+ __FUNCTION__, __LINE__);
>
> __FUNCTION__ is a GNU extension, C99 uses __func__.

Changed, although it's used extensively elsewhere.

> Not sure what they've got for __LINE__.

No clue. Leaving it as is.

> >+ if (strchr(modestr, 'r') || strchr(modestr, 'R'))
> >+ rule.smk_access |= MAY_READ;
> >+ if (strchr(modestr, 'w') || strchr(modestr, 'W'))
> >+ rule.smk_access |= MAY_WRITE;
> >+ if (strchr(modestr, 'x') || strchr(modestr, 'X'))
> >+ rule.smk_access |= MAY_EXEC;
> >+ if (strchr(modestr, 'a') || strchr(modestr, 'A'))
> >+ rule.smk_access |= MAY_APPEND;
>
> There's strpbrk() available for you.

Makes it a little cleaner. Thank you.

> >+static char *smk_digit(char *cp)
> >+{
> >+ for (; *cp != '\0'; cp++)
> >+ if (*cp >= '0' && *cp <= '9')
> >+ return cp;
> >+
> >+ return NULL;
> >+}
>
> strpbrk again.

Thank you again.

> >+static int smk_cipso_doied;
> >+static int smk_cipso_written;
>
> Votes for unsigned int if it {can never be, is not intended to be} negative.

No problem. Changed.

> >+ for (slp = smack_cipso; slp != NULL; slp = slp->smk_next) {
> >+ sprintf(cp, "%-8s %3d", (char *)&slp->smk_smack,slp->smk_level);
> >+ cp += strlen(cp);
>
> sprintf returns the number of bytes it has written, so
> cp += sprintf(cp, ...)
> might be used. [Can sprintf even return -1 here?]

Better.

> >+ *(data + count) = '\0';
>
> data[count] = '\0';

Yeah, it's clearer.


> >+ char temp[80];
> >+ ssize_t rc;
> >+
> >+ if (*ppos != 0)
> >+ return 0;
> >+
> >+ sprintf(temp, "%d", smk_cipso_doi_value);
> >+ rc = simple_read_from_buffer(buf, count, ppos, temp, strlen(temp));
>
> 80 is plenty for a 11 char string.
>
> Look, they've got funny ideas! :)
> net/ipv4/netfilter/nf_nat_irc.c:char buffer[sizeof("4294967296 65635")];
>
> >+static struct option_names netlbl_choices[] = {
> >+ { NETLBL_NLTYPE_RIPSO,
> >+ NETLBL_NLTYPE_RIPSO_NAME, "ripso" },
> >+ { NETLBL_NLTYPE_CIPSOV4,
> >+ NETLBL_NLTYPE_CIPSOV4_NAME, "cipsov4" },
> >+ { NETLBL_NLTYPE_CIPSOV4,
> >+ NETLBL_NLTYPE_CIPSOV4_NAME, "cipso" },
> >+ { NETLBL_NLTYPE_CIPSOV6,
> >+ NETLBL_NLTYPE_CIPSOV6_NAME, "cipsov6" },
> >+ { NETLBL_NLTYPE_UNLABELED,
> >+ NETLBL_NLTYPE_UNLABELED_NAME, "unlabeled" },
> >+};
> >+#define NCHOICES (sizeof(netlbl_choices) / sizeof(struct option_names))
>
> ARRAY_SIZE()..

Spiffy. Used.

> >+ for (i = 0; i < NCHOICES; i++)
> >+ if (smack_net_nltype == netlbl_choices[i].o_number) {
> >+ sprintf(bound, "%s", netlbl_choices[i].o_name);
>
> looks dangerous; to overflow.

Constant strings all fit.

> >+ for (i = 0; i < NCHOICES; i++)
> >+ if ((strcmp(bound, netlbl_choices[i].o_name) == 0) ||
> >+ (strcmp(bound, netlbl_choices[i].o_alias) == 0)) {
>
> redundant ()

Removed.


> >+#define SMK_TMPPATH_SIZE 32
> >+#define SMK_TMPPATH_ROOT "/moldy/"
>
> What is going to be mold?

/tmp -> /smack/tmp -> /moldy/<yoursmacklabelhere>

> >+ if (slp == NULL) {
> >+ printk("%s:%d failed\n", __FUNCTION__, __LINE__);
>
> KERN_<level> is missing. (Check other places too)

printks in excess. Cleanup and correction in order.

>
> >+#define SMK_MAXLEN (sizeof(smack_t) - 1) /* NULL terminated */
>
> >+
> >+/*
> >+ * I hope these are the hokeyist lines of code in the module. Casey.
> >+ */
> >+#define DEVPTS_SUPER_MAGIC 0x1cd1
> >+#define SOCKFS_MAGIC 0x534F434B
> >+#define PIPEFS_MAGIC 0x50495045
> >+#define TMPFS_MAGIC 0x01021994
>
> Try moving them to linux/magic.h.

Worth looking into.

> >+extern int smack_net_nltype;
> >+extern int smack_cipso_direct;
> >+extern struct smk_cipso_entry *smack_cipso;
>
> for consistency reasons, add extern to the other vars too...

the others?

> >+struct inode_smack *new_inode_smack(smack_t smack)
> >+{
> >+ struct inode_smack *isp;
> >+
> >+ isp = kzalloc(sizeof(struct inode_smack), GFP_KERNEL);
> >+ if (isp == NULL)
> >+ return NULL;
> >+
> >+ isp->smk_inode = smack;
> >+ isp->smk_flags = 0;
> >+ mutex_init(&isp->smk_lock);
> >+
> >+ return isp;
> >+}
>
> Tabs or so.

Yup. Fixed.

> >+static int smack_task_movememory(struct task_struct *p)
> >+{
> >+ int rc;
> >+
> >+ rc = smk_curacc(smk_of_task(p), MAY_WRITE);
> >+ return rc;
> >+}
>
> Uh...
>
> {
> return smk_curacc(smk_of_task(p), MAY_WRITE);
> }
>
> (also others)

That was a little excessive, wasn't it?

> >+
> >+static int smack_task_kill(struct task_struct *p, struct siginfo *info,
> >+ int sig, u32 secid)
> >+{
> >+ smack_t *tsp = smk_of_task(p);
> >+ int rc;
> >+
> >+ /*
> >+ * Sending a signal requires that the sender
> >+ * can write the receiver.
> >+ */
> >+ rc = smk_curacc(tsp, MAY_WRITE);
> >+
> >+ return rc;
> >+}
> >+
> >+static int smack_sb_statfs(struct dentry *dentry)
> >+{
> >+ struct superblock_smack *sbp;
> >+
> >+ if (dentry == NULL || dentry->d_sb == NULL ||
> >+ dentry->d_sb->s_security == NULL)
> >+ return 0;
> >+
> >+ sbp = dentry->d_sb->s_security;
> >+
> >+ return smk_curacc(&sbp->smk_floor, MAY_READ);
> >+}
>
> >+/*
> >+ * Inode hooks
> >+ */
> >+static int smack_inode_alloc_security(struct inode *inode)
> >+{
> >+ smack_t *csp = smk_of_task(current);
> >+
> >+ inode->i_security = new_inode_smack(*csp);
> >+ if (inode->i_security == NULL)
> >+ return -ENOMEM;
> >+ return 0;
> >+}
>
> Tabz.

Fixxxed.


> >+static int smack_inode_init_security(struct inode *inode, struct inode
> *dir,
> >+ char **name, void **value, size_t *len)
> >+{
> >+ smack_t *isp = smk_of_inode(inode);
> >+
> >+ if (name && (*name = kstrdup(XATTR_SMACK_SUFFIX, GFP_KERNEL)) == NULL)
> >+ return -ENOMEM;
> >+
> >+ if (value && (*value = kstrdup((char *)isp, GFP_KERNEL)) == NULL)
> >+ return -ENOMEM;
>
> Try not to do big assignments in if.

Fixed.

> >+static const char *smack_inode_xattr_getsuffix(void)
> >+{
> >+ return XATTR_SMACK_SUFFIX;
> >+}
>
> inline it, or opencode.

Better yet, no one is using the hook. nuked.

> >+static int smack_inode_setsecurity(struct inode *inode, const char *name,
> >+ const void *value, size_t size, int flags)
> >+{
> >+ smack_t smack;
> >+ smack_t *isp = smk_of_inode(inode);
> >+ struct socket_smack *ssp;
> >+ struct socket *sock;
> >+ struct super_block *sbp;
> >+ struct inode *ip = (struct inode *)inode;
>
> This cast is really pointless.

The variable is really pointless. Repaired.

> >+static int smack_inode_listsecurity(struct inode *inode, char *buffer,
> >+ size_t buffer_size)
>
> Whitespace broken.

Fixed

>
> [attention span ran out]

It's a summer Saturday. Thank you for your helpful comments.

> Nice.

Thank you.


Casey Schaufler
[email protected]

2007-08-12 01:22:17

by Casey Schaufler

[permalink] [raw]
Subject: Re: [PATCH] Smack: Simplified Mandatory Access Control Kernel


--- Kyle Moffett <[email protected]> wrote:

> On Aug 11, 2007, at 17:01:09, Casey Schaufler wrote:
> >> [SELinux...] which can do *all* of this, completely and without
> >> exceptions,
> >
> > That's quite a strong assertion.
>
> It is, but I stand by it. If anyone can point out some portion of
> this which *cannot* be implemented as SELinux policy I will humbly
> step completely out of this discussion.

Ok. I accept that you believe that this.

> >> but one does have to have complexity in order to handle everything
> >> from CD burning in X, to Apache daemons, to only allowing Top-
> >> Secret-level logins over the IPsec tunnel on the Top Secret
> >> network, etc.
> >
> > I do not agree with you. The MLS systems from the 1990's could do
> > all that (except the IPsec tunnel, the function of which was
> > preceeded by TSIG interfaces and protocols) without the complexity
> > required by SELinux policy.
>
> No, most/all of the MLS systems from the 1990s did not integrate Bell-
> LaPadula, Type Enforcement, and Role-Based-Access-Control together.

Err, that wasn't the claim I was refuting. I was refuting the claim
that performing those actions required complexity. The claim I was
refering to said nothing about any of those features or anything about
their integration.

> In addition, none of the MLS systems from the 1990s had modifiable
> constraints the way SELinux does (for example I can modify the
> fundamental policy to allow a process whose domain has the
> "i_am_special" attribute to override the BLP model for certain target
> types in certain special situations, all depending on how I apply
> that attribute and those types).

That's fine, but hardly relevent to the fact that the 1990's systems
performed the list of functions.

> >> For example, this set of rules basically defines your described
> >> "read-vs-write-vs-exec" policy as best I can figure out:
> >>
> >> user uu roles rr;
> >>
> >> role rr types { star floor hat huh };
> >>
> >> define(`r',`
> >> allow $1 $2:file { read getattr };
> >> allow $1 $2:socket { read getattr getopt recvfrom recv_msg };
> >> allow $1 $2:ipc { getattr read associate unix_read };
> >> ## List of more "read" allow rules for different types of
> >> objects... ##
> >> ')
> >
> > It would be instructive for those who are not well versed in the
> > nuances of SELinux policy if you actually spelled out the whole
> > thing, rather than using "## and more ##". Part of the point of
> > Smack is the makeup of the full list that would be required here.
>
> Well, yes, but how exactly do you define "read", "write", and
> "execute" for the full list of SELinux object classes found here:
> http://www.tresys.com/selinux/obj_perms_help.html

Smack is designed to treat objects as consistantly as is reasonable.
The list of object classes defined by SELinux is great for a system
designed to treat accesses with the highest possible granularity.
This is not a design goal for Smack.

> I was considering compiling the complete list, but such an exercise
> would take me at least an hour to do properly and which categories
> individual permissions should be placed in could be argued for
> weeks.

I will be happy to consider your arguement when you are willing
to present the complete argument.

> The SELinux reference policy doesn't even completely qualify
> all of those things with MLS restrictions, and they're the ones who
> got the hooks implemented originally.

That doesn't help your case any.

> Specifically, do you classify the bind() syscall as a "read" or a
> "write" operation.

Neither. Bind attaches a name (port number) to a socket (which is a
data structure that is part of your process) from a global namespace.
No objects are involved, hence no accesses.

> How would you classify accept()?

I don't. Accept happens after a TCP connection has been established.
The access control is all mediated long before the accept'er finds
out about it.

> In order to
> support the full range of security controls that Linux has hooks for,
> you would need to identify a list of how to define "read", "write",
> and "execute" for every object-class in that massive list.
> Furthermore you would need to ensure that the definitions work for
> every process. From the variations between different portions of the
> SELinux ref policy, I contend that there is no single definition of
> "read", or "write" which works for all usages of a given object-class.

There certainly isn't in SELinux. I don't subscribe the the list
of SELinux object classes. We can argue a long time over whether a
socket is an object or not, and it won't help any real world application
one bit.

> >> And now to describe these rules:
> >>
> >>> Smack defines and uses these labels:
> >>> "*" - pronounced "star"
> >>> "_" - pronounced "floor"
> >>> "^" - pronounced "hat"
> >>> "?" - pronounced "huh"
> >>>
> >>> The access rules enforced by Smack are, in order:
> >>>
> >>> 1. Any access requested by a task labeled "*" is denied.
> >>> 2. A read or execute access requested by a task labeled "^"
> >>> is permitted.
> >>> 3. A read or execute access requested on an object labeled "_"
> >>> is permitted.
> >>> 4. Any access requested on an object labeled "*" is permitted.
> >>> 5. Any access requested by a task on an object with the same
> >>> label is permitted.
> >>> 6. Any access requested that is explicitly defined in the loaded
> >>> rule set is permitted.
> >>> 7. Any other access is denied.
> >>
> >> ## These are calls to the above macros which plug in the necessary
> >> arguments
> >> r(hat, {*})
> >> x(hat, {*})
> >> r(~{star}, floor)
> >> x(~{star}, floor)
> >> r(~{star}, star)
> >> w(~{star}, star)
> >> x(~{star}, star)
> >> r(~{star}, self)
> >> w(~{star}, self)
> >> x(~{star}, self)
> >> ## Include your "loaded rule set" here ##
> >
> > What would that look like?
>
> The same kind of thing as the r, x, and w above, with occasional
> "type my_type_t; role rr types my_type_t;" to declare a type before
> use. If you wanted to add support for attributes which apply to
> multiple types, you could put those after a comma after the "type
> my_type_t" portion of the type declaration.

Well, I'm interested in seeing the actual code, not "the same kind
of thing" as arguement.

> >>> Smack rule sets can be easily defined that describe Bell&LaPadula
> >>> sensitivity, Biba integrity, and a variety of interesting
> >>> configurations. Smack rule sets can be modified on the fly to
> >>> accomodate changes in the operating environment or even the time
> >>> of day.
> >>
> >> SELinux can do this as well. It even includes support for
> >> conditional policy:
> >>
> >> bool foo_can_do_logging true;
> >> if (foo_can_do_logging) {
> >> allow foo_t foo_log_t:file { create read getattr append };
> >> }
> >
> > You have to build the booleans into the policy in advance.
>
> Well, yes, but a policy which completely ignores

This sentence no verb.

> >> The SELinux tools also have support for policy modules, so you can
> >> extend the policy without modifying the base system.
> >
> > And that's a good thing, but you still have to compile and reload
> > your policy, and maybe relabel you filesystem when you do so.
>
> What exactly prevents you from having to "reload" and "relabel the
> filesystem" when using your system?

Good question. First, changing Smack rules is no big deal. They
can be added or changed one by one as required while the system is
running, and there are uses that exploit that. One example is to
put the label "Game" on certain programs and:

at 8:00am "Worker Game no"
at 5:00pm "Worker Game x"

Thus Worker processes can access Game files only during off hours.

As far as relabeling the file system goes, the rules apply to
processes and objects, not to programs. The label on a program
describes who can access it, it will run with the label of the
process that exec's it. A change in the rules does not require a
change to the labels on the file system for things to work
according to the rules of engagement.

> When you change the meaning of
> new types, you clearly would need to first reload the policy then
> relaunch programs or relabel files to take advantage of the new
> types, otherwise they would be categorically ignored by the system.

I'm sorry, but again, I don't know what you mean by "changing the
meaning of new types".

> The only difference is the "compile" step, which converts from a
> reasonably modifiable language into a binary format which the kernel
> can efficiently parse.

I think you may be making assumptions about the behavior of
Smack based on what SELinux does that don't hold. I hesitate
to call a Smack rule list a language.

> I suspect you'd get shot down instantly if
> you tried to stuff the policy compiler in the kernel too.

Heavens, I should hope so!

Smack rule specifications are really really basic, just the
subject label, the object label, and the access permitted.
There are no wildcards, no expressions (regular or otherwise)
and they apply to all objects. Only the labels are taken into
account on an access check, not the userid, the current
directory, the time of day, or the binary involved. Oh,
there is a capability to override the check, but that's just
basic Linux behavior and I wouldn't want to muck that up.


> >>> Some practical use cases:
> >>>
> >>> Hierarchical levels. The less common of the two usual uses for
> >>> MLS systems is to define hierarchical levels, often
> >>> unclassified, confidential, secret, and so on. To set up smack to
> >>> support this, these rules could be defined:
> >>>
> >>> C Unclass rx
> >>> S C rx
> >>> S Unclass rx
> >>> TS S rx
> >>> TS C rx
> >>> TS Unclass rx
> >>>
> >>> A TS process can read S, C, and Unclass data, but cannot write
> >>> it. An S process can read C and Unclass. Note that specifying
> >>> that TS can read S and S can read C does not imply TS can read C,
> >>> it has to be explicitly stated.
> >>
> >> The big problem here is the duplication. Say you have a locked-
> >> down Apache configuration and you want to run 2 apache processes,
> >> one at Secret and one at Top-Secret. Under your model you have to
> >> copy-paste the policy and make sure to apply fixes/changes to both
> >> places.
> >
> > I'm sorry, but I don't understand your point at all.
>
> Ok, let's say you want to use your model to restrict Apache and MySQL
> running at the same classification level. You have to define
> combined labels, such as:
> S_Apache S_MySQL rx
> [...]
>
> Now if you want to run an apache/mysql pair at TS, you have to copy
> all those rules and substitute "S_" with "TS_".

There is only one rule to copy. I think most sysadmins can insert
a "T" with sufficient confidence that this is not going to be a
major issue.

> With SELinux, you
> don't have to do anything other than define new labels for the secret-
> web-docs and the top-secret-web-docs folders,

Which if you actually write out the change is going to require
more than inserting a "T".

> because the Apache
> *type-enforcement* policy is completely independent from the MLS
> levels. You can write a 5-line policy to label 2 slightly different
> initscripts with different range-transitions. The only difference
> between them will be the paths inside and the labels on them. The
> rest of apache's policy stays the same.
>
> >> This is exactly what my company does, but the ability to restrict
> >> *exactly* what mechanisms are used is important. You restrict
> >> against file-system access implicitly, whereas SELinux does it
> >> explicitly:
> >> allow foo_t foo_sock_t:socket { .... };
> >> versus:
> >> allow foo_t foo_log_t:file { ... };
> >>
> >> With SELinux you can also allow your program to create files
> >> labelled as "log files" in one directory in one type and "transfer
> >> files" in another directory in a completely different type.
> >
> > I would be very interested to see the policy that your guard box uses.
>
> Unfortunately that's still considered company-proprietary information
> at the moment, although that's likely to change at some point in the
> future. We're not all that different from the httpd policies and
> similar stuff found in the SELinux "refpolicy" available from tresys'
> website.

I think there's a rule against using proprietary information
that you can't share in a friendly debate on the LKML.

> >> I'm proud to say that the software of the company I work for does
> >> not need any extra privilege at all (aside from binding to ports
> >> <1024) to run under SELinux with strict MLS.
> >
> > That is an aspect of SELinux that has its dark side. While you are
> > not explicitly violating a policy (e.g. requiring a capability) you
> > are doing things that some of us would argue ought to require a
> > capability. SELinux integrates privilege into the policy mechanism.
>
> How does "moving data around a system" require root privileges?

Hmm. You snuck the adjective "root" into the discussion when it
wasn't there before.

> Why should it? Admittedly, moving data from "TopSecret" to "Secret"
> requires a process labeled with a range including at least "S-TS"
> *and* with the MLS "read-to-clearance" attribute, but you can be as
> stingy with that attribute as you like.

Yup. And plenty of people like you think that is really spiffy.

> That attribute is explicitly
> given its magic abilities in the mlsconstrain rules defined in the
> policy file itself, so it's also possible to do information flow
> analysis on it.

I much prefer strait-up process capabilities to magic attributes.
Call me old fashioned if you want to.

> > Smack does not. Smack is designed to use traditional Linux
> > mechanisms for privilege.
> >
> >> Under Trusted Solaris and such we needed all sorts of dirty
> >> privilege hacks to relabel the files consistently, but under
> >> SELinux the policy does all the relabeling for us, we don't need
> >> to do a thing.
> >
> > Yeah. Well, you won't like Smack then. Implicit relabeling of
> > processes and files does not happen. This is a major philisophical
> > difference.
>
> Here's an argument for why implicit labeling is good and explicit
> labeling is bad: If your process wants to label things explicitly it
> needs special privileges to do so, and it can abuse those
> privileges.

With privilege comes responsibility, and there has to be some effort
made to ensure that privileged programs behave correctly.

> Under SELinux the labeling is implicit *AND* the
> transitions are entirely within the policy. This means that you can
> do information flow analysis (as I described above) and it *ALSO*
> means that the program cannot possibly "abuse" its privileges because
> it has none.

It means you have not only the program to worry about being written
correctly, but the policy as well.

> When httpd creates a file in its log directory that
> file is created with httpd_log_t and it is limited to being able to
> append to the file *only*. So when your apache gets compromised, the
> attacker has NO WAY to modify the logs, and you can prove this with a
> 60-second study of the policy. In fact, on my companies productions
> systems I can *PROVE* that the only way to get audit data out of the
> system is via a local TTY by interacting with
> system_u:object_r:local_login_t:SystemLow-SystemHigh to log in with a
> process of someluser:auditreview_r:auditreview_t:SystemHigh, which
> may read the audit files, or
> otherluser:auditadmin_r:auditadmin_t:SystemHigh, which may read and
> delete the audit files. I can even prove that no process on the
> system is allowed to modify the files after they are created, barring
> a kernel-level vulnerability.

Good. That is as it should be.

> So the implicit labeling of files actually restricts every process
> *FURTHER*. They no longer have even an inkling of a choice about
> file labeling, and that's a GOOD thing.

That's true about Smack, you know. The process has no choice about
the label that the files gte.

> The only remaining extension
> we need to really thoroughly lock down our systems is to allow last-
> path-component matching in type-transitions and allow rules (EG:
> When "vipw" creates a file named "passwd" in an "etc_t" directory, it
> is automatically labeled "etc_passwd_t")

Hey, and AppArmor does that today!

> From what I understand
> Stephen Smalley and others are thinking that over even now. I'll do
> it myself as soon as I get time at work beyond prepping systems for
> shipping to clients if they haven't finished it by them.

You might want to review the archives on how that experiment ended up
before saying much more about it.


Thank you for your comments. I hope that I have made the behavior
and value of Smack clearer in responding to them.

Casey Schaufler
[email protected]

2007-08-12 01:36:19

by Casey Schaufler

[permalink] [raw]
Subject: Re: [PATCH] Smack: Simplified Mandatory Access Control Kernel


--- Andi Kleen <[email protected]> wrote:

> Casey Schaufler <[email protected]> writes:
>
> > Smack is the Simplified Mandatory Access Control Kernel.
>
> I like the simplified part.
>
> > +static int smk_get_access(smack_t sub, smack_t obj)
> > +{
> > + struct smk_list_entry *sp = smack_list;
> > +
> > + for (; sp != NULL; sp = sp->smk_next)
> > + if (sp->smk_rule.smk_subject == sub &&
> > + sp->smk_rule.smk_object == obj)
> > + return sp->smk_rule.smk_access;
>
> Do I miss something, or is there really no locking for the reader side
> of the list? That looks dangerous. Of course a global lock for readers
> would be likely a scaling disaster. You could use RCU.

Entries are never deleted, although they can be modified.

> Or if you assume rules are changed only very infrequently it might
> be more cache friendly to compile all the rules into a linear buffer
> and then just replace the whole buffer atomically with a RCU
> grace period on cahnges.

Individual entries can be modified without changing the whole
thing, but they shouldn't change often.

> It doesn't look like it would scale to larger numbers of rules though.
> Is that intended? Would caching of decisions fit into the design?

I put some thought into clever schemes for supporting large rule sets
well but decided to go ahead with the simplest possible mechanism
because I expect that in real deployments the number of rules will
be small. In fact, experiance says that virtually all access choices
will be covered either by the subject==object case or the subject can
read floor case. Cacheing, hashing, and 2D structures are all
possibilties that I would be happy to entertain as enhancements.

> Also in general code style would need some improvements;
> e.g. no externs in .c; no ../.. include hacks etc.
> You also seem weak on the Documentation front.

Yes, it is pretty sparse.

> Other than that it looks reasonably clean (haven't read all of it)

Thank you for your comments. I think the next version will be improved.


Casey Schaufler
[email protected]

2007-08-12 03:39:19

by Keith Owens

[permalink] [raw]
Subject: Re: [PATCH] Smack: Simplified Mandatory Access Control Kernel

Casey Schaufler (on Sat, 11 Aug 2007 12:56:42 -0700 (PDT)) wrote:
>
>--- Arjan van de Ven <[email protected]> wrote:
>> > +#include <linux/kernel.h>
>> > +#include <linux/vmalloc.h>
>> > +#include <linux/security.h>
>> > +#include <linux/mutex.h>
>> > +#include <net/netlabel.h>
>> > +#include "../../net/netlabel/netlabel_domainhash.h"
>>
>> can't you move this header to include/ instead?
>
>Paul Moore, the developer of netlabel, promised to work out
>the right solution for this with me at a future date. He
>doesn't want to move the header, and I respect that.

foo.c has

#include "netlabel_domainhash.h"

Makefile has CFLAGS_foo.o += -I$(srctree)/net/netlabel

I prefer to use -I $(srctree)/net/netlabel for readability but '-I '
breaks on SuSE builds for some reason that I cannot be bothered working
out. -I$(srctree)/net/netlabel works.

>> > + doip = kmalloc(sizeof(struct cipso_v4_doi), GFP_KERNEL);
>> > + if (doip == NULL)
>> > + panic("smack: Failed to initialize cipso DOI.\n");
>> > + doip->map.std = NULL;
>> > +
>> > + ndmp = kmalloc(sizeof(struct netlbl_dom_map), GFP_KERNEL);
>> > + if (ndmp == NULL)
>> > + panic("smack: Failed to initialize cipso ndmp.\n");
>>
>>
>> is panic() really the right thing here? It's usually considered quite
>> rude ;)
>
>It's really early in start-up and if you're out of memory at that
>point you are not going very far into the future.

Not to mention that you might end up running with an insecure system.
Security must be failsafe.

2007-08-12 03:45:31

by Keith Owens

[permalink] [raw]
Subject: Re: [PATCH] Smack: Simplified Mandatory Access Control Kernel

Casey Schaufler (on Sat, 11 Aug 2007 10:57:31 -0700) wrote:
>Smack is the Simplified Mandatory Access Control Kernel.
>
> [snip]
>
>Smack defines and uses these labels:
>
> "*" - pronounced "star"
> "_" - pronounced "floor"
> "^" - pronounced "hat"
> "?" - pronounced "huh"
>
>The access rules enforced by Smack are, in order:
>
>1. Any access requested by a task labeled "*" is denied.
>2. A read or execute access requested by a task labeled "^"
> is permitted.
>3. A read or execute access requested on an object labeled "_"
> is permitted.
>4. Any access requested on an object labeled "*" is permitted.
>5. Any access requested by a task on an object with the same
> label is permitted.
>6. Any access requested that is explicitly defined in the loaded
> rule set is permitted.
>7. Any other access is denied.

Some security systems that have the concept of "no default access"
(task labeled "*") also allow access by those tasks but only if there
is an explicit rule giving access to the task. IOW, rule 6 is applied
before rule 1. In my experience this simplifies special cases where a
task should only have access to a very small set of resources. I'm
curious why smack goes the other way?

2007-08-12 04:32:47

by Kyle Moffett

[permalink] [raw]
Subject: Re: [PATCH] Smack: Simplified Mandatory Access Control Kernel

Linus and AKPM pulled from CC, I'm sure they're bored to tears by
now ;-).

On Aug 11, 2007, at 21:21:55, Casey Schaufler wrote:
> --- Kyle Moffett <[email protected]> wrote:
>> On Aug 11, 2007, at 17:01:09, Casey Schaufler wrote:
>>> It would be instructive for those who are not well versed in the
>>> nuances of SELinux policy if you actually spelled out the whole
>>> thing, rather than using "## and more ##". Part of the point of
>>> Smack is the makeup of the full list that would be required here.
>>
>> Well, yes, but how exactly do you define "read", "write", and
>> "execute" for the full list of SELinux object classes found here:
>> http://www.tresys.com/selinux/obj_perms_help.html
>
> Smack is designed to treat objects as consistantly as is
> reasonable. The list of object classes defined by SELinux is great
> for a system designed to treat accesses with the highest possible
> granularity. This is not a design goal for Smack.
>
>> I was considering compiling the complete list, but such an
>> exercise would take me at least an hour to do properly and which
>> categories individual permissions should be placed in could be
>> argued for weeks.
>
> I will be happy to consider your arguement when you are willing to
> present the complete argument.

Here's my complete argument:

Regardless of how you categorize "read", "write", "execute", and
"doesnt-need-protection" in your policy language, I can write an
SELinux policy and a list of labels which expresses that policy.
Moreover, without too much work I can probably write a Perl script to
do it for you. On the other hand I can only do that if you tell me
exactly how you want to categorize those things, though. In my
personal opinion they cannot be reasonably categorized without
breaking all sorts of tools or leaving gaping holes; precisely the
reason that SELinux uses such a fine-grained list.

>> Specifically, do you classify the bind() syscall as a "read" or a
>> "write" operation.
>
> Neither. Bind attaches a name (port number) to a socket (which is a
> data structure that is part of your process) from a global
> namespace. No objects are involved, hence no accesses.

So "bind()" doesn't present a security hole at all and any program
may bind to any port? Sounds fun! I'm sure lots of crackers would
love to get ahold of an account on such a web-server; what havoc they
could wreak! Even the <1024 restriction doesn't sufficiently limit
things these days, as many critical services use ports >=1024.

>>>> And now to describe these rules:
>>>>
>>>>> Smack defines and uses these labels:
>>>>> "*" - pronounced "star"
>>>>> "_" - pronounced "floor"
>>>>> "^" - pronounced "hat"
>>>>> "?" - pronounced "huh"
>>>>>
>>>>> The access rules enforced by Smack are, in order:
>>>>>
>>>>> 1. Any access requested by a task labeled "*" is denied.
>>>>> 2. A read or execute access requested by a task labeled "^" is
>>>>> permitted.
>>>>> 3. A read or execute access requested on an object labeled "_"
>>>>> is permitted.
>>>>> 4. Any access requested on an object labeled "*" is permitted.
>>>>> 5. Any access requested by a task on an object with the same
>>>>> label is permitted.
>>>>> 6. Any access requested that is explicitly defined in the
>>>>> loaded rule set is permitted.
>>>>> 7. Any other access is denied.
>>>>
>>>> ## These are calls to the above macros which plug in the
>>>> necessary arguments
>>>> r(hat, {*})
>>>> x(hat, {*})
>>>> r(~{star}, floor)
>>>> x(~{star}, floor)
>>>> r(~{star}, star)
>>>> w(~{star}, star)
>>>> x(~{star}, star)
>>>> r(~{star}, self)
>>>> w(~{star}, self)
>>>> x(~{star}, self)
>>>> ## Include your "loaded rule set" here ##
>>>
>>> What would that look like?
>>
>> The same kind of thing as the r, x, and w above, with occasional
>> "type my_type_t; role rr types my_type_t;" to declare a type
>> before use. If you wanted to add support for attributes which
>> apply to multiple types, you could put those after a comma after
>> the "type my_type_t" portion of the type declaration.
>
> Well, I'm interested in seeing the actual code, not "the same kind
> of thing" as arguement.

Ok, you want sample policy to match your "MLS" sample? For
convenience here's one more macro:
define(`rx',`r(`$1',`$2') x(`$1',`$2')')

type unclass;
type c;
type s;
type ts;
rx(c, unclass)
rx(s, c)
rx(s, unclass)
rx(ts, s)
rx(ts, c)
rx(ts, unclass)

In case you don't have the policy you typed into your email, it looks
almost identical with a few exceptions for slightly modified syntax:
> C Unclass rx
> S C rx
> S Unclass rx
> TS S rx
> TS C rx
> TS Unclass rx

>>>>> Smack rule sets can be easily defined that describe
>>>>> Bell&LaPadula sensitivity, Biba integrity, and a variety of
>>>>> interesting configurations. Smack rule sets can be modified on
>>>>> the fly to accomodate changes in the operating environment or
>>>>> even the time of day.
>>>>
>>>> SELinux can do this as well. It even includes support for
>>>> conditional policy:
>>>>
>>>> bool foo_can_do_logging true;
>>>> if (foo_can_do_logging) {
>>>> allow foo_t foo_log_t:file { create read getattr append };
>>>> }
>>>
>>> You have to build the booleans into the policy in advance.
>>
>> Well, yes, but a policy which completely ignores
>
> This sentence no verb.

Whoops, I think I must have smashed the delete key or something while
sending. Here's the paragraphs which got elided:

Well, yes, but a policy which completely ignores future expandability
can't be expanded upon regardless. It would also be very hard to add
new policy without a lot of duplication under your system. On the
other hand, with SELinux you can very easily add attribute-based
policy so adding new capabilities is as simple as sticking existing
attributes on newly defined types. For example:

type my_log_t, file_type, log_file;
type my_log_daemon, daemon;

Right there I just gave permission for the logrotate to recycle files
labelled my_log_t, which the sysadmin and audit admin can also read
(and the audit admin can delete). I also gave permission for my
daemon to send SIGCHLD to init, and for init/initscripts to send it a
SIGTERM/SIGQUIT. All without writing a SINGLE policy rule.
Basically all of those existing behaviors are found in allow rules
built on the "file_type", "log_file", and "daemon" attributes.

>>>> The SELinux tools also have support for policy modules, so you
>>>> can extend the policy without modifying the base system.
>>>
>>> And that's a good thing, but you still have to compile and reload
>>> your policy, and maybe relabel you filesystem when you do so.
>>
>> What exactly prevents you from having to "reload" and "relabel the
>> filesystem" when using your system?
>
> Good question. First, changing Smack rules is no big deal. They can
> be added or changed one by one as required while the system is
> running, and there are uses that exploit that. One example is to
> put the label "Game" on certain programs and:
>
> at 8:00am "Worker Game no"
> at 5:00pm "Worker Game x"
>
> Thus Worker processes can access Game files only during off hours.

This is fundamentally broken: First of all we have no fd revoke
support yet, so all a user has to do is start the game at 5:05pm just
before they leave, then leave it running in the background during the
day for them to play at leisure. Secondly, you can already do the
same thing with DAC and a PAM groups-from-time-of-day module, I don't
see why such a thing is special enough to need MAC. Thirdly, I could
do exactly the same thing with an SELinux boolean and a cronjob (once
we get proper revoke support):

## Rule to allow cron to tweak the "can_play_games" boolean value
if (can_play_games) {
## Insert game-playing allow rules here
}

Then in cron:
0 8 * * * root setsebool can_play_games false
0 17 * * * root setsebool can_play_games true


> As far as relabeling the file system goes, the rules apply to
> processes and objects, not to programs. The label on a program
> describes who can access it, it will run with the label of the
> process that exec's it. A change in the rules does not require a
> change to the labels on the file system for things to work
> according to the rules of engagement.

By this logic, every program will always have the same label, unless
you recode every program to explicitly change it when it starts;
precisely the thing that the SELinux type transitions were designed
to avoid.

>> When you change the meaning of new types, you clearly would need
>> to first reload the policy then relaunch programs or relabel files
>> to take advantage of the new types, otherwise they would be
>> categorically ignored by the system.
>
> I'm sorry, but again, I don't know what you mean by "changing the
> meaning of new types".

Oh, say something like "Oh, drat, I wanted this daemon to run as some
other type". Unless you relabel the files and relaunch the daemon
the system will have no idea how the system will change. And if you
let arbitrary root processes relabel things on the fly then you've
lost all the security advantages to a MAC system.

> I think you may be making assumptions about the behavior of Smack
> based on what SELinux does that don't hold. I hesitate to call a
> Smack rule list a language.

Well, it's a syntactic construction to describe an idea, ergo it's a
language.

>> I suspect you'd get shot down instantly if you tried to stuff the
>> policy compiler in the kernel too.
>
> Smack rule specifications are really really basic, just the subject
> label, the object label, and the access permitted. There are no
> wildcards, no expressions (regular or otherwise) and they apply to
> all objects. Only the labels are taken into account on an access
> check, not the userid, the current directory, the time of day, or
> the binary involved. Oh, there is a capability to override the
> check, but that's just basic Linux behavior and I wouldn't want to
> muck that up.

I think you may be making assumptions about the behaviour of SELinux
based on what other OSes do which don't hold:

(a) SELinux does not care about user id, current directory, time of
day, etc. It cares solely about 3 things:
the Subject, the Object, and the Action. For example, if an init
script attempts to execute the apache binary then SELinux gets the
following data for parameters:
subject="system_u:system_r:initrc_t:SystemLow-SystemHigh"
object="system_u:object_r:httpd_exec_t:SystemLow" action="file-
>execute".

(b) SELinux does NOT have a capability which overrides it. If you
want to do something that the policy categorically does not allow,
you have 2 choices: change the policy or disable SELinux. You can
also use a policy which disables both of those options

>> Ok, let's say you want to use your model to restrict Apache and
>> MySQL running at the same classification level. You have to
>> define combined labels, such as:
>> S_Apache S_MySQL rx
>> [...]
>>
>> Now if you want to run an apache/mysql pair at TS, you have to
>> copy all those rules and substitute "S_" with "TS_".
>
> There is only one rule to copy. I think most sysadmins can insert a
> "T" with sufficient confidence that this is not going to be a major
> issue.

Which means that for every new category/level-alteration you have to
modify a significant portion of your policy. With SELinux I get to
either add 2 lines to a local policy module or run a couple
"semanage" commands.

>> With SELinux, you don't have to do anything other than define new
>> labels for the secret-web-docs and the top-secret-web-docs folders,
>
> Which if you actually write out the change is going to require more
> than inserting a "T".

Umm, how so? Any of these will work:
* The security administrator creates my /www_S and /www_TS
folders, labels them, and remembers not to run "restorecon" on those
folders.

* The security administrator loads a local policy module with just
an fc file containing:
/www_S(/.*)? system_u:object_r:web_docs_t:Secret
/www_TS(/.*)? system_u:object_r:web_docs_t:TopSecret

* The security administrator runs these commands:
semanage fcontext -a -r Secret-Secret -s system_u -t
web_docs_t '/www_S(/.*)?'
semanage fcontext -a -r TopSecret-TopSecret -s system_u -t
web_docs_t '/www_TS(/.*)?'

>>>> This is exactly what my company does, but the ability to
>>>> restrict *exactly* what mechanisms are used is important. You
>>>> restrict against file-system access implicitly, whereas SELinux
>>>> does it explicitly:
>>>> allow foo_t foo_sock_t:socket { .... };
>>>> versus:
>>>> allow foo_t foo_log_t:file { ... };
>>>>
>>>> With SELinux you can also allow your program to create files
>>>> labelled as "log files" in one directory in one type and
>>>> "transfer files" in another directory in a completely different
>>>> type.
>>>
>>> I would be very interested to see the policy that your guard box
>>> uses.
>>
>> Unfortunately that's still considered company-proprietary
>> information at the moment, although that's likely to change at
>> some point in the future. We're not all that different from the
>> httpd policies and similar stuff found in the SELinux "refpolicy"
>> available from tresys' website.
>
> I think there's a rule against using proprietary information that
> you can't share in a friendly debate on the LKML.

Well, you were the one who asked. It's mostly irrelevant to the
discussion since the *same* kind of stuff is found in almost every
module in the tresys-distributed refpolicy.

>>>> I'm proud to say that the software of the company I work for
>>>> does not need any extra privilege at all (aside from binding to
>>>> ports <1024) to run under SELinux with strict MLS.
>>>
>>> That is an aspect of SELinux that has its dark side. While you
>>> are not explicitly violating a policy (e.g. requiring a
>>> capability) you are doing things that some of us would argue
>>> ought to require a capability. SELinux integrates privilege into
>>> the policy mechanism.
>>
>> How does "moving data around a system" require root privileges?
>
> Hmm. You snuck the adjective "root" into the discussion when it
> wasn't there before.

Ok, fine, how does "moving data around a system" require anything
more than bog-standard user-level privileges? Normal users under
Linux are not permitted to change "security.*" attributes at all, but
they may bind to sockets, etc. You can define a "capability" which
lets you do all that, but then you're just blindly giving processes
more privileges than they need to complete an operation. Why should
you get the "rename all files" priv when all you need is "Make my log
files get $LABEL when I put them in /var/log/mydaemon/"?


>> Why should it? Admittedly, moving data from "TopSecret" to
>> "Secret" requires a process labeled with a range including at
>> least "S-TS" *and* with the MLS "read-to-clearance" attribute,
>> but you can be as stingy with that attribute as you like.
>
> Yup. And plenty of people like you think that is really spiffy.

Please explain why you think it isn't?


>> That attribute is explicitly given its magic abilities in the
>> mlsconstrain rules defined in the policy file itself, so it's also
>> possible to do information flow analysis on it.
>
> I much prefer strait-up process capabilities to magic attributes.
> Call me old fashioned if you want to.

You can call them "process capabilities" or "magic attributes",
they're the same thing, just more fine-grained. Instead of (Note: r1
== subject role, r2 == target role, t2 == target type):
mlsconstrain process transition ( (r1 == r2) || (t1 ==
i_am_a_role_changing_capability) )

you have (Note: t1 == subject type):
mlsconstrain process transition ( (r1 == r2) || (r1 == system_r &&
t1 == i_am_a_login_process && t2 == i_am_a_user_entrypoint) )

The latter is like the former, except instead of one single ultimate
"change-any-role" capability, you have a capability which allows a
system login process to change roles *ONLY* if it's also changing to
a user entrypoint. Please tell me which you think is more secure.


>> Under SELinux the labeling is implicit *AND* the transitions are
>> entirely within the policy. This means that you can do
>> information flow analysis (as I described above) and it *ALSO*
>> means that the program cannot possibly "abuse" its privileges
>> because it has none.
>
> It means you have not only the program to worry about being written
> correctly, but the policy as well.

If your security policy sucks then you might as well not have MAC at
all. At least under SELinux the absolute worst possible case is that
you always fall back to DAC-based security.

>> So the implicit labeling of files actually restricts every process
>> *FURTHER*. They no longer have even an inkling of a choice about
>> file labeling, and that's a GOOD thing.
>
> That's true about Smack, you know. The process has no choice about
> the label that the files get.

So how do you selectively label files based on criteria such as
"where did I get put"? How do you distinguish between a "log file"
which is create-and-append-only, a "database file" which I can modify
however I want to, and a "unix socket" which I can bind in /var/run
and get connections from user processes?

>> The only remaining extension we need to really thoroughly lock
>> down our systems is to allow last-path-component matching in type-
>> transitions and allow rules (EG: When "vipw" creates a file named
>> "passwd" in an "etc_t" directory, it is automatically labeled
>> "etc_passwd_t")
>
> Hey, and AppArmor does that today!

No, AppArmor doesn't. It says: "/usr/sbin/httpd may not access /etc/
shadow", for example, whereas SELinux with the proposed extension
says: "Only the 'passwd' program or 'vipw' by a sysadmin may modify
*ANY* password shadow file, and if I create a "shadow" file in *ANY*
etc_t directory then it's automatically a shadow file. The
difference is also in the treatment of hard-links. Under SELinux I
can guarantee that the data in the shadow_t file is secure, no matter
where somebody manages to get it linked to, and in addition I can
guarantee that nobody can spoof me and make me create the shadow file
elsewhere or with the wrong label.

>> From what I understand Stephen Smalley and others are thinking
>> that over even now. I'll do it myself as soon as I get time at
>> work beyond prepping systems for shipping to clients if they
>> haven't finished it by them.
>>
>
> You might want to review the archives on how that experiment ended
> up before saying much more about it.

Hmm, I haven't seen anything pass LKML and digging in the
[email protected] archives (as well as google) doesn't seem to be
turning up anything even vaguely related. Do you happen to have the
link handy?

> Thank you for your comments. I hope that I have made the
> behavior and value of Smack clearer in responding to them.

You're welcome. I endeavor to poke my nose into every little LSM-
related corner in the kernel, this one included.

Cheers,
Kyle Moffett

2007-08-12 11:16:45

by Jan Engelhardt

[permalink] [raw]
Subject: Re: [PATCH] Smack: Simplified Mandatory Access Control Kernel


On Aug 11 2007 16:22, Casey Schaufler wrote:
>> >@@ -0,0 +1,8 @@
>> >+#
>> >+# Makefile for the SMACK LSM
>> >+#
>> >+
>> >+obj-$(CONFIG_SECURITY_SMACK) := smack.o
>> >+
>> >+smack-y := smack_lsm.o smack_access.o smackfs.o
>>
>> smack-objs :=
>
>Added.

I should have added "replace it".

>> >+/*
>> >+ * 'ssssssss oooooooo mmmm\n\0'
>> >+ */
>>
>> I wonder why it's limited to 8 characters? Ah right.. sizeof(smack_t).
>> uhm.. are you trying to tell me that smack_t [typedef'ed to u64]
>> are actually meant as a char[8]? (/me scrathces head)
>
>Yes. "s == o" vs "strcmp(s,o) == 0".

Evil optimization ;-)
[ s == o is memcmp(s, o, sizeof(that)) == 0]

>> >+ char temp[80];
>> >+ ssize_t rc;
>> >+
>> >+ if (*ppos != 0)
>> >+ return 0;
>> >+
>> >+ sprintf(temp, "%d", smk_cipso_doi_value);
>> >+ rc = simple_read_from_buffer(buf, count, ppos, temp, strlen(temp));
>>
>> 80 is plenty for a 11 char string.
>>
>> Look, they've got funny ideas! :)
>> net/ipv4/netfilter/nf_nat_irc.c:char buffer[sizeof("4294967296 65635")];

Proposal would have been: char temp[sizeof("4294967296")];

>> >+extern int smack_net_nltype;
>> >+extern int smack_cipso_direct;
>> >+extern struct smk_cipso_entry *smack_cipso;
>>
>> for consistency reasons, add extern to the other vars too...
>
>the others?

There were some [or some function prototypes] above this point.

>> >+static int smack_task_movememory(struct task_struct *p)
>> >+{
>> >+ int rc;
>> >+
>> >+ rc = smk_curacc(smk_of_task(p), MAY_WRITE);
>> >+ return rc;
>> >+}
>>
>> Uh...
>>
>> {
>> return smk_curacc(smk_of_task(p), MAY_WRITE);
>> }
>>
>> (also others)
>
>That was a little excessive, wasn't it?

How do you mean? I was just suggesting to collapse the three [four] lines into
one, getting rid of the tempotemporaries in a lot of these functions,
for example,

>> >+static int smack_task_kill(struct task_struct *p, struct siginfo *info,
>> >+ int sig, u32 secid)
>> >+{
>> >+ smack_t *tsp = smk_of_task(p);
>> >+ int rc;
>> >+
>> >+ /*
>> >+ * Sending a signal requires that the sender
>> >+ * can write the receiver.
>> >+ */
>> >+ rc = smk_curacc(tsp, MAY_WRITE);
>> >+
>> >+ return rc;
>> >+}

static int smack_task_kill(struct stask_struct *p, struct siginfo *info,
int sig, u32 secid)
{
/* Comment */
return smk_curacc(smk_of_task(p), MAY_WRITE);
}



Jan
--

2007-08-12 11:49:26

by Andi Kleen

[permalink] [raw]
Subject: Re: [PATCH] Smack: Simplified Mandatory Access Control Kernel

> Entries are never deleted, although they can be modified.

The modification case still seems racy then.

-Andi

2007-08-12 17:16:59

by Casey Schaufler

[permalink] [raw]
Subject: Re: [PATCH] Smack: Simplified Mandatory Access Control Kernel


--- Keith Owens <[email protected]> wrote:

> Casey Schaufler (on Sat, 11 Aug 2007 10:57:31 -0700) wrote:
> >Smack is the Simplified Mandatory Access Control Kernel.
> >
> > [snip]
> >
> >Smack defines and uses these labels:
> >
> > "*" - pronounced "star"
> > "_" - pronounced "floor"
> > "^" - pronounced "hat"
> > "?" - pronounced "huh"
> >
> >The access rules enforced by Smack are, in order:
> >
> >1. Any access requested by a task labeled "*" is denied.
> >2. A read or execute access requested by a task labeled "^"
> > is permitted.
> >3. A read or execute access requested on an object labeled "_"
> > is permitted.
> >4. Any access requested on an object labeled "*" is permitted.
> >5. Any access requested by a task on an object with the same
> > label is permitted.
> >6. Any access requested that is explicitly defined in the loaded
> > rule set is permitted.
> >7. Any other access is denied.
>
> Some security systems that have the concept of "no default access"
> (task labeled "*") also allow access by those tasks but only if there
> is an explicit rule giving access to the task. IOW, rule 6 is applied
> before rule 1. In my experience this simplifies special cases where a
> task should only have access to a very small set of resources. I'm
> curious why smack goes the other way?

A task labeled star would create files labeled star, which would be
readable and writable by everyone (rule 4). This would be bad.

Over the past three decade I've seen a lot of secure installations
but I have never seen one where they really wanted to deny any
real application access to "/". That's a major reason for chrooted
environments.

Remember also that a process labeled Coffee would have read access
to floor files, all access to star files (e.g. /dev/null), read and
write access to Coffee objects, and no others unless specified. If
you are serious about reducing the programs a Coffee process can run
you can do so by moving them off the floor, that is, giving them
a different label, say "Tea". There is a cost to doing this, which
is that you now have to give other labels explicit access to Tea
objects. This is how you would implement a Biba integrity policy.

So, can you give me an example where you don't want access to any
file system objects (no read or execute access on floor) and are still
doing something useful? Remember that you can't exec a program if
you can't access it.

I think that access to the file system namespace is universally
required. I'm open to education.


Casey Schaufler
[email protected]

2007-08-12 17:48:22

by Casey Schaufler

[permalink] [raw]
Subject: Re: [PATCH] Smack: Simplified Mandatory Access Control Kernel


--- Andi Kleen <[email protected]> wrote:

> > Entries are never deleted, although they can be modified.
>
> The modification case still seems racy then.

Fair enough. I'll look into real list management.


Casey Schaufler
[email protected]

2007-08-12 19:42:01

by Casey Schaufler

[permalink] [raw]
Subject: Re: [PATCH] Smack: Simplified Mandatory Access Control Kernel


--- Kyle Moffett <[email protected]> wrote:

> Linus and AKPM pulled from CC, I'm sure they're bored to tears by
> now ;-).

Yeah.

> On Aug 11, 2007, at 21:21:55, Casey Schaufler wrote:
> > --- Kyle Moffett <[email protected]> wrote:
> >> On Aug 11, 2007, at 17:01:09, Casey Schaufler wrote:
> >>> It would be instructive for those who are not well versed in the
> >>> nuances of SELinux policy if you actually spelled out the whole
> >>> thing, rather than using "## and more ##". Part of the point of
> >>> Smack is the makeup of the full list that would be required here.
> >>
> >> Well, yes, but how exactly do you define "read", "write", and
> >> "execute" for the full list of SELinux object classes found here:
> >> http://www.tresys.com/selinux/obj_perms_help.html
> >
> > Smack is designed to treat objects as consistantly as is
> > reasonable. The list of object classes defined by SELinux is great
> > for a system designed to treat accesses with the highest possible
> > granularity. This is not a design goal for Smack.
> >
> >> I was considering compiling the complete list, but such an
> >> exercise would take me at least an hour to do properly and which
> >> categories individual permissions should be placed in could be
> >> argued for weeks.
> >
> > I will be happy to consider your arguement when you are willing to
> > present the complete argument.
>
> Here's my complete argument:
>
> Regardless of how you categorize "read", "write", "execute", and
> "doesnt-need-protection" in your policy language, I can write an
> SELinux policy and a list of labels which expresses that policy.
> Moreover, without too much work I can probably write a Perl script to
> do it for you. On the other hand I can only do that if you tell me
> exactly how you want to categorize those things, though. In my
> personal opinion they cannot be reasonably categorized without
> breaking all sorts of tools or leaving gaping holes; precisely the
> reason that SELinux uses such a fine-grained list.

This identifies an important design philosophy issue, that
being what granularity is appropriate. I have no interest in
composing a description of Smack at the granularity that
SELinux uses. The point you make, that you need to have that
in order to create a policy description, is one of the reasons
for Smack. Simplified.

I respect the design decisions that SELinux has made regarding
granularity without agreeing with them myself.

> >> Specifically, do you classify the bind() syscall as a "read" or a
> >> "write" operation.
> >
> > Neither. Bind attaches a name (port number) to a socket (which is a
> > data structure that is part of your process) from a global
> > namespace. No objects are involved, hence no accesses.
>
> So "bind()" doesn't present a security hole at all and any program
> may bind to any port? Sounds fun! I'm sure lots of crackers would
> love to get ahold of an account on such a web-server; what havoc they
> could wreak! Even the <1024 restriction doesn't sufficiently limit
> things these days, as many critical services use ports >=1024.

Heaven forbid I should put all those firewall writers out of work.
Seriously, the port namespace is completely arbitrary by "design".

> >>>> And now to describe these rules:
> >>>>
> >>>>> Smack defines and uses these labels:
> >>>>> "*" - pronounced "star"
> >>>>> "_" - pronounced "floor"
> >>>>> "^" - pronounced "hat"
> >>>>> "?" - pronounced "huh"
> >>>>>
> >>>>> The access rules enforced by Smack are, in order:
> >>>>>
> >>>>> 1. Any access requested by a task labeled "*" is denied.
> >>>>> 2. A read or execute access requested by a task labeled "^" is
> >>>>> permitted.
> >>>>> 3. A read or execute access requested on an object labeled "_"
> >>>>> is permitted.
> >>>>> 4. Any access requested on an object labeled "*" is permitted.
> >>>>> 5. Any access requested by a task on an object with the same
> >>>>> label is permitted.
> >>>>> 6. Any access requested that is explicitly defined in the
> >>>>> loaded rule set is permitted.
> >>>>> 7. Any other access is denied.
> >>>>
> >>>> ## These are calls to the above macros which plug in the
> >>>> necessary arguments
> >>>> r(hat, {*})
> >>>> x(hat, {*})
> >>>> r(~{star}, floor)
> >>>> x(~{star}, floor)
> >>>> r(~{star}, star)
> >>>> w(~{star}, star)
> >>>> x(~{star}, star)
> >>>> r(~{star}, self)
> >>>> w(~{star}, self)
> >>>> x(~{star}, self)
> >>>> ## Include your "loaded rule set" here ##
> >>>
> >>> What would that look like?
> >>
> >> The same kind of thing as the r, x, and w above, with occasional
> >> "type my_type_t; role rr types my_type_t;" to declare a type
> >> before use. If you wanted to add support for attributes which
> >> apply to multiple types, you could put those after a comma after
> >> the "type my_type_t" portion of the type declaration.
> >
> > Well, I'm interested in seeing the actual code, not "the same kind
> > of thing" as arguement.
>
> Ok, you want sample policy to match your "MLS" sample? For
> convenience here's one more macro:
> define(`rx',`r(`$1',`$2') x(`$1',`$2')')
>
> type unclass;
> type c;
> type s;
> type ts;
> rx(c, unclass)
> rx(s, c)
> rx(s, unclass)
> rx(ts, s)
> rx(ts, c)
> rx(ts, unclass)
>
> In case you don't have the policy you typed into your email, it looks
> almost identical with a few exceptions for slightly modified syntax:
> > C Unclass rx
> > S C rx
> > S Unclass rx
> > TS S rx
> > TS C rx
> > TS Unclass rx

Yup. Your macro invocations look very much like the Smack specification.
Your macro definitions are of course unnecessary in Smack, and you
really ought to be using the MLS labels SELinux supports, and you
need a base policy underneath this.


> >>>>> Smack rule sets can be easily defined that describe
> >>>>> Bell&LaPadula sensitivity, Biba integrity, and a variety of
> >>>>> interesting configurations. Smack rule sets can be modified on
> >>>>> the fly to accomodate changes in the operating environment or
> >>>>> even the time of day.
> >>>>
> >>>> SELinux can do this as well. It even includes support for
> >>>> conditional policy:
> >>>>
> >>>> bool foo_can_do_logging true;
> >>>> if (foo_can_do_logging) {
> >>>> allow foo_t foo_log_t:file { create read getattr append };
> >>>> }
> >>>
> >>> You have to build the booleans into the policy in advance.
> >>
> >> Well, yes, but a policy which completely ignores
> >
> > This sentence no verb.
>
> Whoops, I think I must have smashed the delete key or something while
> sending. Here's the paragraphs which got elided:
>
> Well, yes, but a policy which completely ignores future expandability
> can't be expanded upon regardless. It would also be very hard to add
> new policy without a lot of duplication under your system. On the
> other hand, with SELinux you can very easily add attribute-based
> policy so adding new capabilities is as simple as sticking existing
> attributes on newly defined types. For example:
>
> type my_log_t, file_type, log_file;
> type my_log_daemon, daemon;
>
> Right there I just gave permission for the logrotate to recycle files
> labelled my_log_t, which the sysadmin and audit admin can also read
> (and the audit admin can delete). I also gave permission for my
> daemon to send SIGCHLD to init, and for init/initscripts to send it a
> SIGTERM/SIGQUIT. All without writing a SINGLE policy rule.
> Basically all of those existing behaviors are found in allow rules
> built on the "file_type", "log_file", and "daemon" attributes.

Ah, now you're refering to the reference policy, right?

> >>>> The SELinux tools also have support for policy modules, so you
> >>>> can extend the policy without modifying the base system.
> >>>
> >>> And that's a good thing, but you still have to compile and reload
> >>> your policy, and maybe relabel you filesystem when you do so.
> >>
> >> What exactly prevents you from having to "reload" and "relabel the
> >> filesystem" when using your system?
> >
> > Good question. First, changing Smack rules is no big deal. They can
> > be added or changed one by one as required while the system is
> > running, and there are uses that exploit that. One example is to
> > put the label "Game" on certain programs and:
> >
> > at 8:00am "Worker Game no"
> > at 5:00pm "Worker Game x"
> >
> > Thus Worker processes can access Game files only during off hours.
>
> This is fundamentally broken: First of all we have no fd revoke
> support yet, so all a user has to do is start the game at 5:05pm just
> before they leave, then leave it running in the background during the
> day for them to play at leisure.

That's why it's never a good idea to teach the Pointy Haired One
about "ps".

> Secondly, you can already do the
> same thing with DAC and a PAM groups-from-time-of-day module, I don't
> see why such a thing is special enough to need MAC. Thirdly, I could
> do exactly the same thing with an SELinux boolean and a cronjob (once
> we get proper revoke support):

There is usually a way to address any particular problem using
DAC, it's often sufficiently painful that MAC looks like a better
approach.

Your boolean solution requires more forthought than the Smack
rule solution, but I'll give it to you once you've fleshed out
your "##" lines.

> ## Rule to allow cron to tweak the "can_play_games" boolean value
> if (can_play_games) {
> ## Insert game-playing allow rules here
> }
>
> Then in cron:
> 0 8 * * * root setsebool can_play_games false
> 0 17 * * * root setsebool can_play_games true
>
>
> > As far as relabeling the file system goes, the rules apply to
> > processes and objects, not to programs. The label on a program
> > describes who can access it, it will run with the label of the
> > process that exec's it. A change in the rules does not require a
> > change to the labels on the file system for things to work
> > according to the rules of engagement.
>
> By this logic, every program will always have the same label, unless
> you recode every program to explicitly change it when it starts;
> precisely the thing that the SELinux type transitions were designed
> to avoid.

Yes. This is the way it should be. A small set of very carefully
analysed programs that change labels under carefully controlled
circumstances is what I want. login, sshd, cron, special purpose
launchers. Written with the assumption that they will be attacked.


> >> When you change the meaning of new types, you clearly would need
> >> to first reload the policy then relaunch programs or relabel files
> >> to take advantage of the new types, otherwise they would be
> >> categorically ignored by the system.
> >
> > I'm sorry, but again, I don't know what you mean by "changing the
> > meaning of new types".
>
> Oh, say something like "Oh, drat, I wanted this daemon to run as some
> other type".

Sorry, "type" is a meaningless attribute in Smack.

> Unless you relabel the files and relaunch the daemon
> the system will have no idea how the system will change. And if you
> let arbitrary root processes relabel things on the fly then you've
> lost all the security advantages to a MAC system.

MAC systems have been behaving this way for decades. SELinux is
the exception, not the rule on this.

> > I think you may be making assumptions about the behavior of Smack
> > based on what SELinux does that don't hold. I hesitate to call a
> > Smack rule list a language.
>
> Well, it's a syntactic construction to describe an idea, ergo it's a
> language.

Ok. I still don't understand the point.

> >> I suspect you'd get shot down instantly if you tried to stuff the
> >> policy compiler in the kernel too.
> >
> > Smack rule specifications are really really basic, just the subject
> > label, the object label, and the access permitted. There are no
> > wildcards, no expressions (regular or otherwise) and they apply to
> > all objects. Only the labels are taken into account on an access
> > check, not the userid, the current directory, the time of day, or
> > the binary involved. Oh, there is a capability to override the
> > check, but that's just basic Linux behavior and I wouldn't want to
> > muck that up.
>
> I think you may be making assumptions about the behaviour of SELinux
> based on what other OSes do which don't hold:
>
> (a) SELinux does not care about user id, current directory, time of
> day, etc. It cares solely about 3 things:
> the Subject, the Object, and the Action. For example, if an init
> script attempts to execute the apache binary then SELinux gets the
> following data for parameters:
> subject="system_u:system_r:initrc_t:SystemLow-SystemHigh"
> object="system_u:object_r:httpd_exec_t:SystemLow" action="file-
> >execute".

Yes, and each of the components of the subject and object contexts
is dependent on a more circumstances than I'd care to deal with.
You've demonstrated that SELinux implements fine granularity.

> (b) SELinux does NOT have a capability which overrides it. If you
> want to do something that the policy categorically does not allow,
> you have 2 choices: change the policy or disable SELinux. You can
> also use a policy which disables both of those options

Yup. I think that's bad.

> >> Ok, let's say you want to use your model to restrict Apache and
> >> MySQL running at the same classification level. You have to
> >> define combined labels, such as:
> >> S_Apache S_MySQL rx
> >> [...]
> >>
> >> Now if you want to run an apache/mysql pair at TS, you have to
> >> copy all those rules and substitute "S_" with "TS_".
> >
> > There is only one rule to copy. I think most sysadmins can insert a
> > "T" with sufficient confidence that this is not going to be a major
> > issue.
>
> Which means that for every new category/level-alteration you have to
> modify a significant portion of your policy. With SELinux I get to
> either add 2 lines to a local policy module or run a couple
> "semanage" commands.

Yup. And Bell & LaPadula using a combination of levels and
categories is probably the Smack worst case. But guess what?
The dig against that policy has always been that no one wanted
to combine levels and categories.

> >> With SELinux, you don't have to do anything other than define new
> >> labels for the secret-web-docs and the top-secret-web-docs folders,
> >
> > Which if you actually write out the change is going to require more
> > than inserting a "T".
>
> Umm, how so? Any of these will work:
> * The security administrator creates my /www_S and /www_TS
> folders, labels them, and remembers not to run "restorecon" on those
> folders.
>
> * The security administrator loads a local policy module with just
> an fc file containing:
> /www_S(/.*)? system_u:object_r:web_docs_t:Secret
> /www_TS(/.*)? system_u:object_r:web_docs_t:TopSecret
>
> * The security administrator runs these commands:
> semanage fcontext -a -r Secret-Secret -s system_u -t
> web_docs_t '/www_S(/.*)?'
> semanage fcontext -a -r TopSecret-TopSecret -s system_u -t
> web_docs_t '/www_TS(/.*)?'
>
> >>>> This is exactly what my company does, but the ability to
> >>>> restrict *exactly* what mechanisms are used is important. You
> >>>> restrict against file-system access implicitly, whereas SELinux
> >>>> does it explicitly:
> >>>> allow foo_t foo_sock_t:socket { .... };
> >>>> versus:
> >>>> allow foo_t foo_log_t:file { ... };
> >>>>
> >>>> With SELinux you can also allow your program to create files
> >>>> labelled as "log files" in one directory in one type and
> >>>> "transfer files" in another directory in a completely different
> >>>> type.
> >>>
> >>> I would be very interested to see the policy that your guard box
> >>> uses.
> >>
> >> Unfortunately that's still considered company-proprietary
> >> information at the moment, although that's likely to change at
> >> some point in the future. We're not all that different from the
> >> httpd policies and similar stuff found in the SELinux "refpolicy"
> >> available from tresys' website.
> >
> > I think there's a rule against using proprietary information that
> > you can't share in a friendly debate on the LKML.
>
> Well, you were the one who asked. It's mostly irrelevant to the
> discussion since the *same* kind of stuff is found in almost every
> module in the tresys-distributed refpolicy.

OK.

> >>>> I'm proud to say that the software of the company I work for
> >>>> does not need any extra privilege at all (aside from binding to
> >>>> ports <1024) to run under SELinux with strict MLS.
> >>>
> >>> That is an aspect of SELinux that has its dark side. While you
> >>> are not explicitly violating a policy (e.g. requiring a
> >>> capability) you are doing things that some of us would argue
> >>> ought to require a capability. SELinux integrates privilege into
> >>> the policy mechanism.
> >>
> >> How does "moving data around a system" require root privileges?
> >
> > Hmm. You snuck the adjective "root" into the discussion when it
> > wasn't there before.
>
> Ok, fine, how does "moving data around a system" require anything
> more than bog-standard user-level privileges? Normal users under
> Linux are not permitted to change "security.*" attributes at all, but
> they may bind to sockets, etc. You can define a "capability" which
> lets you do all that, but then you're just blindly giving processes
> more privileges than they need to complete an operation. Why should
> you get the "rename all files" priv when all you need is "Make my log
> files get $LABEL when I put them in /var/log/mydaemon/"?

Privilege granularity is a seperate issue. Smack uses Linux
privilege machanisms and is happy to do so.

> >> Why should it? Admittedly, moving data from "TopSecret" to
> >> "Secret" requires a process labeled with a range including at
> >> least "S-TS" *and* with the MLS "read-to-clearance" attribute,
> >> but you can be as stingy with that attribute as you like.
> >
> > Yup. And plenty of people like you think that is really spiffy.
>
> Please explain why you think it isn't?

I grew up believing that downgrading classified information
should always be an explicit, concious act.

> >> That attribute is explicitly given its magic abilities in the
> >> mlsconstrain rules defined in the policy file itself, so it's also
> >> possible to do information flow analysis on it.
> >
> > I much prefer strait-up process capabilities to magic attributes.
> > Call me old fashioned if you want to.
>
> You can call them "process capabilities" or "magic attributes",
> they're the same thing, just more fine-grained. Instead of (Note: r1
> == subject role, r2 == target role, t2 == target type):
> mlsconstrain process transition ( (r1 == r2) || (t1 ==
> i_am_a_role_changing_capability) )
>
> you have (Note: t1 == subject type):
> mlsconstrain process transition ( (r1 == r2) || (r1 == system_r &&
> t1 == i_am_a_login_process && t2 == i_am_a_user_entrypoint) )
>
> The latter is like the former, except instead of one single ultimate
> "change-any-role" capability, you have a capability which allows a
> system login process to change roles *ONLY* if it's also changing to
> a user entrypoint. Please tell me which you think is more secure.

Don't look at me. A well written program could do fine with either.

> >> Under SELinux the labeling is implicit *AND* the transitions are
> >> entirely within the policy. This means that you can do
> >> information flow analysis (as I described above) and it *ALSO*
> >> means that the program cannot possibly "abuse" its privileges
> >> because it has none.
> >
> > It means you have not only the program to worry about being written
> > correctly, but the policy as well.
>
> If your security policy sucks then you might as well not have MAC at
> all. At least under SELinux the absolute worst possible case is that
> you always fall back to DAC-based security.

Ok.

> >> So the implicit labeling of files actually restricts every process
> >> *FURTHER*. They no longer have even an inkling of a choice about
> >> file labeling, and that's a GOOD thing.
> >
> > That's true about Smack, you know. The process has no choice about
> > the label that the files get.
>
> So how do you selectively label files based on criteria such as
> "where did I get put"? How do you distinguish between a "log file"
> which is create-and-append-only, a "database file" which I can modify
> however I want to, and a "unix socket" which I can bind in /var/run
> and get connections from user processes?

I don't. They're all objects.

> >> The only remaining extension we need to really thoroughly lock
> >> down our systems is to allow last-path-component matching in type-
> >> transitions and allow rules (EG: When "vipw" creates a file named
> >> "passwd" in an "etc_t" directory, it is automatically labeled
> >> "etc_passwd_t")
> >
> > Hey, and AppArmor does that today!
>
> No, AppArmor doesn't. It says: "/usr/sbin/httpd may not access /etc/
> shadow", for example, whereas SELinux with the proposed extension
> says: "Only the 'passwd' program or 'vipw' by a sysadmin may modify
> *ANY* password shadow file, and if I create a "shadow" file in *ANY*
> etc_t directory then it's automatically a shadow file. The
> difference is also in the treatment of hard-links. Under SELinux I
> can guarantee that the data in the shadow_t file is secure, no matter
> where somebody manages to get it linked to, and in addition I can
> guarantee that nobody can spoof me and make me create the shadow file
> elsewhere or with the wrong label.

If you say so.

> >> From what I understand Stephen Smalley and others are thinking
> >> that over even now. I'll do it myself as soon as I get time at
> >> work beyond prepping systems for shipping to clients if they
> >> haven't finished it by them.
> >>
> >
> > You might want to review the archives on how that experiment ended
> > up before saying much more about it.
>
> Hmm, I haven't seen anything pass LKML and digging in the
> [email protected] archives (as well as google) doesn't seem to be
> turning up anything even vaguely related. Do you happen to have the
> link handy?

Nope.

> > Thank you for your comments. I hope that I have made the
> > behavior and value of Smack clearer in responding to them.
>
> You're welcome. I endeavor to poke my nose into every little LSM-
> related corner in the kernel, this one included.

Why?


Casey Schaufler
[email protected]

2007-08-12 19:50:54

by Casey Schaufler

[permalink] [raw]
Subject: Re: [PATCH] Smack: Simplified Mandatory Access Control Kernel


--- Jan Engelhardt <[email protected]> wrote:


> >> >+static int smack_task_movememory(struct task_struct *p)
> >> >+{
> >> >+ int rc;
> >> >+
> >> >+ rc = smk_curacc(smk_of_task(p), MAY_WRITE);
> >> >+ return rc;
> >> >+}
> >>
> >> Uh...
> >>
> >> {
> >> return smk_curacc(smk_of_task(p), MAY_WRITE);
> >> }
> >>
> >> (also others)
> >
> >That was a little excessive, wasn't it?
>
> How do you mean? I was just suggesting to collapse the three [four] lines
> into
> one, getting rid of the tempotemporaries in a lot of these functions,
> for example,

Sorry, I meant that my code was excessive, not your comment.
Your comment was fine and most welcome. I'm sorry that I was
unclear and sounded defensive.


Casey Schaufler
[email protected]

2007-08-12 21:36:18

by Andi Kleen

[permalink] [raw]
Subject: Re: [PATCH] Smack: Simplified Mandatory Access Control Kernel

On Sun, Aug 12, 2007 at 10:48:05AM -0700, Casey Schaufler wrote:
>
> --- Andi Kleen <[email protected]> wrote:
>
> > > Entries are never deleted, although they can be modified.
> >
> > The modification case still seems racy then.
>
> Fair enough. I'll look into real list management.

You don't necessarily need more list management if you don't
plan to remove entries, but just replace them.

e.g. what could work to atomically replace is:

- Make the buffer a pointer to an allocated buffer that also
contains a struct rcu_head.
- Reader: Does rcu_read_lock() around list walk (that just disables
preemption on preemptible kernels and is otherwise a nop).
Also uses rcu_reference for reading the pointer.
- Writer: Continues using the mutex to protect against other writers.
When changing an entry allocate a new buffer + rcu_head. Initialize
buffer. Replace pointer. Free old buffer using call_rcu()

The RCU would just make sure the buffer is not freed while other
CPUs are still accessing it. It also means they can use stale
rules for a time, but it is a strictly bounded time
(bounded to max time walking the list plus max time any interrupt
handlers inbetween run [admittedly that can be very long in theory,
but it's all logically only a single rule check])

-Andi

2007-08-12 21:46:19

by Casey Schaufler

[permalink] [raw]
Subject: Re: [PATCH] Smack: Simplified Mandatory Access Control Kernel


--- Andi Kleen <[email protected]> wrote:

> On Sun, Aug 12, 2007 at 10:48:05AM -0700, Casey Schaufler wrote:
> >
> > --- Andi Kleen <[email protected]> wrote:
> >
> > > > Entries are never deleted, although they can be modified.
> > >
> > > The modification case still seems racy then.
> >
> > Fair enough. I'll look into real list management.
>
> You don't necessarily need more list management if you don't
> plan to remove entries, but just replace them.
>
> e.g. what could work to atomically replace is:
>
> - Make the buffer a pointer to an allocated buffer that also
> contains a struct rcu_head.
> - Reader: Does rcu_read_lock() around list walk (that just disables
> preemption on preemptible kernels and is otherwise a nop).
> Also uses rcu_reference for reading the pointer.
> - Writer: Continues using the mutex to protect against other writers.
> When changing an entry allocate a new buffer + rcu_head. Initialize
> buffer. Replace pointer. Free old buffer using call_rcu()
>
> The RCU would just make sure the buffer is not freed while other
> CPUs are still accessing it. It also means they can use stale
> rules for a time, but it is a strictly bounded time
> (bounded to max time walking the list plus max time any interrupt
> handlers inbetween run [admittedly that can be very long in theory,
> but it's all logically only a single rule check])

Thank you. You have no idea how helpful that little suggestion was.


Casey Schaufler
[email protected]

2007-08-12 23:15:31

by Crispin Cowan

[permalink] [raw]
Subject: Re: [PATCH] Smack: Simplified Mandatory Access Control Kernel

Casey Schaufler wrote:
> I respect the design decisions that SELinux has made regarding
> granularity without agreeing with them myself.
>
It isn't even an exclusive decision: both design points can be "right",
but aimed at different use cases. Which is why LSM exists, so users can
decide on an appropriate mechanism.

Crispin

--
Crispin Cowan, Ph.D. http://crispincowan.com/~crispin/
Director of Software Engineering http://novell.com
AppArmor Chat: irc.oftc.net/#apparmor

2007-08-13 01:38:42

by Kyle Moffett

[permalink] [raw]
Subject: Re: [PATCH] Smack: Simplified Mandatory Access Control Kernel

On Aug 12, 2007, at 15:41:46, Casey Schaufler wrote:
> --- Kyle Moffett <[email protected]> wrote:
>> On Aug 11, 2007, at 21:21:55, Casey Schaufler wrote:
>>> --- Kyle Moffett <[email protected]> wrote:
>>>> I was considering compiling the complete list, but such an
>>>> exercise would take me at least an hour to do properly and which
>>>> categories individual permissions should be placed in could be
>>>> argued for weeks.
>>>
>>> I will be happy to consider your arguement when you are willing
>>> to present the complete argument.
>>
>> Here's my complete argument:
>>
>> Regardless of how you categorize "read", "write", "execute", and
>> "doesnt-need-protection" in your policy language, I can write an
>> SELinux policy and a list of labels which expresses that policy.
>> Moreover, without too much work I can probably write a Perl script
>> to do it for you. On the other hand I can only do that if you
>> tell me exactly how you want to categorize those things, though.
>> In my personal opinion they cannot be reasonably categorized
>> without breaking all sorts of tools or leaving gaping holes;
>> precisely the reason that SELinux uses such a fine-grained list.
>
> This identifies an important design philosophy issue, that being
> what granularity is appropriate. I have no interest in composing a
> description of Smack at the granularity that SELinux uses.

If you have no interest in categorizing the SELinux access vectors,
then how do you expect to categorize the LSM hooks, which are almost
1-to-1 mapped with the SELinux access vectors?


> The point you make, that you need to have that in order to create a
> policy description, is one of the reasons for Smack. Simplified.

Well yes, but a simplified policy is useless if it uses no LSM
hooks. I will write you a Perl script which will generate a complete
and functionally equivalent SELinux policy (assuming I have enough
free time) given a file with your policy language. But I can do this
if and only if you tell me which of the SELinux access vectors you
care about (In other words, which of the LSM hooks you want to
require "read", "write", or "execute" privileges for). With such a
little script you could write all the "simplified" policy you want,
without having to change the kernel code at all.


>> Ok, you want sample policy to match your "MLS" sample? For
>> convenience here's one more macro:
>> define(`rx',`r(`$1',`$2') x(`$1',`$2')')
>>
>> type unclass;
>> type c;
>> type s;
>> type ts;
>> rx(c, unclass)
>> rx(s, c)
>> rx(s, unclass)
>> rx(ts, s)
>> rx(ts, c)
>> rx(ts, unclass)
>>
>> In case you don't have the policy you typed into your email, it
>> looks almost identical with a few exceptions for slightly modified
>> syntax:
>>> C Unclass rx
>>> S C rx
>>> S Unclass rx
>>> TS S rx
>>> TS C rx
>>> TS Unclass rx
>
> Yup. Your macro invocations look very much like the Smack
> specification. Your macro definitions are of course unnecessary in
> Smack, and you really ought to be using the MLS labels SELinux
> supports, and you need a base policy underneath this.

My point is your policy format is SO simple it doesn't even need the
SELinux MLS code to handle it. From the way you've described it the
base policy (~200 lines) would be *identical* regardless of the
entries in your policy language, and the rest could be generated by a
script directly from the "C Unclass rx"-type stuff.

>> Whoops, I think I must have smashed the delete key or something
>> while sending. Here's the paragraphs which got elided:
>>
>> Well, yes, but a policy which completely ignores future
>> expandability can't be expanded upon regardless. It would also be
>> very hard to add new policy without a lot of duplication under
>> your system. On the other hand, with SELinux you can very easily
>> add attribute-based policy so adding new capabilities is as simple
>> as sticking existing attributes on newly defined types. For example:
>>
>> type my_log_t, file_type, log_file;
>> type my_log_daemon, daemon;
>>
>> Right there I just gave permission for the logrotate to recycle
>> files labelled my_log_t, which the sysadmin and audit admin can
>> also read (and the audit admin can delete). I also gave
>> permission for my daemon to send SIGCHLD to init, and for init/
>> initscripts to send it a SIGTERM/SIGQUIT. All without writing a
>> SINGLE policy rule. Basically all of those existing behaviors are
>> found in allow rules built on the "file_type", "log_file", and
>> "daemon" attributes.
>
> Ah, now you're refering to the reference policy, right?

Yes, precisely. For most of that functionality there are existing
attributes and types defined in the reference policy to make custom
policy much easier. Furthermore, there are interface files which
allow me to say something like "Let this program spawn an Apache
daemon in the right domain" with a single line. If I only want to do
that when the "httpd" module is loaded I can put the line in an
"optional" block. A policy for a basic network daemon with a couple
log files, a config file, and a little database is all of 30 lines,
maybe 50 if you throw in comments.


>>> They can be added or changed one by one as required while the
>>> system is running, and there are uses that exploit that. One
>>> example is to put the label "Game" on certain programs and:
>>>
>>> at 8:00am "Worker Game no"
>>> at 5:00pm "Worker Game x"
>>>
>>> Thus Worker processes can access Game files only during off hours.
>>
>> This is fundamentally broken:
>> [...]
>> Secondly, you can already do the same thing with DAC and a PAM
>> groups-from-time-of-day module, I don't see why such a thing is
>> special enough to need MAC. Thirdly, I could do exactly the same
>> thing with an SELinux boolean and a cronjob (once we get proper
>> revoke support):
>
> There is usually a way to address any particular problem using DAC,
> it's often sufficiently painful that MAC looks like a better approach.

No, generally the only reason to use MAC is when it's security-
critical (system compromise, classified data, critical
infrastructure, etc). Denying users access to games during the
workday is hardly "security-critical". If that system's CPU time was
exclusively needed for a life support machine during the day then
maybe, but that's what renice or realtime scheduling are for and why
the hell are you installing games on a heart monitor?


> Your boolean solution requires more forthought than the Smack rule
> solution, but I'll give it to you once you've fleshed out your "##"
> lines.

How does it require more forethought? When I want to turn it on, I
write and load the 5 line policy then add the cronjobs. Yours
involves giving cron unconditional permission to write to your
security database (always a bad idea) and then adding similar cronjobs.


>> ## Rule to allow cron to tweak the "can_play_games" boolean value

Hmm, looking at this again the current policy language doesn't have a
way of delegating access to boolean values, so there's no way to let
cron directly poke at booleans right now. You could do it with this
shell script:

#! /bin/sh
now=$(date +'%H%M')
if [ 800 -le "$now" -a 1700 -ge "$now" ]; then
setsebool can_play_games false
else
setsebool can_play_games true
fi


Label that script as games_changer_exec_t then use this bit of policy
type_transition cron_t games_changer_exec_t:process games_changer_t;
can_setbool(games_changer_t);


>> if (can_play_games) {
>> ## Insert game-playing allow rules here
>> }

Once you have written a policy specific to the game you want to allow
or disallow, you would just put that policy inside of the
conditional. As a simple policy example for the ping program (from
an older refpolicy, now a bit out of date, but still useful as an
example):

## The ping domain
type ping_t, domain, privlog;

## Daemons, sysadmins, and users may assume the ping type if allowed
below
role system_r types ping_t;
role sysadm_r types ping_t;
in_user_role(ping_t)

## The ping binary
type ping_exec_t, file_type, sysadmfile, exec_type;

## Allow the sysadmin and init scripts to run ping
domain_auto_trans(sysadm_t, ping_exec_t, ping_t)
domain_auto_trans(initrc_t, ping_exec_t, ping_t)

## Boolean controlling user access to ping
bool user_ping false;
if (user_ping) {
domain_auto_trans(unpriv_userdomain, ping_exec_t, ping_t)
allow ping_t { ttyfile ptyfile }:chr_file rw_file_perms;
}

## Allow ping to do what it needs to do
uses_shlib(ping_t)
can_network(ping_t)
can_ypbind(ping_t)

## Allow additional permissions that ping needs to operate
allow ping_t etc_t:file { getattr read };
allow ping_t self:unix_stream_socket create_socket_perms;
allow ping_t self:rawip_socket { create ioctl read write bind getopt
setopt };
allow ping_t netif_type:netif { rawip_send rawip_recv };
allow ping_t node_type:node { rawip_send rawip_recv };
allow ping_t self:capability { net_raw setuid };

## Allow ping to poke at the terminal
allow ping_t admin_tty_type:chr_file rw_file_perms;
allow ping_t { userdomain privfd kernel_t }:fd use;

## Ping wants to get FS attributes and look in /var but doesn't need to
dontaudit ping_t fs_t:filesystem getattr;
dontaudit ping_t var_t:dir search;


It should be pretty clear what every part of the policy is there for;
basically that's the minimum necessary set of permissions for ping to
run, with an optional boolean to control user pings. There's also a
macro to give your arbitrary process the ability to run ping, such as
for the "heartbeat" daemon for example.


>>> As far as relabeling the file system goes, the rules apply to
>>> processes and objects, not to programs. The label on a program
>>> describes who can access it, it will run with the label of the
>>> process that exec's it. A change in the rules does not require a
>>> change to the labels on the file system for things to work
>>> according to the rules of engagement.
>>
>> By this logic, every program will always have the same label,
>> unless you recode every program to explicitly change it when it
>> starts; precisely the thing that the SELinux type transitions were
>> designed to avoid.
>
> Yes. This is the way it should be. A small set of very carefully
> analysed programs that change labels under carefully controlled
> circumstances is what I want. login, sshd, cron, special purpose
> launchers. Written with the assumption that they will be attacked.

Well, under SELinux, all 3 of those processes go through the special
purpose PAM module to get their labels changed. Are you planning to
modify *every* daemon to have special type-changing code? Hell, most
don't even have setuid/chroot support and that's all of 15 lines of
code and is supported in every UNIX/Linux distro released in the last
10 years (or more). Besides, why allow the program (say, "ping") to
change its own label when the policy could forcibly change it for
you. The label change is NOT just to *give* permissions, it's also
to take them away. For example, when I run "ping", the process gains
raw network access but loses access to almost every user file and
disables LD_PRELOAD, etc, making it a thousand times harder to
compromise it from the inside too.

>>>> When you change the meaning of new types, you clearly would need
>>>> to first reload the policy then relaunch programs or relabel
>>>> files to take advantage of the new types, otherwise they would
>>>> be categorically ignored by the system.
>>>
>>> I'm sorry, but again, I don't know what you mean by "changing the
>>> meaning of new types".
>>
>> Oh, say something like "Oh, drat, I wanted this daemon to run as
>> some other type".
>
> Sorry, "type" is a meaningless attribute in Smack.

Sorry for being unclear. Assume s/type/label/g; the question still
stands.


>> Unless you relabel the files and relaunch the daemon the system
>> will have no idea how the system will change. And if you let
>> arbitrary root processes relabel things on the fly then you've
>> lost all the security advantages to a MAC system.
>
> MAC systems have been behaving this way for decades. SELinux is the
> exception, not the rule on this.

"People have been burning witches for decades, that must be the right
thing to do"

The fact that something is commonly done does not make it right,
especially when there is a significantly more secure alternative
available.


>> I think you may be making assumptions about the behaviour of
>> SELinux based on what other OSes do which don't hold:
>>
>> (a) SELinux does not care about user id, current directory, time
>> of day, etc. It cares solely about 3 things: the Subject, the
>> Object, and the Action. For example, if an init script attempts
>> to execute the apache binary then SELinux gets the following data
>> for parameters: subject="system_u:system_r:initrc_t:SystemLow-
>> SystemHigh" object="system_u:object_r:httpd_exec_t:SystemLow"
>> action="file->execute".
>
> Yes, and each of the components of the subject and object contexts
> is dependent on a more circumstances than I'd care to deal with.
> You've demonstrated that SELinux implements fine granularity.

The beauty of fine granularity is that you can ignore it without
changing code. If you don't want to use the "user" and "role"
portions, then just set them to always be "allusers" and "allroles"
and be done with it. If you don't want MLS then just define one
sensitivity (s0) and no categories. Likewise if want your system to
use only 8 types then only define 8 types. It's just that easy!
Admittedly your policy can't then deal with most of the real-world
situations (like logging in or running the "passwd" command), but
that's your choice.


>> (b) SELinux does NOT have a capability which overrides it. If
>> you want to do something that the policy categorically does not
>> allow, you have 2 choices: change the policy or disable SELinux.
>> You can also use a policy which disables both of those options
>
> Yup. I think that's bad.

Which part do you think is bad? The fact that the policy is
guaranteed to be non-overrideable, or the fact that you can even
prevent changing the security policy from the policy? If you really
want a root-hole in the policy then you can write one, see the
"unconfined_t" in the "targeted" policy.


> And Bell & LaPadula using a combination of levels and categories is
> probably the Smack worst case. But guess what? The dig against
> that policy has always been that no one wanted to combine levels
> and categories.

So how would you secure a system moving data between 3 TS/SCI
compartments and a Secret network? You can say "no one wanted
to...", but we have a couple million dollars worth of business that
says they do.


>> Ok, fine, how does "moving data around a system" require anything
>> more than bog-standard user-level privileges? Normal users under
>> Linux are not permitted to change "security.*" attributes at all,
>> but they may bind to sockets, etc. You can define a "capability"
>> which lets you do all that, but then you're just blindly giving
>> processes more privileges than they need to complete an
>> operation. Why should you get the "rename all files" priv when
>> all you need is "Make my log files get $LABEL when I put them in /
>> var/log/mydaemon/"?
>
> Privilege granularity is a seperate issue. Smack uses Linux
> privilege machanisms and is happy to do so.

"Linux privilege mechanisms" covers no less than 20,000 lines of
code, including DAC, capabilities, LSM, namespaces, keyrings, and
more. Please be more specific here.

>>>> Why should it? Admittedly, moving data from "TopSecret" to
>>>> "Secret" requires a process labeled with a range including at
>>>> least "S-TS" *and* with the MLS "read-to-clearance" attribute,
>>>> but you can be as stingy with that attribute as you like.
>>>
>>> Yup. And plenty of people like you think that is really spiffy.
>>
>> Please explain why you think it isn't?
>
> I grew up believing that downgrading classified information should
> always be an explicit, concious act.

It still is in SELinux too. The only difference is that in SELinux
*all* of the possible ways to downgrade classified information are
listed for you in the policy file for easy analysis; one of the most
common complaints about SUID binaries in UNIX is that they aren't all
listed in one place for easy auditing. Moreover, you can see exactly
how the information may be downgraded. For example, in one policy I
use, I can prove that specific components of our guard software have
permission to move data from a category such as "Secret:NukeTesting"
to just "Secret", but cannot access "TopSecret". I can also verify
that the program itself cannot modify the levels it has access to,
even though it does have permissions to move data between them. It
doesn't make you immune to flaws, but it lets you vastly limit the
damage any given flaw can accomplish.


>> You can call them "process capabilities" or "magic attributes",
>> they're the same thing, just more fine-grained. Instead of (Note:
>> r1 == subject role, r2 == target role, t2 == target type):
>> mlsconstrain process transition ( (r1 == r2) || (t1 ==
>> i_am_a_role_changing_capability) )
>>
>> you have (Note: t1 == subject type):
>> mlsconstrain process transition ( (r1 == r2) || (r1 == system_r
>> && t1 == i_am_a_login_process && t2 == i_am_a_user_entrypoint) )
>>
>> The latter is like the former, except instead of one single
>> ultimate "change-any-role" capability, you have a capability which
>> allows a system login process to change roles *ONLY* if it's also
>> changing to a user entrypoint. Please tell me which you think is
>> more secure.
>
> Don't look at me. A well written program could do fine with either.

The WHOLE POINT of a mandatory access control system is to protect
against *poorly* written programs. If we can verify the kernel (~8
million lines), and the policy (~8,000 lines), then we've saved
ourselves from having to audit all of userspace (~80+ million
lines). Seems a worthy goal to me.


>>>> So the implicit labeling of files actually restricts every
>>>> process *FURTHER*. They no longer have even an inkling of a
>>>> choice about file labeling, and that's a GOOD thing.
>>>
>>> That's true about Smack, you know. The process has no choice
>>> about the label that the files get.
>>
>> So how do you selectively label files based on criteria such as
>> "where did I get put"? How do you distinguish between a "log
>> file" which is create-and-append-only, a "database file" which I
>> can modify however I want to, and a "unix socket" which I can bind
>> in /var/run and get connections from user processes?
>
> I don't. They're all objects.

And by extension you have no way of saying "The apache log files
can't be modified after they are written." That feature is part of
virtually every MAC system anybody's ever made, precisely because you
can't really trust audit logs without it.

>>> Thank you for your comments. I hope that I have made the
>>> behavior and value of Smack clearer in responding to them.
>>
>> You're welcome. I endeavor to poke my nose into every little LSM-
>> related corner in the kernel, this one included.
>
> Why?

Firstly because it's interesting, and secondly because it's related
to my work (which of course comes back to point 1) :-D.

Cheers,
Kyle Moffett

2007-08-13 02:45:37

by Kyle Moffett

[permalink] [raw]
Subject: Re: [PATCH] Smack: Simplified Mandatory Access Control Kernel

On Aug 12, 2007, at 22:36:15, Joshua Brindle wrote:
> Kyle Moffett wrote:
>> On Aug 12, 2007, at 15:41:46, Casey Schaufler wrote:
>>> Your boolean solution requires more forthought than the Smack
>>> rule solution, but I'll give it to you once you've fleshed out
>>> your "##" lines.
>>
>> How does it require more forethought? When I want to turn it on,
>> I write and load the 5 line policy then add the cronjobs. Yours
>> involves giving cron unconditional permission to write to your
>> security database (always a bad idea) and then adding similar
>> cronjobs.
>
> nit: without the selinux policy server (which is not production
> ready by any means) we have to grant the same to cron in this case
> (or at least to the domain that cron runs the cronjobs in). SELinux
> and Smack alike need special permissions to modify the running
> policy, no surprises there.

Yeah, I figured this out a couple minutes ago. Turns out you can get
a similar effect with a little properly labeled shell script though
(text included in my last email), but it does decrease overall system
security.

Cheers,
Kyle Moffett

2007-08-13 02:51:18

by Joshua Brindle

[permalink] [raw]
Subject: Re: [PATCH] Smack: Simplified Mandatory Access Control Kernel

Kyle Moffett wrote:
> On Aug 12, 2007, at 15:41:46, Casey Schaufler wrote:
>
>> Your boolean solution requires more forthought than the Smack rule
>> solution, but I'll give it to you once you've fleshed out your "##"
>> lines.
>
> How does it require more forethought? When I want to turn it on, I
> write and load the 5 line policy then add the cronjobs. Yours
> involves giving cron unconditional permission to write to your
> security database (always a bad idea) and then adding similar cronjobs.
>

nit: without the selinux policy server (which is not production ready by
any means) we have to grant the same to cron in this case (or at least
to the domain that cron runs the cronjobs in). SELinux and Smack alike
need special permissions to modify the running policy, no surprises there.

2007-08-13 04:23:31

by Casey Schaufler

[permalink] [raw]
Subject: Re: [PATCH] Smack: Simplified Mandatory Access Control Kernel


--- Kyle Moffett <[email protected]> wrote:

> <really big snip>
>
> If you have no interest in categorizing the SELinux access vectors,
> then how do you expect to categorize the LSM hooks, which are almost
> 1-to-1 mapped with the SELinux access vectors?

Those that refer to object accesses and those that do not. The former
are generally interesting, the latter are generally not. That's a
rule of thumb, mind you. Anyway, have a look at the hook table, it's
in smack_lsm.c

> > The point you make, that you need to have that in order to create a
> > policy description, is one of the reasons for Smack. Simplified.
>
> Well yes, but a simplified policy is useless if it uses no LSM
> hooks.

As above, that's smack_lsm.c, a little over 2000 lines of good old
fashioned C code.

> I will write you a Perl script which will generate a complete
> and functionally equivalent SELinux policy (assuming I have enough
> free time) given a file with your policy language. But I can do this
> if and only if you tell me which of the SELinux access vectors you
> care about (In other words, which of the LSM hooks you want to
> require "read", "write", or "execute" privileges for). With such a
> little script you could write all the "simplified" policy you want,
> without having to change the kernel code at all.

It's all spelled out in the module. Go wild.

> <another major snip>
>
> My point is your policy format is SO simple it doesn't even need the
> SELinux MLS code to handle it. From the way you've described it the
> base policy (~200 lines) would be *identical* regardless of the
> entries in your policy language, and the rest could be generated by a
> script directly from the "C Unclass rx"-type stuff.

Ok.

> >> Whoops, I think I must have smashed the delete key or something
> >> while sending. Here's the paragraphs which got elided:
> >>
> >> Well, yes, but a policy which completely ignores future
> >> expandability can't be expanded upon regardless. It would also be
> >> very hard to add new policy without a lot of duplication under
> >> your system. On the other hand, with SELinux you can very easily
> >> add attribute-based policy so adding new capabilities is as simple
> >> as sticking existing attributes on newly defined types. For example:
> >>
> >> type my_log_t, file_type, log_file;
> >> type my_log_daemon, daemon;
> >>
> >> Right there I just gave permission for the logrotate to recycle
> >> files labelled my_log_t, which the sysadmin and audit admin can
> >> also read (and the audit admin can delete). I also gave
> >> permission for my daemon to send SIGCHLD to init, and for init/
> >> initscripts to send it a SIGTERM/SIGQUIT. All without writing a
> >> SINGLE policy rule. Basically all of those existing behaviors are
> >> found in allow rules built on the "file_type", "log_file", and
> >> "daemon" attributes.
> >
> > Ah, now you're refering to the reference policy, right?
>
> Yes, precisely. For most of that functionality there are existing
> attributes and types defined in the reference policy to make custom
> policy much easier. Furthermore, there are interface files which
> allow me to say something like "Let this program spawn an Apache
> daemon in the right domain" with a single line. If I only want to do
> that when the "httpd" module is loaded I can put the line in an
> "optional" block. A policy for a basic network daemon with a couple
> log files, a config file, and a little database is all of 30 lines,
> maybe 50 if you throw in comments.

After you have the 400,000 lines of reference policy behind it.

> >>> They can be added or changed one by one as required while the
> >>> system is running, and there are uses that exploit that. One
> >>> example is to put the label "Game" on certain programs and:
> >>>
> >>> at 8:00am "Worker Game no"
> >>> at 5:00pm "Worker Game x"
> >>>
> >>> Thus Worker processes can access Game files only during off hours.
> >>
> >> This is fundamentally broken:
> >> [...]
> >> Secondly, you can already do the same thing with DAC and a PAM
> >> groups-from-time-of-day module, I don't see why such a thing is
> >> special enough to need MAC. Thirdly, I could do exactly the same
> >> thing with an SELinux boolean and a cronjob (once we get proper
> >> revoke support):
> >
> > There is usually a way to address any particular problem using DAC,
> > it's often sufficiently painful that MAC looks like a better approach.
>
> No, generally the only reason to use MAC is when it's security-
> critical (system compromise, classified data, critical
> infrastructure, etc). Denying users access to games during the
> workday is hardly "security-critical". If that system's CPU time was
> exclusively needed for a life support machine during the day then
> maybe, but that's what renice or realtime scheduling are for and why
> the hell are you installing games on a heart monitor?

HeeHee. Don't think heart monitor, think in-car trip recorder.
The games can only be used when the car's at rest. It's a strange
world we live in today.

> <Another big snip>

> > Yes. This is the way it should be. A small set of very carefully
> > analysed programs that change labels under carefully controlled
> > circumstances is what I want. login, sshd, cron, special purpose
> > launchers. Written with the assumption that they will be attacked.
>
> Well, under SELinux, all 3 of those processes go through the special
> purpose PAM module to get their labels changed. Are you planning to
> modify *every* daemon to have special type-changing code? Hell, most
> don't even have setuid/chroot support and that's all of 15 lines of
> code and is supported in every UNIX/Linux distro released in the last
> 10 years (or more). Besides, why allow the program (say, "ping") to
> change its own label when the policy could forcibly change it for
> you. The label change is NOT just to *give* permissions, it's also
> to take them away. For example, when I run "ping", the process gains
> raw network access but loses access to almost every user file and
> disables LD_PRELOAD, etc, making it a thousand times harder to
> compromise it from the inside too.

I don't think we're debating the same issues any more. I can see
that you are well versed in the nuances of SELinux behavior. I
understand what SELinux does, and the arguements in it's favor.
I don't see that the complexity it requires is necessary in many
cases.


> >> Unless you relabel the files and relaunch the daemon the system
> >> will have no idea how the system will change. And if you let
> >> arbitrary root processes relabel things on the fly then you've
> >> lost all the security advantages to a MAC system.
> >
> > MAC systems have been behaving this way for decades. SELinux is the
> > exception, not the rule on this.
>
> "People have been burning witches for decades, that must be the right
> thing to do"
>
> The fact that something is commonly done does not make it right,
> especially when there is a significantly more secure alternative
> available.

That sword cuts both ways, you know. And if a complex system
is intractable for an environment it cannot make you feel
more secure.

I think that below here it's all philosophical differences based
on significantly different experiance and goals. Can you run
SELinux successfully on an 2 megahertz ARM processor with 8 meg
of ram, and no labeled file systems? I don't know that Smack
will ever be as appropriate for an enterprise server as SELinux is
today, but time will tell.




Casey Schaufler
[email protected]

2007-08-16 21:00:50

by Pavel Machek

[permalink] [raw]
Subject: Re: [PATCH] Smack: Simplified Mandatory Access Control Kernel

Hi!

> > I will write you a Perl script which will generate a complete
> > and functionally equivalent SELinux policy (assuming I have enough
> > free time) given a file with your policy language. But I can do this
> > if and only if you tell me which of the SELinux access vectors you
> > care about (In other words, which of the LSM hooks you want to
> > require "read", "write", or "execute" privileges for). With such a
> > little script you could write all the "simplified" policy you want,
> > without having to change the kernel code at all.
>
> It's all spelled out in the module. Go wild.

Kyle claims he can emulate SMACK with SELinux + perl script, but I
don't think it is fair to force him to write that perl. You want the
code merged, so it should be up to you to do the work... or
acknowledge that selinux ineed is smack supperset and argue that SMACK
is better, anyway, because of its simplicity / speed / something.

Or maybe that perl script is impossible to write for some reason? Tell
us if so...
Pavel
(who is no security expert)

--
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

2007-08-17 04:56:57

by Casey Schaufler

[permalink] [raw]
Subject: Re: [PATCH] Smack: Simplified Mandatory Access Control Kernel


--- Pavel Machek <[email protected]> wrote:

> Hi!
>
> > > I will write you a Perl script which will generate a complete
> > > and functionally equivalent SELinux policy (assuming I have enough
> > > free time) given a file with your policy language. But I can do this
> > > if and only if you tell me which of the SELinux access vectors you
> > > care about (In other words, which of the LSM hooks you want to
> > > require "read", "write", or "execute" privileges for). With such a
> > > little script you could write all the "simplified" policy you want,
> > > without having to change the kernel code at all.
> >
> > It's all spelled out in the module. Go wild.
>
> Kyle claims he can emulate SMACK with SELinux + perl script, but I
> don't think it is fair to force him to write that perl.

It would not surprise me particularly much if Kyle or someone
like him could produce a perl script that could generate an SELinux
policy that, when added to the reference policy on a system
described by the reference policy, could do a fair imitation of
the Smack scheme.

One point that I would like to make clear however is that the
requirement for a 400,000 line reference policy for a jumping
off point is one of the reasons for Smack. Another point that I
think is important, and the rationale behind my being a butt
on this (sorry, Kyle, I knew some one would come after me like
this, I wasn't aiming at you) is that I have seen several cases
where the flexability and capability of SELinux policy has been
asserted but the followthrough was missing. I am interested in
seeing what an SELinux policy to do what Smack does would look
like. I personally though it would be easier to write an LSM
than to write that policy.

> You want the
> code merged, so it should be up to you to do the work... or
> acknowledge that selinux ineed is smack supperset

I acknowledge that I do not know how to prove that there is no
way, using any and all of the facilities of SELinux, to duplicate
any particular facility of Smack.

I do not acknowledge it as a superset. I am not convinced that
all of the proposed SELinux eqivalence claims would actually
result in working systems.

> and argue that SMACK
> is better, anyway, because of its simplicity / speed / something.

My understanding of the current SELinux philosophy is that
policy should only be written by professionals, and that this
was "always" the intention. I respect that, and for policy that
requires the level of sophistication that SELinux does I would
have a hard time arguing otherwise.

One of the things that limited the widespread adoption of MLS
systems was that the policy, even one as simple as Bell & LaPadula,
was considered to complex for most uses. I do not see that SELinux,
or AppArmor for that matter, addresses this fundimental impediment
to the use of mandatory access control. Yes, you can do just about
anything with the right combination of classes, booleans, and
other interesting facilities, but you can't do simple things
directly.

I was actually quite pleased to see the beginings of an SELinux
policy "equivalent" for Smack. I am disappointed that there is
insufficient wind in the sails to follow through with it. I
would like to compare, contrast, and benchmark against it. I just
don't want to write it.

> Or maybe that perl script is impossible to write for some reason? Tell
> us if so...

Goodness Pavel, it's perl! A good sysadmin can do anything in perl!
Seriously, maybe he could do it. For the reasons above, I'd be happy
to see the attempt. I would love to debate the Smack vs. SELinux
question with real data. I am not much concerned with comparisons
based on assertion and speculation. I have gotten some very good,
meaty comparison data for guardbox applications (Thanks Joshua)
and I look forward to more.

> Pavel
> (who is no security expert)

You just don't want the rock star lifestyle.

... And thank you for suggestions.


Casey Schaufler
[email protected]

2007-08-17 09:46:59

by Miguel Ojeda

[permalink] [raw]
Subject: Re: [PATCH] Smack: Simplified Mandatory Access Control Kernel

Btw, at:

diff -uprN -X linux-2.6.22-base/Documentation/dontdiff
linux-2.6.22-base/security/smack/Kconfig
linux-2.6.22/security/smack/Kconfig
--- linux-2.6.22-base/security/smack/Kconfig 1969-12-31
16:00:00.000000000 -0800
+++ linux-2.6.22/security/smack/Kconfig 2007-07-10 01:08:05.000000000 -0700
@@ -0,0 +1,10 @@
+config SECURITY_SMACK
+ bool "Simplified Mandatory Access Control Kernel Support"
+ depends on NETLABEL && SECURITY_NETWORK
+ default n
+ help
+ This selects the Simplified Mandatory Access Control Kernel.
+ SMACK is useful for sensitivity, integrity, and a variety
+ of other madatory security schemes.
+ If you are unsure how to answer this question, answer N.
+

change:

+ of other madatory security schemes.

to:

+ of other mandatory security schemes.

--
Miguel Ojeda
http://maxextreme.googlepages.com/index.htm

2007-08-18 05:30:26

by Kyle Moffett

[permalink] [raw]
Subject: Re: [PATCH] Smack: Simplified Mandatory Access Control Kernel

Finally moved back in and with internet. Yay!

On Aug 17, 2007, at 00:56:44, Casey Schaufler wrote:
> It would not surprise me particularly much if Kyle or someone like
> him could produce a perl script that could generate an SELinux
> policy that, when added to the reference policy on a system
> described by the reference policy, could do a fair imitation of the
> Smack scheme.

Umm, when did I ever say "emulate smack on top of the reference
policy"? I state categorically that I can write an estimated 500
line perl script which will generate a standalone SELinux policy
based directly on a smack ruleset. It would require no additional
policy beyond what the script outputs, and the script would be only
roughly 500 lines so it can't contain all that much direct source-to-
output text.

I've started tinkering with that perl script, though I probably won't
get it finished till tomorrow or sunday.


> One point that I would like to make clear however is that the
> requirement for a 400,000 line reference policy for a jumping off
> point is one of the reasons for Smack.

There is no "requirement" for a 400,000-line reference policy to
reproduce exactly the behavior of SMACK. The SMACK architecture is
trivial and therefore the SELinux policy is also simple.


>> and argue that SMACK is better, anyway, because of its
>> simplicity / speed / something.
>
> My understanding of the current SELinux philosophy is that policy
> should only be written by professionals, and that this was "always"
> the intention. I respect that, and for policy that requires the
> level of sophistication that SELinux does I would have a hard time
> arguing otherwise.

I can also state categorically that given the set of all admins,
users, and software developers, hardly a fraction of them are
qualified to write security policy at all. Hell, most admins and
software developers can't get SUID binaries right, and that's a
thousand times simpler than a MAC security policy. Ergo the only
people who should be writing security policy for deployment are those
people who have studied and trained in the stuff. Those people are
also known as "security professionals".


> One of the things that limited the widespread adoption of MLS
> systems was that the policy, even one as simple as Bell & LaPadula,
> was considered to complex for most uses. I do not see that SELinux,
> or AppArmor for that matter, addresses this fundimental impediment
> to the use of mandatory access control. Yes, you can do just about
> anything with the right combination of classes, booleans, and other
> interesting facilities, but you can't do simple things directly.

Neither security nor your average distro nowadays is "simple" by any
stretch of the imagination. Hell, my desktop system hits at least 2
million unique lines of code during boot, let alone logging in to
XFCE. If you can show me a security system other than SELinux which
is sufficiently flexible to secure those 2 million lines of code
along with the other 50 million lines of code found in various pieces
of software on my Debian box then I'll go put on my dunce hat and sit
in the corner.


Cheers,
Kyle Moffett



2007-08-19 21:12:59

by Valdis Klētnieks

[permalink] [raw]
Subject: Re: [PATCH] Smack: Simplified Mandatory Access Control Kernel

On Sat, 18 Aug 2007 01:29:58 EDT, Kyle Moffett said:

> XFCE. If you can show me a security system other than SELinux which
> is sufficiently flexible to secure those 2 million lines of code
> along with the other 50 million lines of code found in various pieces
> of software on my Debian box then I'll go put on my dunce hat and sit
> in the corner.

/me hands Kyle a dunce cap. :)

Unfortunately, I have to agree that both AppArmor and Smack have at least
the potential of qualifying as "securing the 2M lines of code".

The part that Kyle forgot was what most evals these days call the "protection
profile" - What's the threat model, who are you defending against, and just
how good a job does it have to do? I'll posit that for a computer that
is (a) not networked, (b) doesn't process sensitive information, and (c) has
reasonable physical security, a security policy of "return(permitted);" for
everything may be quite sufficient.

(Of course, I also have boxes where "the SELinux reference policy with all
the MCS extensions plus all the LSPP work" is someplace I'm trying to get to).


Attachments:
(No filename) (226.00 B)

2007-08-20 14:29:48

by Casey Schaufler

[permalink] [raw]
Subject: Re: [PATCH] Smack: Simplified Mandatory Access Control Kernel


--- Kyle Moffett <[email protected]> wrote:

> Finally moved back in and with internet. Yay!
>
> On Aug 17, 2007, at 00:56:44, Casey Schaufler wrote:
> > It would not surprise me particularly much if Kyle or someone like
> > him could produce a perl script that could generate an SELinux
> > policy that, when added to the reference policy on a system
> > described by the reference policy, could do a fair imitation of the
> > Smack scheme.
>
> Umm, when did I ever say "emulate smack on top of the reference
> policy"? I state categorically that I can write an estimated 500
> line perl script which will generate a standalone SELinux policy
> based directly on a smack ruleset.

Even better.

> It would require no additional
> policy beyond what the script outputs, and the script would be only
> roughly 500 lines so it can't contain all that much direct source-to-
> output text.

Better still.

> I've started tinkering with that perl script, though I probably won't
> get it finished till tomorrow or sunday.

Ok. I've been a developement manager, so I'll expect it Thursday.
(That's a Pointy Haired Manager joke, BTW)

> > One point that I would like to make clear however is that the
> > requirement for a 400,000 line reference policy for a jumping off
> > point is one of the reasons for Smack.
>
> There is no "requirement" for a 400,000-line reference policy to
> reproduce exactly the behavior of SMACK. The SMACK architecture is
> trivial

I prefer "simple" over "trivial", but yes, you have the nut of it.

> and therefore the SELinux policy is also simple.

Well, that's what the exercise is out to demonstrate. I admit to
being curious to see the proposed policy.

> >> and argue that SMACK is better, anyway, because of its
> >> simplicity / speed / something.
> >
> > My understanding of the current SELinux philosophy is that policy
> > should only be written by professionals, and that this was "always"
> > the intention. I respect that, and for policy that requires the
> > level of sophistication that SELinux does I would have a hard time
> > arguing otherwise.
>
> I can also state categorically that given the set of all admins,
> users, and software developers, hardly a fraction of them are
> qualified to write security policy at all.

I can understand your postion on the SELinux policy.

> Hell, most admins and
> software developers can't get SUID binaries right,

Sadly true.

> and that's a
> thousand times simpler than a MAC security policy.

Now you see, I don't agree with that. It's simpler than an SELinux
policy. It's not simpler than a Bell & LaPadula (once we were able
to get past the initial resistance to MAC and get someone a three
day training class they were able to do some cool things) and it's
certainly not simpler than Smack.

> Ergo the only
> people who should be writing security policy for deployment are those
> people who have studied and trained in the stuff. Those people are
> also known as "security professionals".

If only security professionals can use the system you have failed
to provide a general purpose facility. It may have value in limited
circumstances but it is not for everybody.

> > One of the things that limited the widespread adoption of MLS
> > systems was that the policy, even one as simple as Bell & LaPadula,
> > was considered to complex for most uses. I do not see that SELinux,
> > or AppArmor for that matter, addresses this fundimental impediment
> > to the use of mandatory access control. Yes, you can do just about
> > anything with the right combination of classes, booleans, and other
> > interesting facilities, but you can't do simple things directly.
>
> Neither security nor your average distro nowadays is "simple" by any
> stretch of the imagination. Hell, my desktop system hits at least 2
> million unique lines of code during boot, let alone logging in to
> XFCE. If you can show me a security system other than SELinux which
> is sufficiently flexible to secure those 2 million lines of code
> along with the other 50 million lines of code found in various pieces
> of software on my Debian box then I'll go put on my dunce hat and sit
> in the corner.

It is my position that the SELinux reference policy fails the
"small enough to analyze" clause of the TCB principle.
As a result SELinux does not help me feel secure. Restating
the complexity of the applications in the policy is arguably an
improvement, but I can't help but think the effort would be better
put into fixing the applications.

So long as your definition of "secure" allows for something other
than "runs SELinux", and so long as you are willing to accept the
conclusions of "security professionals", I can point to several
examples of systems that have been evaluated using other mechanisms.
There aren't other mechanisms upstream in Linux yet.


Casey Schaufler
[email protected]

2007-08-21 09:34:45

by Pavel Machek

[permalink] [raw]
Subject: Re: [PATCH] Smack: Simplified Mandatory Access Control Kernel

Hi!

> > Ergo the only
> > people who should be writing security policy for deployment are those
> > people who have studied and trained in the stuff. Those people are
> > also known as "security professionals".
>
> If only security professionals can use the system you have failed
> to provide a general purpose facility. It may have value in limited
> circumstances but it is not for everybody.

But that's okay. Maybe SElinux is not simple enough to use for
everyone, but that does not mean you can't auto-generate policy from
something else, "easy to understand".

IOW smack may be great idea, but you written it in wrong language. You
written it in C, while you should have written it in SELinux policy
language (and your favourite scripting language as frontend).
Pavel
--
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

2007-08-21 13:16:35

by Kyle Moffett

[permalink] [raw]
Subject: Re: [PATCH] Smack: Simplified Mandatory Access Control Kernel

On Aug 19, 2007, at 17:12:41, [email protected] wrote:
> On Sat, 18 Aug 2007 01:29:58 EDT, Kyle Moffett said:
>> If you can show me a security system other than SELinux which is
>> sufficiently flexible to secure those 2 million lines of code
>> along with the other 50 million lines of code found in various
>> pieces of software on my Debian box then I'll go put on my dunce
>> hat and sit in the corner.
>
> /me hands Kyle a dunce cap. :)
>
> Unfortunately, I have to agree that both AppArmor and Smack have at
> least the potential of qualifying as "securing the 2M lines of code".
>
> The part that Kyle forgot was what most evals these days call the
> "protection profile" - What's the threat model, who are you
> defending against, and just how good a job does it have to do?
> I'll posit that for a computer that is (a) not networked, (b)
> doesn't process sensitive information, and (c) has reasonable
> physical security, a security policy of "return(permitted);" for
> everything may be quite sufficient.

Well, in this case the "box" I want to secure will eventually be
running multi-user X on a multi-level-with-IPsec network. For that
kind of protection profile, there is presently no substitute for
SELinux with some X11 patches. AppArmor certainly doesn't meet the
confidentiality requirements (no data labelling), and SMACK has no
way of doing the very tight per-syscall security requirements we have
to meet. I didn't make this clear initially but that is the kind of
system I'm talking about wanting to secure some 50 million lines of
code on.


> (Of course, I also have boxes where "the SELinux reference policy
> with all the MCS extensions plus all the LSPP work" is someplace
> I'm trying to get to).

Well, for some of the systems we distribute, "all the MCS extensions
plus all the LSPP work" is nowhere near enough security; we need full-
fledged multi-level-security, role-based-access-control, and specific
per-daemon MAC restrictions.

Cheers,
Kyle Moffett

2007-08-21 15:35:52

by Casey Schaufler

[permalink] [raw]
Subject: Re: [PATCH] Smack: Simplified Mandatory Access Control Kernel


--- Pavel Machek <[email protected]> wrote:

> Hi!
>
> > > Ergo the only
> > > people who should be writing security policy for deployment are those
> > > people who have studied and trained in the stuff. Those people are
> > > also known as "security professionals".
> >
> > If only security professionals can use the system you have failed
> > to provide a general purpose facility. It may have value in limited
> > circumstances but it is not for everybody.
>
> But that's okay. Maybe SElinux is not simple enough to use for
> everyone, but that does not mean you can't auto-generate policy from
> something else, "easy to understand". IOW smack may be great idea,

Thank you.

> but you written it in wrong language. You
> written it in C, while you should have written it in SELinux policy
> language (and your favourite scripting language as frontend).

I have often marvelled at the notion of a simplification layer.
I believe that you build complex things on top of simple things,
not the other way around.


Casey Schaufler
[email protected]

2007-08-21 15:51:06

by Casey Schaufler

[permalink] [raw]
Subject: Re: [PATCH] Smack: Simplified Mandatory Access Control Kernel


--- Kyle Moffett <[email protected]> wrote:

> On Aug 19, 2007, at 17:12:41, [email protected] wrote:
> > On Sat, 18 Aug 2007 01:29:58 EDT, Kyle Moffett said:
> >> If you can show me a security system other than SELinux which is
> >> sufficiently flexible to secure those 2 million lines of code
> >> along with the other 50 million lines of code found in various
> >> pieces of software on my Debian box then I'll go put on my dunce
> >> hat and sit in the corner.
> >
> > /me hands Kyle a dunce cap. :)
> >
> > Unfortunately, I have to agree that both AppArmor and Smack have at
> > least the potential of qualifying as "securing the 2M lines of code".
> >
> > The part that Kyle forgot was what most evals these days call the
> > "protection profile" - What's the threat model, who are you
> > defending against, and just how good a job does it have to do?
> > I'll posit that for a computer that is (a) not networked, (b)
> > doesn't process sensitive information, and (c) has reasonable
> > physical security, a security policy of "return(permitted);" for
> > everything may be quite sufficient.
>
> Well, in this case the "box" I want to secure will eventually be
> running multi-user X on a multi-level-with-IPsec network. For that
> kind of protection profile, there is presently no substitute for
> SELinux with some X11 patches. AppArmor certainly doesn't meet the
> confidentiality requirements (no data labelling), and SMACK has no
> way of doing the very tight per-syscall security requirements we have
> to meet.

And what requirements would those be? Seriously, I've done
Common Criteria and TCSEC evaluations on systems with less
flexibility and granularity than Smack that included X, NFSv3,
NIS, clusters, and all sorts of spiffy stuff. I mean, if the
requirement is anything short of "runs SELinux" I have good
reason to believe that a Smack based system is up to it.

> I didn't make this clear initially but that is the kind of
> system I'm talking about wanting to secure some 50 million lines of
> code on.

Cool. SELinux provides one approach to dealing with that, and the
huge multiuser general purpose machine chuck full of legacy software
hits the SELinux sweet spot.

> > (Of course, I also have boxes where "the SELinux reference policy
> > with all the MCS extensions plus all the LSPP work" is someplace
> > I'm trying to get to).
>
> Well, for some of the systems we distribute, "all the MCS extensions
> plus all the LSPP work" is nowhere near enough security; we need full-
> fledged multi-level-security, role-based-access-control, and specific
> per-daemon MAC restrictions.

Sounds like more of what SELinux is good for.


Casey Schaufler
[email protected]

2007-08-22 03:43:31

by Kyle Moffett

[permalink] [raw]
Subject: Re: [PATCH] Smack: Simplified Mandatory Access Control Kernel

On Aug 21, 2007, at 11:50:48, Casey Schaufler wrote:
> --- Kyle Moffett <[email protected]> wrote:
>> Well, in this case the "box" I want to secure will eventually be
>> running multi-user X on a multi-level-with-IPsec network. For
>> that kind of protection profile, there is presently no substitute
>> for SELinux with some X11 patches. AppArmor certainly doesn't
>> meet the confidentiality requirements (no data labelling), and
>> SMACK has no way of doing the very tight per-syscall security
>> requirements we have to meet.
>
> And what requirements would those be? Seriously, I've done Common
> Criteria and TCSEC evaluations on systems with less flexibility and
> granularity than Smack that included X, NFSv3, NIS, clusters, and
> all sorts of spiffy stuff.

These are requirements more of the "give the client warm fuzzies".
On the other hand, when designing a box that could theoretically be
run on a semi-public unclassified network and yet still be safe
enough to run classified data over IPsec links, you want to give the
client all the warm fuzzies they ask for and more.


> I mean, if the requirement is anything short of "runs SELinux" I
> have good reason to believe that a Smack based system is up to it.

"up to it", yes, but I think you'll find that beyond the simplest
policies, an SELinux policy that properly uses the SELinux
infrastructure will be much shorter than the equivalent SMACK policy,
not even including all the things that SELinux does and SMACK doesn't.


>> I didn't make this clear initially but that is the kind of system
>> I'm talking about wanting to secure some 50 million lines of code on.
>
> Cool. SELinux provides one approach to dealing with that, and the
> huge multiuser general purpose machine chuck full of legacy
> software hits the SELinux sweet spot.

Well, given that 99.9% of the systems people are really concerned
about security on are multi-user general-purpose machines chuck full
of legacy software, that seems to work just fine. If it's a single-
user box then you don't even need MAC, just a firewall, a good locked
rack/case/keyboard/etc, and decent physical security. If it's
entirely custom-controlled software then you can just implement the
"MAC" entirely in your own software. "General-purpose" vs "special-
purpose" is debatable, so I'll just leave that one lie.

Replying to another email:
>> but you written it in wrong language. You written it in C, while
>> you should have written it in SELinux policy language (and your
>> favourite scripting language as frontend).
>
> I have often marvelled at the notion of a simplification layer. I
> believe that you build complex things on top of simple things, not
> the other way around.

There is no "one answer" to this question in software development.
Generally you prioritize things based on maximizing maintainability
and speed and minimizing code, bugs, and complexity. Those are often
both conflicting and in agreement. Here are a few common examples of
simple-thing-on-complex-thing:
* pthreads on top of clone()
* open(some_string) on top of all the complex VFS machinery
* "netcat" on top of the vast Linux network stack including
support for arbitrary packet filtering and transformation.

In addition, "simple" is undesirable if it makes the implementation
less generic for no good reason. Would you want to use the "simple"
MS Windows disk-drive model under Linux? Every disk is its own
letter and has its files under it. Oh, you wanted to mount a
filesystem over C:\tmp? Sorry, we don't support that, too bad.
Under Linux we have a very flexible and powerful VFS which lets you
do very crazy things, and then for the user's convenience we have
various "simple" interfaces (like Gnome/KDE/XFCE).

Software development is very much about finding the Right Model(TM)
to underlie the system, and then building any simplifications-to-the-
user on top of the very simple model.

Look at the SELinux model again; it has the following things:
(A) Labels on almost-all user-visible kernel objects
(B) Individual access rules for almost every operation on those
objects
(C) "Transition" rules to set the label on newly created objects.
(D) Fundamental "constraints" which enforce hard limits on what
may be permitted with "allow" rules

From a fundamental standpoint it's harder to get much simpler than
that. On top of that model, we also have a bit of additional
*flexibility* for MLS/RBAC, although that flexibility may be ignored
completely.
(1) You can define "users" which may only assume some "roles"
(2) You can define "roles" may only run in some "types"
(3) There's a simple way of declaring multiple "levels" and
"dominance".

So you see, SELinux is a pretty fundamental description of the
degrees of flexibility needed to secure everything. That kind of
FUNDAMENTAL description is what belongs in the kernel. Anything else
can and should be built on top with better libraries and/or user
interfaces.

As for the script, I'm partway through debugging it but my time is
all chewed up with other stuff now, so it may take me an extra couple
days.

Cheers,
Kyle Moffett



2007-08-22 04:08:24

by Casey Schaufler

[permalink] [raw]
Subject: Re: [PATCH] Smack: Simplified Mandatory Access Control Kernel


--- Kyle Moffett <[email protected]> wrote:

> On Aug 21, 2007, at 11:50:48, Casey Schaufler wrote:
> > --- Kyle Moffett <[email protected]> wrote:
> >> Well, in this case the "box" I want to secure will eventually be
> >> running multi-user X on a multi-level-with-IPsec network. For
> >> that kind of protection profile, there is presently no substitute
> >> for SELinux with some X11 patches. AppArmor certainly doesn't
> >> meet the confidentiality requirements (no data labelling), and
> >> SMACK has no way of doing the very tight per-syscall security
> >> requirements we have to meet.
> >
> > And what requirements would those be? Seriously, I've done Common
> > Criteria and TCSEC evaluations on systems with less flexibility and
> > granularity than Smack that included X, NFSv3, NIS, clusters, and
> > all sorts of spiffy stuff.
>
> These are requirements more of the "give the client warm fuzzies".

OK, that's perfectly reasonable. If the client has been sold
on the concept of SELinux the client will get warm fuzzies
only from SELinux. Security is how you feel about it, after all.

> On the other hand, when designing a box that could theoretically be
> run on a semi-public unclassified network and yet still be safe
> enough to run classified data over IPsec links, you want to give the
> client all the warm fuzzies they ask for and more.

Yes. Of course, a little hard technology behind it doesn't hurt, either.

> > I mean, if the requirement is anything short of "runs SELinux" I
> > have good reason to believe that a Smack based system is up to it.
>
> "up to it", yes, but I think you'll find that beyond the simplest
> policies, an SELinux policy that properly uses the SELinux
> infrastructure will be much shorter than the equivalent SMACK policy,

Well, I find that hard to believe. Maybe I'm only thinking of what
you would consider the simplest policies.

> not even including all the things that SELinux does and SMACK doesn't.

Of course.

> >> I didn't make this clear initially but that is the kind of system
> >> I'm talking about wanting to secure some 50 million lines of code on.
> >
> > Cool. SELinux provides one approach to dealing with that, and the
> > huge multiuser general purpose machine chuck full of legacy
> > software hits the SELinux sweet spot.
>
> Well, given that 99.9% of the systems people are really concerned
> about security on are multi-user general-purpose machines chuck full
> of legacy software, that seems to work just fine.

Err, no. By unit count such systems are extremely rare. There is
tremendous concern for security in your cell phone, your DVR,
your PDA, and even your toaster.

> If it's a single-
> user box then you don't even need MAC, just a firewall, a good locked
> rack/case/keyboard/etc, and decent physical security.

You cell phone has really lousy physical security.

> If it's
> entirely custom-controlled software then you can just implement the
> "MAC" entirely in your own software. "General-purpose" vs "special-
> purpose" is debatable, so I'll just leave that one lie.

Indeed. Total control over the software on your phone is not
a competetive option for a provider.

> Replying to another email:
> >> but you written it in wrong language. You written it in C, while
> >> you should have written it in SELinux policy language (and your
> >> favourite scripting language as frontend).
> >
> > I have often marvelled at the notion of a simplification layer. I
> > believe that you build complex things on top of simple things, not
> > the other way around.
>
> There is no "one answer" to this question in software development.

You're correct. Can I quote you on that?

> Generally you prioritize things based on maximizing maintainability
> and speed and minimizing code, bugs, and complexity. Those are often
> both conflicting and in agreement. Here are a few common examples of
> simple-thing-on-complex-thing:
> ...
>
> Look at the SELinux model again; it has the following things:
> (A) Labels on almost-all user-visible kernel objects
> (B) Individual access rules for almost every operation on those
> objects
> (C) "Transition" rules to set the label on newly created objects.
> (D) Fundamental "constraints" which enforce hard limits on what
> may be permitted with "allow" rules
>
> From a fundamental standpoint it's harder to get much simpler than
> that.

It's easy to get simpler than that:
(A) Labels on all objects and subjects
(B) Access rules for subjects and objects

No transformations. Operations in terms of rwx. lots simpler.

> On top of that model, we also have a bit of additional
> *flexibility* for MLS/RBAC, although that flexibility may be ignored
> completely.
> (1) You can define "users" which may only assume some "roles"
> (2) You can define "roles" may only run in some "types"
> (3) There's a simple way of declaring multiple "levels" and
> "dominance".
>
> So you see, SELinux is a pretty fundamental description of the
> degrees of flexibility needed to secure everything. That kind of
> FUNDAMENTAL description is what belongs in the kernel. Anything else
> can and should be built on top with better libraries and/or user
> interfaces.

I don't believe that. I understand that some people do.

> As for the script, I'm partway through debugging it but my time is
> all chewed up with other stuff now, so it may take me an extra couple
> days.

Curse that day job!

Thank you.


Casey Schaufler
[email protected]

2007-08-22 15:06:05

by Pavel Machek

[permalink] [raw]
Subject: Re: [PATCH] Smack: Simplified Mandatory Access Control Kernel


> > but you written it in wrong language. You
> > written it in C, while you should have written it in SELinux policy
> > language (and your favourite scripting language as frontend).
>
> I have often marvelled at the notion of a simplification layer.
> I believe that you build complex things on top of simple things,
> not the other way around.

As we have to maintain selinux, anyway, I don't see why simplification
layer is a problem.
Pavel

--
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

2007-08-22 18:47:28

by Casey Schaufler

[permalink] [raw]
Subject: Re: [PATCH] Smack: Simplified Mandatory Access Control Kernel


--- Pavel Machek <[email protected]> wrote:

>
> > > but you written it in wrong language. You
> > > written it in C, while you should have written it in SELinux policy
> > > language (and your favourite scripting language as frontend).
> >
> > I have often marvelled at the notion of a simplification layer.
> > I believe that you build complex things on top of simple things,
> > not the other way around.
>
> As we have to maintain selinux, anyway, I don't see why simplification
> layer is a problem.

It's an issue if you want to do simple things, have the resources to
do simple things, but go over budget because the simple things are
built on top of complex things that you don't need. I see this crop
up frequently with IT infrastructures, where simple problems get
solved using completely unnecessary components just because those
components are available. If you want to maintain an SELinux policy
that looks like it does smackish things in addition to the reference
policy that's OK by me.


Casey Schaufler
[email protected]

2007-08-23 07:14:36

by Jan Engelhardt

[permalink] [raw]
Subject: Re: [PATCH] Smack: Simplified Mandatory Access Control Kernel


On Aug 22 2007 11:47, Casey Schaufler wrote:
>> As we have to maintain selinux, anyway, I don't see why simplification
>> layer is a problem.
>
>It's an issue if you want to do simple things, have the resources to
>do simple things, but go over budget because the simple things are
>built on top of complex things that you don't need. I see this crop
>up frequently with IT infrastructures, where simple problems get
>solved using completely unnecessary components just because those
>components are available.

class LinkedList {} for example. Just look how simple the linux
kernel does it ;-)


Jan
--

2007-09-07 16:03:06

by Casey Schaufler

[permalink] [raw]
Subject: Re: [PATCH] Smack: Simplified Mandatory Access Control Kernel


--- Kyle Moffett <[email protected]> wrote:

> ...
>
> As for the script, I'm partway through debugging it but my time is
> all chewed up with other stuff now, so it may take me an extra couple
> days.

Any progress on this?


Casey Schaufler
[email protected]