[PATCH v2 2/2] Add new interface 'relabel-list' to smackfs

From: Zbigniew Jasinski
Date: Tue Sep 29 2015 - 09:46:40 EST


This list is used to control smack label transition mechanism.
Process can transit to new label only if label is on the list.
Only process with CAP_MAC_ADMIN capability can add label to this list.

Changes in v2:
* use list_for_each_entry instead of _rcu during label write
* added missing description in security/Smack.txt

Signed-off-by: Zbigniew Jasinski <z.jasinski@xxxxxxxxxxx>
Signed-off-by: Rafal Krypa <r.krypa@xxxxxxxxxxx>
---
Documentation/security/Smack.txt | 13 ++++
security/smack/smack.h | 8 +++
security/smack/smack_lsm.c | 17 +++++
security/smack/smackfs.c | 147 +++++++++++++++++++++++++++++++++++++++
4 files changed, 185 insertions(+)

diff --git a/Documentation/security/Smack.txt b/Documentation/security/Smack.txt
index 5e6d07f..0ffd194 100644
--- a/Documentation/security/Smack.txt
+++ b/Documentation/security/Smack.txt
@@ -255,6 +255,19 @@ unconfined
the access permitted if it wouldn't be otherwise. Note that this
is dangerous and can ruin the proper labeling of your system.
It should never be used in production.
+relabel-possible
+ This interface is used to set relabel-possible flag. A process
+ with this flag is able to change its label without CAP_MAC_ADMIN,
+ but only once. After label transition this flag is zeroed.
+ 0 - default: process is not allowed to label transition
+ 1 - process is allowed to one-time label tranistion
+relabel-list
+ This interface contains a list of labels, in which process can
+ transition to. The format accepted on write is:
+ "%s"
+ for adding label, and:
+ "-%s"
+ for removing label from list.

If you are using the smackload utility
you can add access rules in /etc/smack/accesses. They take the form:
diff --git a/security/smack/smack.h b/security/smack/smack.h
index d17580e..db4a1a3 100644
--- a/security/smack/smack.h
+++ b/security/smack/smack.h
@@ -193,6 +193,12 @@ enum {
Opt_fstransmute = 5,
};

+struct smack_relabel {
+ struct rcu_head rcu;
+ struct list_head list;
+ struct smack_known *smk_label;
+};
+
/*
* Mount options
*/
@@ -333,6 +339,8 @@ extern struct list_head smk_net6addr_list;
extern struct mutex smack_onlycap_lock;
extern struct list_head smack_onlycap_list;

+extern struct list_head smack_relabel_list;
+
#define SMACK_HASH_SLOTS 16
extern struct hlist_head smack_known_hash[SMACK_HASH_SLOTS];

diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
index 0766a4b..596f270 100644
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -66,6 +66,8 @@ static const match_table_t smk_mount_tokens = {
{Opt_error, NULL},
};

