smack: convert smack to standard linux lists
authorEtienne Basset <etienne.basset@numericable.fr>
Tue, 24 Mar 2009 19:53:24 +0000 (20:53 +0100)
committerJames Morris <jmorris@namei.org>
Wed, 25 Mar 2009 22:17:04 +0000 (09:17 +1100)
the following patch (on top of 2.6.29) converts Smack lists to standard linux lists
Please review and consider for inclusion in 2.6.30-rc

regards,
Etienne

Signed-off-by: Etienne Basset <etienne.basset@numericable.fr>
Acked-by: Casey Schaufler <casey@schaufler-ca.com>
security/smack/smack.h
security/smack/smack_access.c
security/smack/smack_lsm.c
security/smack/smackfs.c

index b79582e4fbfd6e82dc2679890731953f5ba1926f..64164f8fde707022ed0d9e11770cc00fbc7913fa 100644 (file)
@@ -18,6 +18,8 @@
 #include <linux/security.h>
 #include <linux/in.h>
 #include <net/netlabel.h>
+#include <linux/list.h>
+#include <linux/rculist.h>
 
 /*
  * Why 23? CIPSO is constrained to 30, so a 32 byte buffer is
@@ -59,17 +61,10 @@ struct inode_smack {
  * A label access rule.
  */
 struct smack_rule {
-       char    *smk_subject;
-       char    *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;
+       struct list_head        list;
+       char                    *smk_subject;
+       char                    *smk_object;
+       int                     smk_access;
 };
 
 /*
@@ -85,7 +80,7 @@ struct smack_cipso {
  * An entry in the table identifying hosts.
  */
 struct smk_netlbladdr {
-       struct smk_netlbladdr   *smk_next;
+       struct list_head        list;
        struct sockaddr_in      smk_host;       /* network address */
        struct in_addr          smk_mask;       /* network mask */
        char                    *smk_label;     /* label */
@@ -113,7 +108,7 @@ struct smk_netlbladdr {
  * the cipso direct mapping in used internally.
  */
 struct smack_known {
-       struct smack_known      *smk_next;
+       struct list_head        list;
        char                    smk_known[SMK_LABELLEN];
        u32                     smk_secid;
        struct smack_cipso      *smk_cipso;
@@ -206,7 +201,6 @@ extern int smack_cipso_direct;
 extern char *smack_net_ambient;
 extern char *smack_onlycap;
 
-extern struct smack_known *smack_known;
 extern struct smack_known smack_known_floor;
 extern struct smack_known smack_known_hat;
 extern struct smack_known smack_known_huh;
@@ -214,8 +208,10 @@ extern struct smack_known smack_known_invalid;
 extern struct smack_known smack_known_star;
 extern struct smack_known smack_known_web;
 
-extern struct smk_list_entry *smack_list;
-extern struct smk_netlbladdr *smack_netlbladdrs;
+extern struct list_head smack_known_list;
+extern struct list_head smack_rule_list;
+extern struct list_head smk_netlbladdr_list;
+
 extern struct security_operations smack_ops;
 
 /*
index cfa19ca125e3596ed2b80b3bb0d2a0f05a594903..58564195bb09c4f895efc04c811f02a28218f593 100644 (file)
 #include "smack.h"
 
 struct smack_known smack_known_huh = {
-       .smk_next       = NULL,
        .smk_known      = "?",
        .smk_secid      = 2,
        .smk_cipso      = NULL,
 };
 
 struct smack_known smack_known_hat = {
-       .smk_next       = &smack_known_huh,
        .smk_known      = "^",
        .smk_secid      = 3,
        .smk_cipso      = NULL,
 };
 
 struct smack_known smack_known_star = {
-       .smk_next       = &smack_known_hat,
        .smk_known      = "*",
        .smk_secid      = 4,
        .smk_cipso      = NULL,
 };
 
 struct smack_known smack_known_floor = {
-       .smk_next       = &smack_known_star,
        .smk_known      = "_",
        .smk_secid      = 5,
        .smk_cipso      = NULL,
 };
 
 struct smack_known smack_known_invalid = {
-       .smk_next       = &smack_known_floor,
        .smk_known      = "",
        .smk_secid      = 6,
        .smk_cipso      = NULL,
 };
 
 struct smack_known smack_known_web = {
-       .smk_next       = &smack_known_invalid,
        .smk_known      = "@",
        .smk_secid      = 7,
        .smk_cipso      = NULL,
 };
 
-struct smack_known *smack_known = &smack_known_web;
+LIST_HEAD(smack_known_list);
 
 /*
  * The initial value needs to be bigger than any of the
@@ -87,7 +81,6 @@ static u32 smack_next_secid = 10;
 int smk_access(char *subject_label, char *object_label, int request)
 {
        u32 may = MAY_NOT;
-       struct smk_list_entry *sp;
        struct smack_rule *srp;
 
        /*
@@ -139,9 +132,8 @@ int smk_access(char *subject_label, char *object_label, int request)
         * access (e.g. read is included in readwrite) it's
         * good.
         */
-       for (sp = smack_list; sp != NULL; sp = sp->smk_next) {
-               srp = &sp->smk_rule;
-
+       rcu_read_lock();
+       list_for_each_entry_rcu(srp, &smack_rule_list, list) {
                if (srp->smk_subject == subject_label ||
                    strcmp(srp->smk_subject, subject_label) == 0) {
                        if (srp->smk_object == object_label ||
@@ -151,6 +143,7 @@ int smk_access(char *subject_label, char *object_label, int request)
                        }
                }
        }
+       rcu_read_unlock();
        /*
         * This is a bit map operation.
         */
@@ -228,14 +221,17 @@ struct smack_known *smk_import_entry(const char *string, int len)
 
        mutex_lock(&smack_known_lock);
 
-       for (skp = smack_known; skp != NULL; skp = skp->smk_next)
-               if (strncmp(skp->smk_known, smack, SMK_MAXLEN) == 0)
+       found = 0;
+       list_for_each_entry_rcu(skp, &smack_known_list, list) {
+               if (strncmp(skp->smk_known, smack, SMK_MAXLEN) == 0) {
+                       found = 1;
                        break;
+               }
+       }
 
-       if (skp == NULL) {
+       if (found == 0) {
                skp = kzalloc(sizeof(struct smack_known), GFP_KERNEL);
                if (skp != NULL) {
-                       skp->smk_next = smack_known;
                        strncpy(skp->smk_known, smack, SMK_MAXLEN);
                        skp->smk_secid = smack_next_secid++;
                        skp->smk_cipso = NULL;
@@ -244,8 +240,7 @@ struct smack_known *smk_import_entry(const char *string, int len)
                         * Make sure that the entry is actually
                         * filled before putting it on the list.
                         */
-                       smp_mb();
-                       smack_known = skp;
+                       list_add_rcu(&skp->list, &smack_known_list);
                }
        }
 
@@ -283,14 +278,19 @@ char *smack_from_secid(const u32 secid)
 {
        struct smack_known *skp;
 
-       for (skp = smack_known; skp != NULL; skp = skp->smk_next)
-               if (skp->smk_secid == secid)
+       rcu_read_lock();
+       list_for_each_entry_rcu(skp, &smack_known_list, list) {
+               if (skp->smk_secid == secid) {
+                       rcu_read_unlock();
                        return skp->smk_known;
+               }
+       }
 
        /*
         * If we got this far someone asked for the translation
         * of a secid that is not on the list.
         */
+       rcu_read_unlock();
        return smack_known_invalid.smk_known;
 }
 
@@ -305,9 +305,14 @@ u32 smack_to_secid(const char *smack)
 {
        struct smack_known *skp;
 
-       for (skp = smack_known; skp != NULL; skp = skp->smk_next)
-               if (strncmp(skp->smk_known, smack, SMK_MAXLEN) == 0)
+       rcu_read_lock();
+       list_for_each_entry_rcu(skp, &smack_known_list, list) {
+               if (strncmp(skp->smk_known, smack, SMK_MAXLEN) == 0) {
+                       rcu_read_unlock();
                        return skp->smk_secid;
+               }
+       }
+       rcu_read_unlock();
        return 0;
 }
 
@@ -332,7 +337,8 @@ void smack_from_cipso(u32 level, char *cp, char *result)
        struct smack_known *kp;
        char *final = NULL;
 
-       for (kp = smack_known; final == NULL && kp != NULL; kp = kp->smk_next) {
+       rcu_read_lock();
+       list_for_each_entry(kp, &smack_known_list, list) {
                if (kp->smk_cipso == NULL)
                        continue;
 
@@ -344,6 +350,7 @@ void smack_from_cipso(u32 level, char *cp, char *result)
 
                spin_unlock_bh(&kp->smk_cipsolock);
        }
+       rcu_read_unlock();
        if (final == NULL)
                final = smack_known_huh.smk_known;
        strncpy(result, final, SMK_MAXLEN);
@@ -360,13 +367,19 @@ void smack_from_cipso(u32 level, char *cp, char *result)
 int smack_to_cipso(const char *smack, struct smack_cipso *cp)
 {
        struct smack_known *kp;
+       int found = 0;
 
-       for (kp = smack_known; kp != NULL; kp = kp->smk_next)
+       rcu_read_lock();
+       list_for_each_entry_rcu(kp, &smack_known_list, list) {
                if (kp->smk_known == smack ||
-                   strcmp(kp->smk_known, smack) == 0)
+                   strcmp(kp->smk_known, smack) == 0) {
+                       found = 1;
                        break;
+               }
+       }
+       rcu_read_unlock();
 
-       if (kp == NULL || kp->smk_cipso == NULL)
+       if (found == 0 || kp->smk_cipso == NULL)
                return -ENOENT;
 
        memcpy(cp, kp->smk_cipso, sizeof(struct smack_cipso));
index 84b62b5e9e2c681b3837a5da2a8cd2f39ce409f6..fd20d15f5b9a5830e4280612efc488904055789f 100644 (file)
@@ -1508,7 +1508,8 @@ static char *smack_host_label(struct sockaddr_in *sip)
        if (siap->s_addr == 0)
                return NULL;
 
-       for (snp = smack_netlbladdrs; snp != NULL; snp = snp->smk_next) {
+       rcu_read_lock();
+       list_for_each_entry_rcu(snp, &smk_netlbladdr_list, list) {
                /*
                 * we break after finding the first match because
                 * the list is sorted from longest to shortest mask
@@ -1516,10 +1517,11 @@ static char *smack_host_label(struct sockaddr_in *sip)
                 */
                if ((&snp->smk_host.sin_addr)->s_addr  ==
                        (siap->s_addr & (&snp->smk_mask)->s_addr)) {
+                       rcu_read_unlock();
                        return snp->smk_label;
                }
        }
-
+       rcu_read_unlock();
        return NULL;
 }
 
@@ -2930,6 +2932,17 @@ struct security_operations smack_ops = {
        .release_secctx =               smack_release_secctx,
 };
 
+
+static __init void init_smack_know_list(void)
+{
+       list_add(&smack_known_huh.list, &smack_known_list);
+       list_add(&smack_known_hat.list, &smack_known_list);
+       list_add(&smack_known_star.list, &smack_known_list);
+       list_add(&smack_known_floor.list, &smack_known_list);
+       list_add(&smack_known_invalid.list, &smack_known_list);
+       list_add(&smack_known_web.list, &smack_known_list);
+}
+
 /**
  * smack_init - initialize the smack system
  *
@@ -2950,6 +2963,8 @@ static __init int smack_init(void)
        cred = (struct cred *) current->cred;
        cred->security = &smack_known_floor.smk_known;
 
+       /* initilize the smack_know_list */
+       init_smack_know_list();
        /*
         * Initialize locks
         */
index a1b57e4dba3e0865902a7a3691ee1d3afc1f8748..856c8a2875231f2a6d64075bb114f99e0c356628 100644 (file)
@@ -80,10 +80,11 @@ char *smack_onlycap;
  * Packets are sent there unlabeled, but only from tasks that
  * can write to the specified label.
  */
-struct smk_netlbladdr *smack_netlbladdrs;
+
+LIST_HEAD(smk_netlbladdr_list);
+LIST_HEAD(smack_rule_list);
 
 static int smk_cipso_doi_value = SMACK_CIPSO_DOI_DEFAULT;
-struct smk_list_entry *smack_list;
 
 #define        SEQ_READ_FINISHED       1
 
@@ -134,24 +135,27 @@ static void *load_seq_start(struct seq_file *s, loff_t *pos)
 {
        if (*pos == SEQ_READ_FINISHED)
                return NULL;
-
-       return smack_list;
+       if (list_empty(&smack_rule_list))
+               return NULL;
+       return smack_rule_list.next;
 }
 
 static void *load_seq_next(struct seq_file *s, void *v, loff_t *pos)
 {
-       struct smk_list_entry *skp = ((struct smk_list_entry *) v)->smk_next;
+       struct list_head *list = v;
 
-       if (skp == NULL)
+       if (list_is_last(list, &smack_rule_list)) {
                *pos = SEQ_READ_FINISHED;
-
-       return skp;
+               return NULL;
+       }
+       return list->next;
 }
 
 static int load_seq_show(struct seq_file *s, void *v)
 {
-       struct smk_list_entry *slp = (struct smk_list_entry *) v;
-       struct smack_rule *srp = &slp->smk_rule;
+       struct list_head *list = v;
+       struct smack_rule *srp =
+                list_entry(list, struct smack_rule, list);
 
        seq_printf(s, "%s %s", (char *)srp->smk_subject,
                   (char *)srp->smk_object);
@@ -212,32 +216,23 @@ static int smk_open_load(struct inode *inode, struct file *file)
  */
 static int smk_set_access(struct smack_rule *srp)
 {
-       struct smk_list_entry *sp;
-       struct smk_list_entry *newp;
+       struct smack_rule *sp;
        int ret = 0;
-
+       int found;
        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;
+       found = 0;
+       list_for_each_entry_rcu(sp, &smack_rule_list, list) {
+               if (sp->smk_subject == srp->smk_subject &&
+                   sp->smk_object == srp->smk_object) {
+                       found = 1;
+                       sp->smk_access = srp->smk_access;
                        break;
                }
-
-       if (sp == NULL) {
-               newp = kzalloc(sizeof(struct smk_list_entry), GFP_KERNEL);
-               if (newp == NULL) {
-                       ret = -ENOMEM;
-                       goto out;
-               }
-
-               newp->smk_rule = *srp;
-               newp->smk_next = smack_list;
-               smack_list = newp;
        }
+       if (found == 0)
+               list_add_rcu(&srp->list, &smack_rule_list);
 
-out:
        mutex_unlock(&smack_list_lock);
 
        return ret;
@@ -261,7 +256,7 @@ out:
 static ssize_t smk_write_load(struct file *file, const char __user *buf,
                              size_t count, loff_t *ppos)
 {
-       struct smack_rule rule;
+       struct smack_rule *rule;
        char *data;
        int rc = -EINVAL;
 
@@ -272,9 +267,8 @@ static ssize_t smk_write_load(struct file *file, const char __user *buf,
         */
        if (!capable(CAP_MAC_ADMIN))
                return -EPERM;
-       if (*ppos != 0)
-               return -EINVAL;
-       if (count != SMK_LOADLEN)
+
+       if (*ppos != 0 || count != SMK_LOADLEN)
                return -EINVAL;
 
        data = kzalloc(count, GFP_KERNEL);
@@ -286,25 +280,31 @@ static ssize_t smk_write_load(struct file *file, const char __user *buf,
                goto out;
        }
 
-       rule.smk_subject = smk_import(data, 0);
-       if (rule.smk_subject == NULL)
+       rule = kzalloc(sizeof(*rule), GFP_KERNEL);
+       if (rule == NULL) {
+               rc = -ENOMEM;
                goto out;
+       }
 
-       rule.smk_object = smk_import(data + SMK_LABELLEN, 0);
-       if (rule.smk_object == NULL)
-               goto out;
+       rule->smk_subject = smk_import(data, 0);
+       if (rule->smk_subject == NULL)
+               goto out_free_rule;
 
-       rule.smk_access = 0;
+       rule->smk_object = smk_import(data + SMK_LABELLEN, 0);
+       if (rule->smk_object == NULL)
+               goto out_free_rule;
+
+       rule->smk_access = 0;
 
        switch (data[SMK_LABELLEN + SMK_LABELLEN]) {
        case '-':
                break;
        case 'r':
        case 'R':
-               rule.smk_access |= MAY_READ;
+               rule->smk_access |= MAY_READ;
                break;
        default:
-               goto out;
+               goto out_free_rule;
        }
 
        switch (data[SMK_LABELLEN + SMK_LABELLEN + 1]) {
@@ -312,10 +312,10 @@ static ssize_t smk_write_load(struct file *file, const char __user *buf,
                break;
        case 'w':
        case 'W':
-               rule.smk_access |= MAY_WRITE;
+               rule->smk_access |= MAY_WRITE;
                break;
        default:
-               goto out;
+               goto out_free_rule;
        }
 
        switch (data[SMK_LABELLEN + SMK_LABELLEN + 2]) {
@@ -323,10 +323,10 @@ static ssize_t smk_write_load(struct file *file, const char __user *buf,
                break;
        case 'x':
        case 'X':
-               rule.smk_access |= MAY_EXEC;
+               rule->smk_access |= MAY_EXEC;
                break;
        default:
-               goto out;
+               goto out_free_rule;
        }
 
        switch (data[SMK_LABELLEN + SMK_LABELLEN + 3]) {
@@ -334,17 +334,20 @@ static ssize_t smk_write_load(struct file *file, const char __user *buf,
                break;
        case 'a':
        case 'A':
-               rule.smk_access |= MAY_APPEND;
+               rule->smk_access |= MAY_APPEND;
                break;
        default:
-               goto out;
+               goto out_free_rule;
        }
 
-       rc = smk_set_access(&rule);
+       rc = smk_set_access(rule);
 
        if (!rc)
                rc = count;
+       goto out;
 
+out_free_rule:
+       kfree(rule);
 out:
        kfree(data);
        return rc;
@@ -433,24 +436,26 @@ static void *cipso_seq_start(struct seq_file *s, loff_t *pos)
 {
        if (*pos == SEQ_READ_FINISHED)
                return NULL;
+       if (list_empty(&smack_known_list))
+               return NULL;
 
-       return smack_known;
+       return smack_known_list.next;
 }
 
 static void *cipso_seq_next(struct seq_file *s, void *v, loff_t *pos)
 {
-       struct smack_known *skp = ((struct smack_known *) v)->smk_next;
+       struct list_head  *list = v;
 
        /*
-        * Omit labels with no associated cipso value
+        * labels with no associated cipso value wont be printed
+        * in cipso_seq_show
         */
-       while (skp != NULL && !skp->smk_cipso)
-               skp = skp->smk_next;
-
-       if (skp == NULL)
+       if (list_is_last(list, &smack_known_list)) {
                *pos = SEQ_READ_FINISHED;
+               return NULL;
+       }
 
-       return skp;
+       return list->next;
 }
 
 /*
@@ -459,7 +464,9 @@ static void *cipso_seq_next(struct seq_file *s, void *v, loff_t *pos)
  */
 static int cipso_seq_show(struct seq_file *s, void *v)
 {
-       struct smack_known *skp = (struct smack_known *) v;
+       struct list_head  *list = v;
+       struct smack_known *skp =
+                list_entry(list, struct smack_known, list);
        struct smack_cipso *scp = skp->smk_cipso;
        char *cbp;
        char sep = '/';
@@ -638,18 +645,21 @@ static void *netlbladdr_seq_start(struct seq_file *s, loff_t *pos)
 {
        if (*pos == SEQ_READ_FINISHED)
                return NULL;
-
-       return smack_netlbladdrs;
+       if (list_empty(&smk_netlbladdr_list))
+               return NULL;
+       return smk_netlbladdr_list.next;
 }
 
 static void *netlbladdr_seq_next(struct seq_file *s, void *v, loff_t *pos)
 {
-       struct smk_netlbladdr *skp = ((struct smk_netlbladdr *) v)->smk_next;
+       struct list_head *list = v;
 
-       if (skp == NULL)
+       if (list_is_last(list, &smk_netlbladdr_list)) {
                *pos = SEQ_READ_FINISHED;
+               return NULL;
+       }
 
-       return skp;
+       return list->next;
 }
 #define BEBITS (sizeof(__be32) * 8)
 
@@ -658,7 +668,9 @@ static void *netlbladdr_seq_next(struct seq_file *s, void *v, loff_t *pos)
  */
 static int netlbladdr_seq_show(struct seq_file *s, void *v)
 {
-       struct smk_netlbladdr *skp = (struct smk_netlbladdr *) v;
+       struct list_head *list = v;
+       struct smk_netlbladdr *skp =
+                        list_entry(list, struct smk_netlbladdr, list);
        unsigned char *hp = (char *) &skp->smk_host.sin_addr.s_addr;
        int maskn;
        u32 temp_mask = be32_to_cpu(skp->smk_mask.s_addr);
@@ -702,30 +714,36 @@ static int smk_open_netlbladdr(struct inode *inode, struct file *file)
  *
  * This helper insert netlabel in the smack_netlbladdrs list
  * sorted by netmask length (longest to smallest)
+ * locked by &smk_netlbladdr_lock in smk_write_netlbladdr
+ *
  */
 static void smk_netlbladdr_insert(struct smk_netlbladdr *new)
 {
-       struct smk_netlbladdr *m;
+       struct smk_netlbladdr *m, *m_next;
 
-       if (smack_netlbladdrs == NULL) {
-               smack_netlbladdrs = new;
+       if (list_empty(&smk_netlbladdr_list)) {
+               list_add_rcu(&new->list, &smk_netlbladdr_list);
                return;
        }
 
+       m = list_entry(rcu_dereference(smk_netlbladdr_list.next),
+                        struct smk_netlbladdr, list);
+
        /* the comparison '>' is a bit hacky, but works */
-       if (new->smk_mask.s_addr > smack_netlbladdrs->smk_mask.s_addr) {
-               new->smk_next = smack_netlbladdrs;
-               smack_netlbladdrs = new;
+       if (new->smk_mask.s_addr > m->smk_mask.s_addr) {
+               list_add_rcu(&new->list, &smk_netlbladdr_list);
                return;
        }
-       for (m = smack_netlbladdrs; m != NULL; m = m->smk_next) {
-               if (m->smk_next == NULL) {
-                       m->smk_next = new;
+
+       list_for_each_entry_rcu(m, &smk_netlbladdr_list, list) {
+               if (list_is_last(&m->list, &smk_netlbladdr_list)) {
+                       list_add_rcu(&new->list, &m->list);
                        return;
                }
-               if (new->smk_mask.s_addr > m->smk_next->smk_mask.s_addr) {
-                       new->smk_next = m->smk_next;
-                       m->smk_next = new;
+               m_next = list_entry(rcu_dereference(m->list.next),
+                                struct smk_netlbladdr, list);
+               if (new->smk_mask.s_addr > m_next->smk_mask.s_addr) {
+                       list_add_rcu(&new->list, &m->list);
                        return;
                }
        }
@@ -755,6 +773,7 @@ static ssize_t smk_write_netlbladdr(struct file *file, const char __user *buf,
        struct netlbl_audit audit_info;
        struct in_addr mask;
        unsigned int m;
+       int found;
        u32 mask_bits = (1<<31);
        __be32 nsa;
        u32 temp_mask;
@@ -808,14 +827,17 @@ static ssize_t smk_write_netlbladdr(struct file *file, const char __user *buf,
 
        nsa = newname.sin_addr.s_addr;
        /* try to find if the prefix is already in the list */
-       for (skp = smack_netlbladdrs; skp != NULL; skp = skp->smk_next)
+       found = 0;
+       list_for_each_entry_rcu(skp, &smk_netlbladdr_list, list) {
                if (skp->smk_host.sin_addr.s_addr == nsa &&
-                   skp->smk_mask.s_addr == mask.s_addr)
+                   skp->smk_mask.s_addr == mask.s_addr) {
+                       found = 1;
                        break;
-
+               }
+       }
        smk_netlabel_audit_set(&audit_info);
 
-       if (skp == NULL) {
+       if (found == 0) {
                skp = kzalloc(sizeof(*skp), GFP_KERNEL);
                if (skp == NULL)
                        rc = -ENOMEM;