clps711xfb: convert to proc_fops
[safe/jmp/linux-2.6] / security / smack / smackfs.c
index bf107a3..aeead75 100644 (file)
@@ -41,6 +41,7 @@ enum smk_inos {
        SMK_AMBIENT     = 7,    /* internet ambient label */
        SMK_NETLBLADDR  = 8,    /* single label hosts */
        SMK_ONLYCAP     = 9,    /* the only "capable" label */
+       SMK_LOGGING     = 10,   /* logging */
 };
 
 /*
@@ -80,10 +81,14 @@ 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;
+
+const char *smack_cipso_option = SMACK_CIPSO_OPTION;
+
 
 #define        SEQ_READ_FINISHED       1
 
@@ -134,24 +139,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);
@@ -179,7 +187,7 @@ static void load_seq_stop(struct seq_file *s, void *v)
        /* No-op */
 }
 
-static struct seq_operations load_seq_ops = {
+static const struct seq_operations load_seq_ops = {
        .start = load_seq_start,
        .next  = load_seq_next,
        .show  = load_seq_show,
@@ -212,32 +220,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;
@@ -245,7 +244,7 @@ out:
 
 /**
  * smk_write_load - write() for /smack/load
- * @filp: file pointer, not actually used
+ * @file: file pointer, not actually used
  * @buf: where to get the data from
  * @count: bytes sent
  * @ppos: where to start - must be 0
@@ -261,7 +260,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 +271,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 +284,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_object = smk_import(data + SMK_LABELLEN, 0);
+       if (rule->smk_object == NULL)
+               goto out_free_rule;
 
-       rule.smk_access = 0;
+       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 +316,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 +327,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 +338,20 @@ static ssize_t smk_write_load(struct file *file, const char __user *buf,
                break;
        case 'a':
        case 'A':
-               rule.smk_access |= MAY_READ;
+               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;
@@ -402,6 +409,7 @@ static void smk_cipso_doi(void)
 
 /**
  * smk_unlbl_ambient - initialize the unlabeled domain
+ * @oldambient: previous domain string
  */
 static void smk_unlbl_ambient(char *oldambient)
 {
@@ -432,24 +440,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;
 }
 
 /*
@@ -458,7 +468,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 = '/';
@@ -491,7 +503,7 @@ static void cipso_seq_stop(struct seq_file *s, void *v)
        /* No-op */
 }
 
-static struct seq_operations cipso_seq_ops = {
+static const struct seq_operations cipso_seq_ops = {
        .start = cipso_seq_start,
        .stop  = cipso_seq_stop,
        .next  = cipso_seq_next,
@@ -513,7 +525,7 @@ static int smk_open_cipso(struct inode *inode, struct file *file)
 
 /**
  * smk_write_cipso - write() for /smack/cipso
- * @filp: file pointer, not actually used
+ * @file: file pointer, not actually used
  * @buf: where to get the data from
  * @count: bytes sent
  * @ppos: where to start
@@ -557,6 +569,11 @@ static ssize_t smk_write_cipso(struct file *file, const char __user *buf,
                goto unlockedout;
        }
 
+       /* labels cannot begin with a '-' */
+       if (data[0] == '-') {
+               rc = -EINVAL;
+               goto unlockedout;
+       }
        data[count] = '\0';
        rule = data;
        /*
@@ -569,7 +586,7 @@ static ssize_t smk_write_cipso(struct file *file, const char __user *buf,
        if (skp == NULL)
                goto out;
 
-       rule += SMK_LABELLEN;;
+       rule += SMK_LABELLEN;
        ret = sscanf(rule, "%d", &maplevel);
        if (ret != 1 || maplevel > SMACK_CIPSO_MAXLEVEL)
                goto out;
@@ -637,23 +654,22 @@ 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 BEMASK 0x80000000
-*/
-#define BEMASK 0x00000001
 #define BEBITS (sizeof(__be32) * 8)
 
 /*
@@ -661,14 +677,14 @@ 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;
-       __be32 bebits;
-       int maskn = 0;
+       int maskn;
+       u32 temp_mask = be32_to_cpu(skp->smk_mask.s_addr);
 
-       for (bebits = BEMASK; bebits != 0; maskn++, bebits <<= 1)
-               if ((skp->smk_mask.s_addr & bebits) == 0)
-                       break;
+       for (maskn = 0; temp_mask; temp_mask <<= 1, maskn++);
 
        seq_printf(s, "%u.%u.%u.%u/%d %s\n",
                hp[0], hp[1], hp[2], hp[3], maskn, skp->smk_label);
@@ -681,7 +697,7 @@ static void netlbladdr_seq_stop(struct seq_file *s, void *v)
        /* No-op */
 }
 
-static struct seq_operations netlbladdr_seq_ops = {
+static const struct seq_operations netlbladdr_seq_ops = {
        .start = netlbladdr_seq_start,
        .stop  = netlbladdr_seq_stop,
        .next  = netlbladdr_seq_next,
@@ -702,8 +718,50 @@ static int smk_open_netlbladdr(struct inode *inode, struct file *file)
 }
 
 /**
+ * smk_netlbladdr_insert
+ * @new : netlabel to insert
+ *
+ * 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, *m_next;
+
+       if (list_empty(&smk_netlbladdr_list)) {
+               list_add_rcu(&new->list, &smk_netlbladdr_list);
+               return;
+       }
+
+       m = list_entry_rcu(smk_netlbladdr_list.next,
+                          struct smk_netlbladdr, list);
+
+       /* the comparison '>' is a bit hacky, but works */
+       if (new->smk_mask.s_addr > m->smk_mask.s_addr) {
+               list_add_rcu(&new->list, &smk_netlbladdr_list);
+               return;
+       }
+
+       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;
+               }
+               m_next = list_entry_rcu(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;
+               }
+       }
+}
+
+
+/**
  * smk_write_netlbladdr - write() for /smack/netlabel
- * @filp: file pointer, not actually used
+ * @file: file pointer, not actually used
  * @buf: where to get the data from
  * @count: bytes sent
  * @ppos: where to start
@@ -718,14 +776,16 @@ static ssize_t smk_write_netlbladdr(struct file *file, const char __user *buf,
        struct sockaddr_in newname;
        char smack[SMK_LABELLEN];
        char *sp;
-       char data[SMK_NETLBLADDRMAX];
+       char data[SMK_NETLBLADDRMAX + 1];
        char *host = (char *)&newname.sin_addr.s_addr;
        int rc;
        struct netlbl_audit audit_info;
        struct in_addr mask;
        unsigned int m;
-       __be32 bebits = BEMASK;
+       int found;
+       u32 mask_bits = (1<<31);
        __be32 nsa;
+       u32 temp_mask;
 
        /*
         * Must have privilege.
@@ -757,14 +817,26 @@ static ssize_t smk_write_netlbladdr(struct file *file, const char __user *buf,
        if (m > BEBITS)
                return -EINVAL;
 
-       sp = smk_import(smack, 0);
-       if (sp == NULL)
-               return -EINVAL;
+       /* if smack begins with '-', its an option, don't import it */
+       if (smack[0] != '-') {
+               sp = smk_import(smack, 0);
+               if (sp == NULL)
+                       return -EINVAL;
+       } else {
+               /* check known options */
+               if (strcmp(smack, smack_cipso_option) == 0)
+                       sp = (char *)smack_cipso_option;
+               else
+                       return -EINVAL;
+       }
 
