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.jasin...@samsung.com>
Signed-off-by: Rafal Krypa <r.kr...@samsung.com>
---
 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 majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to