]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - net/ipv4/cipso_ipv4.c
NetLabel: check for a CIPSOv4 option before we do call into the CIPSOv4 layer
[linux-2.6-omap-h63xx.git] / net / ipv4 / cipso_ipv4.c
index c4e469ff842d46ca0c0eb367fcb52d69d752c976..fb5d913f5815dd8462a3c9bb69787b3ac963454c 100644 (file)
@@ -43,6 +43,7 @@
 #include <net/tcp.h>
 #include <net/netlabel.h>
 #include <net/cipso_ipv4.h>
+#include <asm/atomic.h>
 #include <asm/bug.h>
 
 struct cipso_v4_domhsh_entry {
@@ -79,7 +80,7 @@ struct cipso_v4_map_cache_entry {
        unsigned char *key;
        size_t key_len;
 
-       struct netlbl_lsm_cache lsm_data;
+       struct netlbl_lsm_cache *lsm_data;
 
        u32 activity;
        struct list_head list;
@@ -188,13 +189,14 @@ static void cipso_v4_doi_domhsh_free(struct rcu_head *entry)
  * @entry: the entry to free
  *
  * Description:
- * This function frees the memory associated with a cache entry.
+ * This function frees the memory associated with a cache entry including the
+ * LSM cache data if there are no longer any users, i.e. reference count == 0.
  *
  */
 static void cipso_v4_cache_entry_free(struct cipso_v4_map_cache_entry *entry)
 {
-       if (entry->lsm_data.free)
-               entry->lsm_data.free(entry->lsm_data.data);
+       if (entry->lsm_data)
+               netlbl_secattr_cache_free(entry->lsm_data);
        kfree(entry->key);
        kfree(entry);
 }
@@ -315,8 +317,9 @@ static int cipso_v4_cache_check(const unsigned char *key,
                    entry->key_len == key_len &&
                    memcmp(entry->key, key, key_len) == 0) {
                        entry->activity += 1;
-                       secattr->cache.free = entry->lsm_data.free;
-                       secattr->cache.data = entry->lsm_data.data;
+                       atomic_inc(&entry->lsm_data->refcount);
+                       secattr->cache = entry->lsm_data;
+                       secattr->flags |= NETLBL_SECATTR_CACHE;
                        if (prev_entry == NULL) {
                                spin_unlock_bh(&cipso_v4_cache[bkt].lock);
                                return 0;
@@ -375,16 +378,15 @@ int cipso_v4_cache_add(const struct sk_buff *skb,
        entry = kzalloc(sizeof(*entry), GFP_ATOMIC);
        if (entry == NULL)
                return -ENOMEM;
-       entry->key = kmalloc(cipso_ptr_len, GFP_ATOMIC);
+       entry->key = kmemdup(cipso_ptr, cipso_ptr_len, GFP_ATOMIC);
        if (entry->key == NULL) {
                ret_val = -ENOMEM;
                goto cache_add_failure;
        }
-       memcpy(entry->key, cipso_ptr, cipso_ptr_len);
        entry->key_len = cipso_ptr_len;
        entry->hash = cipso_v4_map_cache_hash(cipso_ptr, cipso_ptr_len);
-       entry->lsm_data.free = secattr->cache.free;
-       entry->lsm_data.data = secattr->cache.data;
+       atomic_inc(&secattr->cache->refcount);
+       entry->lsm_data = secattr->cache;
 
        bkt = entry->hash & (CIPSO_V4_CACHE_BUCKETBITS - 1);
        spin_lock_bh(&cipso_v4_cache[bkt].lock);
@@ -485,7 +487,7 @@ doi_add_failure_rlock:
  *
  */
 int cipso_v4_doi_remove(u32 doi,
-                       u32 audit_secid,
+                       struct netlbl_audit *audit_info,
                        void (*callback) (struct rcu_head * head))
 {
        struct cipso_v4_doi *doi_def;
@@ -506,7 +508,7 @@ int cipso_v4_doi_remove(u32 doi,
                list_for_each_entry_rcu(dom_iter, &doi_def->dom_list, list)
                        if (dom_iter->valid)
                                netlbl_domhsh_remove(dom_iter->domain,
-                                                    audit_secid);
+                                                    audit_info);
                cipso_v4_cache_invalidate();
                rcu_read_unlock();
 
@@ -771,13 +773,15 @@ static int cipso_v4_map_cat_rbm_valid(const struct cipso_v4_doi *doi_def,
 {
        int cat = -1;
        u32 bitmap_len_bits = bitmap_len * 8;
-       u32 cipso_cat_size = doi_def->map.std->cat.cipso_size;
-       u32 *cipso_array = doi_def->map.std->cat.cipso;
+       u32 cipso_cat_size;
+       u32 *cipso_array;
 
        switch (doi_def->type) {
        case CIPSO_V4_MAP_PASS:
                return 0;
        case CIPSO_V4_MAP_STD:
+               cipso_cat_size = doi_def->map.std->cat.cipso_size;
+               cipso_array = doi_def->map.std->cat.cipso;
                for (;;) {
                        cat = cipso_v4_bitmap_walk(bitmap,
                                                   bitmap_len_bits,
@@ -823,19 +827,21 @@ static int cipso_v4_map_cat_rbm_hton(const struct cipso_v4_doi *doi_def,
        u32 net_spot_max = 0;
        u32 host_clen_bits = host_cat_len * 8;
        u32 net_clen_bits = net_cat_len * 8;
-       u32 host_cat_size = doi_def->map.std->cat.local_size;
-       u32 *host_cat_array = doi_def->map.std->cat.local;
+       u32 host_cat_size;
+       u32 *host_cat_array;
 
        switch (doi_def->type) {
        case CIPSO_V4_MAP_PASS:
-               net_spot_max = host_cat_len - 1;
-               while (net_spot_max > 0 && host_cat[net_spot_max] == 0)
+               net_spot_max = host_cat_len;
+               while (net_spot_max > 0 && host_cat[net_spot_max - 1] == 0)
                        net_spot_max--;
                if (net_spot_max > net_cat_len)
                        return -EINVAL;
                memcpy(net_cat, host_cat, net_spot_max);
                return net_spot_max;
        case CIPSO_V4_MAP_STD:
+               host_cat_size = doi_def->map.std->cat.local_size;
+               host_cat_array = doi_def->map.std->cat.local;
                for (;;) {
                        host_spot = cipso_v4_bitmap_walk(host_cat,
                                                         host_clen_bits,
@@ -891,8 +897,8 @@ static int cipso_v4_map_cat_rbm_ntoh(const struct cipso_v4_doi *doi_def,
        int net_spot = -1;
        u32 net_clen_bits = net_cat_len * 8;
        u32 host_clen_bits = host_cat_len * 8;
-       u32 net_cat_size = doi_def->map.std->cat.cipso_size;
-       u32 *net_cat_array = doi_def->map.std->cat.cipso;
+       u32 net_cat_size;
+       u32 *net_cat_array;
 
        switch (doi_def->type) {
        case CIPSO_V4_MAP_PASS:
@@ -901,6 +907,8 @@ static int cipso_v4_map_cat_rbm_ntoh(const struct cipso_v4_doi *doi_def,
                memcpy(host_cat, net_cat, net_cat_len);
                return net_cat_len;
        case CIPSO_V4_MAP_STD:
+               net_cat_size = doi_def->map.std->cat.cipso_size;
+               net_cat_array = doi_def->map.std->cat.cipso;
                for (;;) {
                        net_spot = cipso_v4_bitmap_walk(net_cat,
                                                        net_clen_bits,
@@ -958,7 +966,7 @@ static int cipso_v4_gentag_hdr(const struct cipso_v4_doi *doi_def,
 
        buf[0] = IPOPT_CIPSO;
        buf[1] = CIPSO_V4_HDR_LEN + len;
-       *(u32 *)&buf[2] = htonl(doi_def->doi);
+       *(__be32 *)&buf[2] = htonl(doi_def->doi);
 
        return 0;
 }
@@ -984,12 +992,15 @@ static int cipso_v4_gentag_rbm(const struct cipso_v4_doi *doi_def,
                               unsigned char **buffer,
                               u32 *buffer_len)
 {
-       int ret_val = -EPERM;
+       int ret_val;
        unsigned char *buf = NULL;
        u32 buf_len;
        u32 level;
 
-       if (secattr->mls_cat) {
+       if ((secattr->flags & NETLBL_SECATTR_MLS_LVL) == 0)
+               return -EPERM;
+
+       if (secattr->flags & NETLBL_SECATTR_MLS_CAT) {
                buf = kzalloc(CIPSO_V4_HDR_LEN + 4 + CIPSO_V4_TAG1_CAT_LEN,
                              GFP_ATOMIC);
                if (buf == NULL)
@@ -1006,10 +1017,10 @@ static int cipso_v4_gentag_rbm(const struct cipso_v4_doi *doi_def,
                /* This will send packets using the "optimized" format when
                 * possibile as specified in  section 3.4.2.6 of the
                 * CIPSO draft. */
-               if (cipso_v4_rbm_optfmt && (ret_val > 0 && ret_val < 10))
-                       ret_val = 10;
-
-               buf_len = 4 + ret_val;
+               if (cipso_v4_rbm_optfmt && ret_val > 0 && ret_val <= 10)
+                       buf_len = 14;
+               else
+                       buf_len = 4 + ret_val;
        } else {
                buf = kzalloc(CIPSO_V4_HDR_LEN + 4, GFP_ATOMIC);
                if (buf == NULL)
@@ -1063,7 +1074,7 @@ static int cipso_v4_parsetag_rbm(const struct cipso_v4_doi *doi_def,
        if (ret_val != 0)
                return ret_val;
        secattr->mls_lvl = level;
-       secattr->mls_lvl_vld = 1;
+       secattr->flags |= NETLBL_SECATTR_MLS_LVL;
 
        if (tag_len > 4) {
                switch (doi_def->type) {
@@ -1087,8 +1098,10 @@ static int cipso_v4_parsetag_rbm(const struct cipso_v4_doi *doi_def,
                if (ret_val < 0) {
                        kfree(secattr->mls_cat);
                        return ret_val;
+               } else if (ret_val > 0) {
+                       secattr->mls_cat_len = ret_val;
+                       secattr->flags |= NETLBL_SECATTR_MLS_CAT;
                }
-               secattr->mls_cat_len = ret_val;
        }
 
        return 0;
@@ -1132,7 +1145,7 @@ int cipso_v4_validate(unsigned char **option)
        }
 
        rcu_read_lock();
-       doi_def = cipso_v4_doi_getdef(ntohl(*((u32 *)&opt[2])));
+       doi_def = cipso_v4_doi_getdef(ntohl(*((__be32 *)&opt[2])));
        if (doi_def == NULL) {
                err_offset = 2;
                goto validate_return_locked;
@@ -1299,7 +1312,8 @@ int cipso_v4_socket_setattr(const struct socket *sock,
 
        /* We can't use ip_options_get() directly because it makes a call to
         * ip_options_get_alloc() which allocates memory with GFP_KERNEL and
-        * we can't block here. */
+        * we won't always have CAP_NET_RAW even though we _always_ want to
+        * set the IPOPT_CIPSO option. */
        opt_len = (buf_len + 3) & ~3;
        opt = kzalloc(sizeof(*opt) + opt_len, GFP_ATOMIC);
        if (opt == NULL) {
@@ -1309,11 +1323,9 @@ int cipso_v4_socket_setattr(const struct socket *sock,
        memcpy(opt->__data, buf, buf_len);
        opt->optlen = opt_len;
        opt->is_data = 1;
+       opt->cipso = sizeof(struct iphdr);
        kfree(buf);
        buf = NULL;
-       ret_val = ip_options_compile(opt, NULL);
-       if (ret_val != 0)
-               goto socket_setattr_failure;
 
        sk_inet = inet_sk(sk);
        if (sk_inet->is_icsk) {
@@ -1363,7 +1375,7 @@ int cipso_v4_sock_getattr(struct sock *sk, struct netlbl_lsm_secattr *secattr)
        if (ret_val == 0)
                return ret_val;
 
-       doi = ntohl(*(u32 *)&cipso_ptr[2]);
+       doi = ntohl(*(__be32 *)&cipso_ptr[2]);
        rcu_read_lock();
        doi_def = cipso_v4_doi_getdef(doi);
        if (doi_def == NULL) {
@@ -1423,13 +1435,11 @@ int cipso_v4_skbuff_getattr(const struct sk_buff *skb,
        u32 doi;
        struct cipso_v4_doi *doi_def;
 
-       if (!CIPSO_V4_OPTEXIST(skb))
-               return -ENOMSG;
        cipso_ptr = CIPSO_V4_OPTPTR(skb);
        if (cipso_v4_cache_check(cipso_ptr, cipso_ptr[1], secattr) == 0)
                return 0;
 
-       doi = ntohl(*(u32 *)&cipso_ptr[2]);
+       doi = ntohl(*(__be32 *)&cipso_ptr[2]);
        rcu_read_lock();
        doi_def = cipso_v4_doi_getdef(doi);
        if (doi_def == NULL)