smack: fixes for unlabeled host support
etienne [Wed, 4 Mar 2009 06:33:51 +0000 (07:33 +0100)]
The following patch (against 2.6.29rc5) fixes a few issues in the
smack/netlabel "unlabeled host support" functionnality that was added in
2.6.29rc.  It should go in before -final.

1) smack_host_label disregard a "0.0.0.0/0 @" rule (or other label),
preventing 'tagged' tasks to access Internet (many systems drop packets with
IP options)

2) netmasks were not handled correctly, they were stored in a way _not
equivalent_ to conversion to be32 (it was equivalent for /0, /8, /16, /24,
/32 masks but not other masks)

3) smack_netlbladdr prefixes (IP/mask) were not consistent (mask&IP was not
done), so there could have been different list entries for the same IP
prefix; if those entries had different labels, well ...

4) they were not sorted

1) 2) 3) are bugs, 4) is a more cosmetic issue.
The patch :

-creates a new helper smk_netlbladdr_insert to insert a smk_netlbladdr,
-sorted by netmask length

-use the new sorted nature of  smack_netlbladdrs list to simplify
 smack_host_label : the first match _will_ be the more specific

-corrects endianness issues in smk_write_netlbladdr &  netlbladdr_seq_show

Signed-off-by: <etienne.basset@numericable.fr>
Acked-by: Casey Schaufler <casey@schaufler-ca.com>
Reviewed-by: Paul Moore <paul.moore@hp.com>
Signed-off-by: James Morris <jmorris@namei.org>

security/smack/smack_lsm.c
security/smack/smackfs.c

index 0278bc0..e7ded13 100644 (file)
@@ -1498,58 +1498,31 @@ static int smack_socket_post_create(struct socket *sock, int family,
  * looks for host based access restrictions
  *
  * This version will only be appropriate for really small
- * sets of single label hosts. Because of the masking
- * it cannot shortcut out on the first match. There are
- * numerious ways to address the problem, but none of them
- * have been applied here.
+ * sets of single label hosts.
  *
  * Returns the label of the far end or NULL if it's not special.
  */
 static char *smack_host_label(struct sockaddr_in *sip)
 {
        struct smk_netlbladdr *snp;
-       char *bestlabel = NULL;
        struct in_addr *siap = &sip->sin_addr;
-       struct in_addr *liap;
-       struct in_addr *miap;
-       struct in_addr bestmask;
 
        if (siap->s_addr == 0)
                return NULL;
 
-       bestmask.s_addr = 0;
-
        for (snp = smack_netlbladdrs; snp != NULL; snp = snp->smk_next) {
-               liap = &snp->smk_host.sin_addr;
-               miap = &snp->smk_mask;
-               /*
-                * If the addresses match after applying the list entry mask
-                * the entry matches the address. If it doesn't move along to
-                * the next entry.
-                */
-               if ((liap->s_addr & miap->s_addr) !=
-                   (siap->s_addr & miap->s_addr))
-                       continue;
                /*
-                * If the list entry mask identifies a single address
-                * it can't get any more specific.
+                * we break after finding the first match because
+                * the list is sorted from longest to shortest mask
+                * so we have found the most specific match
                 */
-               if (miap->s_addr == 0xffffffff)
+               if ((&snp->smk_host.sin_addr)->s_addr  ==
+                       (siap->s_addr & (&snp->smk_mask)->s_addr)) {
                        return snp->smk_label;
-               /*
-                * If the list entry mask is less specific than the best
-                * already found this entry is uninteresting.
-                */
-               if ((miap->s_addr | bestmask.s_addr) == bestmask.s_addr)
-                       continue;
-               /*
-                * This is better than any entry found so far.
-                */
-               bestmask.s_addr = miap->s_addr;
-               bestlabel = snp->smk_label;
+               }
        }
 
-       return bestlabel;
+       return NULL;
 }
 
 /**
index 8e42800..51f0efc 100644 (file)
@@ -650,10 +650,6 @@ static void *netlbladdr_seq_next(struct seq_file *s, void *v, loff_t *pos)
 
        return skp;
 }
-/*
-#define BEMASK 0x80000000
-*/
-#define BEMASK 0x00000001
 #define BEBITS (sizeof(__be32) * 8)
 
 /*
@@ -663,12 +659,10 @@ 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;
+       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);
@@ -702,6 +696,42 @@ 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)
+ */
+static void smk_netlbladdr_insert(struct smk_netlbladdr *new)
+{
+       struct smk_netlbladdr *m;
+
+       if (smack_netlbladdrs == NULL) {
+               smack_netlbladdrs = new;
+               return;
+       }
+
+       /* 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;
+               return;
+       }
+       for (m = smack_netlbladdrs; m != NULL; m = m->smk_next) {
+               if (m->smk_next == NULL) {
+                       m->smk_next = new;
+                       return;
+               }
+               if (new->smk_mask.s_addr > m->smk_next->smk_mask.s_addr) {
+                       new->smk_next = m->smk_next;
+                       m->smk_next = new;
+                       return;
+               }
+       }
+}
+
+
+/**
  * smk_write_netlbladdr - write() for /smack/netlabel
  * @filp: file pointer, not actually used
  * @buf: where to get the data from
@@ -724,8 +754,9 @@ 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;
-       __be32 bebits = BEMASK;
+       u32 mask_bits = (1<<31);
        __be32 nsa;
+       u32 temp_mask;
 
        /*
         * Must have privilege.
@@ -761,10 +792,13 @@ static ssize_t smk_write_netlbladdr(struct file *file, const char __user *buf,
        if (sp == NULL)
                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,6 +806,7 @@ static ssize_t smk_write_netlbladdr(struct file *file, const char __user *buf,
        mutex_lock(&smk_netlbladdr_lock);
 
        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)
                if (skp->smk_host.sin_addr.s_addr == nsa &&
                    skp->smk_mask.s_addr == mask.s_addr)
@@ -787,9 +822,8 @@ 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,