+LIST_HEAD(smack_relabel_list);
+
#ifdef CONFIG_SECURITY_SMACK_BRINGUP
static char *smk_bu_mess[] = {
"Bringup Error", /* Unused */
@@ -3554,6 +3556,8 @@ static int smack_setprocattr(struct task_struct *p, char *name,
struct task_smack *tsp = current_security();
struct cred *new;
struct smack_known *skp;
+ struct smack_relabel *srp;
+ int rc;

/*
* Changing another process' Smack value is too dangerous
@@ -3581,6 +3585,19 @@ static int smack_setprocattr(struct task_struct *p, char *name,
if (skp == &smack_known_web)
return -EPERM;

+ if (tsp->smk_relabel) {
+ rc = -EPERM;
+ rcu_read_lock();
+ list_for_each_entry_rcu(srp, &smack_relabel_list, list)
+ if (srp->smk_label == skp) {
+ rc = 0;
+ break;
+ }
+ rcu_read_unlock();
+ if (rc)
+ return rc;
+ }
+
new = prepare_creds();
if (new == NULL)
return -ENOMEM;
diff --git a/security/smack/smackfs.c b/security/smack/smackfs.c
index 18c1b934..11012f1 100644
--- a/security/smack/smackfs.c
+++ b/security/smack/smackfs.c
@@ -62,6 +62,7 @@ enum smk_inos {
SMK_NET6ADDR = 23, /* single label IPv6 hosts */
#endif /* CONFIG_IPV6 */
SMK_RELABEL_POSSIBLE = 24, /* relabel possible without CAP_MAC_ADMIN */
+ SMK_RELABEL_LIST = 25,
};

/*
@@ -73,6 +74,7 @@ static DEFINE_MUTEX(smk_net4addr_lock);
#if IS_ENABLED(CONFIG_IPV6)
static DEFINE_MUTEX(smk_net6addr_lock);
#endif /* CONFIG_IPV6 */
+static DEFINE_MUTEX(smack_relabel_list_lock);

/*
* This is the "ambient" label for network traffic.
@@ -2770,6 +2772,148 @@ static const struct file_operations smk_relabel_possible_ops = {
.llseek = default_llseek,
};

+/*
+ * Seq_file read operations for /smack/relabel-list
+ */
+
+static void *relabel_list_seq_start(struct seq_file *s, loff_t *pos)
+{
+ return smk_seq_start(s, pos, &smack_relabel_list);
+}
+
+static void *relabel_list_seq_next(struct seq_file *s, void *v, loff_t *pos)
+{
+ return smk_seq_next(s, v, pos, &smack_relabel_list);
+}
+
+static int relabel_list_seq_show(struct seq_file *s, void *v)
+{
+ struct list_head *list = v;
+ struct smack_relabel *srp =
+ list_entry_rcu(list, struct smack_relabel, list);
+
+ seq_printf(s, "%s\n", srp->smk_label->smk_known);
+
+ return 0;
+}
+
+static const struct seq_operations relabel_list_seq_ops = {
+ .start = relabel_list_seq_start,
+ .next = relabel_list_seq_next,
+ .show = relabel_list_seq_show,
+ .stop = smk_seq_stop,
+};
+
+/**
+ * smk_open_relabel_list - open() for /smack/relabel-list
+ * @inode: inode structure representing file
+ * @file: "relabel-list" file pointer
+ *
+ * For reading, use load2_seq_* seq_file reading operations.
+ */
+static int smk_open_relabel_list(struct inode *inode, struct file *file)
+{
+ return seq_open(file, &relabel_list_seq_ops);
+}
+
+/**
+ * smk_write_relabel_list - write() for /smack/relabel-list
+ * @file: file pointer, not actually used
+ * @buf: where to get the data from
+ * @count: bytes sent
+ * @ppos: where to start - must be 0
+ *
+ */
+static ssize_t smk_write_relabel_list(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct smack_known *skp;
+ struct smack_relabel *srp;
+ int rc = count;
+ int remove;
+ char *data;
+ char *label;
+
+ /*
+ * Must have privilege.
+ */
+ if (!smack_privileged(CAP_MAC_ADMIN))
+ return -EPERM;
+
+ /*
+ * Enough data must be present.
+ * One label per line.
+ */
+ if (*ppos != 0 || count >= SMK_LONGLABEL)
+ return -EINVAL;
+
+ data = kzalloc(count + 1, GFP_KERNEL);
+ if (data == NULL)
+ return -ENOMEM;
+
+ if (copy_from_user(data, buf, count) != 0) {
+ kfree(data);
+ return -EFAULT;
+ }
+
+ if (data[0] == '-') {
+ remove = 1;
+ label = smk_parse_smack(data + 1, count - 1);
+ if (IS_ERR(label)) {
+ kfree(data);
+ return PTR_ERR(label);
+ }
+ skp = smk_find_entry(label);
+ kfree(label);
+ } else {
+ remove = 0;
+ skp = smk_import_entry(data, count);
+ }
+ kfree(data);
+
+ if (IS_ERR(skp))
+ return PTR_ERR(skp);
+
+ mutex_lock(&smack_relabel_list_lock);
+ list_for_each_entry(srp, &smack_relabel_list, list)
+ if (srp->smk_label == skp) {
+ if (remove) {
+ list_del_rcu(&srp->list);
+ mutex_unlock(&smack_relabel_list_lock);
+ kfree_rcu(srp, rcu);
+ return rc;
+ } else
+ goto out;
+ }
+
+ /* Entry not found on smack_relabel_list */
+ if (remove) {
+ rc = -EINVAL;
+ goto out;
+ }
+
+ srp = kzalloc(sizeof(*srp), GFP_KERNEL);
+ if (srp == NULL) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ srp->smk_label = skp;
+ list_add_rcu(&srp->list, &smack_relabel_list);
+
+out:
+ mutex_unlock(&smack_relabel_list_lock);
+ return rc;
+}
+
+static const struct file_operations smk_relabel_list_ops = {
+ .open = smk_open_relabel_list,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .write = smk_write_relabel_list,
+ .release = seq_release,
+};
+
/**
* smk_read_ptrace - read() for /smack/ptrace
* @filp: file pointer, not actually used
@@ -2898,6 +3042,9 @@ static int smk_fill_super(struct super_block *sb, void *data, int silent)
[SMK_RELABEL_POSSIBLE] = {
"relabel-possible", &smk_relabel_possible_ops,
S_IRUGO|S_IWUGO},
+ [SMK_RELABEL_LIST] = {
+ "relabel-list", &smk_relabel_list_ops,
+ S_IRUGO|S_IWUGO},
/* last one */
{""}
};
--
1.9.1

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/