]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - security/smack/smackfs.c
smackfs load append mode fix
[linux-2.6-omap-h63xx.git] / security / smack / smackfs.c
index ca257dfdc75d08ccd52bddce447d5bb331a92242..8e42800878f468912c56cece9336e7c8c6bfa727 100644 (file)
@@ -20,6 +20,7 @@
 #include <linux/vmalloc.h>
 #include <linux/security.h>
 #include <linux/mutex.h>
+#include <net/net_namespace.h>
 #include <net/netlabel.h>
 #include <net/cipso_ipv4.h>
 #include <linux/seq_file.h>
@@ -38,7 +39,7 @@ enum smk_inos {
        SMK_DOI         = 5,    /* CIPSO DOI */
        SMK_DIRECT      = 6,    /* CIPSO level indicating direct label */
        SMK_AMBIENT     = 7,    /* internet ambient label */
-       SMK_NLTYPE      = 8,    /* label scheme to use by default */
+       SMK_NETLBLADDR  = 8,    /* single label hosts */
        SMK_ONLYCAP     = 9,    /* the only "capable" label */
 };
 
@@ -48,6 +49,7 @@ enum smk_inos {
 static DEFINE_MUTEX(smack_list_lock);
 static DEFINE_MUTEX(smack_cipso_lock);
 static DEFINE_MUTEX(smack_ambient_lock);
+static DEFINE_MUTEX(smk_netlbladdr_lock);
 
 /*
  * This is the "ambient" label for network traffic.
@@ -56,12 +58,6 @@ static DEFINE_MUTEX(smack_ambient_lock);
  */
 char *smack_net_ambient = smack_known_floor.smk_known;
 
-/*
- * This is the default packet marking scheme for network traffic.
- * It can be reset via smackfs/nltype
- */
-int smack_net_nltype = NETLBL_NLTYPE_CIPSOV4;
-
 /*
  * This is the level in a CIPSO header that indicates a
  * smack label is contained directly in the category set.
@@ -79,6 +75,13 @@ int smack_cipso_direct = SMACK_CIPSO_DIRECT_DEFAULT;
  */
 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.
+ */
+struct smk_netlbladdr *smack_netlbladdrs;
+
 static int smk_cipso_doi_value = SMACK_CIPSO_DOI_DEFAULT;
 struct smk_list_entry *smack_list;
 
@@ -104,6 +107,24 @@ struct smk_list_entry *smack_list;
 #define SMK_ACCESSLEN (sizeof(SMK_ACCESS) - 1)
 #define SMK_LOADLEN   (SMK_LABELLEN + SMK_LABELLEN + SMK_ACCESSLEN)
 
+/**
+ * smk_netlabel_audit_set - fill a netlbl_audit struct
+ * @nap: structure to fill
+ */
+static void smk_netlabel_audit_set(struct netlbl_audit *nap)
+{
+       nap->loginuid = audit_get_loginuid(current);
+       nap->sessionid = audit_get_sessionid(current);
+       nap->secid = smack_to_secid(current_security());
+}
+
+/*
+ * Values for parsing single label host rules
+ * "1.2.3.4 X"
+ * "192.168.138.129/32 abcdefghijklmnopqrstuvw"
+ */
+#define SMK_NETLBLADDRMIN      9
+#define SMK_NETLBLADDRMAX      42
 
 /*
  * Seq_file read operations for /smack/load
@@ -185,11 +206,15 @@ static int smk_open_load(struct inode *inode, struct file *file)
  * the subject/object pair and replaces the access that was
  * there. If the pair isn't found add it with the specified
  * access.
+ *
+ * Returns 0 if nothing goes wrong or -ENOMEM if it fails
+ * during the allocation of the new pair to add.
  */
-static void smk_set_access(struct smack_rule *srp)
+static int smk_set_access(struct smack_rule *srp)
 {
        struct smk_list_entry *sp;
        struct smk_list_entry *newp;
+       int ret = 0;
 
        mutex_lock(&smack_list_lock);
 
@@ -202,14 +227,20 @@ static void smk_set_access(struct smack_rule *srp)
 
        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;
        }
 
+out:
        mutex_unlock(&smack_list_lock);
 
-       return;
+       return ret;
 }
 
 /**
@@ -303,14 +334,16 @@ 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;
        }
 
-       smk_set_access(&rule);
-       rc = count;
+       rc = smk_set_access(&rule);
+
+       if (!rc)
+               rc = count;
 
 out:
        kfree(data);
@@ -332,13 +365,11 @@ static void smk_cipso_doi(void)
 {
        int rc;
        struct cipso_v4_doi *doip;
-       struct netlbl_audit audit_info;
+       struct netlbl_audit nai;
 
-       audit_info.loginuid = audit_get_loginuid(current);
-       audit_info.sessionid = audit_get_sessionid(current);
-       audit_info.secid = smack_to_secid(current_security());
+       smk_netlabel_audit_set(&nai);
 
-       rc = netlbl_cfg_map_del(NULL, &audit_info);
+       rc = netlbl_cfg_map_del(NULL, PF_INET, NULL, NULL, &nai);
        if (rc != 0)
                printk(KERN_WARNING "%s:%d remove rc = %d\n",
                       __func__, __LINE__, rc);
@@ -353,11 +384,19 @@ static void smk_cipso_doi(void)
        for (rc = 1; rc < CIPSO_V4_TAG_MAXCNT; rc++)
                doip->tags[rc] = CIPSO_V4_TAG_INVALID;
 
-       rc = netlbl_cfg_cipsov4_add_map(doip, NULL, &audit_info);
+       rc = netlbl_cfg_cipsov4_add(doip, &nai);
        if (rc != 0) {
-               printk(KERN_WARNING "%s:%d add rc = %d\n",
+               printk(KERN_WARNING "%s:%d cipso add rc = %d\n",
                       __func__, __LINE__, rc);
                kfree(doip);
+               return;
+       }
+       rc = netlbl_cfg_cipsov4_map_add(doip->doi, NULL, NULL, NULL, &nai);
+       if (rc != 0) {
+               printk(KERN_WARNING "%s:%d map add rc = %d\n",
+                      __func__, __LINE__, rc);
+               kfree(doip);
+               return;
        }
 }
 
@@ -367,20 +406,19 @@ static void smk_cipso_doi(void)
 static void smk_unlbl_ambient(char *oldambient)
 {
        int rc;
-       struct netlbl_audit audit_info;
+       struct netlbl_audit nai;
 
-       audit_info.loginuid = audit_get_loginuid(current);
-       audit_info.sessionid = audit_get_sessionid(current);
-       audit_info.secid = smack_to_secid(current_security());
+       smk_netlabel_audit_set(&nai);
 
        if (oldambient != NULL) {
-               rc = netlbl_cfg_map_del(oldambient, &audit_info);
+               rc = netlbl_cfg_map_del(oldambient, PF_INET, NULL, NULL, &nai);
                if (rc != 0)
                        printk(KERN_WARNING "%s:%d remove rc = %d\n",
                               __func__, __LINE__, rc);
        }
 
-       rc = netlbl_cfg_unlbl_add_map(smack_net_ambient, &audit_info);
+       rc = netlbl_cfg_unlbl_map_add(smack_net_ambient, PF_INET,
+                                     NULL, NULL, &nai);
        if (rc != 0)
                printk(KERN_WARNING "%s:%d add rc = %d\n",
                       __func__, __LINE__, rc);
@@ -531,7 +569,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;
@@ -591,6 +629,201 @@ static const struct file_operations smk_cipso_ops = {
        .release        = seq_release,
 };
 
+/*
+ * Seq_file read operations for /smack/netlabel
+ */
+
+static void *netlbladdr_seq_start(struct seq_file *s, loff_t *pos)
+{
+       if (*pos == SEQ_READ_FINISHED)
+               return NULL;
+
+       return smack_netlbladdrs;
+}
+
+static void *netlbladdr_seq_next(struct seq_file *s, void *v, loff_t *pos)
+{
+       struct smk_netlbladdr *skp = ((struct smk_netlbladdr *) v)->smk_next;
+
+       if (skp == NULL)
+               *pos = SEQ_READ_FINISHED;
+
+       return skp;
+}
+/*
+#define BEMASK 0x80000000
+*/
+#define BEMASK 0x00000001
+#define BEBITS (sizeof(__be32) * 8)
+
+/*
+ * Print host/label pairs
+ */
+static int netlbladdr_seq_show(struct seq_file *s, void *v)
+{
+       struct smk_netlbladdr *skp = (struct smk_netlbladdr *) v;
+       unsigned char *hp = (char *) &skp->smk_host.sin_addr.s_addr;
+       __be32 bebits;
+       int maskn = 0;
+
+       for (bebits = BEMASK; bebits != 0; maskn++, bebits <<= 1)
+               if ((skp->smk_mask.s_addr & bebits) == 0)
+                       break;
+
+       seq_printf(s, "%u.%u.%u.%u/%d %s\n",
+               hp[0], hp[1], hp[2], hp[3], maskn, skp->smk_label);
+
+       return 0;
+}
+
+static void netlbladdr_seq_stop(struct seq_file *s, void *v)
+{
+       /* No-op */
+}
+
+static struct seq_operations netlbladdr_seq_ops = {
+       .start = netlbladdr_seq_start,
+       .stop  = netlbladdr_seq_stop,
+       .next  = netlbladdr_seq_next,
+       .show  = netlbladdr_seq_show,
+};
+
+/**
+ * smk_open_netlbladdr - open() for /smack/netlabel
+ * @inode: inode structure representing file
+ * @file: "netlabel" file pointer
+ *
+ * Connect our netlbladdr_seq_* operations with /smack/netlabel
+ * file_operations
+ */
+static int smk_open_netlbladdr(struct inode *inode, struct file *file)
+{
+       return seq_open(file, &netlbladdr_seq_ops);
+}
+
+/**
+ * smk_write_netlbladdr - write() for /smack/netlabel
+ * @filp: file pointer, not actually used
+ * @buf: where to get the data from
+ * @count: bytes sent
+ * @ppos: where to start
+ *
+ * Accepts only one netlbladdr per write call.
+ * Returns number of bytes written or error code, as appropriate
+ */
+static ssize_t smk_write_netlbladdr(struct file *file, const char __user *buf,
+                               size_t count, loff_t *ppos)
+{
+       struct smk_netlbladdr *skp;
+       struct sockaddr_in newname;
+       char smack[SMK_LABELLEN];
+       char *sp;
+       char data[SMK_NETLBLADDRMAX];
+       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;
+       __be32 nsa;
+
+       /*
+        * Must have privilege.
+        * No partial writes.
+        * Enough data must be present.
+        * "<addr/mask, as a.b.c.d/e><space><label>"
+        * "<addr, as a.b.c.d><space><label>"
+        */
+       if (!capable(CAP_MAC_ADMIN))
+               return -EPERM;
+       if (*ppos != 0)
+               return -EINVAL;
+       if (count < SMK_NETLBLADDRMIN || count > SMK_NETLBLADDRMAX)
+               return -EINVAL;
+       if (copy_from_user(data, buf, count) != 0)
+               return -EFAULT;
+
+       data[count] = '\0';
+
+       rc = sscanf(data, "%hhd.%hhd.%hhd.%hhd/%d %s",
+               &host[0], &host[1], &host[2], &host[3], &m, smack);
+       if (rc != 6) {
+               rc = sscanf(data, "%hhd.%hhd.%hhd.%hhd %s",
+                       &host[0], &host[1], &host[2], &host[3], smack);
+               if (rc != 5)
+                       return -EINVAL;
+               m = BEBITS;
+       }
+       if (m > BEBITS)
+               return -EINVAL;
+
+       sp = smk_import(smack, 0);
+       if (sp == NULL)
+               return -EINVAL;
+
+       for (mask.s_addr = 0; m > 0; m--) {
+               mask.s_addr |= bebits;
+               bebits <<= 1;
+       }
+       /*
+        * Only allow one writer at a time. Writes should be
+        * quite rare and small in any case.
+        */
+       mutex_lock(&smk_netlbladdr_lock);
+
+       nsa = newname.sin_addr.s_addr;
+       for (skp = smack_netlbladdrs; skp != NULL; skp = skp->smk_next)
+               if (skp->smk_host.sin_addr.s_addr == nsa &&
+                   skp->smk_mask.s_addr == mask.s_addr)
+                       break;
+
+       smk_netlabel_audit_set(&audit_info);
+
+       if (skp == NULL) {
+               skp = kzalloc(sizeof(*skp), GFP_KERNEL);
+               if (skp == NULL)
+                       rc = -ENOMEM;
+               else {
+                       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;
+               }
+       } else {
+               rc = netlbl_cfg_unlbl_static_del(&init_net, NULL,
+                       &skp->smk_host.sin_addr, &skp->smk_mask,
+                       PF_INET, &audit_info);
+               skp->smk_label = sp;
+       }
+
+       /*
+        * Now tell netlabel about the single label nature of
+        * this host so that incoming packets get labeled.
+        */
+
+       if (rc == 0)
+               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);
+
+       if (rc == 0)
+               rc = count;
+
+       mutex_unlock(&smk_netlbladdr_lock);
+
+       return rc;
+}
+
+static const struct file_operations smk_netlbladdr_ops = {
+       .open           = smk_open_netlbladdr,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .write          = smk_write_netlbladdr,
+       .release        = seq_release,
+};
+
 /**
  * smk_read_doi - read() for /smack/doi
  * @filp: file pointer, not actually used
@@ -879,110 +1112,6 @@ static const struct file_operations smk_onlycap_ops = {
        .write          = smk_write_onlycap,
 };
 
-struct option_names {
-       int     o_number;
-       char    *o_name;
-       char    *o_alias;
-};
-
-static struct option_names netlbl_choices[] = {
-       { NETLBL_NLTYPE_RIPSO,
-               NETLBL_NLTYPE_RIPSO_NAME,       "ripso" },
-       { NETLBL_NLTYPE_CIPSOV4,
-               NETLBL_NLTYPE_CIPSOV4_NAME,     "cipsov4" },
-       { NETLBL_NLTYPE_CIPSOV4,
-               NETLBL_NLTYPE_CIPSOV4_NAME,     "cipso" },
-       { NETLBL_NLTYPE_CIPSOV6,
-               NETLBL_NLTYPE_CIPSOV6_NAME,     "cipsov6" },
-       { NETLBL_NLTYPE_UNLABELED,
-               NETLBL_NLTYPE_UNLABELED_NAME,   "unlabeled" },
-};
-
-/**
- * smk_read_nltype - read() for /smack/nltype
- * @filp: file pointer, not actually used
- * @buf: where to put the result
- * @count: maximum to send along
- * @ppos: where to start
- *
- * Returns number of bytes read or error code, as appropriate
- */
-static ssize_t smk_read_nltype(struct file *filp, char __user *buf,
-                              size_t count, loff_t *ppos)
-{
-       char bound[40];
-       ssize_t rc;
-       int i;
-
-       if (count < SMK_LABELLEN)
-               return -EINVAL;
-
-       if (*ppos != 0)
-               return 0;
-
-       sprintf(bound, "unknown");
-
-       for (i = 0; i < ARRAY_SIZE(netlbl_choices); i++)
-               if (smack_net_nltype == netlbl_choices[i].o_number) {
-                       sprintf(bound, "%s", netlbl_choices[i].o_name);
-                       break;
-               }
-
-       rc = simple_read_from_buffer(buf, count, ppos, bound, strlen(bound));
-
-       return rc;
-}
-
-/**
- * smk_write_nltype - write() for /smack/nltype
- * @filp: 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_nltype(struct file *file, const char __user *buf,
-                               size_t count, loff_t *ppos)
-{
-       char bound[40];
-       char *cp;
-       int i;
-
-       if (!capable(CAP_MAC_ADMIN))
-               return -EPERM;
-
-       if (count >= 40)
-               return -EINVAL;
-
-       if (copy_from_user(bound, buf, count) != 0)
-               return -EFAULT;
-
-       bound[count] = '\0';
-       cp = strchr(bound, ' ');
-       if (cp != NULL)
-               *cp = '\0';
-       cp = strchr(bound, '\n');
-       if (cp != NULL)
-               *cp = '\0';
-
-       for (i = 0; i < ARRAY_SIZE(netlbl_choices); i++)
-               if (strcmp(bound, netlbl_choices[i].o_name) == 0 ||
-                   strcmp(bound, netlbl_choices[i].o_alias) == 0) {
-                       smack_net_nltype = netlbl_choices[i].o_number;
-                       return count;
-               }
-       /*
-        * Not a valid choice.
-        */
-       return -EINVAL;
-}
-
-static const struct file_operations smk_nltype_ops = {
-       .read           = smk_read_nltype,
-       .write          = smk_write_nltype,
-};
-
 /**
  * smk_fill_super - fill the /smackfs superblock
  * @sb: the empty superblock
@@ -1009,8 +1138,8 @@ static int smk_fill_super(struct super_block *sb, void *data, int silent)
                        {"direct", &smk_direct_ops, S_IRUGO|S_IWUSR},
                [SMK_AMBIENT]   =
                        {"ambient", &smk_ambient_ops, S_IRUGO|S_IWUSR},
-               [SMK_NLTYPE]    =
-                       {"nltype", &smk_nltype_ops, S_IRUGO|S_IWUSR},
+               [SMK_NETLBLADDR] =
+                       {"netlabel", &smk_netlbladdr_ops, S_IRUGO|S_IWUSR},
                [SMK_ONLYCAP]   =
                        {"onlycap", &smk_onlycap_ops, S_IRUGO|S_IWUSR},
                /* last one */ {""}