-       for (mask.s_addr = 0; m > 0; m--) {
-               mask.s_addr |= bebits;
-               bebits <<= 1;
+       for (temp_mask = 0; m > 0; m--) {
+               temp_mask |= mask_bits;
+               mask_bits >>= 1;
        }
+       mask.s_addr = cpu_to_be32(temp_mask);
+
+       newname.sin_addr.s_addr &= mask.s_addr;
        /*
         * Only allow one writer at a time. Writes should be
         * quite rare and small in any case.
@@ -772,14 +844,18 @@ static ssize_t smk_write_netlbladdr(struct file *file, const char __user *buf,
        mutex_lock(&smk_netlbladdr_lock);
 
        nsa = newname.sin_addr.s_addr;
-       for (skp = smack_netlbladdrs; skp != NULL; skp = skp->smk_next)
+       /* try to find if the prefix is already in the list */
+       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;
@@ -787,23 +863,27 @@ static ssize_t smk_write_netlbladdr(struct file *file, const char __user *buf,
                        rc = 0;
                        skp->smk_host.sin_addr.s_addr = newname.sin_addr.s_addr;
                        skp->smk_mask.s_addr = mask.s_addr;
-                       skp->smk_next = smack_netlbladdrs;
                        skp->smk_label = sp;
-                       smack_netlbladdrs = skp;
+                       smk_netlbladdr_insert(skp);
                }
        } else {
-               rc = netlbl_cfg_unlbl_static_del(&init_net, NULL,
-                       &skp->smk_host.sin_addr, &skp->smk_mask,
-                       PF_INET, &audit_info);
+               /* we delete the unlabeled entry, only if the previous label
+                * wasnt the special CIPSO option */
+               if (skp->smk_label != smack_cipso_option)
+                       rc = netlbl_cfg_unlbl_static_del(&init_net, NULL,
+                                       &skp->smk_host.sin_addr, &skp->smk_mask,
+                                       PF_INET, &audit_info);
+               else
+                       rc = 0;
                skp->smk_label = sp;
        }
 
        /*
         * Now tell netlabel about the single label nature of
         * this host so that incoming packets get labeled.
+        * but only if we didn't get the special CIPSO option
         */
