tizen 2.4 release
[kernel/linux-3.0.git] / security / smack / smackfs.c
index 54e2fb1..efa575a 100644 (file)
@@ -86,16 +86,6 @@ int smack_cipso_direct = SMACK_CIPSO_DIRECT_DEFAULT;
 int smack_cipso_mapped = SMACK_CIPSO_MAPPED_DEFAULT;
 
 /*
- * Unless a process is running with this label even
- * having CAP_MAC_OVERRIDE isn't enough to grant
- * privilege to violate MAC policy. If no label is
- * designated (the NULL case) capabilities apply to
- * everyone. It is expected that the hat (^) label
- * will be used if any label is used.
- */
-char *smack_onlycap;
-
-/*
  * Certain IP addresses may be designated as single label hosts.
  * Packets are sent there unlabeled, but only from tasks that
  * can write to the specified label.
@@ -529,23 +519,17 @@ static void *smk_seq_start(struct seq_file *s, loff_t *pos,
                                struct list_head *head)
 {
        struct list_head *list;
+       int i = *pos;
+
+       rcu_read_lock();
+       for (list = rcu_dereference(list_next_rcu(head));
+               list != head;
+               list = rcu_dereference(list_next_rcu(list))) {
+               if (i-- == 0)
+                       return list;
+       }
 
-       /*
-        * This is 0 the first time through.
-        */
-       if (s->index == 0)
-               s->private = head;
-
-       if (s->private == NULL)
-               return NULL;
-
-       list = s->private;
-       if (list_empty(list))
-               return NULL;
-
-       if (s->index == 0)
-               return list->next;
-       return list;
+       return NULL;
 }
 
 static void *smk_seq_next(struct seq_file *s, void *v, loff_t *pos,
@@ -553,17 +537,15 @@ static void *smk_seq_next(struct seq_file *s, void *v, loff_t *pos,
 {
        struct list_head *list = v;
 
-       if (list_is_last(list, head)) {
-               s->private = NULL;
-               return NULL;
-       }
-       s->private = list->next;
-       return list->next;
+       ++*pos;
+       list = rcu_dereference(list_next_rcu(list));
+
+       return (list == head) ? NULL : list;
 }
 
 static void smk_seq_stop(struct seq_file *s, void *v)
 {
-       /* No-op */
+       rcu_read_unlock();
 }
 
 static void smk_rule_show(struct seq_file *s, struct smack_rule *srp, int max)
@@ -619,7 +601,7 @@ static int load_seq_show(struct seq_file *s, void *v)
 {
        struct list_head *list = v;
        struct smack_master_list *smlp =
-                list_entry(list, struct smack_master_list, list);
+               list_entry_rcu(list, struct smack_master_list, list);
 
        smk_rule_show(s, smlp->smk_rule, SMK_LABELLEN);
 
@@ -832,7 +814,7 @@ static int cipso_seq_show(struct seq_file *s, void *v)
 {
        struct list_head  *list = v;
        struct smack_known *skp =
-                list_entry(list, struct smack_known, list);
+               list_entry_rcu(list, struct smack_known, list);
        struct netlbl_lsm_secattr_catmap *cmp = skp->smk_netlabel.attr.mls.cat;
        char sep = '/';
        int i;
@@ -1021,7 +1003,7 @@ static int cipso2_seq_show(struct seq_file *s, void *v)
 {
        struct list_head  *list = v;
        struct smack_known *skp =
-                list_entry(list, struct smack_known, list);
+               list_entry_rcu(list, struct smack_known, list);
        struct netlbl_lsm_secattr_catmap *cmp = skp->smk_netlabel.attr.mls.cat;
        char sep = '/';
        int i;
@@ -1105,7 +1087,7 @@ static int netlbladdr_seq_show(struct seq_file *s, void *v)
 {
        struct list_head *list = v;
        struct smk_netlbladdr *skp =
-                        list_entry(list, struct smk_netlbladdr, list);
+                       list_entry_rcu(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);
@@ -1666,34 +1648,78 @@ static const struct file_operations smk_ambient_ops = {
        .llseek         = default_llseek,
 };
 
-/**
- * smk_read_onlycap - read() for /smack/onlycap
- * @filp: file pointer, not actually used
- * @buf: where to put the result
- * @cn: maximum to send along
- * @ppos: where to start
- *
- * Returns number of bytes read or error code, as appropriate
+/*
+ * Seq_file operations for /smack/onlycap
  */
-static ssize_t smk_read_onlycap(struct file *filp, char __user *buf,
-                               size_t cn, loff_t *ppos)
+static void *onlycap_seq_start(struct seq_file *s, loff_t *pos)
 {
-       char *smack = "";
-       ssize_t rc = -EINVAL;
-       int asize;
+       return smk_seq_start(s, pos, &smack_onlycap_list);
+}
 
-       if (*ppos != 0)
-               return 0;
+static void *onlycap_seq_next(struct seq_file *s, void *v, loff_t *pos)
+{
+       return smk_seq_next(s, v, pos, &smack_onlycap_list);
+}
+
+static int onlycap_seq_show(struct seq_file *s, void *v)
+{
+       struct list_head *list = v;
+       struct smack_onlycap *sop =
+               list_entry_rcu(list, struct smack_onlycap, list);
 
-       if (smack_onlycap != NULL)
-               smack = smack_onlycap;
+       seq_puts(s, sop->smk_label->smk_known);
+       seq_putc(s, ' ');
 
-       asize = strlen(smack) + 1;
+       return 0;
+}
 
-       if (cn >= asize)
-               rc = simple_read_from_buffer(buf, cn, ppos, smack, asize);
+static const struct seq_operations onlycap_seq_ops = {
+       .start = onlycap_seq_start,
+       .next  = onlycap_seq_next,
+       .show  = onlycap_seq_show,
+       .stop  = smk_seq_stop,
+};
 
-       return rc;
+static int smk_open_onlycap(struct inode *inode, struct file *file)
+{
+       return seq_open(file, &onlycap_seq_ops);
+}
+
+/**
+ * list_swap_rcu - swap public list with a private one in RCU-safe way
+ * The caller must hold appropriate mutex to prevent concurrent modifications
+ * to the public list.
+ * Private list is assumed to be not accessible to other threads yet.
+ *
+ * @public: public list
+ * @private: private list
+ */
+static void list_swap_rcu(struct list_head *public, struct list_head *private)
+{
+       struct list_head *first, *last;
+
+       if (list_empty(public)) {
+               list_splice_init_rcu(private, public, synchronize_rcu);
+       } else {
+               /* Remember public list before replacing it */
+               first = public->next;
+               last = public->prev;
+
+               /* Publish private list in place of public in RCU-safe way */
+               private->prev->next = public;
+               private->next->prev = public;
+               rcu_assign_pointer(public->next, private->next);
+               public->prev = private->prev;
+
+               synchronize_rcu();
+
+               /* When all readers are done with the old public list,
+                * attach it in place of private */
+               private->next = first;
+               private->prev = last;
+               first->prev = private;
+               last->next = private;
+       }
 }
 
 /**
@@ -1709,24 +1735,48 @@ static ssize_t smk_write_onlycap(struct file *file, const char __user *buf,
                                 size_t count, loff_t *ppos)
 {
        char *data;
-       struct smack_known *skp = smk_of_task(current->cred->security);
+       char *data_parse;
+       char *tok;
+       struct smack_known *skp;
+       struct smack_onlycap *sop;
+       struct smack_onlycap *sop2;
+       LIST_HEAD(list_tmp);
        int rc = count;
 
        if (!smack_privileged(CAP_MAC_ADMIN))
                return -EPERM;
 
-       /*
-        * This can be done using smk_access() but is done
-        * explicitly for clarity. The smk_access() implementation
-        * would use smk_access(smack_onlycap, MAY_WRITE)
-        */
-       if (smack_onlycap != NULL && smack_onlycap != skp->smk_known)
-               return -EPERM;
-
-       data = kzalloc(count, GFP_KERNEL);
+       data = kzalloc(count + 1, GFP_KERNEL);
        if (data == NULL)
                return -ENOMEM;
 
+       if (copy_from_user(data, buf, count) != 0) {
+               kfree(data);
+               return -EFAULT;
+       }
+
+       data_parse = data;
+       while ((tok = strsep(&data_parse, " ")) != NULL) {
+               if (!*tok)
+                       continue;
+
+               skp = smk_import_entry(tok, 0);
+               if (IS_ERR(skp)) {
+                       rc = PTR_ERR(skp);
+                       break;
+               }
+
+               sop = kzalloc(sizeof(*sop), GFP_KERNEL);
+               if (sop == NULL) {
+                       rc = -ENOMEM;
+                       break;
+               }
+
+               sop->smk_label = skp;
+               list_add_rcu(&sop->list, &list_tmp);
+       }
+       kfree(data);
+
        /*
         * Should the null string be passed in unset the onlycap value.
         * This seems like something to be careful with as usually
@@ -1736,20 +1786,31 @@ static ssize_t smk_write_onlycap(struct file *file, const char __user *buf,
         *
         * smk_import will also reject a label beginning with '-',
         * so "-usecapabilities" will also work.
+        *
+        * But do so only on invalid label, not on system errors.
+        * The invalid label must be first to count as clearing attempt.
         */
-       if (copy_from_user(data, buf, count) != 0)
-               rc = -EFAULT;
-       else
-               smack_onlycap = smk_import(data, count);
+       if (rc == -EINVAL && list_empty(&list_tmp))
+               rc = count;
+
+       if (rc >= 0) {
+               mutex_lock(&smack_onlycap_lock);
+               list_swap_rcu(&smack_onlycap_list, &list_tmp);
+               mutex_unlock(&smack_onlycap_lock);
+       }
+
+       list_for_each_entry_safe(sop, sop2, &list_tmp, list)
+               kfree(sop);
 
-       kfree(data);
        return rc;
 }
 
 static const struct file_operations smk_onlycap_ops = {
-       .read           = smk_read_onlycap,
+       .open           = smk_open_onlycap,
+       .read           = seq_read,
        .write          = smk_write_onlycap,
-       .llseek         = default_llseek,
+       .llseek         = seq_lseek,
+       .release        = seq_release,
 };
 
 /**
@@ -1839,7 +1900,7 @@ static int load_self_seq_show(struct seq_file *s, void *v)
 {
        struct list_head *list = v;
        struct smack_rule *srp =
-                list_entry(list, struct smack_rule, list);
+               list_entry_rcu(list, struct smack_rule, list);
 
        smk_rule_show(s, srp, SMK_LABELLEN);
 
@@ -1962,7 +2023,7 @@ static int load2_seq_show(struct seq_file *s, void *v)
 {
        struct list_head *list = v;
        struct smack_master_list *smlp =
-                list_entry(list, struct smack_master_list, list);
+               list_entry_rcu(list, struct smack_master_list, list);
 
        smk_rule_show(s, smlp->smk_rule, SMK_LONGLABEL);
 
@@ -2039,7 +2100,7 @@ static int load_self2_seq_show(struct seq_file *s, void *v)
 {
        struct list_head *list = v;
        struct smack_rule *srp =
-                list_entry(list, struct smack_rule, list);
+               list_entry_rcu(list, struct smack_rule, list);
 
        smk_rule_show(s, srp, SMK_LONGLABEL);