Smack: fix: invalid length set for the result of /smack/access
[cascardo/linux.git] / security / smack / smackfs.c
index f934601..381eecf 100644 (file)
@@ -44,6 +44,7 @@ enum smk_inos {
        SMK_ONLYCAP     = 9,    /* the only "capable" label */
        SMK_LOGGING     = 10,   /* logging */
        SMK_LOAD_SELF   = 11,   /* task specific rules */
+       SMK_ACCESSES    = 12,   /* access policy */
 };
 
 /*
@@ -85,6 +86,16 @@ char *smack_onlycap;
  */
 
 LIST_HEAD(smk_netlbladdr_list);
+
+/*
+ * Rule lists are maintained for each label.
+ * This master list is just for reading /smack/load.
+ */
+struct smack_master_list {
+       struct list_head        list;
+       struct smack_rule       *smk_rule;
+};
+
 LIST_HEAD(smack_rule_list);
 
 static int smk_cipso_doi_value = SMACK_CIPSO_DOI_DEFAULT;
@@ -92,7 +103,7 @@ static int smk_cipso_doi_value = SMACK_CIPSO_DOI_DEFAULT;
 const char *smack_cipso_option = SMACK_CIPSO_OPTION;
 
 
-#define        SEQ_READ_FINISHED       1
+#define        SEQ_READ_FINISHED       ((loff_t)-1)
 
 /*
  * Values for parsing cipso rules
@@ -159,9 +170,13 @@ static int smk_set_access(struct smack_rule *srp, struct list_head *rule_list,
 
        mutex_lock(rule_lock);
 
+       /*
+        * Because the object label is less likely to match
+        * than the subject label check it first
+        */
        list_for_each_entry_rcu(sp, rule_list, list) {
-               if (sp->smk_subject == srp->smk_subject &&
-                   sp->smk_object == srp->smk_object) {
+               if (sp->smk_object == srp->smk_object &&
+                   sp->smk_subject == srp->smk_subject) {
                        found = 1;
                        sp->smk_access = srp->smk_access;
                        break;
@@ -175,6 +190,81 @@ static int smk_set_access(struct smack_rule *srp, struct list_head *rule_list,
        return found;
 }
 
+/**
+ * smk_parse_rule - parse subject, object and access type
+ * @data: string to be parsed whose size is SMK_LOADLEN
+ * @rule: parsed entities are stored in here
+ */
+static int smk_parse_rule(const char *data, struct smack_rule *rule)
+{
+       rule->smk_subject = smk_import(data, 0);
+       if (rule->smk_subject == NULL)
+               return -1;
+
+       rule->smk_object = smk_import(data + SMK_LABELLEN, 0);
+       if (rule->smk_object == NULL)
+               return -1;
+
+       rule->smk_access = 0;
+
+       switch (data[SMK_LABELLEN + SMK_LABELLEN]) {
+       case '-':
+               break;
+       case 'r':
+       case 'R':
+               rule->smk_access |= MAY_READ;
+               break;
+       default:
+               return -1;
+       }
+
+       switch (data[SMK_LABELLEN + SMK_LABELLEN + 1]) {
+       case '-':
+               break;
+       case 'w':
+       case 'W':
+               rule->smk_access |= MAY_WRITE;
+               break;
+       default:
+               return -1;
+       }
+
+       switch (data[SMK_LABELLEN + SMK_LABELLEN + 2]) {
+       case '-':
+               break;
+       case 'x':
+       case 'X':
+               rule->smk_access |= MAY_EXEC;
+               break;
+       default:
+               return -1;
+       }
+
+       switch (data[SMK_LABELLEN + SMK_LABELLEN + 3]) {
+       case '-':
+               break;
+       case 'a':
+       case 'A':
+               rule->smk_access |= MAY_APPEND;
+               break;
+       default:
+               return -1;
+       }
+
+       switch (data[SMK_LABELLEN + SMK_LABELLEN + 4]) {
+       case '-':
+               break;
+       case 't':
+       case 'T':
+               rule->smk_access |= MAY_TRANSMUTE;
+               break;
+       default:
+               return -1;
+       }
+
+       return 0;
+}
+
 /**
  * smk_write_load_list - write() for any /smack/load
  * @file: file pointer, not actually used
@@ -197,9 +287,12 @@ static ssize_t smk_write_load_list(struct file *file, const char __user *buf,
                                struct list_head *rule_list,
                                struct mutex *rule_lock)
 {
+       struct smack_master_list *smlp;
+       struct smack_known *skp;
        struct smack_rule *rule;
        char *data;
        int rc = -EINVAL;
+       int load = 0;
 
        /*
         * No partial writes.
@@ -234,69 +327,14 @@ static ssize_t smk_write_load_list(struct file *file, const char __user *buf,
                goto out;
        }
 
-       rule->smk_subject = smk_import(data, 0);
-       if (rule->smk_subject == NULL)
-               goto out_free_rule;
-
-       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;
-               break;
-       default:
-               goto out_free_rule;
-       }
-
-       switch (data[SMK_LABELLEN + SMK_LABELLEN + 1]) {
-       case '-':
-               break;
-       case 'w':
-       case 'W':
-               rule->smk_access |= MAY_WRITE;
-               break;
-       default:
-               goto out_free_rule;
-       }
-
-       switch (data[SMK_LABELLEN + SMK_LABELLEN + 2]) {
-       case '-':
-               break;
-       case 'x':
-       case 'X':
-               rule->smk_access |= MAY_EXEC;
-               break;
-       default:
+       if (smk_parse_rule(data, rule))
                goto out_free_rule;
-       }
 
-       switch (data[SMK_LABELLEN + SMK_LABELLEN + 3]) {
-       case '-':
-               break;
-       case 'a':
-       case 'A':
-               rule->smk_access |= MAY_APPEND;
-               break;
-       default:
-               goto out_free_rule;
-       }
-
-       switch (data[SMK_LABELLEN + SMK_LABELLEN + 4]) {
-       case '-':
-               break;
-       case 't':
-       case 'T':
-               rule->smk_access |= MAY_TRANSMUTE;
-               break;
-       default:
-               goto out_free_rule;
+       if (rule_list == NULL) {
+               load = 1;
+               skp = smk_find_entry(rule->smk_subject);
+               rule_list = &skp->smk_rules;
+               rule_lock = &skp->smk_rules_lock;
        }
 
        rc = count;
@@ -304,8 +342,15 @@ static ssize_t smk_write_load_list(struct file *file, const char __user *buf,
         * smk_set_access returns true if there was already a rule
         * for the subject/object pair, and false if it was new.
         */
-       if (!smk_set_access(rule, rule_list, rule_lock))
+       if (!smk_set_access(rule, rule_list, rule_lock)) {
+               smlp = kzalloc(sizeof(*smlp), GFP_KERNEL);
+               if (smlp != NULL) {
+                       smlp->smk_rule = rule;
+                       list_add_rcu(&smlp->list, &smack_rule_list);
+               } else
+                       rc = -ENOMEM;
                goto out;
+       }
 
 out_free_rule:
        kfree(rule);
@@ -321,11 +366,24 @@ out:
 
 static void *load_seq_start(struct seq_file *s, loff_t *pos)
 {
-       if (*pos == SEQ_READ_FINISHED)
+       struct list_head *list;
+
+       /*
+        * This is 0 the first time through.
+        */
+       if (s->index == 0)
+               s->private = &smack_rule_list;
+
+       if (s->private == NULL)
                return NULL;
-       if (list_empty(&smack_rule_list))
+
+       list = s->private;
+       if (list_empty(list))
                return NULL;
-       return smack_rule_list.next;
+
+       if (s->index == 0)
+               return list->next;
+       return list;
 }
 
 static void *load_seq_next(struct seq_file *s, void *v, loff_t *pos)
@@ -333,17 +391,19 @@ static void *load_seq_next(struct seq_file *s, void *v, loff_t *pos)
        struct list_head *list = v;
 
        if (list_is_last(list, &smack_rule_list)) {
-               *pos = SEQ_READ_FINISHED;
+               s->private = NULL;
                return NULL;
        }
+       s->private = list->next;
        return list->next;
 }
 
 static int load_seq_show(struct seq_file *s, void *v)
 {
        struct list_head *list = v;
-       struct smack_rule *srp =
-                list_entry(list, struct smack_rule, list);
+       struct smack_master_list *smlp =
+                list_entry(list, struct smack_master_list, list);
+       struct smack_rule *srp = smlp->smk_rule;
 
        seq_printf(s, "%s %s", (char *)srp->smk_subject,
                   (char *)srp->smk_object);
@@ -412,8 +472,7 @@ static ssize_t smk_write_load(struct file *file, const char __user *buf,
        if (!capable(CAP_MAC_ADMIN))
                return -EPERM;
 
-       return smk_write_load_list(file, buf, count, ppos, &smack_rule_list,
-                                       &smack_list_lock);
+       return smk_write_load_list(file, buf, count, ppos, NULL, NULL);
 }
 
 static const struct file_operations smk_load_ops = {
@@ -1425,6 +1484,47 @@ static const struct file_operations smk_load_self_ops = {
        .write          = smk_write_load_self,
        .release        = seq_release,
 };
+
+/**
+ * smk_write_access - handle access check transaction
+ * @file: file pointer
+ * @buf: data from user space
+ * @count: bytes sent
+ * @ppos: where to start - must be 0
+ */
+static ssize_t smk_write_access(struct file *file, const char __user *buf,
+                               size_t count, loff_t *ppos)
+{
+       struct smack_rule rule;
+       char *data;
+       int res;
+
+       if (!capable(CAP_MAC_ADMIN))
+               return -EPERM;
+
+       data = simple_transaction_get(file, buf, count);
+       if (IS_ERR(data))
+               return PTR_ERR(data);
+
+       if (count < SMK_LOADLEN || smk_parse_rule(data, &rule))
+               return -EINVAL;
+
+       res = smk_access(rule.smk_subject, rule.smk_object, rule.smk_access,
+                         NULL);
+       data[0] = res == 0 ? '1' : '0';
+       data[1] = '\0';
+
+       simple_transaction_set(file, 2);
+       return SMK_LOADLEN;
+}
+
+static const struct file_operations smk_access_ops = {
+       .write          = smk_write_access,
+       .read           = simple_transaction_read,
+       .release        = simple_transaction_release,
+       .llseek         = generic_file_llseek,
+};
+
 /**
  * smk_fill_super - fill the /smackfs superblock
  * @sb: the empty superblock
@@ -1459,6 +1559,8 @@ static int smk_fill_super(struct super_block *sb, void *data, int silent)
                        "logging", &smk_logging_ops, S_IRUGO|S_IWUSR},
                [SMK_LOAD_SELF] = {
                        "load-self", &smk_load_self_ops, S_IRUGO|S_IWUGO},
+               [SMK_ACCESSES] = {
+                       "access", &smk_access_ops, S_IRUGO|S_IWUSR},
                /* last one */
                        {""}
        };
@@ -1534,6 +1636,20 @@ static int __init init_smk_fs(void)
        smk_cipso_doi();
        smk_unlbl_ambient(NULL);
 
+       mutex_init(&smack_known_floor.smk_rules_lock);
+       mutex_init(&smack_known_hat.smk_rules_lock);
+       mutex_init(&smack_known_huh.smk_rules_lock);
+       mutex_init(&smack_known_invalid.smk_rules_lock);
+       mutex_init(&smack_known_star.smk_rules_lock);
+       mutex_init(&smack_known_web.smk_rules_lock);
+
+       INIT_LIST_HEAD(&smack_known_floor.smk_rules);
+       INIT_LIST_HEAD(&smack_known_hat.smk_rules);
+       INIT_LIST_HEAD(&smack_known_huh.smk_rules);
+       INIT_LIST_HEAD(&smack_known_invalid.smk_rules);
+       INIT_LIST_HEAD(&smack_known_star.smk_rules);
+       INIT_LIST_HEAD(&smack_known_web.smk_rules);
+
        return err;
 }