-
-       if (rc == 0)
+       if (rc == 0 && sp != smack_cipso_option)
                rc = netlbl_cfg_unlbl_static_add(&init_net, NULL,
                        &skp->smk_host.sin_addr, &skp->smk_mask, PF_INET,
                        smack_to_secid(skp->smk_label), &audit_info);
@@ -850,7 +930,7 @@ static ssize_t smk_read_doi(struct file *filp, char __user *buf,
 
 /**
  * smk_write_doi - write() for /smack/doi
- * @filp: file pointer, not actually used
+ * @file: file pointer, not actually used
  * @buf: where to get the data from
  * @count: bytes sent
  * @ppos: where to start
@@ -915,7 +995,7 @@ static ssize_t smk_read_direct(struct file *filp, char __user *buf,
 
 /**
  * smk_write_direct - write() for /smack/direct
- * @filp: file pointer, not actually used
+ * @file: file pointer, not actually used
  * @buf: where to get the data from
  * @count: bytes sent
  * @ppos: where to start
@@ -990,7 +1070,7 @@ static ssize_t smk_read_ambient(struct file *filp, char __user *buf,
 
 /**
  * smk_write_ambient - write() for /smack/ambient
- * @filp: file pointer, not actually used
+ * @file: file pointer, not actually used
  * @buf: where to get the data from
  * @count: bytes sent
  * @ppos: where to start
@@ -1065,7 +1145,7 @@ static ssize_t smk_read_onlycap(struct file *filp, char __user *buf,
 
 /**
  * smk_write_onlycap - write() for /smack/onlycap
- * @filp: file pointer, not actually used
+ * @file: file pointer, not actually used
  * @buf: where to get the data from
  * @count: bytes sent
  * @ppos: where to start
@@ -1113,6 +1193,69 @@ static const struct file_operations smk_onlycap_ops = {
 };
 
 /**
+ * smk_read_logging - read() for /smack/logging
+ * @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
+ */
+static ssize_t smk_read_logging(struct file *filp, char __user *buf,
+                               size_t count, loff_t *ppos)
+{
+       char temp[32];
+       ssize_t rc;
+
+       if (*ppos != 0)
+               return 0;
+
+       sprintf(temp, "%d\n", log_policy);
+       rc = simple_read_from_buffer(buf, count, ppos, temp, strlen(temp));
+       return rc;
+}
+
+/**
+ * smk_write_logging - write() for /smack/logging
+ * @file: file pointer, not actually used
+ * @buf: where to get the data from
+ * @count: bytes sent
+ * @ppos: where to start
+ *
+ * Returns number of bytes written or error code, as appropriate
+ */
+static ssize_t smk_write_logging(struct file *file, const char __user *buf,
+                               size_t count, loff_t *ppos)
+{
+       char temp[32];
+       int i;
+
+       if (!capable(CAP_MAC_ADMIN))
+               return -EPERM;
+
+       if (count >= sizeof(temp) || count == 0)
+               return -EINVAL;
+
+       if (copy_from_user(temp, buf, count) != 0)
+               return -EFAULT;
+
+       temp[count] = '\0';
+
+       if (sscanf(temp, "%d", &i) != 1)
+               return -EINVAL;
+       if (i < 0 || i > 3)
+               return -EINVAL;
+       log_policy = i;
+       return count;
+}
+
+
+
+static const struct file_operations smk_logging_ops = {
+       .read           = smk_read_logging,
+       .write          = smk_write_logging,
+};
+/**
  * smk_fill_super - fill the /smackfs superblock
  * @sb: the empty superblock
  * @data: unused
@@ -1142,6 +1285,8 @@ static int smk_fill_super(struct super_block *sb, void *data, int silent)
                        {"netlabel", &smk_netlbladdr_ops, S_IRUGO|S_IWUSR},
                [SMK_ONLYCAP]   =
                        {"onlycap", &smk_onlycap_ops, S_IRUGO|S_IWUSR},
+               [SMK_LOGGING]   =
+                       {"logging", &smk_logging_ops, S_IRUGO|S_IWUSR},
                /* last one */ {""}
        };