Merge branch 'master' of git://git.infradead.org/users/pcmoore/lblnet-2.6_next into...
authorJames Morris <jmorris@namei.org>
Fri, 10 Oct 2008 22:26:14 +0000 (09:26 +1100)
committerJames Morris <jmorris@namei.org>
Fri, 10 Oct 2008 22:26:14 +0000 (09:26 +1100)
22 files changed:
include/net/cipso_ipv4.h
include/net/netlabel.h
net/ipv4/cipso_ipv4.c
net/ipv4/ip_options.c
net/netlabel/Makefile
net/netlabel/netlabel_addrlist.c [new file with mode: 0644]
net/netlabel/netlabel_addrlist.h [new file with mode: 0644]
net/netlabel/netlabel_cipso_v4.c
net/netlabel/netlabel_cipso_v4.h
net/netlabel/netlabel_domainhash.c
net/netlabel/netlabel_domainhash.h
net/netlabel/netlabel_kapi.c
net/netlabel/netlabel_mgmt.c
net/netlabel/netlabel_mgmt.h
net/netlabel/netlabel_unlabeled.c
security/selinux/hooks.c
security/selinux/include/netlabel.h
security/selinux/include/objsec.h
security/selinux/netlabel.c
security/selinux/ss/services.c
security/smack/smack_lsm.c
security/smack/smackfs.c

index a6bb945..9909774 100644 (file)
 #include <linux/net.h>
 #include <linux/skbuff.h>
 #include <net/netlabel.h>
+#include <asm/atomic.h>
 
 /* known doi values */
 #define CIPSO_V4_DOI_UNKNOWN          0x00000000
 
-/* tag types */
+/* standard tag types */
 #define CIPSO_V4_TAG_INVALID          0
 #define CIPSO_V4_TAG_RBITMAP          1
 #define CIPSO_V4_TAG_ENUM             2
 #define CIPSO_V4_TAG_PBITMAP          6
 #define CIPSO_V4_TAG_FREEFORM         7
 
+/* non-standard tag types (tags > 127) */
+#define CIPSO_V4_TAG_LOCAL            128
+
 /* doi mapping types */
 #define CIPSO_V4_MAP_UNKNOWN          0
-#define CIPSO_V4_MAP_STD              1
+#define CIPSO_V4_MAP_TRANS            1
 #define CIPSO_V4_MAP_PASS             2
+#define CIPSO_V4_MAP_LOCAL            3
 
 /* limits */
 #define CIPSO_V4_MAX_REM_LVLS         255
@@ -79,10 +84,9 @@ struct cipso_v4_doi {
        } map;
        u8 tags[CIPSO_V4_TAG_MAXCNT];
 
-       u32 valid;
+       atomic_t refcount;
        struct list_head list;
        struct rcu_head rcu;
-       struct list_head dom_list;
 };
 
 /* Standard CIPSO mapping table */
@@ -128,25 +132,26 @@ extern int cipso_v4_rbm_strictvalid;
 
 #ifdef CONFIG_NETLABEL
 int cipso_v4_doi_add(struct cipso_v4_doi *doi_def);
-int cipso_v4_doi_remove(u32 doi,
-                       struct netlbl_audit *audit_info,
-                       void (*callback) (struct rcu_head * head));
+void cipso_v4_doi_free(struct cipso_v4_doi *doi_def);
+int cipso_v4_doi_remove(u32 doi, struct netlbl_audit *audit_info);
 struct cipso_v4_doi *cipso_v4_doi_getdef(u32 doi);
+void cipso_v4_doi_putdef(struct cipso_v4_doi *doi_def);
 int cipso_v4_doi_walk(u32 *skip_cnt,
                     int (*callback) (struct cipso_v4_doi *doi_def, void *arg),
                     void *cb_arg);
-int cipso_v4_doi_domhsh_add(struct cipso_v4_doi *doi_def, const char *domain);
-int cipso_v4_doi_domhsh_remove(struct cipso_v4_doi *doi_def,
-                              const char *domain);
 #else
 static inline int cipso_v4_doi_add(struct cipso_v4_doi *doi_def)
 {
        return -ENOSYS;
 }
 
+static inline void cipso_v4_doi_free(struct cipso_v4_doi *doi_def)
+{
+       return;
+}
+
 static inline int cipso_v4_doi_remove(u32 doi,
-                                   struct netlbl_audit *audit_info,
-                                   void (*callback) (struct rcu_head * head))
+                                     struct netlbl_audit *audit_info)
 {
        return 0;
 }
@@ -206,10 +211,15 @@ void cipso_v4_error(struct sk_buff *skb, int error, u32 gateway);
 int cipso_v4_sock_setattr(struct sock *sk,
                          const struct cipso_v4_doi *doi_def,
                          const struct netlbl_lsm_secattr *secattr);
+void cipso_v4_sock_delattr(struct sock *sk);
 int cipso_v4_sock_getattr(struct sock *sk, struct netlbl_lsm_secattr *secattr);
+int cipso_v4_skbuff_setattr(struct sk_buff *skb,
+                           const struct cipso_v4_doi *doi_def,
+                           const struct netlbl_lsm_secattr *secattr);
+int cipso_v4_skbuff_delattr(struct sk_buff *skb);
 int cipso_v4_skbuff_getattr(const struct sk_buff *skb,
                            struct netlbl_lsm_secattr *secattr);
-int cipso_v4_validate(unsigned char **option);
+int cipso_v4_validate(const struct sk_buff *skb, unsigned char **option);
 #else
 static inline void cipso_v4_error(struct sk_buff *skb,
                                  int error,
@@ -225,19 +235,36 @@ static inline int cipso_v4_sock_setattr(struct sock *sk,
        return -ENOSYS;
 }
 
+static inline void cipso_v4_sock_delattr(struct sock *sk)
+{
+}
+
 static inline int cipso_v4_sock_getattr(struct sock *sk,
                                        struct netlbl_lsm_secattr *secattr)
 {
        return -ENOSYS;
 }
 
+static inline int cipso_v4_skbuff_setattr(struct sk_buff *skb,
+                                     const struct cipso_v4_doi *doi_def,
+                                     const struct netlbl_lsm_secattr *secattr)
+{
+       return -ENOSYS;
+}
+
+static inline int cipso_v4_skbuff_delattr(struct sk_buff *skb)
+{
+       return -ENOSYS;
+}
+
 static inline int cipso_v4_skbuff_getattr(const struct sk_buff *skb,
                                          struct netlbl_lsm_secattr *secattr)
 {
        return -ENOSYS;
 }
 
-static inline int cipso_v4_validate(unsigned char **option)
+static inline int cipso_v4_validate(const struct sk_buff *skb,
+                                   unsigned char **option)
 {
        return -ENOSYS;
 }
index e4d2d6b..17c442a 100644 (file)
@@ -9,7 +9,7 @@
  */
 
 /*
- * (c) Copyright Hewlett-Packard Development Company, L.P., 2006
+ * (c) Copyright Hewlett-Packard Development Company, L.P., 2006, 2008
  *
  * This program is free software;  you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -72,8 +72,10 @@ struct cipso_v4_doi;
 /* NetLabel NETLINK protocol version
  *  1: initial version
  *  2: added static labels for unlabeled connections
+ *  3: network selectors added to the NetLabel/LSM domain mapping and the
+ *     CIPSO_V4_MAP_LOCAL CIPSO mapping was added
  */
-#define NETLBL_PROTO_VERSION            2
+#define NETLBL_PROTO_VERSION            3
 
 /* NetLabel NETLINK types/families */
 #define NETLBL_NLTYPE_NONE              0
@@ -87,6 +89,8 @@ struct cipso_v4_doi;
 #define NETLBL_NLTYPE_CIPSOV6_NAME      "NLBL_CIPSOv6"
 #define NETLBL_NLTYPE_UNLABELED         5
 #define NETLBL_NLTYPE_UNLABELED_NAME    "NLBL_UNLBL"
+#define NETLBL_NLTYPE_ADDRSELECT        6
+#define NETLBL_NLTYPE_ADDRSELECT_NAME   "NLBL_ADRSEL"
 
 /*
  * NetLabel - Kernel API for accessing the network packet label mappings.
@@ -200,7 +204,7 @@ struct netlbl_lsm_secattr {
        u32 type;
        char *domain;
        struct netlbl_lsm_cache *cache;
-       union {
+       struct {
                struct {
                        struct netlbl_lsm_secattr_catmap *cat;
                        u32 lvl;
@@ -352,12 +356,9 @@ static inline void netlbl_secattr_free(struct netlbl_lsm_secattr *secattr)
 int netlbl_cfg_map_del(const char *domain, struct netlbl_audit *audit_info);
 int netlbl_cfg_unlbl_add_map(const char *domain,
                             struct netlbl_audit *audit_info);
-int netlbl_cfg_cipsov4_add(struct cipso_v4_doi *doi_def,
-                          struct netlbl_audit *audit_info);
 int netlbl_cfg_cipsov4_add_map(struct cipso_v4_doi *doi_def,
                               const char *domain,
                               struct netlbl_audit *audit_info);
-int netlbl_cfg_cipsov4_del(u32 doi, struct netlbl_audit *audit_info);
 
 /*
  * LSM security attribute operations
@@ -380,12 +381,19 @@ int netlbl_secattr_catmap_setrng(struct netlbl_lsm_secattr_catmap *catmap,
 int netlbl_enabled(void);
 int netlbl_sock_setattr(struct sock *sk,
                        const struct netlbl_lsm_secattr *secattr);
+void netlbl_sock_delattr(struct sock *sk);
 int netlbl_sock_getattr(struct sock *sk,
                        struct netlbl_lsm_secattr *secattr);
+int netlbl_conn_setattr(struct sock *sk,
+                       struct sockaddr *addr,
+                       const struct netlbl_lsm_secattr *secattr);
+int netlbl_skbuff_setattr(struct sk_buff *skb,
+                         u16 family,
+                         const struct netlbl_lsm_secattr *secattr);
 int netlbl_skbuff_getattr(const struct sk_buff *skb,
                          u16 family,
                          struct netlbl_lsm_secattr *secattr);
-void netlbl_skbuff_err(struct sk_buff *skb, int error);
+void netlbl_skbuff_err(struct sk_buff *skb, int error, int gateway);
 
 /*
  * LSM label mapping cache operations
@@ -404,22 +412,12 @@ static inline int netlbl_cfg_unlbl_add_map(const char *domain,
 {
        return -ENOSYS;
 }
-static inline int netlbl_cfg_cipsov4_add(struct cipso_v4_doi *doi_def,
-                                        struct netlbl_audit *audit_info)
-{
-       return -ENOSYS;
-}
 static inline int netlbl_cfg_cipsov4_add_map(struct cipso_v4_doi *doi_def,
                                             const char *domain,
                                             struct netlbl_audit *audit_info)
 {
        return -ENOSYS;
 }
-static inline int netlbl_cfg_cipsov4_del(u32 doi,
-                                        struct netlbl_audit *audit_info)
-{
-       return -ENOSYS;
-}
 static inline int netlbl_secattr_catmap_walk(
                                      struct netlbl_lsm_secattr_catmap *catmap,
                                      u32 offset)
@@ -456,18 +454,35 @@ static inline int netlbl_sock_setattr(struct sock *sk,
 {
        return -ENOSYS;
 }
+static inline void netlbl_sock_delattr(struct sock *sk)
+{
+}
 static inline int netlbl_sock_getattr(struct sock *sk,
                                      struct netlbl_lsm_secattr *secattr)
 {
        return -ENOSYS;
 }
+static inline int netlbl_conn_setattr(struct sock *sk,
+                                     struct sockaddr *addr,
+                                     const struct netlbl_lsm_secattr *secattr)
+{
+       return -ENOSYS;
+}
+static inline int netlbl_skbuff_setattr(struct sk_buff *skb,
+                                     u16 family,
+                                     const struct netlbl_lsm_secattr *secattr)
+{
+       return -ENOSYS;
+}
 static inline int netlbl_skbuff_getattr(const struct sk_buff *skb,
                                        u16 family,
                                        struct netlbl_lsm_secattr *secattr)
 {
        return -ENOSYS;
 }
-static inline void netlbl_skbuff_err(struct sk_buff *skb, int error)
+static inline void netlbl_skbuff_err(struct sk_buff *skb,
+                                    int error,
+                                    int gateway)
 {
        return;
 }
index 2c0e457..490e035 100644 (file)
@@ -13,7 +13,7 @@
  */
 
 /*
- * (c) Copyright Hewlett-Packard Development Company, L.P., 2006
+ * (c) Copyright Hewlett-Packard Development Company, L.P., 2006, 2008
  *
  * This program is free software;  you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
 #include <asm/bug.h>
 #include <asm/unaligned.h>
 
-struct cipso_v4_domhsh_entry {
-       char *domain;
-       u32 valid;
-       struct list_head list;
-       struct rcu_head rcu;
-};
-
 /* List of available DOI definitions */
-/* XXX - Updates should be minimal so having a single lock for the
- * cipso_v4_doi_list and the cipso_v4_doi_list->dom_list should be
- * okay. */
 /* XXX - This currently assumes a minimal number of different DOIs in use,
  * if in practice there are a lot of different DOIs this list should
  * probably be turned into a hash table or something similar so we
@@ -119,6 +109,19 @@ int cipso_v4_rbm_strictvalid = 1;
  * be omitted. */
 #define CIPSO_V4_TAG_RNG_CAT_MAX      8
 
+/* Base length of the local tag (non-standard tag).
+ *  Tag definition (may change between kernel versions)
+ *
+ * 0          8          16         24         32
+ * +----------+----------+----------+----------+
+ * | 10000000 | 00000110 | 32-bit secid value  |
+ * +----------+----------+----------+----------+
+ * | in (host byte order)|
+ * +----------+----------+
+ *
+ */
+#define CIPSO_V4_TAG_LOC_BLEN         6
+
 /*
  * Helper Functions
  */
@@ -193,25 +196,6 @@ static void cipso_v4_bitmap_setbit(unsigned char *bitmap,
                bitmap[byte_spot] &= ~bitmask;
 }
 
-/**
- * cipso_v4_doi_domhsh_free - Frees a domain list entry
- * @entry: the entry's RCU field
- *
- * Description:
- * This function is designed to be used as a callback to the call_rcu()
- * function so that the memory allocated to a domain list entry can be released
- * safely.
- *
- */
-static void cipso_v4_doi_domhsh_free(struct rcu_head *entry)
-{
-       struct cipso_v4_domhsh_entry *ptr;
-
-       ptr = container_of(entry, struct cipso_v4_domhsh_entry, rcu);
-       kfree(ptr->domain);
-       kfree(ptr);
-}
-
 /**
  * cipso_v4_cache_entry_free - Frees a cache entry
  * @entry: the entry to free
@@ -457,7 +441,7 @@ static struct cipso_v4_doi *cipso_v4_doi_search(u32 doi)
        struct cipso_v4_doi *iter;
 
        list_for_each_entry_rcu(iter, &cipso_v4_doi_list, list)
-               if (iter->doi == doi && iter->valid)
+               if (iter->doi == doi && atomic_read(&iter->refcount))
                        return iter;
        return NULL;
 }
@@ -496,14 +480,17 @@ int cipso_v4_doi_add(struct cipso_v4_doi *doi_def)
                        if (doi_def->type != CIPSO_V4_MAP_PASS)
                                return -EINVAL;
                        break;
+               case CIPSO_V4_TAG_LOCAL:
+                       if (doi_def->type != CIPSO_V4_MAP_LOCAL)
+                               return -EINVAL;
+                       break;
                default:
                        return -EINVAL;
                }
        }
 
-       doi_def->valid = 1;
+       atomic_set(&doi_def->refcount, 1);
        INIT_RCU_HEAD(&doi_def->rcu);
-       INIT_LIST_HEAD(&doi_def->dom_list);
 
        spin_lock(&cipso_v4_doi_list_lock);
        if (cipso_v4_doi_search(doi_def->doi) != NULL)
@@ -518,60 +505,130 @@ doi_add_failure:
        return -EEXIST;
 }
 
+/**
+ * cipso_v4_doi_free - Frees a DOI definition
+ * @entry: the entry's RCU field
+ *
+ * Description:
+ * This function frees all of the memory associated with a DOI definition.
+ *
+ */
+void cipso_v4_doi_free(struct cipso_v4_doi *doi_def)
+{
+       if (doi_def == NULL)
+               return;
+
+       switch (doi_def->type) {
+       case CIPSO_V4_MAP_TRANS:
+               kfree(doi_def->map.std->lvl.cipso);
+               kfree(doi_def->map.std->lvl.local);
+               kfree(doi_def->map.std->cat.cipso);
+               kfree(doi_def->map.std->cat.local);
+               break;
+       }
+       kfree(doi_def);
+}
+
+/**
+ * cipso_v4_doi_free_rcu - Frees a DOI definition via the RCU pointer
+ * @entry: the entry's RCU field
+ *
+ * Description:
+ * This function is designed to be used as a callback to the call_rcu()
+ * function so that the memory allocated to the DOI definition can be released
+ * safely.
+ *
+ */
+static void cipso_v4_doi_free_rcu(struct rcu_head *entry)
+{
+       struct cipso_v4_doi *doi_def;
+
+       doi_def = container_of(entry, struct cipso_v4_doi, rcu);
+       cipso_v4_doi_free(doi_def);
+}
+
 /**
  * cipso_v4_doi_remove - Remove an existing DOI from the CIPSO protocol engine
  * @doi: the DOI value
  * @audit_secid: the LSM secid to use in the audit message
- * @callback: the DOI cleanup/free callback
  *
  * Description:
- * Removes a DOI definition from the CIPSO engine, @callback is called to
- * free any memory.  The NetLabel routines will be called to release their own
- * LSM domain mappings as well as our own domain list.  Returns zero on
- * success and negative values on failure.
+ * Removes a DOI definition from the CIPSO engine.  The NetLabel routines will
+ * be called to release their own LSM domain mappings as well as our own
+ * domain list.  Returns zero on success and negative values on failure.
  *
  */
-int cipso_v4_doi_remove(u32 doi,
-                       struct netlbl_audit *audit_info,
-                       void (*callback) (struct rcu_head * head))
+int cipso_v4_doi_remove(u32 doi, struct netlbl_audit *audit_info)
 {
        struct cipso_v4_doi *doi_def;
-       struct cipso_v4_domhsh_entry *dom_iter;
 
        spin_lock(&cipso_v4_doi_list_lock);
        doi_def = cipso_v4_doi_search(doi);
-       if (doi_def != NULL) {
-               doi_def->valid = 0;
-               list_del_rcu(&doi_def->list);
+       if (doi_def == NULL) {
                spin_unlock(&cipso_v4_doi_list_lock);
-               rcu_read_lock();
-               list_for_each_entry_rcu(dom_iter, &doi_def->dom_list, list)
-                       if (dom_iter->valid)
-                               netlbl_cfg_map_del(dom_iter->domain,
-                                                  audit_info);
-               rcu_read_unlock();
-               cipso_v4_cache_invalidate();
-               call_rcu(&doi_def->rcu, callback);
-               return 0;
+               return -ENOENT;
+       }
+       if (!atomic_dec_and_test(&doi_def->refcount)) {
+               spin_unlock(&cipso_v4_doi_list_lock);
+               return -EBUSY;
        }
+       list_del_rcu(&doi_def->list);
        spin_unlock(&cipso_v4_doi_list_lock);
 
-       return -ENOENT;
+       cipso_v4_cache_invalidate();
+       call_rcu(&doi_def->rcu, cipso_v4_doi_free_rcu);
+
+       return 0;
 }
 
 /**
- * cipso_v4_doi_getdef - Returns a pointer to a valid DOI definition
+ * cipso_v4_doi_getdef - Returns a reference to a valid DOI definition
  * @doi: the DOI value
  *
  * Description:
  * Searches for a valid DOI definition and if one is found it is returned to
  * the caller.  Otherwise NULL is returned.  The caller must ensure that
- * rcu_read_lock() is held while accessing the returned definition.
+ * rcu_read_lock() is held while accessing the returned definition and the DOI
+ * definition reference count is decremented when the caller is done.
  *
  */
 struct cipso_v4_doi *cipso_v4_doi_getdef(u32 doi)
 {
-       return cipso_v4_doi_search(doi);
+       struct cipso_v4_doi *doi_def;
+
+       rcu_read_lock();
+       doi_def = cipso_v4_doi_search(doi);
+       if (doi_def == NULL)
+               goto doi_getdef_return;
+       if (!atomic_inc_not_zero(&doi_def->refcount))
+               doi_def = NULL;
+
+doi_getdef_return:
+       rcu_read_unlock();
+       return doi_def;
+}
+
+/**
+ * cipso_v4_doi_putdef - Releases a reference for the given DOI definition
+ * @doi_def: the DOI definition
+ *
+ * Description:
+ * Releases a DOI definition reference obtained from cipso_v4_doi_getdef().
+ *
+ */
+void cipso_v4_doi_putdef(struct cipso_v4_doi *doi_def)
+{
+       if (doi_def == NULL)
+               return;
+
+       if (!atomic_dec_and_test(&doi_def->refcount))
+               return;
+       spin_lock(&cipso_v4_doi_list_lock);
+       list_del_rcu(&doi_def->list);
+       spin_unlock(&cipso_v4_doi_list_lock);
+
+       cipso_v4_cache_invalidate();
+       call_rcu(&doi_def->rcu, cipso_v4_doi_free_rcu);
 }
 
 /**
@@ -597,7 +654,7 @@ int cipso_v4_doi_walk(u32 *skip_cnt,
 
        rcu_read_lock();
        list_for_each_entry_rcu(iter_doi, &cipso_v4_doi_list, list)
-               if (iter_doi->valid) {
+               if (atomic_read(&iter_doi->refcount) > 0) {
                        if (doi_cnt++ < *skip_cnt)
                                continue;
                        ret_val = callback(iter_doi, cb_arg);
@@ -613,85 +670,6 @@ doi_walk_return:
        return ret_val;
 }
 
-/**
- * cipso_v4_doi_domhsh_add - Adds a domain entry to a DOI definition
- * @doi_def: the DOI definition
- * @domain: the domain to add
- *
- * Description:
- * Adds the @domain to the DOI specified by @doi_def, this function
- * should only be called by external functions (i.e. NetLabel).  This function
- * does allocate memory.  Returns zero on success, negative values on failure.
- *
- */
-int cipso_v4_doi_domhsh_add(struct cipso_v4_doi *doi_def, const char *domain)
-{
-       struct cipso_v4_domhsh_entry *iter;
-       struct cipso_v4_domhsh_entry *new_dom;
-
-       new_dom = kzalloc(sizeof(*new_dom), GFP_KERNEL);
-       if (new_dom == NULL)
-               return -ENOMEM;
-       if (domain) {
-               new_dom->domain = kstrdup(domain, GFP_KERNEL);
-               if (new_dom->domain == NULL) {
-                       kfree(new_dom);
-                       return -ENOMEM;
-               }
-       }
-       new_dom->valid = 1;
-       INIT_RCU_HEAD(&new_dom->rcu);
-
-       spin_lock(&cipso_v4_doi_list_lock);
-       list_for_each_entry(iter, &doi_def->dom_list, list)
-               if (iter->valid &&
-                   ((domain != NULL && iter->domain != NULL &&
-                     strcmp(iter->domain, domain) == 0) ||
-                    (domain == NULL && iter->domain == NULL))) {
-                       spin_unlock(&cipso_v4_doi_list_lock);
-                       kfree(new_dom->domain);
-                       kfree(new_dom);
-                       return -EEXIST;
-               }
-       list_add_tail_rcu(&new_dom->list, &doi_def->dom_list);
-       spin_unlock(&cipso_v4_doi_list_lock);
-
-       return 0;
-}
-
-/**
- * cipso_v4_doi_domhsh_remove - Removes a domain entry from a DOI definition
- * @doi_def: the DOI definition
- * @domain: the domain to remove
- *
- * Description:
- * Removes the @domain from the DOI specified by @doi_def, this function
- * should only be called by external functions (i.e. NetLabel).   Returns zero
- * on success and negative values on error.
- *
- */
-int cipso_v4_doi_domhsh_remove(struct cipso_v4_doi *doi_def,
-                              const char *domain)
-{
-       struct cipso_v4_domhsh_entry *iter;
-
-       spin_lock(&cipso_v4_doi_list_lock);
-       list_for_each_entry(iter, &doi_def->dom_list, list)
-               if (iter->valid &&
-                   ((domain != NULL && iter->domain != NULL &&
-                     strcmp(iter->domain, domain) == 0) ||
-                    (domain == NULL && iter->domain == NULL))) {
-                       iter->valid = 0;
-                       list_del_rcu(&iter->list);
-                       spin_unlock(&cipso_v4_doi_list_lock);
-                       call_rcu(&iter->rcu, cipso_v4_doi_domhsh_free);
-                       return 0;
-               }
-       spin_unlock(&cipso_v4_doi_list_lock);
-
-       return -ENOENT;
-}
-
 /*
  * Label Mapping Functions
  */
@@ -712,7 +690,7 @@ static int cipso_v4_map_lvl_valid(const struct cipso_v4_doi *doi_def, u8 level)
        switch (doi_def->type) {
        case CIPSO_V4_MAP_PASS:
                return 0;
-       case CIPSO_V4_MAP_STD:
+       case CIPSO_V4_MAP_TRANS:
                if (doi_def->map.std->lvl.cipso[level] < CIPSO_V4_INV_LVL)
                        return 0;
                break;
@@ -741,7 +719,7 @@ static int cipso_v4_map_lvl_hton(const struct cipso_v4_doi *doi_def,
        case CIPSO_V4_MAP_PASS:
                *net_lvl = host_lvl;
                return 0;
-       case CIPSO_V4_MAP_STD:
+       case CIPSO_V4_MAP_TRANS:
                if (host_lvl < doi_def->map.std->lvl.local_size &&
                    doi_def->map.std->lvl.local[host_lvl] < CIPSO_V4_INV_LVL) {
                        *net_lvl = doi_def->map.std->lvl.local[host_lvl];
@@ -775,7 +753,7 @@ static int cipso_v4_map_lvl_ntoh(const struct cipso_v4_doi *doi_def,
        case CIPSO_V4_MAP_PASS:
                *host_lvl = net_lvl;
                return 0;
-       case CIPSO_V4_MAP_STD:
+       case CIPSO_V4_MAP_TRANS:
                map_tbl = doi_def->map.std;
                if (net_lvl < map_tbl->lvl.cipso_size &&
                    map_tbl->lvl.cipso[net_lvl] < CIPSO_V4_INV_LVL) {
@@ -812,7 +790,7 @@ static int cipso_v4_map_cat_rbm_valid(const struct cipso_v4_doi *doi_def,
        switch (doi_def->type) {
        case CIPSO_V4_MAP_PASS:
                return 0;
-       case CIPSO_V4_MAP_STD:
+       case CIPSO_V4_MAP_TRANS:
                cipso_cat_size = doi_def->map.std->cat.cipso_size;
                cipso_array = doi_def->map.std->cat.cipso;
                for (;;) {
@@ -860,7 +838,7 @@ static int cipso_v4_map_cat_rbm_hton(const struct cipso_v4_doi *doi_def,
        u32 host_cat_size = 0;
        u32 *host_cat_array = NULL;
 
-       if (doi_def->type == CIPSO_V4_MAP_STD) {
+       if (doi_def->type == CIPSO_V4_MAP_TRANS) {
                host_cat_size = doi_def->map.std->cat.local_size;
                host_cat_array = doi_def->map.std->cat.local;
        }
@@ -875,7 +853,7 @@ static int cipso_v4_map_cat_rbm_hton(const struct cipso_v4_doi *doi_def,
                case CIPSO_V4_MAP_PASS:
                        net_spot = host_spot;
                        break;
-               case CIPSO_V4_MAP_STD:
+               case CIPSO_V4_MAP_TRANS:
                        if (host_spot >= host_cat_size)
                                return -EPERM;
                        net_spot = host_cat_array[host_spot];
@@ -921,7 +899,7 @@ static int cipso_v4_map_cat_rbm_ntoh(const struct cipso_v4_doi *doi_def,
        u32 net_cat_size = 0;
        u32 *net_cat_array = NULL;
 
-       if (doi_def->type == CIPSO_V4_MAP_STD) {
+       if (doi_def->type == CIPSO_V4_MAP_TRANS) {
                net_cat_size = doi_def->map.std->cat.cipso_size;
                net_cat_array = doi_def->map.std->cat.cipso;
        }
@@ -941,7 +919,7 @@ static int cipso_v4_map_cat_rbm_ntoh(const struct cipso_v4_doi *doi_def,
                case CIPSO_V4_MAP_PASS:
                        host_spot = net_spot;
                        break;
-               case CIPSO_V4_MAP_STD:
+               case CIPSO_V4_MAP_TRANS:
                        if (net_spot >= net_cat_size)
                                return -EPERM;
                        host_spot = net_cat_array[net_spot];
@@ -1277,7 +1255,7 @@ static int cipso_v4_gentag_rbm(const struct cipso_v4_doi *doi_def,
        } else
                tag_len = 4;
 
-       buffer[0] = 0x01;
+       buffer[0] = CIPSO_V4_TAG_RBITMAP;
        buffer[1] = tag_len;
        buffer[3] = level;
 
@@ -1373,7 +1351,7 @@ static int cipso_v4_gentag_enum(const struct cipso_v4_doi *doi_def,
        } else
                tag_len = 4;
 
-       buffer[0] = 0x02;
+       buffer[0] = CIPSO_V4_TAG_ENUM;
        buffer[1] = tag_len;
        buffer[3] = level;
 
@@ -1469,7 +1447,7 @@ static int cipso_v4_gentag_rng(const struct cipso_v4_doi *doi_def,
        } else
                tag_len = 4;
 
-       buffer[0] = 0x05;
+       buffer[0] = CIPSO_V4_TAG_RANGE;
        buffer[1] = tag_len;
        buffer[3] = level;
 
@@ -1522,6 +1500,54 @@ static int cipso_v4_parsetag_rng(const struct cipso_v4_doi *doi_def,
        return 0;
 }
 
+/**
+ * cipso_v4_gentag_loc - Generate a CIPSO local tag (non-standard)
+ * @doi_def: the DOI definition
+ * @secattr: the security attributes
+ * @buffer: the option buffer
+ * @buffer_len: length of buffer in bytes
+ *
+ * Description:
+ * Generate a CIPSO option using the local tag.  Returns the size of the tag
+ * on success, negative values on failure.
+ *
+ */
+static int cipso_v4_gentag_loc(const struct cipso_v4_doi *doi_def,
+                              const struct netlbl_lsm_secattr *secattr,
+                              unsigned char *buffer,
+                              u32 buffer_len)
+{
+       if (!(secattr->flags & NETLBL_SECATTR_SECID))
+               return -EPERM;
+
+       buffer[0] = CIPSO_V4_TAG_LOCAL;
+       buffer[1] = CIPSO_V4_TAG_LOC_BLEN;
+       *(u32 *)&buffer[2] = secattr->attr.secid;
+
+       return CIPSO_V4_TAG_LOC_BLEN;
+}
+
+/**
+ * cipso_v4_parsetag_loc - Parse a CIPSO local tag
+ * @doi_def: the DOI definition
+ * @tag: the CIPSO tag
+ * @secattr: the security attributes
+ *
+ * Description:
+ * Parse a CIPSO local tag and return the security attributes in @secattr.
+ * Return zero on success, negatives values on failure.
+ *
+ */
+static int cipso_v4_parsetag_loc(const struct cipso_v4_doi *doi_def,
+                                const unsigned char *tag,
+                                struct netlbl_lsm_secattr *secattr)
+{
+       secattr->attr.secid = *(u32 *)&tag[2];
+       secattr->flags |= NETLBL_SECATTR_SECID;
+
+       return 0;
+}
+
 /**
  * cipso_v4_validate - Validate a CIPSO option
  * @option: the start of the option, on error it is set to point to the error
@@ -1541,7 +1567,7 @@ static int cipso_v4_parsetag_rng(const struct cipso_v4_doi *doi_def,
  *   that is unrecognized."
  *
  */
-int cipso_v4_validate(unsigned char **option)
+int cipso_v4_validate(const struct sk_buff *skb, unsigned char **option)
 {
        unsigned char *opt = *option;
        unsigned char *tag;
@@ -1566,7 +1592,7 @@ int cipso_v4_validate(unsigned char **option)
                goto validate_return_locked;
        }
 
-       opt_iter = 6;
+       opt_iter = CIPSO_V4_HDR_LEN;
        tag = opt + opt_iter;
        while (opt_iter < opt_len) {
                for (tag_iter = 0; doi_def->tags[tag_iter] != tag[0];)
@@ -1584,7 +1610,7 @@ int cipso_v4_validate(unsigned char **option)
 
                switch (tag[0]) {
                case CIPSO_V4_TAG_RBITMAP:
-                       if (tag_len < 4) {
+                       if (tag_len < CIPSO_V4_TAG_RBM_BLEN) {
                                err_offset = opt_iter + 1;
                                goto validate_return_locked;
                        }
@@ -1602,7 +1628,7 @@ int cipso_v4_validate(unsigned char **option)
                                        err_offset = opt_iter + 3;
                                        goto validate_return_locked;
                                }
-                               if (tag_len > 4 &&
+                               if (tag_len > CIPSO_V4_TAG_RBM_BLEN &&
                                    cipso_v4_map_cat_rbm_valid(doi_def,
                                                            &tag[4],
                                                            tag_len - 4) < 0) {
@@ -1612,7 +1638,7 @@ int cipso_v4_validate(unsigned char **option)
                        }
                        break;
                case CIPSO_V4_TAG_ENUM:
-                       if (tag_len < 4) {
+                       if (tag_len < CIPSO_V4_TAG_ENUM_BLEN) {
                                err_offset = opt_iter + 1;
                                goto validate_return_locked;
                        }
@@ -1622,7 +1648,7 @@ int cipso_v4_validate(unsigned char **option)
                                err_offset = opt_iter + 3;
                                goto validate_return_locked;
                        }
-                       if (tag_len > 4 &&
+                       if (tag_len > CIPSO_V4_TAG_ENUM_BLEN &&
                            cipso_v4_map_cat_enum_valid(doi_def,
                                                        &tag[4],
                                                        tag_len - 4) < 0) {
@@ -1631,7 +1657,7 @@ int cipso_v4_validate(unsigned char **option)
                        }
                        break;
                case CIPSO_V4_TAG_RANGE:
-                       if (tag_len < 4) {
+                       if (tag_len < CIPSO_V4_TAG_RNG_BLEN) {
                                err_offset = opt_iter + 1;
                                goto validate_return_locked;
                        }
@@ -1641,7 +1667,7 @@ int cipso_v4_validate(unsigned char **option)
                                err_offset = opt_iter + 3;
                                goto validate_return_locked;
                        }
-                       if (tag_len > 4 &&
+                       if (tag_len > CIPSO_V4_TAG_RNG_BLEN &&
                            cipso_v4_map_cat_rng_valid(doi_def,
                                                       &tag[4],
                                                       tag_len - 4) < 0) {
@@ -1649,6 +1675,19 @@ int cipso_v4_validate(unsigned char **option)
                                goto validate_return_locked;
                        }
                        break;
+               case CIPSO_V4_TAG_LOCAL:
+                       /* This is a non-standard tag that we only allow for
+                        * local connections, so if the incoming interface is
+                        * not the loopback device drop the packet. */
+                       if (!(skb->dev->flags & IFF_LOOPBACK)) {
+                               err_offset = opt_iter;
+                               goto validate_return_locked;
+                       }
+                       if (tag_len != CIPSO_V4_TAG_LOC_BLEN) {
+                               err_offset = opt_iter + 1;
+                               goto validate_return_locked;
+                       }
+                       break;
                default:
                        err_offset = opt_iter;
                        goto validate_return_locked;
@@ -1704,48 +1743,27 @@ void cipso_v4_error(struct sk_buff *skb, int error, u32 gateway)
 }
 
 /**
- * cipso_v4_sock_setattr - Add a CIPSO option to a socket
- * @sk: the socket
+ * cipso_v4_genopt - Generate a CIPSO option
+ * @buf: the option buffer
+ * @buf_len: the size of opt_buf
  * @doi_def: the CIPSO DOI to use
- * @secattr: the specific security attributes of the socket
+ * @secattr: the security attributes
  *
  * Description:
- * Set the CIPSO option on the given socket using the DOI definition and
- * security attributes passed to the function.  This function requires
- * exclusive access to @sk, which means it either needs to be in the
- * process of being created or locked.  Returns zero on success and negative
- * values on failure.
+ * Generate a CIPSO option using the DOI definition and security attributes
+ * passed to the function.  Returns the length of the option on success and
+ * negative values on failure.
  *
  */
-int cipso_v4_sock_setattr(struct sock *sk,
-                         const struct cipso_v4_doi *doi_def,
-                         const struct netlbl_lsm_secattr *secattr)
+static int cipso_v4_genopt(unsigned char *buf, u32 buf_len,
+                          const struct cipso_v4_doi *doi_def,
+                          const struct netlbl_lsm_secattr *secattr)
 {
-       int ret_val = -EPERM;
+       int ret_val;
        u32 iter;
-       unsigned char *buf;
-       u32 buf_len = 0;
-       u32 opt_len;
-       struct ip_options *opt = NULL;
-       struct inet_sock *sk_inet;
-       struct inet_connection_sock *sk_conn;
 
-       /* In the case of sock_create_lite(), the sock->sk field is not
-        * defined yet but it is not a problem as the only users of these
-        * "lite" PF_INET sockets are functions which do an accept() call
-        * afterwards so we will label the socket as part of the accept(). */
-       if (sk == NULL)
-               return 0;
-
-       /* We allocate the maximum CIPSO option size here so we are probably
-        * being a little wasteful, but it makes our life _much_ easier later
-        * on and after all we are only talking about 40 bytes. */
-       buf_len = CIPSO_V4_OPT_LEN_MAX;
-       buf = kmalloc(buf_len, GFP_ATOMIC);
-       if (buf == NULL) {
-               ret_val = -ENOMEM;
-               goto socket_setattr_failure;
-       }
+       if (buf_len <= CIPSO_V4_HDR_LEN)
+               return -ENOSPC;
 
        /* XXX - This code assumes only one tag per CIPSO option which isn't
         * really a good assumption to make but since we only support the MAC
@@ -1772,9 +1790,14 @@ int cipso_v4_sock_setattr(struct sock *sk,
                                                   &buf[CIPSO_V4_HDR_LEN],
                                                   buf_len - CIPSO_V4_HDR_LEN);
                        break;
+               case CIPSO_V4_TAG_LOCAL:
+                       ret_val = cipso_v4_gentag_loc(doi_def,
+                                                  secattr,
+                                                  &buf[CIPSO_V4_HDR_LEN],
+                                                  buf_len - CIPSO_V4_HDR_LEN);
+                       break;
                default:
-                       ret_val = -EPERM;
-                       goto socket_setattr_failure;
+                       return -EPERM;
                }
 
                iter++;
@@ -1782,9 +1805,58 @@ int cipso_v4_sock_setattr(struct sock *sk,
                 iter < CIPSO_V4_TAG_MAXCNT &&
                 doi_def->tags[iter] != CIPSO_V4_TAG_INVALID);
        if (ret_val < 0)
-               goto socket_setattr_failure;
+               return ret_val;
        cipso_v4_gentag_hdr(doi_def, buf, ret_val);
-       buf_len = CIPSO_V4_HDR_LEN + ret_val;
+       return CIPSO_V4_HDR_LEN + ret_val;
+}
+
+/**
+ * cipso_v4_sock_setattr - Add a CIPSO option to a socket
+ * @sk: the socket
+ * @doi_def: the CIPSO DOI to use
+ * @secattr: the specific security attributes of the socket
+ *
+ * Description:
+ * Set the CIPSO option on the given socket using the DOI definition and
+ * security attributes passed to the function.  This function requires
+ * exclusive access to @sk, which means it either needs to be in the
+ * process of being created or locked.  Returns zero on success and negative
+ * values on failure.
+ *
+ */
+int cipso_v4_sock_setattr(struct sock *sk,
+                         const struct cipso_v4_doi *doi_def,
+                         const struct netlbl_lsm_secattr *secattr)
+{
+       int ret_val = -EPERM;
+       unsigned char *buf = NULL;
+       u32 buf_len;
+       u32 opt_len;
+       struct ip_options *opt = NULL;
+       struct inet_sock *sk_inet;
+       struct inet_connection_sock *sk_conn;
+
+       /* In the case of sock_create_lite(), the sock->sk field is not
+        * defined yet but it is not a problem as the only users of these
+        * "lite" PF_INET sockets are functions which do an accept() call
+        * afterwards so we will label the socket as part of the accept(). */
+       if (sk == NULL)
+               return 0;
+
+       /* We allocate the maximum CIPSO option size here so we are probably
+        * being a little wasteful, but it makes our life _much_ easier later
+        * on and after all we are only talking about 40 bytes. */
+       buf_len = CIPSO_V4_OPT_LEN_MAX;
+       buf = kmalloc(buf_len, GFP_ATOMIC);
+       if (buf == NULL) {
+               ret_val = -ENOMEM;
+               goto socket_setattr_failure;
+       }
+
+       ret_val = cipso_v4_genopt(buf, buf_len, doi_def, secattr);
+       if (ret_val < 0)
+               goto socket_setattr_failure;
+       buf_len = ret_val;
 
        /* 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
@@ -1821,6 +1893,80 @@ socket_setattr_failure:
        return ret_val;
 }
 
+/**
+ * cipso_v4_sock_delattr - Delete the CIPSO option from a socket
+ * @sk: the socket
+ *
+ * Description:
+ * Removes the CIPSO option from a socket, if present.
+ *
+ */
+void cipso_v4_sock_delattr(struct sock *sk)
+{
+       u8 hdr_delta;
+       struct ip_options *opt;
+       struct inet_sock *sk_inet;
+
+       sk_inet = inet_sk(sk);
+       opt = sk_inet->opt;
+       if (opt == NULL || opt->cipso == 0)
+               return;
+
+       if (opt->srr || opt->rr || opt->ts || opt->router_alert) {
+               u8 cipso_len;
+               u8 cipso_off;
+               unsigned char *cipso_ptr;
+               int iter;
+               int optlen_new;
+
+               cipso_off = opt->cipso - sizeof(struct iphdr);
+               cipso_ptr = &opt->__data[cipso_off];
+               cipso_len = cipso_ptr[1];
+
+               if (opt->srr > opt->cipso)
+                       opt->srr -= cipso_len;
+               if (opt->rr > opt->cipso)
+                       opt->rr -= cipso_len;
+               if (opt->ts > opt->cipso)
+                       opt->ts -= cipso_len;
+               if (opt->router_alert > opt->cipso)
+                       opt->router_alert -= cipso_len;
+               opt->cipso = 0;
+
+               memmove(cipso_ptr, cipso_ptr + cipso_len,
+                       opt->optlen - cipso_off - cipso_len);
+
+               /* determining the new total option length is tricky because of
+                * the padding necessary, the only thing i can think to do at
+                * this point is walk the options one-by-one, skipping the
+                * padding at the end to determine the actual option size and
+                * from there we can determine the new total option length */
+               iter = 0;
+               optlen_new = 0;
+               while (iter < opt->optlen)
+                       if (opt->__data[iter] != IPOPT_NOP) {
+                               iter += opt->__data[iter + 1];
+                               optlen_new = iter;
+                       } else
+                               iter++;
+               hdr_delta = opt->optlen;
+               opt->optlen = (optlen_new + 3) & ~3;
+               hdr_delta -= opt->optlen;
+       } else {
+               /* only the cipso option was present on the socket so we can
+                * remove the entire option struct */
+               sk_inet->opt = NULL;
+               hdr_delta = opt->optlen;
+               kfree(opt);
+       }
+
+       if (sk_inet->is_icsk && hdr_delta > 0) {
+               struct inet_connection_sock *sk_conn = inet_csk(sk);
+               sk_conn->icsk_ext_hdr_len -= hdr_delta;
+               sk_conn->icsk_sync_mss(sk, sk_conn->icsk_pmtu_cookie);
+       }
+}
+
 /**
  * cipso_v4_getattr - Helper function for the cipso_v4_*_getattr functions
  * @cipso: the CIPSO v4 option
@@ -1859,6 +2005,9 @@ static int cipso_v4_getattr(const unsigned char *cipso,
        case CIPSO_V4_TAG_RANGE:
                ret_val = cipso_v4_parsetag_rng(doi_def, &cipso[6], secattr);
                break;
+       case CIPSO_V4_TAG_LOCAL:
+               ret_val = cipso_v4_parsetag_loc(doi_def, &cipso[6], secattr);
+               break;
        }
        if (ret_val == 0)
                secattr->type = NETLBL_NLTYPE_CIPSOV4;
@@ -1892,6 +2041,123 @@ int cipso_v4_sock_getattr(struct sock *sk, struct netlbl_lsm_secattr *secattr)
                                secattr);
 }
 
+/**
+ * cipso_v4_skbuff_setattr - Set the CIPSO option on a packet
+ * @skb: the packet
+ * @secattr: the security attributes
+ *
+ * Description:
+ * Set the CIPSO option on the given packet based on the security attributes.
+ * Returns a pointer to the IP header on success and NULL on failure.
+ *
+ */
+int cipso_v4_skbuff_setattr(struct sk_buff *skb,
+                           const struct cipso_v4_doi *doi_def,
+                           const struct netlbl_lsm_secattr *secattr)
+{
+       int ret_val;
+       struct iphdr *iph;
+       struct ip_options *opt = &IPCB(skb)->opt;
+       unsigned char buf[CIPSO_V4_OPT_LEN_MAX];
+       u32 buf_len = CIPSO_V4_OPT_LEN_MAX;
+       u32 opt_len;
+       int len_delta;
+
+       buf_len = cipso_v4_genopt(buf, buf_len, doi_def, secattr);
+       if (buf_len < 0)
+               return buf_len;
+       opt_len = (buf_len + 3) & ~3;
+
+       /* we overwrite any existing options to ensure that we have enough
+        * room for the CIPSO option, the reason is that we _need_ to guarantee
+        * that the security label is applied to the packet - we do the same
+        * thing when using the socket options and it hasn't caused a problem,
+        * if we need to we can always revisit this choice later */
+
+       len_delta = opt_len - opt->optlen;
+       /* if we don't ensure enough headroom we could panic on the skb_push()
+        * call below so make sure we have enough, we are also "mangling" the
+        * packet so we should probably do a copy-on-write call anyway */
+       ret_val = skb_cow(skb, skb_headroom(skb) + len_delta);
+       if (ret_val < 0)
+               return ret_val;
+
+       if (len_delta > 0) {
+               /* we assume that the header + opt->optlen have already been
+                * "pushed" in ip_options_build() or similar */
+               iph = ip_hdr(skb);
+               skb_push(skb, len_delta);
+               memmove((char *)iph - len_delta, iph, iph->ihl << 2);
+               skb_reset_network_header(skb);
+               iph = ip_hdr(skb);
+       } else if (len_delta < 0) {
+               iph = ip_hdr(skb);
+               memset(iph + 1, IPOPT_NOP, opt->optlen);
+       } else
+               iph = ip_hdr(skb);
+
+       if (opt->optlen > 0)
+               memset(opt, 0, sizeof(*opt));
+       opt->optlen = opt_len;
+       opt->cipso = sizeof(struct iphdr);
+       opt->is_changed = 1;
+
+       /* we have to do the following because we are being called from a
+        * netfilter hook which means the packet already has had the header
+        * fields populated and the checksum calculated - yes this means we
+        * are doing more work than needed but we do it to keep the core
+        * stack clean and tidy */
+       memcpy(iph + 1, buf, buf_len);
+       if (opt_len > buf_len)
+               memset((char *)(iph + 1) + buf_len, 0, opt_len - buf_len);
+       if (len_delta != 0) {
+               iph->ihl = 5 + (opt_len >> 2);
+               iph->tot_len = htons(skb->len);
+       }
+       ip_send_check(iph);
+
+       return 0;
+}
+
+/**
+ * cipso_v4_skbuff_delattr - Delete any CIPSO options from a packet
+ * @skb: the packet
+ *
+ * Description:
+ * Removes any and all CIPSO options from the given packet.  Returns zero on
+ * success, negative values on failure.
+ *
+ */
+int cipso_v4_skbuff_delattr(struct sk_buff *skb)
+{
+       int ret_val;
+       struct iphdr *iph;
+       struct ip_options *opt = &IPCB(skb)->opt;
+       unsigned char *cipso_ptr;
+
+       if (opt->cipso == 0)
+               return 0;
+
+       /* since we are changing the packet we should make a copy */
+       ret_val = skb_cow(skb, skb_headroom(skb));
+       if (ret_val < 0)
+               return ret_val;
+
+       /* the easiest thing to do is just replace the cipso option with noop
+        * options since we don't change the size of the packet, although we
+        * still need to recalculate the checksum */
+
+       iph = ip_hdr(skb);
+       cipso_ptr = (unsigned char *)iph + opt->cipso;
+       memset(cipso_ptr, IPOPT_NOOP, cipso_ptr[1]);
+       opt->cipso = 0;
+       opt->is_changed = 1;
+
+       ip_send_check(iph);
+
+       return 0;
+}
+
 /**
  * cipso_v4_skbuff_getattr - Get the security attributes from the CIPSO option
  * @skb: the packet
index be3f18a..2c88da6 100644 (file)
@@ -438,7 +438,7 @@ int ip_options_compile(struct net *net,
                                goto error;
                        }
                        opt->cipso = optptr - iph;
-                       if (cipso_v4_validate(&optptr)) {
+                       if (cipso_v4_validate(skb, &optptr)) {
                                pp_ptr = optptr;
                                goto error;
                        }
index 8af18c0..ea750e9 100644 (file)
@@ -5,7 +5,8 @@
 #
 
 # base objects
-obj-y  := netlabel_user.o netlabel_kapi.o netlabel_domainhash.o
+obj-y  := netlabel_user.o netlabel_kapi.o
+obj-y  += netlabel_domainhash.o netlabel_addrlist.o
 
 # management objects
 obj-y  += netlabel_mgmt.o
diff --git a/net/netlabel/netlabel_addrlist.c b/net/netlabel/netlabel_addrlist.c
new file mode 100644 (file)
index 0000000..b0925a3
--- /dev/null
@@ -0,0 +1,388 @@
+/*
+ * NetLabel Network Address Lists
+ *
+ * This file contains network address list functions used to manage ordered
+ * lists of network addresses for use by the NetLabel subsystem.  The NetLabel
+ * system manages static and dynamic label mappings for network protocols such
+ * as CIPSO and RIPSO.
+ *
+ * Author: Paul Moore <paul.moore@hp.com>
+ *
+ */
+
+/*
+ * (c) Copyright Hewlett-Packard Development Company, L.P., 2008
+ *
+ * This program is free software;  you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY;  without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
+ * the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program;  if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/rcupdate.h>
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <linux/in.h>
+#include <linux/in6.h>
+#include <linux/ip.h>
+#include <linux/ipv6.h>
+#include <net/ip.h>
+#include <net/ipv6.h>
+#include <linux/audit.h>
+
+#include "netlabel_addrlist.h"
+
+/*
+ * Address List Functions
+ */
+
+/**
+ * netlbl_af4list_search - Search for a matching IPv4 address entry
+ * @addr: IPv4 address
+ * @head: the list head
+ *
+ * Description:
+ * Searches the IPv4 address list given by @head.  If a matching address entry
+ * is found it is returned, otherwise NULL is returned.  The caller is
+ * responsible for calling the rcu_read_[un]lock() functions.
+ *
+ */
+struct netlbl_af4list *netlbl_af4list_search(__be32 addr,
+                                            struct list_head *head)
+{
+       struct netlbl_af4list *iter;
+
+       list_for_each_entry_rcu(iter, head, list)
+               if (iter->valid && (addr & iter->mask) == iter->addr)
+                       return iter;
+
+       return NULL;
+}
+
+/**
+ * netlbl_af4list_search_exact - Search for an exact IPv4 address entry
+ * @addr: IPv4 address
+ * @mask: IPv4 address mask
+ * @head: the list head
+ *
+ * Description:
+ * Searches the IPv4 address list given by @head.  If an exact match if found
+ * it is returned, otherwise NULL is returned.  The caller is responsible for
+ * calling the rcu_read_[un]lock() functions.
+ *
+ */
+struct netlbl_af4list *netlbl_af4list_search_exact(__be32 addr,
+                                                  __be32 mask,
+                                                  struct list_head *head)
+{
+       struct netlbl_af4list *iter;
+
+       list_for_each_entry_rcu(iter, head, list)
+               if (iter->valid && iter->addr == addr && iter->mask == mask)
+                       return iter;
+
+       return NULL;
+}
+
+
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+/**
+ * netlbl_af6list_search - Search for a matching IPv6 address entry
+ * @addr: IPv6 address
+ * @head: the list head
+ *
+ * Description:
+ * Searches the IPv6 address list given by @head.  If a matching address entry
+ * is found it is returned, otherwise NULL is returned.  The caller is
+ * responsible for calling the rcu_read_[un]lock() functions.
+ *
+ */
+struct netlbl_af6list *netlbl_af6list_search(const struct in6_addr *addr,
+                                            struct list_head *head)
+{
+       struct netlbl_af6list *iter;
+
+       list_for_each_entry_rcu(iter, head, list)
+               if (iter->valid &&
+                   ipv6_masked_addr_cmp(&iter->addr, &iter->mask, addr) == 0)
+                       return iter;
+
+       return NULL;
+}
+
+/**
+ * netlbl_af6list_search_exact - Search for an exact IPv6 address entry
+ * @addr: IPv6 address
+ * @mask: IPv6 address mask
+ * @head: the list head
+ *
+ * Description:
+ * Searches the IPv6 address list given by @head.  If an exact match if found
+ * it is returned, otherwise NULL is returned.  The caller is responsible for
+ * calling the rcu_read_[un]lock() functions.
+ *
+ */
+struct netlbl_af6list *netlbl_af6list_search_exact(const struct in6_addr *addr,
+                                                  const struct in6_addr *mask,
+                                                  struct list_head *head)
+{
+       struct netlbl_af6list *iter;
+
+       list_for_each_entry_rcu(iter, head, list)
+               if (iter->valid &&
+                   ipv6_addr_equal(&iter->addr, addr) &&
+                   ipv6_addr_equal(&iter->mask, mask))
+                       return iter;
+
+       return NULL;
+}
+#endif /* IPv6 */
+
+/**
+ * netlbl_af4list_add - Add a new IPv4 address entry to a list
+ * @entry: address entry
+ * @head: the list head
+ *
+ * Description:
+ * Add a new address entry to the list pointed to by @head.  On success zero is
+ * returned, otherwise a negative value is returned.  The caller is responsible
+ * for calling the necessary locking functions.
+ *
+ */
+int netlbl_af4list_add(struct netlbl_af4list *entry, struct list_head *head)
+{
+       struct netlbl_af4list *iter;
+
+       iter = netlbl_af4list_search(entry->addr, head);
+       if (iter != NULL &&
+           iter->addr == entry->addr && iter->mask == entry->mask)
+               return -EEXIST;
+
+       /* in order to speed up address searches through the list (the common
+        * case) we need to keep the list in order based on the size of the
+        * address mask such that the entry with the widest mask (smallest
+        * numerical value) appears first in the list */
+       list_for_each_entry_rcu(iter, head, list)
+               if (iter->valid &&
+                   ntohl(entry->mask) > ntohl(iter->mask)) {
+                       __list_add_rcu(&entry->list,
+                                      iter->list.prev,
+                                      &iter->list);
+                       return 0;
+               }
+       list_add_tail_rcu(&entry->list, head);
+       return 0;
+}
+
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+/**
+ * netlbl_af6list_add - Add a new IPv6 address entry to a list
+ * @entry: address entry
+ * @head: the list head
+ *
+ * Description:
+ * Add a new address entry to the list pointed to by @head.  On success zero is
+ * returned, otherwise a negative value is returned.  The caller is responsible
+ * for calling the necessary locking functions.
+ *
+ */
+int netlbl_af6list_add(struct netlbl_af6list *entry, struct list_head *head)
+{
+       struct netlbl_af6list *iter;
+
+       iter = netlbl_af6list_search(&entry->addr, head);
+       if (iter != NULL &&
+           ipv6_addr_equal(&iter->addr, &entry->addr) &&
+           ipv6_addr_equal(&iter->mask, &entry->mask))
+               return -EEXIST;
+
+       /* in order to speed up address searches through the list (the common
+        * case) we need to keep the list in order based on the size of the
+        * address mask such that the entry with the widest mask (smallest
+        * numerical value) appears first in the list */
+       list_for_each_entry_rcu(iter, head, list)
+               if (iter->valid &&
+                   ipv6_addr_cmp(&entry->mask, &iter->mask) > 0) {
+                       __list_add_rcu(&entry->list,
+                                      iter->list.prev,
+                                      &iter->list);
+                       return 0;
+               }
+       list_add_tail_rcu(&entry->list, head);
+       return 0;
+}
+#endif /* IPv6 */
+
+/**
+ * netlbl_af4list_remove_entry - Remove an IPv4 address entry
+ * @entry: address entry
+ *
+ * Description:
+ * Remove the specified IP address entry.  The caller is responsible for
+ * calling the necessary locking functions.
+ *
+ */
+void netlbl_af4list_remove_entry(struct netlbl_af4list *entry)
+{
+       entry->valid = 0;
+       list_del_rcu(&entry->list);
+}
+
+/**
+ * netlbl_af4list_remove - Remove an IPv4 address entry
+ * @addr: IP address
+ * @mask: IP address mask
+ * @head: the list head
+ *
+ * Description:
+ * Remove an IP address entry from the list pointed to by @head.  Returns the
+ * entry on success, NULL on failure.  The caller is responsible for calling
+ * the necessary locking functions.
+ *
+ */
+struct netlbl_af4list *netlbl_af4list_remove(__be32 addr, __be32 mask,
+                                            struct list_head *head)
+{
+       struct netlbl_af4list *entry;
+
+       entry = netlbl_af4list_search(addr, head);
+       if (entry != NULL && entry->addr == addr && entry->mask == mask) {
+               netlbl_af4list_remove_entry(entry);
+               return entry;
+       }
+
+       return NULL;
+}
+
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+/**
+ * netlbl_af6list_remove_entry - Remove an IPv6 address entry
+ * @entry: address entry
+ *
+ * Description:
+ * Remove the specified IP address entry.  The caller is responsible for
+ * calling the necessary locking functions.
+ *
+ */
+void netlbl_af6list_remove_entry(struct netlbl_af6list *entry)
+{
+       entry->valid = 0;
+       list_del_rcu(&entry->list);
+}
+
+/**
+ * netlbl_af6list_remove - Remove an IPv6 address entry
+ * @addr: IP address
+ * @mask: IP address mask
+ * @head: the list head
+ *
+ * Description:
+ * Remove an IP address entry from the list pointed to by @head.  Returns the
+ * entry on success, NULL on failure.  The caller is responsible for calling
+ * the necessary locking functions.
+ *
+ */
+struct netlbl_af6list *netlbl_af6list_remove(const struct in6_addr *addr,
+                                            const struct in6_addr *mask,
+                                            struct list_head *head)
+{
+       struct netlbl_af6list *entry;
+
+       entry = netlbl_af6list_search(addr, head);
+       if (entry != NULL &&
+           ipv6_addr_equal(&entry->addr, addr) &&
+           ipv6_addr_equal(&entry->mask, mask)) {
+               netlbl_af6list_remove_entry(entry);
+               return entry;
+       }
+
+       return NULL;
+}
+#endif /* IPv6 */
+
+/*
+ * Audit Helper Functions
+ */
+
+/**
+ * netlbl_af4list_audit_addr - Audit an IPv4 address
+ * @audit_buf: audit buffer
+ * @src: true if source address, false if destination
+ * @dev: network interface
+ * @addr: IP address
+ * @mask: IP address mask
+ *
+ * Description:
+ * Write the IPv4 address and address mask, if necessary, to @audit_buf.
+ *
+ */
+void netlbl_af4list_audit_addr(struct audit_buffer *audit_buf,
+                                       int src, const char *dev,
+                                       __be32 addr, __be32 mask)
+{
+       u32 mask_val = ntohl(mask);
+       char *dir = (src ? "src" : "dst");
+
+       if (dev != NULL)
+               audit_log_format(audit_buf, " netif=%s", dev);
+       audit_log_format(audit_buf, " %s=" NIPQUAD_FMT, dir, NIPQUAD(addr));
+       if (mask_val != 0xffffffff) {
+               u32 mask_len = 0;
+               while (mask_val > 0) {
+                       mask_val <<= 1;
+                       mask_len++;
+               }
+               audit_log_format(audit_buf, " %s_prefixlen=%d", dir, mask_len);
+       }
+}
+
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+/**
+ * netlbl_af6list_audit_addr - Audit an IPv6 address
+ * @audit_buf: audit buffer
+ * @src: true if source address, false if destination
+ * @dev: network interface
+ * @addr: IP address
+ * @mask: IP address mask
+ *
+ * Description:
+ * Write the IPv6 address and address mask, if necessary, to @audit_buf.
+ *
+ */
+void netlbl_af6list_audit_addr(struct audit_buffer *audit_buf,
+                                int src,
+                                const char *dev,
+                                const struct in6_addr *addr,
+                                const struct in6_addr *mask)
+{
+       char *dir = (src ? "src" : "dst");
+
+       if (dev != NULL)
+               audit_log_format(audit_buf, " netif=%s", dev);
+       audit_log_format(audit_buf, " %s=" NIP6_FMT, dir, NIP6(*addr));
+       if (ntohl(mask->s6_addr32[3]) != 0xffffffff) {
+               u32 mask_len = 0;
+               u32 mask_val;
+               int iter = -1;
+               while (ntohl(mask->s6_addr32[++iter]) == 0xffffffff)
+                       mask_len += 32;
+               mask_val = ntohl(mask->s6_addr32[iter]);
+               while (mask_val > 0) {
+                       mask_val <<= 1;
+                       mask_len++;
+               }
+               audit_log_format(audit_buf, " %s_prefixlen=%d", dir, mask_len);
+       }
+}
+#endif /* IPv6 */
diff --git a/net/netlabel/netlabel_addrlist.h b/net/netlabel/netlabel_addrlist.h
new file mode 100644 (file)
index 0000000..0242bea
--- /dev/null
@@ -0,0 +1,189 @@
+/*
+ * NetLabel Network Address Lists
+ *
+ * This file contains network address list functions used to manage ordered
+ * lists of network addresses for use by the NetLabel subsystem.  The NetLabel
+ * system manages static and dynamic label mappings for network protocols such
+ * as CIPSO and RIPSO.
+ *
+ * Author: Paul Moore <paul.moore@hp.com>
+ *
+ */
+
+/*
+ * (c) Copyright Hewlett-Packard Development Company, L.P., 2008
+ *
+ * This program is free software;  you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY;  without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
+ * the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program;  if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef _NETLABEL_ADDRLIST_H
+#define _NETLABEL_ADDRLIST_H
+
+#include <linux/types.h>
+#include <linux/rcupdate.h>
+#include <linux/list.h>
+#include <linux/in6.h>
+#include <linux/audit.h>
+
+/**
+ * struct netlbl_af4list - NetLabel IPv4 address list
+ * @addr: IPv4 address
+ * @mask: IPv4 address mask
+ * @valid: valid flag
+ * @list: list structure, used internally
+ */
+struct netlbl_af4list {
+       __be32 addr;
+       __be32 mask;
+
+       u32 valid;
+       struct list_head list;
+};
+
+/**
+ * struct netlbl_af6list - NetLabel IPv6 address list
+ * @addr: IPv6 address
+ * @mask: IPv6 address mask
+ * @valid: valid flag
+ * @list: list structure, used internally
+ */
+struct netlbl_af6list {
+       struct in6_addr addr;
+       struct in6_addr mask;
+
+       u32 valid;
+       struct list_head list;
+};
+
+#define __af4list_entry(ptr) container_of(ptr, struct netlbl_af4list, list)
+
+static inline struct netlbl_af4list *__af4list_valid(struct list_head *s,
+                                                    struct list_head *h)
+{
+       struct list_head *i = s;
+       struct netlbl_af4list *n = __af4list_entry(s);
+       while (i != h && !n->valid) {
+               i = i->next;
+               n = __af4list_entry(i);
+       }
+       return n;
+}
+
+static inline struct netlbl_af4list *__af4list_valid_rcu(struct list_head *s,
+                                                        struct list_head *h)
+{
+       struct list_head *i = s;
+       struct netlbl_af4list *n = __af4list_entry(s);
+       while (i != h && !n->valid) {
+               i = rcu_dereference(i->next);
+               n = __af4list_entry(i);
+       }
+       return n;
+}
+
+#define netlbl_af4list_foreach(iter, head)                             \
+       for (iter = __af4list_valid((head)->next, head);                \
+            prefetch(iter->list.next), &iter->list != (head);          \
+            iter = __af4list_valid(iter->list.next, head))
+
+#define netlbl_af4list_foreach_rcu(iter, head)                         \
+       for (iter = __af4list_valid_rcu((head)->next, head);            \
+            prefetch(iter->list.next), &iter->list != (head);          \
+            iter = __af4list_valid_rcu(iter->list.next, head))
+
+#define netlbl_af4list_foreach_safe(iter, tmp, head)                   \
+       for (iter = __af4list_valid((head)->next, head),                \
+                    tmp = __af4list_valid(iter->list.next, head);      \
+            &iter->list != (head);                                     \
+            iter = tmp, tmp = __af4list_valid(iter->list.next, head))
+
+int netlbl_af4list_add(struct netlbl_af4list *entry,
+                      struct list_head *head);
+struct netlbl_af4list *netlbl_af4list_remove(__be32 addr, __be32 mask,
+                                            struct list_head *head);
+void netlbl_af4list_remove_entry(struct netlbl_af4list *entry);
+struct netlbl_af4list *netlbl_af4list_search(__be32 addr,
+                                            struct list_head *head);
+struct netlbl_af4list *netlbl_af4list_search_exact(__be32 addr,
+                                                  __be32 mask,
+                                                  struct list_head *head);
+void netlbl_af4list_audit_addr(struct audit_buffer *audit_buf,
+                              int src, const char *dev,
+                              __be32 addr, __be32 mask);
+
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+
+#define __af6list_entry(ptr) container_of(ptr, struct netlbl_af6list, list)
+
+static inline struct netlbl_af6list *__af6list_valid(struct list_head *s,
+                                                    struct list_head *h)
+{
+       struct list_head *i = s;
+       struct netlbl_af6list *n = __af6list_entry(s);
+       while (i != h && !n->valid) {
+               i = i->next;
+               n = __af6list_entry(i);
+       }
+       return n;
+}
+
+static inline struct netlbl_af6list *__af6list_valid_rcu(struct list_head *s,
+                                                        struct list_head *h)
+{
+       struct list_head *i = s;
+       struct netlbl_af6list *n = __af6list_entry(s);
+       while (i != h && !n->valid) {
+               i = rcu_dereference(i->next);
+               n = __af6list_entry(i);
+       }
+       return n;
+}
+
+#define netlbl_af6list_foreach(iter, head)                             \
+       for (iter = __af6list_valid((head)->next, head);                \
+            prefetch(iter->list.next), &iter->list != (head);          \
+            iter = __af6list_valid(iter->list.next, head))
+
+#define netlbl_af6list_foreach_rcu(iter, head)                         \
+       for (iter = __af6list_valid_rcu((head)->next, head);            \
+            prefetch(iter->list.next), &iter->list != (head);          \
+            iter = __af6list_valid_rcu(iter->list.next, head))
+
+#define netlbl_af6list_foreach_safe(iter, tmp, head)                   \
+       for (iter = __af6list_valid((head)->next, head),                \
+                    tmp = __af6list_valid(iter->list.next, head);      \
+            &iter->list != (head);                                     \
+            iter = tmp, tmp = __af6list_valid(iter->list.next, head))
+
+int netlbl_af6list_add(struct netlbl_af6list *entry,
+                      struct list_head *head);
+struct netlbl_af6list *netlbl_af6list_remove(const struct in6_addr *addr,
+                                            const struct in6_addr *mask,
+                                            struct list_head *head);
+void netlbl_af6list_remove_entry(struct netlbl_af6list *entry);
+struct netlbl_af6list *netlbl_af6list_search(const struct in6_addr *addr,
+                                            struct list_head *head);
+struct netlbl_af6list *netlbl_af6list_search_exact(const struct in6_addr *addr,
+                                                  const struct in6_addr *mask,
+                                                  struct list_head *head);
+void netlbl_af6list_audit_addr(struct audit_buffer *audit_buf,
+                              int src,
+                              const char *dev,
+                              const struct in6_addr *addr,
+                              const struct in6_addr *mask);
+#endif /* IPV6 */
+
+#endif
index 0aec318..fff32b7 100644 (file)
@@ -43,6 +43,7 @@
 #include "netlabel_user.h"
 #include "netlabel_cipso_v4.h"
 #include "netlabel_mgmt.h"
+#include "netlabel_domainhash.h"
 
 /* Argument struct for cipso_v4_doi_walk() */
 struct netlbl_cipsov4_doiwalk_arg {
@@ -51,6 +52,12 @@ struct netlbl_cipsov4_doiwalk_arg {
        u32 seq;
 };
 
+/* Argument struct for netlbl_domhsh_walk() */
+struct netlbl_domhsh_walk_arg {
+       struct netlbl_audit *audit_info;
+       u32 doi;
+};
+
 /* NetLabel Generic NETLINK CIPSOv4 family */
 static struct genl_family netlbl_cipsov4_gnl_family = {
        .id = GENL_ID_GENERATE,
@@ -80,32 +87,6 @@ static const struct nla_policy netlbl_cipsov4_genl_policy[NLBL_CIPSOV4_A_MAX + 1
  * Helper Functions
  */
 
-/**
- * netlbl_cipsov4_doi_free - Frees a CIPSO V4 DOI definition
- * @entry: the entry's RCU field
- *
- * Description:
- * This function is designed to be used as a callback to the call_rcu()
- * function so that the memory allocated to the DOI definition can be released
- * safely.
- *
- */
-void netlbl_cipsov4_doi_free(struct rcu_head *entry)
-{
-       struct cipso_v4_doi *ptr;
-
-       ptr = container_of(entry, struct cipso_v4_doi, rcu);
-       switch (ptr->type) {
-       case CIPSO_V4_MAP_STD:
-               kfree(ptr->map.std->lvl.cipso);
-               kfree(ptr->map.std->lvl.local);
-               kfree(ptr->map.std->cat.cipso);
-               kfree(ptr->map.std->cat.local);
-               break;
-       }
-       kfree(ptr);
-}
-
 /**
  * netlbl_cipsov4_add_common - Parse the common sections of a ADD message
  * @info: the Generic NETLINK info block
@@ -151,9 +132,9 @@ static int netlbl_cipsov4_add_common(struct genl_info *info,
  * @info: the Generic NETLINK info block
  *
  * Description:
- * Create a new CIPSO_V4_MAP_STD DOI definition based on the given ADD message
- * and add it to the CIPSO V4 engine.  Return zero on success and non-zero on
- * error.
+ * Create a new CIPSO_V4_MAP_TRANS DOI definition based on the given ADD
+ * message and add it to the CIPSO V4 engine.  Return zero on success and
+ * non-zero on error.
  *
  */
 static int netlbl_cipsov4_add_std(struct genl_info *info)
@@ -183,7 +164,7 @@ static int netlbl_cipsov4_add_std(struct genl_info *info)
                ret_val = -ENOMEM;
                goto add_std_failure;
        }
-       doi_def->type = CIPSO_V4_MAP_STD;
+       doi_def->type = CIPSO_V4_MAP_TRANS;
 
        ret_val = netlbl_cipsov4_add_common(info, doi_def);
        if (ret_val != 0)
@@ -342,7 +323,7 @@ static int netlbl_cipsov4_add_std(struct genl_info *info)
 
 add_std_failure:
        if (doi_def)
-               netlbl_cipsov4_doi_free(&doi_def->rcu);
+               cipso_v4_doi_free(doi_def);
        return ret_val;
 }
 
@@ -379,7 +360,44 @@ static int netlbl_cipsov4_add_pass(struct genl_info *info)
        return 0;
 
 add_pass_failure:
-       netlbl_cipsov4_doi_free(&doi_def->rcu);
+       cipso_v4_doi_free(doi_def);
+       return ret_val;
+}
+
+/**
+ * netlbl_cipsov4_add_local - Adds a CIPSO V4 DOI definition
+ * @info: the Generic NETLINK info block
+ *
+ * Description:
+ * Create a new CIPSO_V4_MAP_LOCAL DOI definition based on the given ADD
+ * message and add it to the CIPSO V4 engine.  Return zero on success and
+ * non-zero on error.
+ *
+ */
+static int netlbl_cipsov4_add_local(struct genl_info *info)
+{
+       int ret_val;
+       struct cipso_v4_doi *doi_def = NULL;
+
+       if (!info->attrs[NLBL_CIPSOV4_A_TAGLST])
+               return -EINVAL;
+
+       doi_def = kmalloc(sizeof(*doi_def), GFP_KERNEL);
+       if (doi_def == NULL)
+               return -ENOMEM;
+       doi_def->type = CIPSO_V4_MAP_LOCAL;
+
+       ret_val = netlbl_cipsov4_add_common(info, doi_def);
+       if (ret_val != 0)
+               goto add_local_failure;
+
+       ret_val = cipso_v4_doi_add(doi_def);
+       if (ret_val != 0)
+               goto add_local_failure;
+       return 0;
+
+add_local_failure:
+       cipso_v4_doi_free(doi_def);
        return ret_val;
 }
 
@@ -412,14 +430,18 @@ static int netlbl_cipsov4_add(struct sk_buff *skb, struct genl_info *info)
 
        type = nla_get_u32(info->attrs[NLBL_CIPSOV4_A_MTYPE]);
        switch (type) {
-       case CIPSO_V4_MAP_STD:
-               type_str = "std";
+       case CIPSO_V4_MAP_TRANS:
+               type_str = "trans";
                ret_val = netlbl_cipsov4_add_std(info);
                break;
        case CIPSO_V4_MAP_PASS:
                type_str = "pass";
                ret_val = netlbl_cipsov4_add_pass(info);
                break;
+       case CIPSO_V4_MAP_LOCAL:
+               type_str = "local";
+               ret_val = netlbl_cipsov4_add_local(info);
+               break;
        }
        if (ret_val == 0)
                atomic_inc(&netlabel_mgmt_protocount);
@@ -491,7 +513,7 @@ list_start:
        doi_def = cipso_v4_doi_getdef(doi);
        if (doi_def == NULL) {
                ret_val = -EINVAL;
-               goto list_failure;
+               goto list_failure_lock;
        }
 
        ret_val = nla_put_u32(ans_skb, NLBL_CIPSOV4_A_MTYPE, doi_def->type);
@@ -516,7 +538,7 @@ list_start:
        nla_nest_end(ans_skb, nla_a);
 
        switch (doi_def->type) {
-       case CIPSO_V4_MAP_STD:
+       case CIPSO_V4_MAP_TRANS:
                nla_a = nla_nest_start(ans_skb, NLBL_CIPSOV4_A_MLSLVLLST);
                if (nla_a == NULL) {
                        ret_val = -ENOMEM;
@@ -655,7 +677,7 @@ static int netlbl_cipsov4_listall(struct sk_buff *skb,
                                  struct netlink_callback *cb)
 {
        struct netlbl_cipsov4_doiwalk_arg cb_arg;
-       int doi_skip = cb->args[0];
+       u32 doi_skip = cb->args[0];
 
        cb_arg.nl_cb = cb;
        cb_arg.skb = skb;
@@ -667,6 +689,29 @@ static int netlbl_cipsov4_listall(struct sk_buff *skb,
        return skb->len;
 }
 
+/**
+ * netlbl_cipsov4_remove_cb - netlbl_cipsov4_remove() callback for REMOVE
+ * @entry: LSM domain mapping entry
+ * @arg: the netlbl_domhsh_walk_arg structure
+ *
+ * Description:
+ * This function is intended for use by netlbl_cipsov4_remove() as the callback
+ * for the netlbl_domhsh_walk() function; it removes LSM domain map entries
+ * which are associated with the CIPSO DOI specified in @arg.  Returns zero on
+ * success, negative values on failure.
+ *
+ */
+static int netlbl_cipsov4_remove_cb(struct netlbl_dom_map *entry, void *arg)
+{
+       struct netlbl_domhsh_walk_arg *cb_arg = arg;
+
+       if (entry->type == NETLBL_NLTYPE_CIPSOV4 &&
+           entry->type_def.cipsov4->doi == cb_arg->doi)
+               return netlbl_domhsh_remove_entry(entry, cb_arg->audit_info);
+
+       return 0;
+}
+
 /**
  * netlbl_cipsov4_remove - Handle a REMOVE message
  * @skb: the NETLINK buffer
@@ -681,8 +726,11 @@ static int netlbl_cipsov4_remove(struct sk_buff *skb, struct genl_info *info)
 {
        int ret_val = -EINVAL;
        u32 doi = 0;
+       struct netlbl_domhsh_walk_arg cb_arg;
        struct audit_buffer *audit_buf;
        struct netlbl_audit audit_info;
+       u32 skip_bkt = 0;
+       u32 skip_chain = 0;
 
        if (!info->attrs[NLBL_CIPSOV4_A_DOI])
                return -EINVAL;
@@ -690,11 +738,15 @@ static int netlbl_cipsov4_remove(struct sk_buff *skb, struct genl_info *info)
        doi = nla_get_u32(info->attrs[NLBL_CIPSOV4_A_DOI]);
        netlbl_netlink_auditinfo(skb, &audit_info);
 
-       ret_val = cipso_v4_doi_remove(doi,
-                                     &audit_info,
-                                     netlbl_cipsov4_doi_free);
-       if (ret_val == 0)
-               atomic_dec(&netlabel_mgmt_protocount);
+       cb_arg.doi = doi;
+       cb_arg.audit_info = &audit_info;
+       ret_val = netlbl_domhsh_walk(&skip_bkt, &skip_chain,
+                                    netlbl_cipsov4_remove_cb, &cb_arg);
+       if (ret_val == 0 || ret_val == -ENOENT) {
+               ret_val = cipso_v4_doi_remove(doi, &audit_info);
+               if (ret_val == 0)
+                       atomic_dec(&netlabel_mgmt_protocount);
+       }
 
        audit_buf = netlbl_audit_start_common(AUDIT_MAC_CIPSOV4_DEL,
                                              &audit_info);
index 220cb9d..c8a4079 100644 (file)
  *     NLBL_CIPSOV4_A_MTYPE
  *     NLBL_CIPSOV4_A_TAGLST
  *
- *   If using CIPSO_V4_MAP_STD the following attributes are required:
+ *   If using CIPSO_V4_MAP_TRANS the following attributes are required:
  *
  *     NLBL_CIPSOV4_A_MLSLVLLST
  *     NLBL_CIPSOV4_A_MLSCATLST
  *
- *   If using CIPSO_V4_MAP_PASS no additional attributes are required.
+ *   If using CIPSO_V4_MAP_PASS or CIPSO_V4_MAP_LOCAL no additional attributes
+ *   are required.
  *
  * o REMOVE:
  *   Sent by an application to remove a specific DOI mapping table from the
  *     NLBL_CIPSOV4_A_MTYPE
  *     NLBL_CIPSOV4_A_TAGLST
  *
- *   If using CIPSO_V4_MAP_STD the following attributes are required:
+ *   If using CIPSO_V4_MAP_TRANS the following attributes are required:
  *
  *     NLBL_CIPSOV4_A_MLSLVLLST
  *     NLBL_CIPSOV4_A_MLSCATLST
  *
- *   If using CIPSO_V4_MAP_PASS no additional attributes are required.
+ *   If using CIPSO_V4_MAP_PASS or CIPSO_V4_MAP_LOCAL no additional attributes
+ *   are required.
  *
  * o LISTALL:
  *   This message is sent by an application to list the valid DOIs on the
index 643c032..5fadf10 100644 (file)
@@ -11,7 +11,7 @@
  */
 
 /*
- * (c) Copyright Hewlett-Packard Development Company, L.P., 2006
+ * (c) Copyright Hewlett-Packard Development Company, L.P., 2006, 2008
  *
  * This program is free software;  you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -40,6 +40,7 @@
 #include <asm/bug.h>
 
 #include "netlabel_mgmt.h"
+#include "netlabel_addrlist.h"
 #include "netlabel_domainhash.h"
 #include "netlabel_user.h"
 
@@ -72,8 +73,28 @@ static struct netlbl_dom_map *netlbl_domhsh_def = NULL;
 static void netlbl_domhsh_free_entry(struct rcu_head *entry)
 {
        struct netlbl_dom_map *ptr;
+       struct netlbl_af4list *iter4;
+       struct netlbl_af4list *tmp4;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+       struct netlbl_af6list *iter6;
+       struct netlbl_af6list *tmp6;
+#endif /* IPv6 */
 
        ptr = container_of(entry, struct netlbl_dom_map, rcu);
+       if (ptr->type == NETLBL_NLTYPE_ADDRSELECT) {
+               netlbl_af4list_foreach_safe(iter4, tmp4,
+                                           &ptr->type_def.addrsel->list4) {
+                       netlbl_af4list_remove_entry(iter4);
+                       kfree(netlbl_domhsh_addr4_entry(iter4));
+               }
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+               netlbl_af6list_foreach_safe(iter6, tmp6,
+                                           &ptr->type_def.addrsel->list6) {
+                       netlbl_af6list_remove_entry(iter6);
+                       kfree(netlbl_domhsh_addr6_entry(iter6));
+               }
+#endif /* IPv6 */
+       }
        kfree(ptr->domain);
        kfree(ptr);
 }
@@ -115,13 +136,13 @@ static u32 netlbl_domhsh_hash(const char *key)
 static struct netlbl_dom_map *netlbl_domhsh_search(const char *domain)
 {
        u32 bkt;
+       struct list_head *bkt_list;
        struct netlbl_dom_map *iter;
 
        if (domain != NULL) {
                bkt = netlbl_domhsh_hash(domain);
-               list_for_each_entry_rcu(iter,
-                                    &rcu_dereference(netlbl_domhsh)->tbl[bkt],
-                                    list)
+               bkt_list = &rcu_dereference(netlbl_domhsh)->tbl[bkt];
+               list_for_each_entry_rcu(iter, bkt_list, list)
                        if (iter->valid && strcmp(iter->domain, domain) == 0)
                                return iter;
        }
@@ -156,6 +177,69 @@ static struct netlbl_dom_map *netlbl_domhsh_search_def(const char *domain)
        return entry;
 }
 
+/**
+ * netlbl_domhsh_audit_add - Generate an audit entry for an add event
+ * @entry: the entry being added
+ * @addr4: the IPv4 address information
+ * @addr6: the IPv6 address information
+ * @result: the result code
+ * @audit_info: NetLabel audit information
+ *
+ * Description:
+ * Generate an audit record for adding a new NetLabel/LSM mapping entry with
+ * the given information.  Caller is responsibile for holding the necessary
+ * locks.
+ *
+ */
+static void netlbl_domhsh_audit_add(struct netlbl_dom_map *entry,
+                                   struct netlbl_af4list *addr4,
+                                   struct netlbl_af6list *addr6,
+                                   int result,
+                                   struct netlbl_audit *audit_info)
+{
+       struct audit_buffer *audit_buf;
+       struct cipso_v4_doi *cipsov4 = NULL;
+       u32 type;
+
+       audit_buf = netlbl_audit_start_common(AUDIT_MAC_MAP_ADD, audit_info);
+       if (audit_buf != NULL) {
+               audit_log_format(audit_buf, " nlbl_domain=%s",
+                                entry->domain ? entry->domain : "(default)");
+               if (addr4 != NULL) {
+                       struct netlbl_domaddr4_map *map4;
+                       map4 = netlbl_domhsh_addr4_entry(addr4);
+                       type = map4->type;
+                       cipsov4 = map4->type_def.cipsov4;
+                       netlbl_af4list_audit_addr(audit_buf, 0, NULL,
+                                                 addr4->addr, addr4->mask);
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+               } else if (addr6 != NULL) {
+                       struct netlbl_domaddr6_map *map6;
+                       map6 = netlbl_domhsh_addr6_entry(addr6);
+                       type = map6->type;
+                       netlbl_af6list_audit_addr(audit_buf, 0, NULL,
+                                                 &addr6->addr, &addr6->mask);
+#endif /* IPv6 */
+               } else {
+                       type = entry->type;
+                       cipsov4 = entry->type_def.cipsov4;
+               }
+               switch (type) {
+               case NETLBL_NLTYPE_UNLABELED:
+                       audit_log_format(audit_buf, " nlbl_protocol=unlbl");
+                       break;
+               case NETLBL_NLTYPE_CIPSOV4:
+                       BUG_ON(cipsov4 == NULL);
+                       audit_log_format(audit_buf,
+                                        " nlbl_protocol=cipsov4 cipso_doi=%u",
+                                        cipsov4->doi);
+                       break;
+               }
+               audit_log_format(audit_buf, " res=%u", result == 0 ? 1 : 0);
+               audit_log_end(audit_buf);
+       }
+}
+
 /*
  * Domain Hash Table Functions
  */
@@ -213,74 +297,106 @@ int __init netlbl_domhsh_init(u32 size)
 int netlbl_domhsh_add(struct netlbl_dom_map *entry,
                      struct netlbl_audit *audit_info)
 {
-       int ret_val;
-       u32 bkt;
-       struct audit_buffer *audit_buf;
-
-       switch (entry->type) {
-       case NETLBL_NLTYPE_UNLABELED:
-               ret_val = 0;
-               break;
-       case NETLBL_NLTYPE_CIPSOV4:
-               ret_val = cipso_v4_doi_domhsh_add(entry->type_def.cipsov4,
-                                                 entry->domain);
-               break;
-       default:
-               return -EINVAL;
-       }
-       if (ret_val != 0)
-               return ret_val;
-
-       entry->valid = 1;
-       INIT_RCU_HEAD(&entry->rcu);
+       int ret_val = 0;
+       struct netlbl_dom_map *entry_old;
+       struct netlbl_af4list *iter4;
+       struct netlbl_af4list *tmp4;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+       struct netlbl_af6list *iter6;
+       struct netlbl_af6list *tmp6;
+#endif /* IPv6 */
 
        rcu_read_lock();
+
        spin_lock(&netlbl_domhsh_lock);
-       if (entry->domain != NULL) {
-               bkt = netlbl_domhsh_hash(entry->domain);
-               if (netlbl_domhsh_search(entry->domain) == NULL)
+       if (entry->domain != NULL)
+               entry_old = netlbl_domhsh_search(entry->domain);
+       else
+               entry_old = netlbl_domhsh_search_def(entry->domain);
+       if (entry_old == NULL) {
+               entry->valid = 1;
+               INIT_RCU_HEAD(&entry->rcu);
+
+               if (entry->domain != NULL) {
+                       u32 bkt = netlbl_domhsh_hash(entry->domain);
                        list_add_tail_rcu(&entry->list,
                                    &rcu_dereference(netlbl_domhsh)->tbl[bkt]);
-               else
-                       ret_val = -EEXIST;
-       } else {
-               INIT_LIST_HEAD(&entry->list);
-               if (rcu_dereference(netlbl_domhsh_def) == NULL)
+               } else {
+                       INIT_LIST_HEAD(&entry->list);
                        rcu_assign_pointer(netlbl_domhsh_def, entry);
-               else
-                       ret_val = -EEXIST;
-       }
-       spin_unlock(&netlbl_domhsh_lock);
-       audit_buf = netlbl_audit_start_common(AUDIT_MAC_MAP_ADD, audit_info);
-       if (audit_buf != NULL) {
-               audit_log_format(audit_buf,
-                                " nlbl_domain=%s",
-                                entry->domain ? entry->domain : "(default)");
-               switch (entry->type) {
-               case NETLBL_NLTYPE_UNLABELED:
-                       audit_log_format(audit_buf, " nlbl_protocol=unlbl");
-                       break;
-               case NETLBL_NLTYPE_CIPSOV4:
-                       audit_log_format(audit_buf,
-                                        " nlbl_protocol=cipsov4 cipso_doi=%u",
-                                        entry->type_def.cipsov4->doi);
-                       break;
                }
-               audit_log_format(audit_buf, " res=%u", ret_val == 0 ? 1 : 0);
-               audit_log_end(audit_buf);
-       }
-       rcu_read_unlock();
 
-       if (ret_val != 0) {
-               switch (entry->type) {
-               case NETLBL_NLTYPE_CIPSOV4:
-                       if (cipso_v4_doi_domhsh_remove(entry->type_def.cipsov4,
-                                                      entry->domain) != 0)
-                               BUG();
-                       break;
+               if (entry->type == NETLBL_NLTYPE_ADDRSELECT) {
+                       netlbl_af4list_foreach_rcu(iter4,
+                                              &entry->type_def.addrsel->list4)
+                               netlbl_domhsh_audit_add(entry, iter4, NULL,
+                                                       ret_val, audit_info);
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+                       netlbl_af6list_foreach_rcu(iter6,
+                                              &entry->type_def.addrsel->list6)
+                               netlbl_domhsh_audit_add(entry, NULL, iter6,
+                                                       ret_val, audit_info);
+#endif /* IPv6 */
+               } else
+                       netlbl_domhsh_audit_add(entry, NULL, NULL,
+                                               ret_val, audit_info);
+       } else if (entry_old->type == NETLBL_NLTYPE_ADDRSELECT &&
+                  entry->type == NETLBL_NLTYPE_ADDRSELECT) {
+               struct list_head *old_list4;
+               struct list_head *old_list6;
+
+               old_list4 = &entry_old->type_def.addrsel->list4;
+               old_list6 = &entry_old->type_def.addrsel->list6;
+
+               /* we only allow the addition of address selectors if all of
+                * the selectors do not exist in the existing domain map */
+               netlbl_af4list_foreach_rcu(iter4,
+                                          &entry->type_def.addrsel->list4)
+                       if (netlbl_af4list_search_exact(iter4->addr,
+                                                       iter4->mask,
+                                                       old_list4)) {
+                               ret_val = -EEXIST;
+                               goto add_return;
+                       }
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+               netlbl_af6list_foreach_rcu(iter6,
+                                          &entry->type_def.addrsel->list6)
+                       if (netlbl_af6list_search_exact(&iter6->addr,
+                                                       &iter6->mask,
+                                                       old_list6)) {
+                               ret_val = -EEXIST;
+                               goto add_return;
+                       }
+#endif /* IPv6 */
+
+               netlbl_af4list_foreach_safe(iter4, tmp4,
+                                           &entry->type_def.addrsel->list4) {
+                       netlbl_af4list_remove_entry(iter4);
+                       iter4->valid = 1;
+                       ret_val = netlbl_af4list_add(iter4, old_list4);
+                       netlbl_domhsh_audit_add(entry_old, iter4, NULL,
+                                               ret_val, audit_info);
+                       if (ret_val != 0)
+                               goto add_return;
                }
-       }
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+               netlbl_af6list_foreach_safe(iter6, tmp6,
+                                           &entry->type_def.addrsel->list6) {
+                       netlbl_af6list_remove_entry(iter6);
+                       iter6->valid = 1;
+                       ret_val = netlbl_af6list_add(iter6, old_list6);
+                       netlbl_domhsh_audit_add(entry_old, NULL, iter6,
+                                               ret_val, audit_info);
+                       if (ret_val != 0)
+                               goto add_return;
+               }
+#endif /* IPv6 */
+       } else
+               ret_val = -EINVAL;
 
+add_return:
+       spin_unlock(&netlbl_domhsh_lock);
+       rcu_read_unlock();
        return ret_val;
 }
 
@@ -302,35 +418,26 @@ int netlbl_domhsh_add_default(struct netlbl_dom_map *entry,
 }
 
 /**
- * netlbl_domhsh_remove - Removes an entry from the domain hash table
- * @domain: the domain to remove
+ * netlbl_domhsh_remove_entry - Removes a given entry from the domain table
+ * @entry: the entry to remove
  * @audit_info: NetLabel audit information
  *
  * Description:
  * Removes an entry from the domain hash table and handles any updates to the
- * lower level protocol handler (i.e. CIPSO).  Returns zero on success,
- * negative on failure.
+ * lower level protocol handler (i.e. CIPSO).  Caller is responsible for
+ * ensuring that the RCU read lock is held.  Returns zero on success, negative
+ * on failure.
  *
  */
-int netlbl_domhsh_remove(const char *domain, struct netlbl_audit *audit_info)
+int netlbl_domhsh_remove_entry(struct netlbl_dom_map *entry,
+                              struct netlbl_audit *audit_info)
 {
-       int ret_val = -ENOENT;
-       struct netlbl_dom_map *entry;
+       int ret_val = 0;
        struct audit_buffer *audit_buf;
 
-       rcu_read_lock();
-       if (domain)
-               entry = netlbl_domhsh_search(domain);
-       else
-               entry = netlbl_domhsh_search_def(domain);
        if (entry == NULL)
-               goto remove_return;
-       switch (entry->type) {
-       case NETLBL_NLTYPE_CIPSOV4:
-               cipso_v4_doi_domhsh_remove(entry->type_def.cipsov4,
-                                          entry->domain);
-               break;
-       }
+               return -ENOENT;
+
        spin_lock(&netlbl_domhsh_lock);
        if (entry->valid) {
                entry->valid = 0;
@@ -338,8 +445,8 @@ int netlbl_domhsh_remove(const char *domain, struct netlbl_audit *audit_info)
                        list_del_rcu(&entry->list);
                else
                        rcu_assign_pointer(netlbl_domhsh_def, NULL);
-               ret_val = 0;
-       }
+       } else
+               ret_val = -ENOENT;
        spin_unlock(&netlbl_domhsh_lock);
 
        audit_buf = netlbl_audit_start_common(AUDIT_MAC_MAP_DEL, audit_info);
@@ -351,10 +458,54 @@ int netlbl_domhsh_remove(const char *domain, struct netlbl_audit *audit_info)
                audit_log_end(audit_buf);
        }
 
-remove_return:
-       rcu_read_unlock();
-       if (ret_val == 0)
+       if (ret_val == 0) {
+               struct netlbl_af4list *iter4;
+               struct netlbl_domaddr4_map *map4;
+
+               switch (entry->type) {
+               case NETLBL_NLTYPE_ADDRSELECT:
+                       netlbl_af4list_foreach_rcu(iter4,
+                                            &entry->type_def.addrsel->list4) {
+                               map4 = netlbl_domhsh_addr4_entry(iter4);
+                               cipso_v4_doi_putdef(map4->type_def.cipsov4);
+                       }
+                       /* no need to check the IPv6 list since we currently
+                        * support only unlabeled protocols for IPv6 */
+                       break;
+               case NETLBL_NLTYPE_CIPSOV4:
+                       cipso_v4_doi_putdef(entry->type_def.cipsov4);
+                       break;
+               }
                call_rcu(&entry->rcu, netlbl_domhsh_free_entry);
+       }
+
+       return ret_val;
+}
+
+/**
+ * netlbl_domhsh_remove - Removes an entry from the domain hash table
+ * @domain: the domain to remove
+ * @audit_info: NetLabel audit information
+ *
+ * Description:
+ * Removes an entry from the domain hash table and handles any updates to the
+ * lower level protocol handler (i.e. CIPSO).  Returns zero on success,
+ * negative on failure.
+ *
+ */
+int netlbl_domhsh_remove(const char *domain, struct netlbl_audit *audit_info)
+{
+       int ret_val;
+       struct netlbl_dom_map *entry;
+
+       rcu_read_lock();
+       if (domain)
+               entry = netlbl_domhsh_search(domain);
+       else
+               entry = netlbl_domhsh_search_def(domain);
+       ret_val = netlbl_domhsh_remove_entry(entry, audit_info);
+       rcu_read_unlock();
+
        return ret_val;
 }
 
@@ -388,6 +539,70 @@ struct netlbl_dom_map *netlbl_domhsh_getentry(const char *domain)
        return netlbl_domhsh_search_def(domain);
 }
 
+/**
+ * netlbl_domhsh_getentry_af4 - Get an entry from the domain hash table
+ * @domain: the domain name to search for
+ * @addr: the IP address to search for
+ *
+ * Description:
+ * Look through the domain hash table searching for an entry to match @domain
+ * and @addr, return a pointer to a copy of the entry or NULL.  The caller is
+ * responsible for ensuring that rcu_read_[un]lock() is called.
+ *
+ */
+struct netlbl_domaddr4_map *netlbl_domhsh_getentry_af4(const char *domain,
+                                                      __be32 addr)
+{
+       struct netlbl_dom_map *dom_iter;
+       struct netlbl_af4list *addr_iter;
+
+       dom_iter = netlbl_domhsh_search_def(domain);
+       if (dom_iter == NULL)
+               return NULL;
+       if (dom_iter->type != NETLBL_NLTYPE_ADDRSELECT)
+               return NULL;
+
+       addr_iter = netlbl_af4list_search(addr,
+                                         &dom_iter->type_def.addrsel->list4);
+       if (addr_iter == NULL)
+               return NULL;
+
+       return netlbl_domhsh_addr4_entry(addr_iter);
+}
+
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+/**
+ * netlbl_domhsh_getentry_af6 - Get an entry from the domain hash table
+ * @domain: the domain name to search for
+ * @addr: the IP address to search for
+ *
+ * Description:
+ * Look through the domain hash table searching for an entry to match @domain
+ * and @addr, return a pointer to a copy of the entry or NULL.  The caller is
+ * responsible for ensuring that rcu_read_[un]lock() is called.
+ *
+ */
+struct netlbl_domaddr6_map *netlbl_domhsh_getentry_af6(const char *domain,
+                                                  const struct in6_addr *addr)
+{
+       struct netlbl_dom_map *dom_iter;
+       struct netlbl_af6list *addr_iter;
+
+       dom_iter = netlbl_domhsh_search_def(domain);
+       if (dom_iter == NULL)
+               return NULL;
+       if (dom_iter->type != NETLBL_NLTYPE_ADDRSELECT)
+               return NULL;
+
+       addr_iter = netlbl_af6list_search(addr,
+                                         &dom_iter->type_def.addrsel->list6);
+       if (addr_iter == NULL)
+               return NULL;
+
+       return netlbl_domhsh_addr6_entry(addr_iter);
+}
+#endif /* IPv6 */
+
 /**
  * netlbl_domhsh_walk - Iterate through the domain mapping hash table
  * @skip_bkt: the number of buckets to skip at the start
@@ -410,6 +625,7 @@ int netlbl_domhsh_walk(u32 *skip_bkt,
 {
        int ret_val = -ENOENT;
        u32 iter_bkt;
+       struct list_head *iter_list;
        struct netlbl_dom_map *iter_entry;
        u32 chain_cnt = 0;
 
@@ -417,9 +633,8 @@ int netlbl_domhsh_walk(u32 *skip_bkt,
        for (iter_bkt = *skip_bkt;
             iter_bkt < rcu_dereference(netlbl_domhsh)->size;
             iter_bkt++, chain_cnt = 0) {
-               list_for_each_entry_rcu(iter_entry,
-                               &rcu_dereference(netlbl_domhsh)->tbl[iter_bkt],
-                               list)
+               iter_list = &rcu_dereference(netlbl_domhsh)->tbl[iter_bkt];
+               list_for_each_entry_rcu(iter_entry, iter_list, list)
                        if (iter_entry->valid) {
                                if (chain_cnt++ < *skip_chain)
                                        continue;
index 8220990..bfcb676 100644 (file)
@@ -11,7 +11,7 @@
  */
 
 /*
- * (c) Copyright Hewlett-Packard Development Company, L.P., 2006
+ * (c) Copyright Hewlett-Packard Development Company, L.P., 2006, 2008
  *
  * This program is free software;  you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
 #include <linux/rcupdate.h>
 #include <linux/list.h>
 
+#include "netlabel_addrlist.h"
+
 /* Domain hash table size */
 /* XXX - currently this number is an uneducated guess */
 #define NETLBL_DOMHSH_BITSIZE       7
 
-/* Domain mapping definition struct */
+/* Domain mapping definition structures */
+#define netlbl_domhsh_addr4_entry(iter) \
+       container_of(iter, struct netlbl_domaddr4_map, list)
+struct netlbl_domaddr4_map {
+       u32 type;
+       union {
+               struct cipso_v4_doi *cipsov4;
+       } type_def;
+
+       struct netlbl_af4list list;
+};
+#define netlbl_domhsh_addr6_entry(iter) \
+       container_of(iter, struct netlbl_domaddr6_map, list)
+struct netlbl_domaddr6_map {
+       u32 type;
+
+       /* NOTE: no 'type_def' union needed at present since we don't currently
+        *       support any IPv6 labeling protocols */
+
+       struct netlbl_af6list list;
+};
+struct netlbl_domaddr_map {
+       struct list_head list4;
+       struct list_head list6;
+};
 struct netlbl_dom_map {
        char *domain;
        u32 type;
        union {
                struct cipso_v4_doi *cipsov4;
+               struct netlbl_domaddr_map *addrsel;
        } type_def;
 
        u32 valid;
@@ -61,12 +88,21 @@ int netlbl_domhsh_add(struct netlbl_dom_map *entry,
                      struct netlbl_audit *audit_info);
 int netlbl_domhsh_add_default(struct netlbl_dom_map *entry,
                              struct netlbl_audit *audit_info);
+int netlbl_domhsh_remove_entry(struct netlbl_dom_map *entry,
+                              struct netlbl_audit *audit_info);
 int netlbl_domhsh_remove(const char *domain, struct netlbl_audit *audit_info);
 int netlbl_domhsh_remove_default(struct netlbl_audit *audit_info);
 struct netlbl_dom_map *netlbl_domhsh_getentry(const char *domain);
+struct netlbl_domaddr4_map *netlbl_domhsh_getentry_af4(const char *domain,
+                                                      __be32 addr);
 int netlbl_domhsh_walk(u32 *skip_bkt,
                     u32 *skip_chain,
                     int (*callback) (struct netlbl_dom_map *entry, void *arg),
                     void *cb_arg);
 
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+struct netlbl_domaddr6_map *netlbl_domhsh_getentry_af6(const char *domain,
+                                                 const struct in6_addr *addr);
+#endif /* IPv6 */
+
 #endif
index 39793a1..b32eceb 100644 (file)
@@ -10,7 +10,7 @@
  */
 
 /*
- * (c) Copyright Hewlett-Packard Development Company, L.P., 2006
+ * (c) Copyright Hewlett-Packard Development Company, L.P., 2006, 2008
  *
  * This program is free software;  you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -82,7 +82,7 @@ int netlbl_cfg_unlbl_add_map(const char *domain,
 
        entry = kzalloc(sizeof(*entry), GFP_ATOMIC);
        if (entry == NULL)
-               goto cfg_unlbl_add_map_failure;
+               return -ENOMEM;
        if (domain != NULL) {
                entry->domain = kstrdup(domain, GFP_ATOMIC);
                if (entry->domain == NULL)
@@ -103,49 +103,6 @@ cfg_unlbl_add_map_failure:
        return ret_val;
 }
 
-/**
- * netlbl_cfg_cipsov4_add - Add a new CIPSOv4 DOI definition
- * @doi_def: the DOI definition
- * @audit_info: NetLabel audit information
- *
- * Description:
- * Add a new CIPSOv4 DOI definition to the NetLabel subsystem.  Returns zero on
- * success, negative values on failure.
- *
- */
-int netlbl_cfg_cipsov4_add(struct cipso_v4_doi *doi_def,
-                          struct netlbl_audit *audit_info)
-{
-       int ret_val;
-       const char *type_str;
-       struct audit_buffer *audit_buf;
-
-       ret_val = cipso_v4_doi_add(doi_def);
-
-       audit_buf = netlbl_audit_start_common(AUDIT_MAC_CIPSOV4_ADD,
-                                             audit_info);
-       if (audit_buf != NULL) {
-               switch (doi_def->type) {
-               case CIPSO_V4_MAP_STD:
-                       type_str = "std";
-                       break;
-               case CIPSO_V4_MAP_PASS:
-                       type_str = "pass";
-                       break;
-               default:
-                       type_str = "(unknown)";
-               }
-               audit_log_format(audit_buf,
-                                " cipso_doi=%u cipso_type=%s res=%u",
-                                doi_def->doi,
-                                type_str,
-                                ret_val == 0 ? 1 : 0);
-               audit_log_end(audit_buf);
-       }
-
-       return ret_val;
-}
-
 /**
  * netlbl_cfg_cipsov4_add_map - Add a new CIPSOv4 DOI definition and mapping
  * @doi_def: the DOI definition
@@ -164,58 +121,71 @@ int netlbl_cfg_cipsov4_add_map(struct cipso_v4_doi *doi_def,
                               struct netlbl_audit *audit_info)
 {
        int ret_val = -ENOMEM;
+       u32 doi;
+       u32 doi_type;
        struct netlbl_dom_map *entry;
+       const char *type_str;
+       struct audit_buffer *audit_buf;
+
+       doi = doi_def->doi;
+       doi_type = doi_def->type;
 
        entry = kzalloc(sizeof(*entry), GFP_ATOMIC);
        if (entry == NULL)
-               goto cfg_cipsov4_add_map_failure;
+               return -ENOMEM;
        if (domain != NULL) {
                entry->domain = kstrdup(domain, GFP_ATOMIC);
                if (entry->domain == NULL)
                        goto cfg_cipsov4_add_map_failure;
        }
-       entry->type = NETLBL_NLTYPE_CIPSOV4;
-       entry->type_def.cipsov4 = doi_def;
-
-       /* Grab a RCU read lock here so nothing happens to the doi_def variable
-        * between adding it to the CIPSOv4 protocol engine and adding a
-        * domain mapping for it. */
 
-       rcu_read_lock();
-       ret_val = netlbl_cfg_cipsov4_add(doi_def, audit_info);
+       ret_val = cipso_v4_doi_add(doi_def);
        if (ret_val != 0)
-               goto cfg_cipsov4_add_map_failure_unlock;
+               goto cfg_cipsov4_add_map_failure_remove_doi;
+       entry->type = NETLBL_NLTYPE_CIPSOV4;
+       entry->type_def.cipsov4 = cipso_v4_doi_getdef(doi);
+       if (entry->type_def.cipsov4 == NULL) {
+               ret_val = -ENOENT;
+               goto cfg_cipsov4_add_map_failure_remove_doi;
+       }
        ret_val = netlbl_domhsh_add(entry, audit_info);
        if (ret_val != 0)
-               goto cfg_cipsov4_add_map_failure_remove_doi;
-       rcu_read_unlock();
+               goto cfg_cipsov4_add_map_failure_release_doi;
 
-       return 0;
+cfg_cipsov4_add_map_return:
+       audit_buf = netlbl_audit_start_common(AUDIT_MAC_CIPSOV4_ADD,
+                                             audit_info);
+       if (audit_buf != NULL) {
+               switch (doi_type) {
+               case CIPSO_V4_MAP_TRANS:
+                       type_str = "trans";
+                       break;
+               case CIPSO_V4_MAP_PASS:
+                       type_str = "pass";
+                       break;
+               case CIPSO_V4_MAP_LOCAL:
+                       type_str = "local";
+                       break;
+               default:
+                       type_str = "(unknown)";
+               }
+               audit_log_format(audit_buf,
+                                " cipso_doi=%u cipso_type=%s res=%u",
+                                doi, type_str, ret_val == 0 ? 1 : 0);
+               audit_log_end(audit_buf);
+       }
 
+       return ret_val;
+
+cfg_cipsov4_add_map_failure_release_doi:
+       cipso_v4_doi_putdef(doi_def);
 cfg_cipsov4_add_map_failure_remove_doi:
-       cipso_v4_doi_remove(doi_def->doi, audit_info, netlbl_cipsov4_doi_free);
-cfg_cipsov4_add_map_failure_unlock:
-       rcu_read_unlock();
+       cipso_v4_doi_remove(doi, audit_info);
 cfg_cipsov4_add_map_failure:
        if (entry != NULL)
                kfree(entry->domain);
        kfree(entry);
-       return ret_val;
-}
-
-/**
- * netlbl_cfg_cipsov4_del - Removean existing CIPSOv4 DOI definition
- * @doi: the CIPSO DOI value
- * @audit_info: NetLabel audit information
- *
- * Description:
- * Removes an existing CIPSOv4 DOI definition from the NetLabel subsystem.
- * Returns zero on success, negative values on failure.
- *
- */
-int netlbl_cfg_cipsov4_del(u32 doi, struct netlbl_audit *audit_info)
-{
-       return cipso_v4_doi_remove(doi, audit_info, netlbl_cipsov4_doi_free);
+       goto cfg_cipsov4_add_map_return;
 }
 
 /*
@@ -452,7 +422,9 @@ int netlbl_enabled(void)
  * Attach the correct label to the given socket using the security attributes
  * specified in @secattr.  This function requires exclusive access to @sk,
  * which means it either needs to be in the process of being created or locked.
- * Returns zero on success, negative values on failure.
+ * Returns zero on success, -EDESTADDRREQ if the domain is configured to use
+ * network address selectors (can't blindly label the socket), and negative
+ * values on all other failures.
  *
  */
 int netlbl_sock_setattr(struct sock *sk,
@@ -466,6 +438,9 @@ int netlbl_sock_setattr(struct sock *sk,
        if (dom_entry == NULL)
                goto socket_setattr_return;
        switch (dom_entry->type) {
+       case NETLBL_NLTYPE_ADDRSELECT:
+               ret_val = -EDESTADDRREQ;
+               break;
        case NETLBL_NLTYPE_CIPSOV4:
                ret_val = cipso_v4_sock_setattr(sk,
                                                dom_entry->type_def.cipsov4,
@@ -483,6 +458,20 @@ socket_setattr_return:
        return ret_val;
 }
 
+/**
+ * netlbl_sock_delattr - Delete all the NetLabel labels on a socket
+ * @sk: the socket
+ *
+ * Description:
+ * Remove all the NetLabel labeling from @sk.  The caller is responsible for
+ * ensuring that @sk is locked.
+ *
+ */
+void netlbl_sock_delattr(struct sock *sk)
+{
+       cipso_v4_sock_delattr(sk);
+}
+
 /**
  * netlbl_sock_getattr - Determine the security attributes of a sock
  * @sk: the sock
@@ -500,6 +489,128 @@ int netlbl_sock_getattr(struct sock *sk, struct netlbl_lsm_secattr *secattr)
        return cipso_v4_sock_getattr(sk, secattr);
 }
 
+/**
+ * netlbl_conn_setattr - Label a connected socket using the correct protocol
+ * @sk: the socket to label
+ * @addr: the destination address
+ * @secattr: the security attributes
+ *
+ * Description:
+ * Attach the correct label to the given connected socket using the security
+ * attributes specified in @secattr.  The caller is responsible for ensuring
+ * that @sk is locked.  Returns zero on success, negative values on failure.
+ *
+ */
+int netlbl_conn_setattr(struct sock *sk,
+                       struct sockaddr *addr,
+                       const struct netlbl_lsm_secattr *secattr)
+{
+       int ret_val;
+       struct sockaddr_in *addr4;
+       struct netlbl_domaddr4_map *af4_entry;
+
+       rcu_read_lock();
+       switch (addr->sa_family) {
+       case AF_INET:
+               addr4 = (struct sockaddr_in *)addr;
+               af4_entry = netlbl_domhsh_getentry_af4(secattr->domain,
+                                                      addr4->sin_addr.s_addr);
+               if (af4_entry == NULL) {
+                       ret_val = -ENOENT;
+                       goto conn_setattr_return;
+               }
+               switch (af4_entry->type) {
+               case NETLBL_NLTYPE_CIPSOV4:
+                       ret_val = cipso_v4_sock_setattr(sk,
+                                                  af4_entry->type_def.cipsov4,
+                                                  secattr);
+                       break;
+               case NETLBL_NLTYPE_UNLABELED:
+                       /* just delete the protocols we support for right now
+                        * but we could remove other protocols if needed */
+                       cipso_v4_sock_delattr(sk);
+                       ret_val = 0;
+                       break;
+               default:
+                       ret_val = -ENOENT;
+               }
+               break;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+       case AF_INET6:
+               /* since we don't support any IPv6 labeling protocols right
+                * now we can optimize everything away until we do */
+               ret_val = 0;
+               break;
+#endif /* IPv6 */
+       default:
+               ret_val = 0;
+       }
+
+conn_setattr_return:
+       rcu_read_unlock();
+       return ret_val;
+}
+
+/**
+ * netlbl_skbuff_setattr - Label a packet using the correct protocol
+ * @skb: the packet
+ * @family: protocol family
+ * @secattr: the security attributes
+ *
+ * Description:
+ * Attach the correct label to the given packet using the security attributes
+ * specified in @secattr.  Returns zero on success, negative values on failure.
+ *
+ */
+int netlbl_skbuff_setattr(struct sk_buff *skb,
+                         u16 family,
+                         const struct netlbl_lsm_secattr *secattr)
+{
+       int ret_val;
+       struct iphdr *hdr4;
+       struct netlbl_domaddr4_map *af4_entry;
+
+       rcu_read_lock();
+       switch (family) {
+       case AF_INET:
+               hdr4 = ip_hdr(skb);
+               af4_entry = netlbl_domhsh_getentry_af4(secattr->domain,
+                                                      hdr4->daddr);
+               if (af4_entry == NULL) {
+                       ret_val = -ENOENT;
+                       goto skbuff_setattr_return;
+               }
+               switch (af4_entry->type) {
+               case NETLBL_NLTYPE_CIPSOV4:
+                       ret_val = cipso_v4_skbuff_setattr(skb,
+                                                  af4_entry->type_def.cipsov4,
+                                                  secattr);
+                       break;
+               case NETLBL_NLTYPE_UNLABELED:
+                       /* just delete the protocols we support for right now
+                        * but we could remove other protocols if needed */
+                       ret_val = cipso_v4_skbuff_delattr(skb);
+                       break;
+               default:
+                       ret_val = -ENOENT;
+               }
+               break;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+       case AF_INET6:
+               /* since we don't support any IPv6 labeling protocols right
+                * now we can optimize everything away until we do */
+               ret_val = 0;
+               break;
+#endif /* IPv6 */
+       default:
+               ret_val = 0;
+       }
+
+skbuff_setattr_return:
+       rcu_read_unlock();
+       return ret_val;
+}
+
 /**
  * netlbl_skbuff_getattr - Determine the security attributes of a packet
  * @skb: the packet
@@ -528,6 +639,7 @@ int netlbl_skbuff_getattr(const struct sk_buff *skb,
  * netlbl_skbuff_err - Handle a LSM error on a sk_buff
  * @skb: the packet
  * @error: the error code
+ * @gateway: true if host is acting as a gateway, false otherwise
  *
  * Description:
  * Deal with a LSM problem when handling the packet in @skb, typically this is
@@ -535,10 +647,10 @@ int netlbl_skbuff_getattr(const struct sk_buff *skb,
  * according to the packet's labeling protocol.
  *
  */
-void netlbl_skbuff_err(struct sk_buff *skb, int error)
+void netlbl_skbuff_err(struct sk_buff *skb, int error, int gateway)
 {
        if (CIPSO_V4_OPTEXIST(skb))
-               cipso_v4_error(skb, error, 0);
+               cipso_v4_error(skb, error, gateway);
 }
 
 /**
index 44be5d5..ee769ec 100644 (file)
@@ -10,7 +10,7 @@
  */
 
 /*
- * (c) Copyright Hewlett-Packard Development Company, L.P., 2006
+ * (c) Copyright Hewlett-Packard Development Company, L.P., 2006, 2008
  *
  * This program is free software;  you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
 #include <linux/socket.h>
 #include <linux/string.h>
 #include <linux/skbuff.h>
+#include <linux/in.h>
+#include <linux/in6.h>
 #include <net/sock.h>
 #include <net/netlink.h>
 #include <net/genetlink.h>
+#include <net/ip.h>
+#include <net/ipv6.h>
 #include <net/netlabel.h>
 #include <net/cipso_ipv4.h>
 #include <asm/atomic.h>
@@ -71,85 +75,336 @@ static const struct nla_policy netlbl_mgmt_genl_policy[NLBL_MGMT_A_MAX + 1] = {
 };
 
 /*
- * NetLabel Command Handlers
+ * Helper Functions
  */
 
 /**
  * netlbl_mgmt_add - Handle an ADD message
- * @skb: the NETLINK buffer
  * @info: the Generic NETLINK info block
+ * @audit_info: NetLabel audit information
  *
  * Description:
- * Process a user generated ADD message and add the domains from the message
- * to the hash table.  See netlabel.h for a description of the message format.
- * Returns zero on success, negative values on failure.
+ * Helper function for the ADD and ADDDEF messages to add the domain mappings
+ * from the message to the hash table.  See netlabel.h for a description of the
+ * message format.  Returns zero on success, negative values on failure.
  *
  */
-static int netlbl_mgmt_add(struct sk_buff *skb, struct genl_info *info)
+static int netlbl_mgmt_add_common(struct genl_info *info,
+                                 struct netlbl_audit *audit_info)
 {
        int ret_val = -EINVAL;
        struct netlbl_dom_map *entry = NULL;
-       size_t tmp_size;
+       struct netlbl_domaddr_map *addrmap = NULL;
+       struct cipso_v4_doi *cipsov4 = NULL;
        u32 tmp_val;
-       struct netlbl_audit audit_info;
-
-       if (!info->attrs[NLBL_MGMT_A_DOMAIN] ||
-           !info->attrs[NLBL_MGMT_A_PROTOCOL])
-               goto add_failure;
-
-       netlbl_netlink_auditinfo(skb, &audit_info);
 
        entry = kzalloc(sizeof(*entry), GFP_KERNEL);
        if (entry == NULL) {
                ret_val = -ENOMEM;
                goto add_failure;
        }
-       tmp_size = nla_len(info->attrs[NLBL_MGMT_A_DOMAIN]);
-       entry->domain = kmalloc(tmp_size, GFP_KERNEL);
-       if (entry->domain == NULL) {
-               ret_val = -ENOMEM;
-               goto add_failure;
-       }
        entry->type = nla_get_u32(info->attrs[NLBL_MGMT_A_PROTOCOL]);
-       nla_strlcpy(entry->domain, info->attrs[NLBL_MGMT_A_DOMAIN], tmp_size);
+       if (info->attrs[NLBL_MGMT_A_DOMAIN]) {
+               size_t tmp_size = nla_len(info->attrs[NLBL_MGMT_A_DOMAIN]);
+               entry->domain = kmalloc(tmp_size, GFP_KERNEL);
+               if (entry->domain == NULL) {
+                       ret_val = -ENOMEM;
+                       goto add_failure;
+               }
+               nla_strlcpy(entry->domain,
+                           info->attrs[NLBL_MGMT_A_DOMAIN], tmp_size);
+       }
+
+       /* NOTE: internally we allow/use a entry->type value of
+        *       NETLBL_NLTYPE_ADDRSELECT but we don't currently allow users
+        *       to pass that as a protocol value because we need to know the
+        *       "real" protocol */
 
        switch (entry->type) {
        case NETLBL_NLTYPE_UNLABELED:
-               ret_val = netlbl_domhsh_add(entry, &audit_info);
                break;
        case NETLBL_NLTYPE_CIPSOV4:
                if (!info->attrs[NLBL_MGMT_A_CV4DOI])
                        goto add_failure;
 
                tmp_val = nla_get_u32(info->attrs[NLBL_MGMT_A_CV4DOI]);
-               /* We should be holding a rcu_read_lock() here while we hold
-                * the result but since the entry will always be deleted when
-                * the CIPSO DOI is deleted we aren't going to keep the
-                * lock. */
-               rcu_read_lock();
-               entry->type_def.cipsov4 = cipso_v4_doi_getdef(tmp_val);
-               if (entry->type_def.cipsov4 == NULL) {
-                       rcu_read_unlock();
+               cipsov4 = cipso_v4_doi_getdef(tmp_val);
+               if (cipsov4 == NULL)
                        goto add_failure;
-               }
-               ret_val = netlbl_domhsh_add(entry, &audit_info);
-               rcu_read_unlock();
+               entry->type_def.cipsov4 = cipsov4;
                break;
        default:
                goto add_failure;
        }
+
+       if (info->attrs[NLBL_MGMT_A_IPV4ADDR]) {
+               struct in_addr *addr;
+               struct in_addr *mask;
+               struct netlbl_domaddr4_map *map;
+
+               addrmap = kzalloc(sizeof(*addrmap), GFP_KERNEL);
+               if (addrmap == NULL) {
+                       ret_val = -ENOMEM;
+                       goto add_failure;
+               }
+               INIT_LIST_HEAD(&addrmap->list4);
+               INIT_LIST_HEAD(&addrmap->list6);
+
+               if (nla_len(info->attrs[NLBL_MGMT_A_IPV4ADDR]) !=
+                   sizeof(struct in_addr)) {
+                       ret_val = -EINVAL;
+                       goto add_failure;
+               }
+               if (nla_len(info->attrs[NLBL_MGMT_A_IPV4MASK]) !=
+                   sizeof(struct in_addr)) {
+                       ret_val = -EINVAL;
+                       goto add_failure;
+               }
+               addr = nla_data(info->attrs[NLBL_MGMT_A_IPV4ADDR]);
+               mask = nla_data(info->attrs[NLBL_MGMT_A_IPV4MASK]);
+
+               map = kzalloc(sizeof(*map), GFP_KERNEL);
+               if (map == NULL) {
+                       ret_val = -ENOMEM;
+                       goto add_failure;
+               }
+               map->list.addr = addr->s_addr & mask->s_addr;
+               map->list.mask = mask->s_addr;
+               map->list.valid = 1;
+               map->type = entry->type;
+               if (cipsov4)
+                       map->type_def.cipsov4 = cipsov4;
+
+               ret_val = netlbl_af4list_add(&map->list, &addrmap->list4);
+               if (ret_val != 0) {
+                       kfree(map);
+                       goto add_failure;
+               }
+
+               entry->type = NETLBL_NLTYPE_ADDRSELECT;
+               entry->type_def.addrsel = addrmap;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+       } else if (info->attrs[NLBL_MGMT_A_IPV6ADDR]) {
+               struct in6_addr *addr;
+               struct in6_addr *mask;
+               struct netlbl_domaddr6_map *map;
+
+               addrmap = kzalloc(sizeof(*addrmap), GFP_KERNEL);
+               if (addrmap == NULL) {
+                       ret_val = -ENOMEM;
+                       goto add_failure;
+               }
+               INIT_LIST_HEAD(&addrmap->list4);
+               INIT_LIST_HEAD(&addrmap->list6);
+
+               if (nla_len(info->attrs[NLBL_MGMT_A_IPV6ADDR]) !=
+                   sizeof(struct in6_addr)) {
+                       ret_val = -EINVAL;
+                       goto add_failure;
+               }
+               if (nla_len(info->attrs[NLBL_MGMT_A_IPV6MASK]) !=
+                   sizeof(struct in6_addr)) {
+                       ret_val = -EINVAL;
+                       goto add_failure;
+               }
+               addr = nla_data(info->attrs[NLBL_MGMT_A_IPV6ADDR]);
+               mask = nla_data(info->attrs[NLBL_MGMT_A_IPV6MASK]);
+
+               map = kzalloc(sizeof(*map), GFP_KERNEL);
+               if (map == NULL) {
+                       ret_val = -ENOMEM;
+                       goto add_failure;
+               }
+               ipv6_addr_copy(&map->list.addr, addr);
+               map->list.addr.s6_addr32[0] &= mask->s6_addr32[0];
+               map->list.addr.s6_addr32[1] &= mask->s6_addr32[1];
+               map->list.addr.s6_addr32[2] &= mask->s6_addr32[2];
+               map->list.addr.s6_addr32[3] &= mask->s6_addr32[3];
+               ipv6_addr_copy(&map->list.mask, mask);
+               map->list.valid = 1;
+               map->type = entry->type;
+
+               ret_val = netlbl_af6list_add(&map->list, &addrmap->list6);
+               if (ret_val != 0) {
+                       kfree(map);
+                       goto add_failure;
+               }
+
+               entry->type = NETLBL_NLTYPE_ADDRSELECT;
+               entry->type_def.addrsel = addrmap;
+#endif /* IPv6 */
+       }
+
+       ret_val = netlbl_domhsh_add(entry, audit_info);
        if (ret_val != 0)
                goto add_failure;
 
        return 0;
 
 add_failure:
+       if (cipsov4)
+               cipso_v4_doi_putdef(cipsov4);
        if (entry)
                kfree(entry->domain);
+       kfree(addrmap);
        kfree(entry);
        return ret_val;
 }
 
+/**
+ * netlbl_mgmt_listentry - List a NetLabel/LSM domain map entry
+ * @skb: the NETLINK buffer
+ * @entry: the map entry
+ *
+ * Description:
+ * This function is a helper function used by the LISTALL and LISTDEF command
+ * handlers.  The caller is responsibile for ensuring that the RCU read lock
+ * is held.  Returns zero on success, negative values on failure.
+ *
+ */
+static int netlbl_mgmt_listentry(struct sk_buff *skb,
+                                struct netlbl_dom_map *entry)
+{
+       int ret_val;
+       struct nlattr *nla_a;
+       struct nlattr *nla_b;
+       struct netlbl_af4list *iter4;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+       struct netlbl_af6list *iter6;
+#endif
+
+       if (entry->domain != NULL) {
+               ret_val = nla_put_string(skb,
+                                        NLBL_MGMT_A_DOMAIN, entry->domain);
+               if (ret_val != 0)
+                       return ret_val;
+       }
+
+       switch (entry->type) {
+       case NETLBL_NLTYPE_ADDRSELECT:
+               nla_a = nla_nest_start(skb, NLBL_MGMT_A_SELECTORLIST);
+               if (nla_a == NULL)
+                       return -ENOMEM;
+
+               netlbl_af4list_foreach_rcu(iter4,
+                                          &entry->type_def.addrsel->list4) {
+                       struct netlbl_domaddr4_map *map4;
+                       struct in_addr addr_struct;
+
+                       nla_b = nla_nest_start(skb, NLBL_MGMT_A_ADDRSELECTOR);
+                       if (nla_b == NULL)
+                               return -ENOMEM;
+
+                       addr_struct.s_addr = iter4->addr;
+                       ret_val = nla_put(skb, NLBL_MGMT_A_IPV4ADDR,
+                                         sizeof(struct in_addr),
+                                         &addr_struct);
+                       if (ret_val != 0)
+                               return ret_val;
+                       addr_struct.s_addr = iter4->mask;
+                       ret_val = nla_put(skb, NLBL_MGMT_A_IPV4MASK,
+                                         sizeof(struct in_addr),
+                                         &addr_struct);
+                       if (ret_val != 0)
+                               return ret_val;
+                       map4 = netlbl_domhsh_addr4_entry(iter4);
+                       ret_val = nla_put_u32(skb, NLBL_MGMT_A_PROTOCOL,
+                                             map4->type);
+                       if (ret_val != 0)
+                               return ret_val;
+                       switch (map4->type) {
+                       case NETLBL_NLTYPE_CIPSOV4:
+                               ret_val = nla_put_u32(skb, NLBL_MGMT_A_CV4DOI,
+                                                 map4->type_def.cipsov4->doi);
+                               if (ret_val != 0)
+                                       return ret_val;
+                               break;
+                       }
+
+                       nla_nest_end(skb, nla_b);
+               }
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+               netlbl_af6list_foreach_rcu(iter6,
+                                          &entry->type_def.addrsel->list6) {
+                       struct netlbl_domaddr6_map *map6;
+
+                       nla_b = nla_nest_start(skb, NLBL_MGMT_A_ADDRSELECTOR);
+                       if (nla_b == NULL)
+                               return -ENOMEM;
+
+                       ret_val = nla_put(skb, NLBL_MGMT_A_IPV6ADDR,
+                                         sizeof(struct in6_addr),
+                                         &iter6->addr);
+                       if (ret_val != 0)
+                               return ret_val;
+                       ret_val = nla_put(skb, NLBL_MGMT_A_IPV6MASK,
+                                         sizeof(struct in6_addr),
+                                         &iter6->mask);
+                       if (ret_val != 0)
+                               return ret_val;
+                       map6 = netlbl_domhsh_addr6_entry(iter6);
+                       ret_val = nla_put_u32(skb, NLBL_MGMT_A_PROTOCOL,
+                                             map6->type);
+                       if (ret_val != 0)
+                               return ret_val;
+
+                       nla_nest_end(skb, nla_b);
+               }
+#endif /* IPv6 */
+
+               nla_nest_end(skb, nla_a);
+               break;
+       case NETLBL_NLTYPE_UNLABELED:
+               ret_val = nla_put_u32(skb, NLBL_MGMT_A_PROTOCOL, entry->type);
+               break;
+       case NETLBL_NLTYPE_CIPSOV4:
+               ret_val = nla_put_u32(skb, NLBL_MGMT_A_PROTOCOL, entry->type);
+               if (ret_val != 0)
+                       return ret_val;
+               ret_val = nla_put_u32(skb, NLBL_MGMT_A_CV4DOI,
+                                     entry->type_def.cipsov4->doi);
+               break;
+       }
+
+       return ret_val;
+}
+
+/*
+ * NetLabel Command Handlers
+ */
+
+/**
+ * netlbl_mgmt_add - Handle an ADD message
+ * @skb: the NETLINK buffer
+ * @info: the Generic NETLINK info block
+ *
+ * Description:
+ * Process a user generated ADD message and add the domains from the message
+ * to the hash table.  See netlabel.h for a description of the message format.
+ * Returns zero on success, negative values on failure.
+ *
+ */
+static int netlbl_mgmt_add(struct sk_buff *skb, struct genl_info *info)
+{
+       struct netlbl_audit audit_info;
+
+       if ((!info->attrs[NLBL_MGMT_A_DOMAIN]) ||
+           (!info->attrs[NLBL_MGMT_A_PROTOCOL]) ||
+           (info->attrs[NLBL_MGMT_A_IPV4ADDR] &&
+            info->attrs[NLBL_MGMT_A_IPV6ADDR]) ||
+           (info->attrs[NLBL_MGMT_A_IPV4MASK] &&
+            info->attrs[NLBL_MGMT_A_IPV6MASK]) ||
+           ((info->attrs[NLBL_MGMT_A_IPV4ADDR] != NULL) ^
+            (info->attrs[NLBL_MGMT_A_IPV4MASK] != NULL)) ||
+           ((info->attrs[NLBL_MGMT_A_IPV6ADDR] != NULL) ^
+            (info->attrs[NLBL_MGMT_A_IPV6MASK] != NULL)))
+               return -EINVAL;
+
+       netlbl_netlink_auditinfo(skb, &audit_info);
+
+       return netlbl_mgmt_add_common(info, &audit_info);
+}
+
 /**
  * netlbl_mgmt_remove - Handle a REMOVE message
  * @skb: the NETLINK buffer
@@ -198,23 +453,9 @@ static int netlbl_mgmt_listall_cb(struct netlbl_dom_map *entry, void *arg)
        if (data == NULL)
                goto listall_cb_failure;
 
-       ret_val = nla_put_string(cb_arg->skb,
-                                NLBL_MGMT_A_DOMAIN,
-                                entry->domain);
+       ret_val = netlbl_mgmt_listentry(cb_arg->skb, entry);
        if (ret_val != 0)
                goto listall_cb_failure;
-       ret_val = nla_put_u32(cb_arg->skb, NLBL_MGMT_A_PROTOCOL, entry->type);
-       if (ret_val != 0)
-               goto listall_cb_failure;
-       switch (entry->type) {
-       case NETLBL_NLTYPE_CIPSOV4:
-               ret_val = nla_put_u32(cb_arg->skb,
-                                     NLBL_MGMT_A_CV4DOI,
-                                     entry->type_def.cipsov4->doi);
-               if (ret_val != 0)
-                       goto listall_cb_failure;
-               break;
-       }
 
        cb_arg->seq++;
        return genlmsg_end(cb_arg->skb, data);
@@ -268,56 +509,22 @@ static int netlbl_mgmt_listall(struct sk_buff *skb,
  */
 static int netlbl_mgmt_adddef(struct sk_buff *skb, struct genl_info *info)
 {
-       int ret_val = -EINVAL;
-       struct netlbl_dom_map *entry = NULL;
-       u32 tmp_val;
        struct netlbl_audit audit_info;
 
-       if (!info->attrs[NLBL_MGMT_A_PROTOCOL])
-               goto adddef_failure;
+       if ((!info->attrs[NLBL_MGMT_A_PROTOCOL]) ||
+           (info->attrs[NLBL_MGMT_A_IPV4ADDR] &&
+            info->attrs[NLBL_MGMT_A_IPV6ADDR]) ||
+           (info->attrs[NLBL_MGMT_A_IPV4MASK] &&
+            info->attrs[NLBL_MGMT_A_IPV6MASK]) ||
+           ((info->attrs[NLBL_MGMT_A_IPV4ADDR] != NULL) ^
+            (info->attrs[NLBL_MGMT_A_IPV4MASK] != NULL)) ||
+           ((info->attrs[NLBL_MGMT_A_IPV6ADDR] != NULL) ^
+            (info->attrs[NLBL_MGMT_A_IPV6MASK] != NULL)))
+               return -EINVAL;
 
        netlbl_netlink_auditinfo(skb, &audit_info);
 
-       entry = kzalloc(sizeof(*entry), GFP_KERNEL);
-       if (entry == NULL) {
-               ret_val = -ENOMEM;
-               goto adddef_failure;
-       }
-       entry->type = nla_get_u32(info->attrs[NLBL_MGMT_A_PROTOCOL]);
-
-       switch (entry->type) {
-       case NETLBL_NLTYPE_UNLABELED:
-               ret_val = netlbl_domhsh_add_default(entry, &audit_info);
-               break;
-       case NETLBL_NLTYPE_CIPSOV4:
-               if (!info->attrs[NLBL_MGMT_A_CV4DOI])
-                       goto adddef_failure;
-
-               tmp_val = nla_get_u32(info->attrs[NLBL_MGMT_A_CV4DOI]);
-               /* We should be holding a rcu_read_lock() here while we hold
-                * the result but since the entry will always be deleted when
-                * the CIPSO DOI is deleted we aren't going to keep the
-                * lock. */
-               rcu_read_lock();
-               entry->type_def.cipsov4 = cipso_v4_doi_getdef(tmp_val);
-               if (entry->type_def.cipsov4 == NULL) {
-                       rcu_read_unlock();
-                       goto adddef_failure;
-               }
-               ret_val = netlbl_domhsh_add_default(entry, &audit_info);
-               rcu_read_unlock();
-               break;
-       default:
-               goto adddef_failure;
-       }
-       if (ret_val != 0)
-               goto adddef_failure;
-
-       return 0;
-
-adddef_failure:
-       kfree(entry);
-       return ret_val;
+       return netlbl_mgmt_add_common(info, &audit_info);
 }
 
 /**
@@ -371,19 +578,10 @@ static int netlbl_mgmt_listdef(struct sk_buff *skb, struct genl_info *info)
                ret_val = -ENOENT;
                goto listdef_failure_lock;
        }
-       ret_val = nla_put_u32(ans_skb, NLBL_MGMT_A_PROTOCOL, entry->type);
-       if (ret_val != 0)
-               goto listdef_failure_lock;
-       switch (entry->type) {
-       case NETLBL_NLTYPE_CIPSOV4:
-               ret_val = nla_put_u32(ans_skb,
-                                     NLBL_MGMT_A_CV4DOI,
-                                     entry->type_def.cipsov4->doi);
-               if (ret_val != 0)
-                       goto listdef_failure_lock;
-               break;
-       }
+       ret_val = netlbl_mgmt_listentry(ans_skb, entry);
        rcu_read_unlock();
+       if (ret_val != 0)
+               goto listdef_failure;
 
        genlmsg_end(ans_skb, data);
        return genlmsg_reply(ans_skb, info);
index a43bff1..05d9643 100644 (file)
  *     NLBL_MGMT_A_DOMAIN
  *     NLBL_MGMT_A_PROTOCOL
  *
+ *   If IPv4 is specified the following attributes are required:
+ *
+ *     NLBL_MGMT_A_IPV4ADDR
+ *     NLBL_MGMT_A_IPV4MASK
+ *
+ *   If IPv6 is specified the following attributes are required:
+ *
+ *     NLBL_MGMT_A_IPV6ADDR
+ *     NLBL_MGMT_A_IPV6MASK
+ *
  *   If using NETLBL_NLTYPE_CIPSOV4 the following attributes are required:
  *
  *     NLBL_MGMT_A_CV4DOI
  *   Required attributes:
  *
  *     NLBL_MGMT_A_DOMAIN
+ *
+ *   If the IP address selectors are not used the following attribute is
+ *   required:
+ *
  *     NLBL_MGMT_A_PROTOCOL
  *
- *   If using NETLBL_NLTYPE_CIPSOV4 the following attributes are required:
+ *   If the IP address selectors are used then the following attritbute is
+ *   required:
+ *
+ *     NLBL_MGMT_A_SELECTORLIST
+ *
+ *   If the mapping is using the NETLBL_NLTYPE_CIPSOV4 type then the following
+ *   attributes are required:
  *
  *     NLBL_MGMT_A_CV4DOI
  *
- *   If using NETLBL_NLTYPE_UNLABELED no other attributes are required.
+ *   If the mapping is using the NETLBL_NLTYPE_UNLABELED type no other
+ *   attributes are required.
  *
  * o ADDDEF:
  *   Sent by an application to set the default domain mapping for the NetLabel
  *   application there is no payload.  On success the kernel should send a
  *   response using the following format.
  *
- *   Required attributes:
+ *   If the IP address selectors are not used the following attribute is
+ *   required:
  *
  *     NLBL_MGMT_A_PROTOCOL
  *
- *   If using NETLBL_NLTYPE_CIPSOV4 the following attributes are required:
+ *   If the IP address selectors are used then the following attritbute is
+ *   required:
+ *
+ *     NLBL_MGMT_A_SELECTORLIST
+ *
+ *   If the mapping is using the NETLBL_NLTYPE_CIPSOV4 type then the following
+ *   attributes are required:
  *
  *     NLBL_MGMT_A_CV4DOI
  *
- *   If using NETLBL_NLTYPE_UNLABELED no other attributes are required.
+ *   If the mapping is using the NETLBL_NLTYPE_UNLABELED type no other
+ *   attributes are required.
  *
  * o PROTOCOLS:
  *   Sent by an application to request a list of configured NetLabel protocols
@@ -162,6 +191,26 @@ enum {
        NLBL_MGMT_A_CV4DOI,
        /* (NLA_U32)
         * the CIPSOv4 DOI value */
+       NLBL_MGMT_A_IPV6ADDR,
+       /* (NLA_BINARY, struct in6_addr)
+        * an IPv6 address */
+       NLBL_MGMT_A_IPV6MASK,
+       /* (NLA_BINARY, struct in6_addr)
+        * an IPv6 address mask */
+       NLBL_MGMT_A_IPV4ADDR,
+       /* (NLA_BINARY, struct in_addr)
+        * an IPv4 address */
+       NLBL_MGMT_A_IPV4MASK,
+       /* (NLA_BINARY, struct in_addr)
+        * and IPv4 address mask */
+       NLBL_MGMT_A_ADDRSELECTOR,
+       /* (NLA_NESTED)
+        * an IP address selector, must contain an address, mask, and protocol
+        * attribute plus any protocol specific attributes */
+       NLBL_MGMT_A_SELECTORLIST,
+       /* (NLA_NESTED)
+        * the selector list, there must be at least one
+        * NLBL_MGMT_A_ADDRSELECTOR attribute */
        __NLBL_MGMT_A_MAX,
 };
 #define NLBL_MGMT_A_MAX (__NLBL_MGMT_A_MAX - 1)
index 921c118..e8a5c32 100644 (file)
@@ -10,7 +10,7 @@
  */
 
 /*
- * (c) Copyright Hewlett-Packard Development Company, L.P., 2006 - 2007
+ * (c) Copyright Hewlett-Packard Development Company, L.P., 2006 - 2008
  *
  * This program is free software;  you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -54,6 +54,7 @@
 #include <asm/atomic.h>
 
 #include "netlabel_user.h"
+#include "netlabel_addrlist.h"
 #include "netlabel_domainhash.h"
 #include "netlabel_unlabeled.h"
 #include "netlabel_mgmt.h"
@@ -76,22 +77,20 @@ struct netlbl_unlhsh_tbl {
        struct list_head *tbl;
        u32 size;
 };
+#define netlbl_unlhsh_addr4_entry(iter) \
+       container_of(iter, struct netlbl_unlhsh_addr4, list)
 struct netlbl_unlhsh_addr4 {
-       __be32 addr;
-       __be32 mask;
        u32 secid;
 
-       u32 valid;
-       struct list_head list;
+       struct netlbl_af4list list;
        struct rcu_head rcu;
 };
+#define netlbl_unlhsh_addr6_entry(iter) \
+       container_of(iter, struct netlbl_unlhsh_addr6, list)
 struct netlbl_unlhsh_addr6 {
-       struct in6_addr addr;
-       struct in6_addr mask;
        u32 secid;
 
-       u32 valid;
-       struct list_head list;
+       struct netlbl_af6list list;
        struct rcu_head rcu;
 };
 struct netlbl_unlhsh_iface {
@@ -146,76 +145,6 @@ static const struct nla_policy netlbl_unlabel_genl_policy[NLBL_UNLABEL_A_MAX + 1
        [NLBL_UNLABEL_A_SECCTX] = { .type = NLA_BINARY }
 };
 
-/*
- * Audit Helper Functions
- */
-
-/**
- * netlbl_unlabel_audit_addr4 - Audit an IPv4 address
- * @audit_buf: audit buffer
- * @dev: network interface
- * @addr: IP address
- * @mask: IP address mask
- *
- * Description:
- * Write the IPv4 address and address mask, if necessary, to @audit_buf.
- *
- */
-static void netlbl_unlabel_audit_addr4(struct audit_buffer *audit_buf,
-                                    const char *dev,
-                                    __be32 addr, __be32 mask)
-{
-       u32 mask_val = ntohl(mask);
-
-       if (dev != NULL)
-               audit_log_format(audit_buf, " netif=%s", dev);
-       audit_log_format(audit_buf, " src=" NIPQUAD_FMT, NIPQUAD(addr));
-       if (mask_val != 0xffffffff) {
-               u32 mask_len = 0;
-               while (mask_val > 0) {
-                       mask_val <<= 1;
-                       mask_len++;
-               }
-               audit_log_format(audit_buf, " src_prefixlen=%d", mask_len);
-       }
-}
-
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-/**
- * netlbl_unlabel_audit_addr6 - Audit an IPv6 address
- * @audit_buf: audit buffer
- * @dev: network interface
- * @addr: IP address
- * @mask: IP address mask
- *
- * Description:
- * Write the IPv6 address and address mask, if necessary, to @audit_buf.
- *
- */
-static void netlbl_unlabel_audit_addr6(struct audit_buffer *audit_buf,
-                                    const char *dev,
-                                    const struct in6_addr *addr,
-                                    const struct in6_addr *mask)
-{
-       if (dev != NULL)
-               audit_log_format(audit_buf, " netif=%s", dev);
-       audit_log_format(audit_buf, " src=" NIP6_FMT, NIP6(*addr));
-       if (ntohl(mask->s6_addr32[3]) != 0xffffffff) {
-               u32 mask_len = 0;
-               u32 mask_val;
-               int iter = -1;
-               while (ntohl(mask->s6_addr32[++iter]) == 0xffffffff)
-                       mask_len += 32;
-               mask_val = ntohl(mask->s6_addr32[iter]);
-               while (mask_val > 0) {
-                       mask_val <<= 1;
-                       mask_len++;
-               }
-               audit_log_format(audit_buf, " src_prefixlen=%d", mask_len);
-       }
-}
-#endif /* IPv6 */
-
 /*
  * Unlabeled Connection Hash Table Functions
  */
@@ -274,26 +203,28 @@ static void netlbl_unlhsh_free_addr6(struct rcu_head *entry)
 static void netlbl_unlhsh_free_iface(struct rcu_head *entry)
 {
        struct netlbl_unlhsh_iface *iface;
-       struct netlbl_unlhsh_addr4 *iter4;
-       struct netlbl_unlhsh_addr4 *tmp4;
-       struct netlbl_unlhsh_addr6 *iter6;
-       struct netlbl_unlhsh_addr6 *tmp6;
+       struct netlbl_af4list *iter4;
+       struct netlbl_af4list *tmp4;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+       struct netlbl_af6list *iter6;
+       struct netlbl_af6list *tmp6;
+#endif /* IPv6 */
 
        iface = container_of(entry, struct netlbl_unlhsh_iface, rcu);
 
        /* no need for locks here since we are the only one with access to this
         * structure */
 
-       list_for_each_entry_safe(iter4, tmp4, &iface->addr4_list, list)
-               if (iter4->valid) {
-                       list_del_rcu(&iter4->list);
-                       kfree(iter4);
-               }
-       list_for_each_entry_safe(iter6, tmp6, &iface->addr6_list, list)
-               if (iter6->valid) {
-                       list_del_rcu(&iter6->list);
-                       kfree(iter6);
-               }
+       netlbl_af4list_foreach_safe(iter4, tmp4, &iface->addr4_list) {
+               netlbl_af4list_remove_entry(iter4);
+               kfree(netlbl_unlhsh_addr4_entry(iter4));
+       }
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+       netlbl_af6list_foreach_safe(iter6, tmp6, &iface->addr6_list) {
+               netlbl_af6list_remove_entry(iter6);
+               kfree(netlbl_unlhsh_addr6_entry(iter6));
+       }
+#endif /* IPv6 */
        kfree(iface);
 }
 
@@ -315,59 +246,6 @@ static u32 netlbl_unlhsh_hash(int ifindex)
        return ifindex & (rcu_dereference(netlbl_unlhsh)->size - 1);
 }
 
-/**
- * netlbl_unlhsh_search_addr4 - Search for a matching IPv4 address entry
- * @addr: IPv4 address
- * @iface: the network interface entry
- *
- * Description:
- * Searches the IPv4 address list of the network interface specified by @iface.
- * If a matching address entry is found it is returned, otherwise NULL is
- * returned.  The caller is responsible for calling the rcu_read_[un]lock()
- * functions.
- *
- */
-static struct netlbl_unlhsh_addr4 *netlbl_unlhsh_search_addr4(
-                                      __be32 addr,
-                                      const struct netlbl_unlhsh_iface *iface)
-{
-       struct netlbl_unlhsh_addr4 *iter;
-
-       list_for_each_entry_rcu(iter, &iface->addr4_list, list)
-               if (iter->valid && (addr & iter->mask) == iter->addr)
-                       return iter;
-
-       return NULL;
-}
-
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-/**
- * netlbl_unlhsh_search_addr6 - Search for a matching IPv6 address entry
- * @addr: IPv6 address
- * @iface: the network interface entry
- *
- * Description:
- * Searches the IPv6 address list of the network interface specified by @iface.
- * If a matching address entry is found it is returned, otherwise NULL is
- * returned.  The caller is responsible for calling the rcu_read_[un]lock()
- * functions.
- *
- */
-static struct netlbl_unlhsh_addr6 *netlbl_unlhsh_search_addr6(
-                                      const struct in6_addr *addr,
-                                      const struct netlbl_unlhsh_iface *iface)
-{
-       struct netlbl_unlhsh_addr6 *iter;
-
-       list_for_each_entry_rcu(iter, &iface->addr6_list, list)
-               if (iter->valid &&
-                   ipv6_masked_addr_cmp(&iter->addr, &iter->mask, addr) == 0)
-               return iter;
-
-       return NULL;
-}
-#endif /* IPv6 */
-
 /**
  * netlbl_unlhsh_search_iface - Search for a matching interface entry
  * @ifindex: the network interface
@@ -381,12 +259,12 @@ static struct netlbl_unlhsh_addr6 *netlbl_unlhsh_search_addr6(
 static struct netlbl_unlhsh_iface *netlbl_unlhsh_search_iface(int ifindex)
 {
        u32 bkt;
+       struct list_head *bkt_list;
        struct netlbl_unlhsh_iface *iter;
 
        bkt = netlbl_unlhsh_hash(ifindex);
-       list_for_each_entry_rcu(iter,
-                               &rcu_dereference(netlbl_unlhsh)->tbl[bkt],
-                               list)
+       bkt_list = &rcu_dereference(netlbl_unlhsh)->tbl[bkt];
+       list_for_each_entry_rcu(iter, bkt_list, list)
                if (iter->valid && iter->ifindex == ifindex)
                        return iter;
 
@@ -439,43 +317,26 @@ static int netlbl_unlhsh_add_addr4(struct netlbl_unlhsh_iface *iface,
                                   const struct in_addr *mask,
                                   u32 secid)
 {
+       int ret_val;
        struct netlbl_unlhsh_addr4 *entry;
-       struct netlbl_unlhsh_addr4 *iter;
 
        entry = kzalloc(sizeof(*entry), GFP_ATOMIC);
        if (entry == NULL)
                return -ENOMEM;
 
-       entry->addr = addr->s_addr & mask->s_addr;
-       entry->mask = mask->s_addr;
-       entry->secid = secid;
-       entry->valid = 1;
+       entry->list.addr = addr->s_addr & mask->s_addr;
+       entry->list.mask = mask->s_addr;
+       entry->list.valid = 1;
        INIT_RCU_HEAD(&entry->rcu);
+       entry->secid = secid;
 
        spin_lock(&netlbl_unlhsh_lock);
-       iter = netlbl_unlhsh_search_addr4(entry->addr, iface);
-       if (iter != NULL &&
-           iter->addr == addr->s_addr && iter->mask == mask->s_addr) {
-               spin_unlock(&netlbl_unlhsh_lock);
-               kfree(entry);
-               return -EEXIST;
-       }
-       /* in order to speed up address searches through the list (the common
-        * case) we need to keep the list in order based on the size of the
-        * address mask such that the entry with the widest mask (smallest
-        * numerical value) appears first in the list */
-       list_for_each_entry_rcu(iter, &iface->addr4_list, list)
-               if (iter->valid &&
-                   ntohl(entry->mask) > ntohl(iter->mask)) {
-                       __list_add_rcu(&entry->list,
-                                      iter->list.prev,
-                                      &iter->list);
-                       spin_unlock(&netlbl_unlhsh_lock);
-                       return 0;
-               }
-       list_add_tail_rcu(&entry->list, &iface->addr4_list);
+       ret_val = netlbl_af4list_add(&entry->list, &iface->addr4_list);
        spin_unlock(&netlbl_unlhsh_lock);
-       return 0;
+
+       if (ret_val != 0)
+               kfree(entry);
+       return ret_val;
 }
 
 #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
@@ -498,47 +359,29 @@ static int netlbl_unlhsh_add_addr6(struct netlbl_unlhsh_iface *iface,
                                   const struct in6_addr *mask,
                                   u32 secid)
 {
+       int ret_val;
        struct netlbl_unlhsh_addr6 *entry;
-       struct netlbl_unlhsh_addr6 *iter;
 
        entry = kzalloc(sizeof(*entry), GFP_ATOMIC);
        if (entry == NULL)
                return -ENOMEM;
 
-       ipv6_addr_copy(&entry->addr, addr);
-       entry->addr.s6_addr32[0] &= mask->s6_addr32[0];
-       entry->addr.s6_addr32[1] &= mask->s6_addr32[1];
-       entry->addr.s6_addr32[2] &= mask->s6_addr32[2];
-       entry->addr.s6_addr32[3] &= mask->s6_addr32[3];
-       ipv6_addr_copy(&entry->mask, mask);
-       entry->secid = secid;
-       entry->valid = 1;
+       ipv6_addr_copy(&entry->list.addr, addr);
+       entry->list.addr.s6_addr32[0] &= mask->s6_addr32[0];
+       entry->list.addr.s6_addr32[1] &= mask->s6_addr32[1];
+       entry->list.addr.s6_addr32[2] &= mask->s6_addr32[2];
+       entry->list.addr.s6_addr32[3] &= mask->s6_addr32[3];
+       ipv6_addr_copy(&entry->list.mask, mask);
+       entry->list.valid = 1;
        INIT_RCU_HEAD(&entry->rcu);
+       entry->secid = secid;
 
        spin_lock(&netlbl_unlhsh_lock);
-       iter = netlbl_unlhsh_search_addr6(&entry->addr, iface);
-       if (iter != NULL &&
-           (ipv6_addr_equal(&iter->addr, addr) &&
-            ipv6_addr_equal(&iter->mask, mask))) {
-               spin_unlock(&netlbl_unlhsh_lock);
-               kfree(entry);
-               return -EEXIST;
-       }
-       /* in order to speed up address searches through the list (the common
-        * case) we need to keep the list in order based on the size of the
-        * address mask such that the entry with the widest mask (smallest
-        * numerical value) appears first in the list */
-       list_for_each_entry_rcu(iter, &iface->addr6_list, list)
-               if (iter->valid &&
-                   ipv6_addr_cmp(&entry->mask, &iter->mask) > 0) {
-                       __list_add_rcu(&entry->list,
-                                      iter->list.prev,
-                                      &iter->list);
-                       spin_unlock(&netlbl_unlhsh_lock);
-                       return 0;
-               }
-       list_add_tail_rcu(&entry->list, &iface->addr6_list);
+       ret_val = netlbl_af6list_add(&entry->list, &iface->addr6_list);
        spin_unlock(&netlbl_unlhsh_lock);
+
+       if (ret_val != 0)
+               kfree(entry);
        return 0;
 }
 #endif /* IPv6 */
@@ -658,10 +501,10 @@ static int netlbl_unlhsh_add(struct net *net,
                mask4 = (struct in_addr *)mask;
                ret_val = netlbl_unlhsh_add_addr4(iface, addr4, mask4, secid);
                if (audit_buf != NULL)
-                       netlbl_unlabel_audit_addr4(audit_buf,
-                                                  dev_name,
-                                                  addr4->s_addr,
-                                                  mask4->s_addr);
+                       netlbl_af4list_audit_addr(audit_buf, 1,
+                                                 dev_name,
+                                                 addr4->s_addr,
+                                                 mask4->s_addr);
                break;
        }
 #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
@@ -672,9 +515,9 @@ static int netlbl_unlhsh_add(struct net *net,
                mask6 = (struct in6_addr *)mask;
                ret_val = netlbl_unlhsh_add_addr6(iface, addr6, mask6, secid);
                if (audit_buf != NULL)
-                       netlbl_unlabel_audit_addr6(audit_buf,
-                                                  dev_name,
-                                                  addr6, mask6);
+                       netlbl_af6list_audit_addr(audit_buf, 1,
+                                                 dev_name,
+                                                 addr6, mask6);
                break;
        }
 #endif /* IPv6 */
@@ -719,35 +562,34 @@ static int netlbl_unlhsh_remove_addr4(struct net *net,
                                      const struct in_addr *mask,
                                      struct netlbl_audit *audit_info)
 {
-       int ret_val = -ENOENT;
+       int ret_val = 0;
+       struct netlbl_af4list *list_entry;
        struct netlbl_unlhsh_addr4 *entry;
-       struct audit_buffer *audit_buf = NULL;
+       struct audit_buffer *audit_buf;
        struct net_device *dev;
-       char *secctx = NULL;
+       char *secctx;
        u32 secctx_len;
 
        spin_lock(&netlbl_unlhsh_lock);
-       entry = netlbl_unlhsh_search_addr4(addr->s_addr, iface);
-       if (entry != NULL &&
-           entry->addr == addr->s_addr && entry->mask == mask->s_addr) {
-               entry->valid = 0;
-               list_del_rcu(&entry->list);
-               ret_val = 0;
-       }
+       list_entry = netlbl_af4list_remove(addr->s_addr, mask->s_addr,
+                                          &iface->addr4_list);
        spin_unlock(&netlbl_unlhsh_lock);
+       if (list_entry == NULL)
+               ret_val = -ENOENT;
+       entry = netlbl_unlhsh_addr4_entry(list_entry);
 
        audit_buf = netlbl_audit_start_common(AUDIT_MAC_UNLBL_STCDEL,
                                              audit_info);
        if (audit_buf != NULL) {
                dev = dev_get_by_index(net, iface->ifindex);
-               netlbl_unlabel_audit_addr4(audit_buf,
-                                          (dev != NULL ? dev->name : NULL),
-                                          entry->addr, entry->mask);
+               netlbl_af4list_audit_addr(audit_buf, 1,
+                                         (dev != NULL ? dev->name : NULL),
+                                         addr->s_addr, mask->s_addr);
                if (dev != NULL)
                        dev_put(dev);
-               if (security_secid_to_secctx(entry->secid,
-                                            &secctx,
-                                            &secctx_len) == 0) {
+               if (entry && security_secid_to_secctx(entry->secid,
+                                                     &secctx,
+                                                     &secctx_len) == 0) {
                        audit_log_format(audit_buf, " sec_obj=%s", secctx);
                        security_release_secctx(secctx, secctx_len);
                }
@@ -781,36 +623,33 @@ static int netlbl_unlhsh_remove_addr6(struct net *net,
                                      const struct in6_addr *mask,
                                      struct netlbl_audit *audit_info)
 {
-       int ret_val = -ENOENT;
+       int ret_val = 0;
+       struct netlbl_af6list *list_entry;
        struct netlbl_unlhsh_addr6 *entry;
-       struct audit_buffer *audit_buf = NULL;
+       struct audit_buffer *audit_buf;
        struct net_device *dev;
-       char *secctx = NULL;
+       char *secctx;
        u32 secctx_len;
 
        spin_lock(&netlbl_unlhsh_lock);
-       entry = netlbl_unlhsh_search_addr6(addr, iface);
-       if (entry != NULL &&
-           (ipv6_addr_equal(&entry->addr, addr) &&
-            ipv6_addr_equal(&entry->mask, mask))) {
-               entry->valid = 0;
-               list_del_rcu(&entry->list);
-               ret_val = 0;
-       }
+       list_entry = netlbl_af6list_remove(addr, mask, &iface->addr6_list);
        spin_unlock(&netlbl_unlhsh_lock);
+       if (list_entry == NULL)
+               ret_val = -ENOENT;
+       entry = netlbl_unlhsh_addr6_entry(list_entry);
 
        audit_buf = netlbl_audit_start_common(AUDIT_MAC_UNLBL_STCDEL,
                                              audit_info);
        if (audit_buf != NULL) {
                dev = dev_get_by_index(net, iface->ifindex);
-               netlbl_unlabel_audit_addr6(audit_buf,
-                                          (dev != NULL ? dev->name : NULL),
-                                          addr, mask);
+               netlbl_af6list_audit_addr(audit_buf, 1,
+                                         (dev != NULL ? dev->name : NULL),
+                                         addr, mask);
                if (dev != NULL)
                        dev_put(dev);
-               if (security_secid_to_secctx(entry->secid,
-                                            &secctx,
-                                            &secctx_len) == 0) {
+               if (entry && security_secid_to_secctx(entry->secid,
+                                                     &secctx,
+                                                     &secctx_len) == 0) {
                        audit_log_format(audit_buf, " sec_obj=%s", secctx);
                        security_release_secctx(secctx, secctx_len);
                }
@@ -836,16 +675,18 @@ static int netlbl_unlhsh_remove_addr6(struct net *net,
  */
 static void netlbl_unlhsh_condremove_iface(struct netlbl_unlhsh_iface *iface)
 {
-       struct netlbl_unlhsh_addr4 *iter4;
-       struct netlbl_unlhsh_addr6 *iter6;
+       struct netlbl_af4list *iter4;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+       struct netlbl_af6list *iter6;
+#endif /* IPv6 */
 
        spin_lock(&netlbl_unlhsh_lock);
-       list_for_each_entry_rcu(iter4, &iface->addr4_list, list)
-               if (iter4->valid)
-                       goto unlhsh_condremove_failure;
-       list_for_each_entry_rcu(iter6, &iface->addr6_list, list)
-               if (iter6->valid)
-                       goto unlhsh_condremove_failure;
+       netlbl_af4list_foreach_rcu(iter4, &iface->addr4_list)
+               goto unlhsh_condremove_failure;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+       netlbl_af6list_foreach_rcu(iter6, &iface->addr6_list)
+               goto unlhsh_condremove_failure;
+#endif /* IPv6 */
        iface->valid = 0;
        if (iface->ifindex > 0)
                list_del_rcu(&iface->list);
@@ -1349,7 +1190,7 @@ static int netlbl_unlabel_staticlist_gen(u32 cmd,
        if (addr4) {
                struct in_addr addr_struct;
 
-               addr_struct.s_addr = addr4->addr;
+               addr_struct.s_addr = addr4->list.addr;
                ret_val = nla_put(cb_arg->skb,
                                  NLBL_UNLABEL_A_IPV4ADDR,
                                  sizeof(struct in_addr),
@@ -1357,7 +1198,7 @@ static int netlbl_unlabel_staticlist_gen(u32 cmd,
                if (ret_val != 0)
                        goto list_cb_failure;
 
-               addr_struct.s_addr = addr4->mask;
+               addr_struct.s_addr = addr4->list.mask;
                ret_val = nla_put(cb_arg->skb,
                                  NLBL_UNLABEL_A_IPV4MASK,
                                  sizeof(struct in_addr),
@@ -1370,14 +1211,14 @@ static int netlbl_unlabel_staticlist_gen(u32 cmd,
                ret_val = nla_put(cb_arg->skb,
                                  NLBL_UNLABEL_A_IPV6ADDR,
                                  sizeof(struct in6_addr),
-                                 &addr6->addr);
+                                 &addr6->list.addr);
                if (ret_val != 0)
                        goto list_cb_failure;
 
                ret_val = nla_put(cb_arg->skb,
                                  NLBL_UNLABEL_A_IPV6MASK,
                                  sizeof(struct in6_addr),
-                                 &addr6->mask);
+                                 &addr6->list.mask);
                if (ret_val != 0)
                        goto list_cb_failure;
 
@@ -1425,8 +1266,11 @@ static int netlbl_unlabel_staticlist(struct sk_buff *skb,
        u32 iter_bkt;
        u32 iter_chain = 0, iter_addr4 = 0, iter_addr6 = 0;
        struct netlbl_unlhsh_iface *iface;
-       struct netlbl_unlhsh_addr4 *addr4;
-       struct netlbl_unlhsh_addr6 *addr6;
+       struct list_head *iter_list;
+       struct netlbl_af4list *addr4;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+       struct netlbl_af6list *addr6;
+#endif
 
        cb_arg.nl_cb = cb;
        cb_arg.skb = skb;
@@ -1436,44 +1280,43 @@ static int netlbl_unlabel_staticlist(struct sk_buff *skb,
        for (iter_bkt = skip_bkt;
             iter_bkt < rcu_dereference(netlbl_unlhsh)->size;
             iter_bkt++, iter_chain = 0, iter_addr4 = 0, iter_addr6 = 0) {
-               list_for_each_entry_rcu(iface,
-                               &rcu_dereference(netlbl_unlhsh)->tbl[iter_bkt],
-                               list) {
+               iter_list = &rcu_dereference(netlbl_unlhsh)->tbl[iter_bkt];
+               list_for_each_entry_rcu(iface, iter_list, list) {
                        if (!iface->valid ||
                            iter_chain++ < skip_chain)
                                continue;
-                       list_for_each_entry_rcu(addr4,
-                                               &iface->addr4_list,
-                                               list) {
-                               if (!addr4->valid || iter_addr4++ < skip_addr4)
+                       netlbl_af4list_foreach_rcu(addr4,
+                                                  &iface->addr4_list) {
+                               if (iter_addr4++ < skip_addr4)
                                        continue;
                                if (netlbl_unlabel_staticlist_gen(
-                                                    NLBL_UNLABEL_C_STATICLIST,
-                                                    iface,
-                                                    addr4,
-                                                    NULL,
-                                                    &cb_arg) < 0) {
+                                             NLBL_UNLABEL_C_STATICLIST,
+                                             iface,
+                                             netlbl_unlhsh_addr4_entry(addr4),
+                                             NULL,
+                                             &cb_arg) < 0) {
                                        iter_addr4--;
                                        iter_chain--;
                                        goto unlabel_staticlist_return;
                                }
                        }
-                       list_for_each_entry_rcu(addr6,
-                                               &iface->addr6_list,
-                                               list) {
-                               if (!addr6->valid || iter_addr6++ < skip_addr6)
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+                       netlbl_af6list_foreach_rcu(addr6,
+                                                  &iface->addr6_list) {
+                               if (iter_addr6++ < skip_addr6)
                                        continue;
                                if (netlbl_unlabel_staticlist_gen(
-                                                    NLBL_UNLABEL_C_STATICLIST,
-                                                    iface,
-                                                    NULL,
-                                                    addr6,
-                                                    &cb_arg) < 0) {
+                                             NLBL_UNLABEL_C_STATICLIST,
+                                             iface,
+                                             NULL,
+                                             netlbl_unlhsh_addr6_entry(addr6),
+                                             &cb_arg) < 0) {
                                        iter_addr6--;
                                        iter_chain--;
                                        goto unlabel_staticlist_return;
                                }
                        }
+#endif /* IPv6 */
                }
        }
 
@@ -1504,9 +1347,12 @@ static int netlbl_unlabel_staticlistdef(struct sk_buff *skb,
        struct netlbl_unlhsh_iface *iface;
        u32 skip_addr4 = cb->args[0];
        u32 skip_addr6 = cb->args[1];
-       u32 iter_addr4 = 0, iter_addr6 = 0;
-       struct netlbl_unlhsh_addr4 *addr4;
-       struct netlbl_unlhsh_addr6 *addr6;
+       u32 iter_addr4 = 0;
+       struct netlbl_af4list *addr4;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+       u32 iter_addr6 = 0;
+       struct netlbl_af6list *addr6;
+#endif
 
        cb_arg.nl_cb = cb;
        cb_arg.skb = skb;
@@ -1517,30 +1363,32 @@ static int netlbl_unlabel_staticlistdef(struct sk_buff *skb,
        if (iface == NULL || !iface->valid)
                goto unlabel_staticlistdef_return;
 
-       list_for_each_entry_rcu(addr4, &iface->addr4_list, list) {
-               if (!addr4->valid || iter_addr4++ < skip_addr4)
+       netlbl_af4list_foreach_rcu(addr4, &iface->addr4_list) {
+               if (iter_addr4++ < skip_addr4)
                        continue;
                if (netlbl_unlabel_staticlist_gen(NLBL_UNLABEL_C_STATICLISTDEF,
-                                          iface,
-                                          addr4,
-                                          NULL,
-                                          &cb_arg) < 0) {
+                                             iface,
+                                             netlbl_unlhsh_addr4_entry(addr4),
+                                             NULL,
+                                             &cb_arg) < 0) {
                        iter_addr4--;
                        goto unlabel_staticlistdef_return;
                }
        }
-       list_for_each_entry_rcu(addr6, &iface->addr6_list, list) {
-               if (!addr6->valid || iter_addr6++ < skip_addr6)
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+       netlbl_af6list_foreach_rcu(addr6, &iface->addr6_list) {
+               if (iter_addr6++ < skip_addr6)
                        continue;
                if (netlbl_unlabel_staticlist_gen(NLBL_UNLABEL_C_STATICLISTDEF,
-                                          iface,
-                                          NULL,
-                                          addr6,
-                                          &cb_arg) < 0) {
+                                             iface,
+                                             NULL,
+                                             netlbl_unlhsh_addr6_entry(addr6),
+                                             &cb_arg) < 0) {
                        iter_addr6--;
                        goto unlabel_staticlistdef_return;
                }
        }
+#endif /* IPv6 */
 
 unlabel_staticlistdef_return:
        rcu_read_unlock();
@@ -1718,25 +1566,27 @@ int netlbl_unlabel_getattr(const struct sk_buff *skb,
        switch (family) {
        case PF_INET: {
                struct iphdr *hdr4;
-               struct netlbl_unlhsh_addr4 *addr4;
+               struct netlbl_af4list *addr4;
 
                hdr4 = ip_hdr(skb);
-               addr4 = netlbl_unlhsh_search_addr4(hdr4->saddr, iface);
+               addr4 = netlbl_af4list_search(hdr4->saddr,
+                                             &iface->addr4_list);
                if (addr4 == NULL)
                        goto unlabel_getattr_nolabel;
-               secattr->attr.secid = addr4->secid;
+               secattr->attr.secid = netlbl_unlhsh_addr4_entry(addr4)->secid;
                break;
        }
 #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
        case PF_INET6: {
                struct ipv6hdr *hdr6;
-               struct netlbl_unlhsh_addr6 *addr6;
+               struct netlbl_af6list *addr6;
 
                hdr6 = ipv6_hdr(skb);
-               addr6 = netlbl_unlhsh_search_addr6(&hdr6->saddr, iface);
+               addr6 = netlbl_af6list_search(&hdr6->saddr,
+                                             &iface->addr6_list);
                if (addr6 == NULL)
                        goto unlabel_getattr_nolabel;
-               secattr->attr.secid = addr6->secid;
+               secattr->attr.secid = netlbl_unlhsh_addr6_entry(addr6)->secid;
                break;
        }
 #endif /* IPv6 */
index 4a7374c..c679ba6 100644 (file)
@@ -291,6 +291,7 @@ static void sk_free_security(struct sock *sk)
        struct sk_security_struct *ssec = sk->sk_security;
 
        sk->sk_security = NULL;
+       selinux_netlbl_sk_security_free(ssec);
        kfree(ssec);
 }
 
@@ -3801,6 +3802,7 @@ out:
 
 static int selinux_socket_connect(struct socket *sock, struct sockaddr *address, int addrlen)
 {
+       struct sock *sk = sock->sk;
        struct inode_security_struct *isec;
        int err;
 
@@ -3814,7 +3816,6 @@ static int selinux_socket_connect(struct socket *sock, struct sockaddr *address,
        isec = SOCK_INODE(sock)->i_security;
        if (isec->sclass == SECCLASS_TCP_SOCKET ||
            isec->sclass == SECCLASS_DCCP_SOCKET) {
-               struct sock *sk = sock->sk;
                struct avc_audit_data ad;
                struct sockaddr_in *addr4 = NULL;
                struct sockaddr_in6 *addr6 = NULL;
@@ -3848,6 +3849,8 @@ static int selinux_socket_connect(struct socket *sock, struct sockaddr *address,
                        goto out;
        }
 
+       err = selinux_netlbl_socket_connect(sk, address);
+
 out:
        return err;
 }
@@ -4077,20 +4080,28 @@ static int selinux_sock_rcv_skb_iptables_compat(struct sock *sk,
 }
 
 static int selinux_sock_rcv_skb_compat(struct sock *sk, struct sk_buff *skb,
-                                      struct avc_audit_data *ad,
-                                      u16 family, char *addrp)
+                                      u16 family)
 {
        int err;
        struct sk_security_struct *sksec = sk->sk_security;
        u32 peer_sid;
        u32 sk_sid = sksec->sid;
+       struct avc_audit_data ad;
+       char *addrp;
+
+       AVC_AUDIT_DATA_INIT(&ad, NET);
+       ad.u.net.netif = skb->iif;
+       ad.u.net.family = family;
+       err = selinux_parse_skb(skb, &ad, &addrp, 1, NULL);
+       if (err)
+               return err;
 
        if (selinux_compat_net)
-               err = selinux_sock_rcv_skb_iptables_compat(sk, skb, ad,
+               err = selinux_sock_rcv_skb_iptables_compat(sk, skb, &ad,
                                                           family, addrp);
        else
                err = avc_has_perm(sk_sid, skb->secmark, SECCLASS_PACKET,
-                                  PACKET__RECV, ad);
+                                  PACKET__RECV, &ad);
        if (err)
                return err;
 
@@ -4099,12 +4110,14 @@ static int selinux_sock_rcv_skb_compat(struct sock *sk, struct sk_buff *skb,
                if (err)
                        return err;
                err = avc_has_perm(sk_sid, peer_sid,
-                                  SECCLASS_PEER, PEER__RECV, ad);
+                                  SECCLASS_PEER, PEER__RECV, &ad);
+               if (err)
+                       selinux_netlbl_err(skb, err, 0);
        } else {
-               err = selinux_netlbl_sock_rcv_skb(sksec, skb, family, ad);
+               err = selinux_netlbl_sock_rcv_skb(sksec, skb, family, &ad);
                if (err)
                        return err;
-               err = selinux_xfrm_sock_rcv_skb(sksec->sid, skb, ad);
+               err = selinux_xfrm_sock_rcv_skb(sksec->sid, skb, &ad);
        }
 
        return err;
@@ -4118,6 +4131,8 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
        u32 sk_sid = sksec->sid;
        struct avc_audit_data ad;
        char *addrp;
+       u8 secmark_active;
+       u8 peerlbl_active;
 
        if (family != PF_INET && family != PF_INET6)
                return 0;
@@ -4126,6 +4141,18 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
        if (family == PF_INET6 && skb->protocol == htons(ETH_P_IP))
                family = PF_INET;
 
+       /* If any sort of compatibility mode is enabled then handoff processing
+        * to the selinux_sock_rcv_skb_compat() function to deal with the
+        * special handling.  We do this in an attempt to keep this function
+        * as fast and as clean as possible. */
+       if (selinux_compat_net || !selinux_policycap_netpeer)
+               return selinux_sock_rcv_skb_compat(sk, skb, family);
+
+       secmark_active = selinux_secmark_enabled();
+       peerlbl_active = netlbl_enabled() || selinux_xfrm_enabled();
+       if (!secmark_active && !peerlbl_active)
+               return 0;
+
        AVC_AUDIT_DATA_INIT(&ad, NET);
        ad.u.net.netif = skb->iif;
        ad.u.net.family = family;
@@ -4133,15 +4160,7 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
        if (err)
                return err;
 
-       /* If any sort of compatibility mode is enabled then handoff processing
-        * to the selinux_sock_rcv_skb_compat() function to deal with the
-        * special handling.  We do this in an attempt to keep this function
-        * as fast and as clean as possible. */
-       if (selinux_compat_net || !selinux_policycap_netpeer)
-               return selinux_sock_rcv_skb_compat(sk, skb, &ad,
-                                                  family, addrp);
-
-       if (netlbl_enabled() || selinux_xfrm_enabled()) {
+       if (peerlbl_active) {
                u32 peer_sid;
 
                err = selinux_skb_peerlbl_sid(skb, family, &peer_sid);
@@ -4149,13 +4168,17 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
                        return err;
                err = selinux_inet_sys_rcv_skb(skb->iif, addrp, family,
                                               peer_sid, &ad);
-               if (err)
+               if (err) {
+                       selinux_netlbl_err(skb, err, 0);
                        return err;
+               }
                err = avc_has_perm(sk_sid, peer_sid, SECCLASS_PEER,
                                   PEER__RECV, &ad);
+               if (err)
+                       selinux_netlbl_err(skb, err, 0);
        }
 
-       if (selinux_secmark_enabled()) {
+       if (secmark_active) {
                err = avc_has_perm(sk_sid, skb->secmark, SECCLASS_PACKET,
                                   PACKET__RECV, &ad);
                if (err)
@@ -4214,10 +4237,12 @@ static int selinux_socket_getpeersec_dgram(struct socket *sock, struct sk_buff *
        u32 peer_secid = SECSID_NULL;
        u16 family;
 
-       if (sock)
+       if (skb && skb->protocol == htons(ETH_P_IP))
+               family = PF_INET;
+       else if (skb && skb->protocol == htons(ETH_P_IPV6))
+               family = PF_INET6;
+       else if (sock)
                family = sock->sk->sk_family;
-       else if (skb && skb->sk)
-               family = skb->sk->sk_family;
        else
                goto out;
 
@@ -4275,8 +4300,6 @@ static void selinux_sock_graft(struct sock *sk, struct socket *parent)
            sk->sk_family == PF_UNIX)
                isec->sid = sksec->sid;
        sksec->sclass = isec->sclass;
-
-       selinux_netlbl_sock_graft(sk, parent);
 }
 
 static int selinux_inet_conn_request(struct sock *sk, struct sk_buff *skb,
@@ -4284,10 +4307,15 @@ static int selinux_inet_conn_request(struct sock *sk, struct sk_buff *skb,
 {
        struct sk_security_struct *sksec = sk->sk_security;
        int err;
+       u16 family = sk->sk_family;
        u32 newsid;
        u32 peersid;
 
-       err = selinux_skb_peerlbl_sid(skb, sk->sk_family, &peersid);
+       /* handle mapped IPv4 packets arriving via IPv6 sockets */
+       if (family == PF_INET6 && skb->protocol == htons(ETH_P_IP))
+               family = PF_INET;
+
+       err = selinux_skb_peerlbl_sid(skb, family, &peersid);
        if (err)
                return err;
        if (peersid == SECSID_NULL) {
@@ -4322,12 +4350,18 @@ static void selinux_inet_csk_clone(struct sock *newsk,
        selinux_netlbl_sk_security_reset(newsksec, req->rsk_ops->family);
 }
 
-static void selinux_inet_conn_established(struct sock *sk,
-                               struct sk_buff *skb)
+static void selinux_inet_conn_established(struct sock *sk, struct sk_buff *skb)
 {
+       u16 family = sk->sk_family;
        struct sk_security_struct *sksec = sk->sk_security;
 
-       selinux_skb_peerlbl_sid(skb, sk->sk_family, &sksec->peer_sid);
+       /* handle mapped IPv4 packets arriving via IPv6 sockets */
+       if (family == PF_INET6 && skb->protocol == htons(ETH_P_IP))
+               family = PF_INET;
+
+       selinux_skb_peerlbl_sid(skb, family, &sksec->peer_sid);
+
+       selinux_netlbl_inet_conn_established(sk, family);
 }
 
 static void selinux_req_classify_flow(const struct request_sock *req,
@@ -4377,39 +4411,54 @@ out:
 static unsigned int selinux_ip_forward(struct sk_buff *skb, int ifindex,
                                       u16 family)
 {
+       int err;
        char *addrp;
        u32 peer_sid;
        struct avc_audit_data ad;
        u8 secmark_active;
+       u8 netlbl_active;
        u8 peerlbl_active;
 
        if (!selinux_policycap_netpeer)
                return NF_ACCEPT;
 
        secmark_active = selinux_secmark_enabled();
-       peerlbl_active = netlbl_enabled() || selinux_xfrm_enabled();
+       netlbl_active = netlbl_enabled();
+       peerlbl_active = netlbl_active || selinux_xfrm_enabled();
        if (!secmark_active && !peerlbl_active)
                return NF_ACCEPT;
 
+       if (selinux_skb_peerlbl_sid(skb, family, &peer_sid) != 0)
+               return NF_DROP;
+
        AVC_AUDIT_DATA_INIT(&ad, NET);
        ad.u.net.netif = ifindex;
        ad.u.net.family = family;
        if (selinux_parse_skb(skb, &ad, &addrp, 1, NULL) != 0)
                return NF_DROP;
 
-       if (selinux_skb_peerlbl_sid(skb, family, &peer_sid) != 0)
-               return NF_DROP;
-
-       if (peerlbl_active)
-               if (selinux_inet_sys_rcv_skb(ifindex, addrp, family,
-                                            peer_sid, &ad) != 0)
+       if (peerlbl_active) {
+               err = selinux_inet_sys_rcv_skb(ifindex, addrp, family,
+                                              peer_sid, &ad);
+               if (err) {
+                       selinux_netlbl_err(skb, err, 1);
                        return NF_DROP;
+               }
+       }
 
        if (secmark_active)
                if (avc_has_perm(peer_sid, skb->secmark,
                                 SECCLASS_PACKET, PACKET__FORWARD_IN, &ad))
                        return NF_DROP;
 
+       if (netlbl_active)
+               /* we do this in the FORWARD path and not the POST_ROUTING
+                * path because we want to make sure we apply the necessary
+                * labeling before IPsec is applied so we can leverage AH
+                * protection */
+               if (selinux_netlbl_skbuff_setsid(skb, family, peer_sid) != 0)
+                       return NF_DROP;
+
        return NF_ACCEPT;
 }
 
@@ -4433,6 +4482,37 @@ static unsigned int selinux_ipv6_forward(unsigned int hooknum,
 }
 #endif /* IPV6 */
 
+static unsigned int selinux_ip_output(struct sk_buff *skb,
+                                     u16 family)
+{
+       u32 sid;
+
+       if (!netlbl_enabled())
+               return NF_ACCEPT;
+
+       /* we do this in the LOCAL_OUT path and not the POST_ROUTING path
+        * because we want to make sure we apply the necessary labeling
+        * before IPsec is applied so we can leverage AH protection */
+       if (skb->sk) {
+               struct sk_security_struct *sksec = skb->sk->sk_security;
+               sid = sksec->sid;
+       } else
+               sid = SECINITSID_KERNEL;
+       if (selinux_netlbl_skbuff_setsid(skb, family, sid) != 0)
+               return NF_DROP;
+
+       return NF_ACCEPT;
+}
+
+static unsigned int selinux_ipv4_output(unsigned int hooknum,
+                                       struct sk_buff *skb,
+                                       const struct net_device *in,
+                                       const struct net_device *out,
+                                       int (*okfn)(struct sk_buff *))
+{
+       return selinux_ip_output(skb, PF_INET);
+}
+
 static int selinux_ip_postroute_iptables_compat(struct sock *sk,
                                                int ifindex,
                                                struct avc_audit_data *ad,
@@ -4500,30 +4580,36 @@ static int selinux_ip_postroute_iptables_compat(struct sock *sk,
 
 static unsigned int selinux_ip_postroute_compat(struct sk_buff *skb,
                                                int ifindex,
-                                               struct avc_audit_data *ad,
-                                               u16 family,
-                                               char *addrp,
-                                               u8 proto)
+                                               u16 family)
 {
        struct sock *sk = skb->sk;
        struct sk_security_struct *sksec;
+       struct avc_audit_data ad;
+       char *addrp;
+       u8 proto;
 
        if (sk == NULL)
                return NF_ACCEPT;
        sksec = sk->sk_security;
 
+       AVC_AUDIT_DATA_INIT(&ad, NET);
+       ad.u.net.netif = ifindex;
+       ad.u.net.family = family;
+       if (selinux_parse_skb(skb, &ad, &addrp, 0, &proto))
+               return NF_DROP;
+
        if (selinux_compat_net) {
                if (selinux_ip_postroute_iptables_compat(skb->sk, ifindex,
-                                                        ad, family, addrp))
+                                                        &ad, family, addrp))
                        return NF_DROP;
        } else {
                if (avc_has_perm(sksec->sid, skb->secmark,
-                                SECCLASS_PACKET, PACKET__SEND, ad))
+                                SECCLASS_PACKET, PACKET__SEND, &ad))
                        return NF_DROP;
        }
 
        if (selinux_policycap_netpeer)
-               if (selinux_xfrm_postroute_last(sksec->sid, skb, ad, proto))
+               if (selinux_xfrm_postroute_last(sksec->sid, skb, &ad, proto))
                        return NF_DROP;
 
        return NF_ACCEPT;
@@ -4537,23 +4623,15 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb, int ifindex,
        struct sock *sk;
        struct avc_audit_data ad;
        char *addrp;
-       u8 proto;
        u8 secmark_active;
        u8 peerlbl_active;
 
-       AVC_AUDIT_DATA_INIT(&ad, NET);
-       ad.u.net.netif = ifindex;
-       ad.u.net.family = family;
-       if (selinux_parse_skb(skb, &ad, &addrp, 0, &proto))
-               return NF_DROP;
-
        /* If any sort of compatibility mode is enabled then handoff processing
         * to the selinux_ip_postroute_compat() function to deal with the
         * special handling.  We do this in an attempt to keep this function
         * as fast and as clean as possible. */
        if (selinux_compat_net || !selinux_policycap_netpeer)
-               return selinux_ip_postroute_compat(skb, ifindex, &ad,
-                                                  family, addrp, proto);
+               return selinux_ip_postroute_compat(skb, ifindex, family);
 
        /* If skb->dst->xfrm is non-NULL then the packet is undergoing an IPsec
         * packet transformation so allow the packet to pass without any checks
@@ -4569,21 +4647,45 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb, int ifindex,
        if (!secmark_active && !peerlbl_active)
                return NF_ACCEPT;
 
-       /* if the packet is locally generated (skb->sk != NULL) then use the
-        * socket's label as the peer label, otherwise the packet is being
-        * forwarded through this system and we need to fetch the peer label
-        * directly from the packet */
+       /* if the packet is being forwarded then get the peer label from the
+        * packet itself; otherwise check to see if it is from a local
+        * application or the kernel, if from an application get the peer label
+        * from the sending socket, otherwise use the kernel's sid */
        sk = skb->sk;
-       if (sk) {
+       if (sk == NULL) {
+               switch (family) {
+               case PF_INET:
+                       if (IPCB(skb)->flags & IPSKB_FORWARDED)
+                               secmark_perm = PACKET__FORWARD_OUT;
+                       else
+                               secmark_perm = PACKET__SEND;
+                       break;
+               case PF_INET6:
+                       if (IP6CB(skb)->flags & IP6SKB_FORWARDED)
+                               secmark_perm = PACKET__FORWARD_OUT;
+                       else
+                               secmark_perm = PACKET__SEND;
+                       break;
+               default:
+                       return NF_DROP;
+               }
+               if (secmark_perm == PACKET__FORWARD_OUT) {
+                       if (selinux_skb_peerlbl_sid(skb, family, &peer_sid))
+                               return NF_DROP;
+               } else
+                       peer_sid = SECINITSID_KERNEL;
+       } else {
                struct sk_security_struct *sksec = sk->sk_security;
                peer_sid = sksec->sid;
                secmark_perm = PACKET__SEND;
-       } else {
-               if (selinux_skb_peerlbl_sid(skb, family, &peer_sid))
-                               return NF_DROP;
-               secmark_perm = PACKET__FORWARD_OUT;
        }
 
+       AVC_AUDIT_DATA_INIT(&ad, NET);
+       ad.u.net.netif = ifindex;
+       ad.u.net.family = family;
+       if (selinux_parse_skb(skb, &ad, &addrp, 0, NULL))
+               return NF_DROP;
+
        if (secmark_active)
                if (avc_has_perm(peer_sid, skb->secmark,
                                 SECCLASS_PACKET, secmark_perm, &ad))
@@ -5657,6 +5759,13 @@ static struct nf_hook_ops selinux_ipv4_ops[] = {
                .pf =           PF_INET,
                .hooknum =      NF_INET_FORWARD,
                .priority =     NF_IP_PRI_SELINUX_FIRST,
+       },
+       {
+               .hook =         selinux_ipv4_output,
+               .owner =        THIS_MODULE,
+               .pf =           PF_INET,
+               .hooknum =      NF_INET_LOCAL_OUT,
+               .priority =     NF_IP_PRI_SELINUX_FIRST,
        }
 };
 
index 487a7d8..b913c8d 100644 (file)
@@ -39,6 +39,9 @@
 #ifdef CONFIG_NETLABEL
 void selinux_netlbl_cache_invalidate(void);
 
+void selinux_netlbl_err(struct sk_buff *skb, int error, int gateway);
+
+void selinux_netlbl_sk_security_free(struct sk_security_struct *ssec);
 void selinux_netlbl_sk_security_reset(struct sk_security_struct *ssec,
                                      int family);
 
@@ -46,8 +49,11 @@ int selinux_netlbl_skbuff_getsid(struct sk_buff *skb,
                                 u16 family,
                                 u32 *type,
                                 u32 *sid);
+int selinux_netlbl_skbuff_setsid(struct sk_buff *skb,
+                                u16 family,
+                                u32 sid);
 
-void selinux_netlbl_sock_graft(struct sock *sk, struct socket *sock);
+void selinux_netlbl_inet_conn_established(struct sock *sk, u16 family);
 int selinux_netlbl_socket_post_create(struct socket *sock);
 int selinux_netlbl_inode_permission(struct inode *inode, int mask);
 int selinux_netlbl_sock_rcv_skb(struct sk_security_struct *sksec,
@@ -57,12 +63,27 @@ int selinux_netlbl_sock_rcv_skb(struct sk_security_struct *sksec,
 int selinux_netlbl_socket_setsockopt(struct socket *sock,
                                     int level,
                                     int optname);
+int selinux_netlbl_socket_connect(struct sock *sk, struct sockaddr *addr);
+
 #else
 static inline void selinux_netlbl_cache_invalidate(void)
 {
        return;
 }
 
+static inline void selinux_netlbl_err(struct sk_buff *skb,
+                                     int error,
+                                     int gateway)
+{
+       return;
+}
+
+static inline void selinux_netlbl_sk_security_free(
+                                              struct sk_security_struct *ssec)
+{
+       return;
+}
+
 static inline void selinux_netlbl_sk_security_reset(
                                               struct sk_security_struct *ssec,
                                               int family)
@@ -79,9 +100,21 @@ static inline int selinux_netlbl_skbuff_getsid(struct sk_buff *skb,
        *sid = SECSID_NULL;
        return 0;
 }
+static inline int selinux_netlbl_skbuff_setsid(struct sk_buff *skb,
+                                              u16 family,
+                                              u32 sid)
+{
+       return 0;
+}
 
-static inline void selinux_netlbl_sock_graft(struct sock *sk,
-                                            struct socket *sock)
+static inline int selinux_netlbl_conn_setsid(struct sock *sk,
+                                            struct sockaddr *addr)
+{
+       return 0;
+}
+
+static inline void selinux_netlbl_inet_conn_established(struct sock *sk,
+                                                       u16 family)
 {
        return;
 }
@@ -107,6 +140,11 @@ static inline int selinux_netlbl_socket_setsockopt(struct socket *sock,
 {
        return 0;
 }
+static inline int selinux_netlbl_socket_connect(struct sock *sk,
+                                               struct sockaddr *addr)
+{
+       return 0;
+}
 #endif /* CONFIG_NETLABEL */
 
 #endif
index 91070ab..f8be8d7 100644 (file)
@@ -109,16 +109,19 @@ struct netport_security_struct {
 };
 
 struct sk_security_struct {
-       u32 sid;                        /* SID of this object */
-       u32 peer_sid;                   /* SID of peer */
-       u16 sclass;                     /* sock security class */
 #ifdef CONFIG_NETLABEL
        enum {                          /* NetLabel state */
                NLBL_UNSET = 0,
                NLBL_REQUIRE,
                NLBL_LABELED,
+               NLBL_REQSKB,
+               NLBL_CONNLABELED,
        } nlbl_state;
+       struct netlbl_lsm_secattr *nlbl_secattr; /* NetLabel sec attributes */
 #endif
+       u32 sid;                        /* SID of this object */
+       u32 peer_sid;                   /* SID of peer */
+       u16 sclass;                     /* sock security class */
 };
 
 struct key_security_struct {
index 89b4183..f58701a 100644 (file)
@@ -9,7 +9,7 @@
  */
 
 /*
- * (c) Copyright Hewlett-Packard Development Company, L.P., 2007
+ * (c) Copyright Hewlett-Packard Development Company, L.P., 2007, 2008
  *
  * This program is free software;  you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
 
 #include <linux/spinlock.h>
 #include <linux/rcupdate.h>
+#include <linux/ip.h>
+#include <linux/ipv6.h>
 #include <net/sock.h>
 #include <net/netlabel.h>
+#include <net/ip.h>
+#include <net/ipv6.h>
 
 #include "objsec.h"
 #include "security.h"
@@ -63,33 +67,70 @@ static int selinux_netlbl_sidlookup_cached(struct sk_buff *skb,
        return rc;
 }
 
+/**
+ * selinux_netlbl_sock_genattr - Generate the NetLabel socket secattr
+ * @sk: the socket
+ *
+ * Description:
+ * Generate the NetLabel security attributes for a socket, making full use of
+ * the socket's attribute cache.  Returns a pointer to the security attributes
+ * on success, NULL on failure.
+ *
+ */
+static struct netlbl_lsm_secattr *selinux_netlbl_sock_genattr(struct sock *sk)
+{
+       int rc;
+       struct sk_security_struct *sksec = sk->sk_security;
+       struct netlbl_lsm_secattr *secattr;
+
+       if (sksec->nlbl_secattr != NULL)
+               return sksec->nlbl_secattr;
+
+       secattr = netlbl_secattr_alloc(GFP_ATOMIC);
+       if (secattr == NULL)
+               return NULL;
+       rc = security_netlbl_sid_to_secattr(sksec->sid, secattr);
+       if (rc != 0) {
+               netlbl_secattr_free(secattr);
+               return NULL;
+       }
+       sksec->nlbl_secattr = secattr;
+
+       return secattr;
+}
+
 /**
  * selinux_netlbl_sock_setsid - Label a socket using the NetLabel mechanism
  * @sk: the socket to label
- * @sid: the SID to use
  *
  * Description:
- * Attempt to label a socket using the NetLabel mechanism using the given
- * SID.  Returns zero values on success, negative values on failure.
+ * Attempt to label a socket using the NetLabel mechanism.  Returns zero values
+ * on success, negative values on failure.
  *
  */
-static int selinux_netlbl_sock_setsid(struct sock *sk, u32 sid)
+static int selinux_netlbl_sock_setsid(struct sock *sk)
 {
        int rc;
        struct sk_security_struct *sksec = sk->sk_security;
-       struct netlbl_lsm_secattr secattr;
+       struct netlbl_lsm_secattr *secattr;
 
-       netlbl_secattr_init(&secattr);
+       if (sksec->nlbl_state != NLBL_REQUIRE)
+               return 0;
 
-       rc = security_netlbl_sid_to_secattr(sid, &secattr);
-       if (rc != 0)
-               goto sock_setsid_return;
-       rc = netlbl_sock_setattr(sk, &secattr);
-       if (rc == 0)
+       secattr = selinux_netlbl_sock_genattr(sk);
+       if (secattr == NULL)
+               return -ENOMEM;
+       rc = netlbl_sock_setattr(sk, secattr);
+       switch (rc) {
+       case 0:
                sksec->nlbl_state = NLBL_LABELED;
+               break;
+       case -EDESTADDRREQ:
+               sksec->nlbl_state = NLBL_REQSKB;
+               rc = 0;
+               break;
+       }
 
-sock_setsid_return:
-       netlbl_secattr_destroy(&secattr);
        return rc;
 }
 
@@ -105,6 +146,38 @@ void selinux_netlbl_cache_invalidate(void)
        netlbl_cache_invalidate();
 }
 
+/**
+ * selinux_netlbl_err - Handle a NetLabel packet error
+ * @skb: the packet
+ * @error: the error code
+ * @gateway: true if host is acting as a gateway, false otherwise
+ *
+ * Description:
+ * When a packet is dropped due to a call to avc_has_perm() pass the error
+ * code to the NetLabel subsystem so any protocol specific processing can be
+ * done.  This is safe to call even if you are unsure if NetLabel labeling is
+ * present on the packet, NetLabel is smart enough to only act when it should.
+ *
+ */
+void selinux_netlbl_err(struct sk_buff *skb, int error, int gateway)
+{
+       netlbl_skbuff_err(skb, error, gateway);
+}
+
+/**
+ * selinux_netlbl_sk_security_free - Free the NetLabel fields
+ * @sssec: the sk_security_struct
+ *
+ * Description:
+ * Free all of the memory in the NetLabel fields of a sk_security_struct.
+ *
+ */
+void selinux_netlbl_sk_security_free(struct sk_security_struct *ssec)
+{
+       if (ssec->nlbl_secattr != NULL)
+               netlbl_secattr_free(ssec->nlbl_secattr);
+}
+
 /**
  * selinux_netlbl_sk_security_reset - Reset the NetLabel fields
  * @ssec: the sk_security_struct
@@ -163,35 +236,118 @@ int selinux_netlbl_skbuff_getsid(struct sk_buff *skb,
 }
 
 /**
- * selinux_netlbl_sock_graft - Netlabel the new socket
+ * selinux_netlbl_skbuff_setsid - Set the NetLabel on a packet given a sid
+ * @skb: the packet
+ * @family: protocol family
+ * @sid: the SID
+ *
+ * Description
+ * Call the NetLabel mechanism to set the label of a packet using @sid.
+ * Returns zero on auccess, negative values on failure.
+ *
+ */
+int selinux_netlbl_skbuff_setsid(struct sk_buff *skb,
+                                u16 family,
+                                u32 sid)
+{
+       int rc;
+       struct netlbl_lsm_secattr secattr_storage;
+       struct netlbl_lsm_secattr *secattr = NULL;
+       struct sock *sk;
+
+       /* if this is a locally generated packet check to see if it is already
+        * being labeled by it's parent socket, if it is just exit */
+       sk = skb->sk;
+       if (sk != NULL) {
+               struct sk_security_struct *sksec = sk->sk_security;
+               if (sksec->nlbl_state != NLBL_REQSKB)
+                       return 0;
+               secattr = sksec->nlbl_secattr;
+       }
+       if (secattr == NULL) {
+               secattr = &secattr_storage;
+               netlbl_secattr_init(secattr);
+               rc = security_netlbl_sid_to_secattr(sid, secattr);
+               if (rc != 0)
+                       goto skbuff_setsid_return;
+       }
+
+       rc = netlbl_skbuff_setattr(skb, family, secattr);
+
+skbuff_setsid_return:
+       if (secattr == &secattr_storage)
+               netlbl_secattr_destroy(secattr);
+       return rc;
+}
+
+/**
+ * selinux_netlbl_inet_conn_established - Netlabel the newly accepted connection
  * @sk: the new connection
- * @sock: the new socket
  *
  * Description:
- * The connection represented by @sk is being grafted onto @sock so set the
- * socket's NetLabel to match the SID of @sk.
+ * A new connection has been established on @sk so make sure it is labeled
+ * correctly with the NetLabel susbsystem.
  *
  */
-void selinux_netlbl_sock_graft(struct sock *sk, struct socket *sock)
+void selinux_netlbl_inet_conn_established(struct sock *sk, u16 family)
 {
+       int rc;
        struct sk_security_struct *sksec = sk->sk_security;
-       struct netlbl_lsm_secattr secattr;
-       u32 nlbl_peer_sid;
+       struct netlbl_lsm_secattr *secattr;
+       struct inet_sock *sk_inet = inet_sk(sk);
+       struct sockaddr_in addr;
 
        if (sksec->nlbl_state != NLBL_REQUIRE)
                return;
 
-       netlbl_secattr_init(&secattr);
-       if (netlbl_sock_getattr(sk, &secattr) == 0 &&
-           secattr.flags != NETLBL_SECATTR_NONE &&
-           security_netlbl_secattr_to_sid(&secattr, &nlbl_peer_sid) == 0)
-               sksec->peer_sid = nlbl_peer_sid;
-       netlbl_secattr_destroy(&secattr);
+       secattr = selinux_netlbl_sock_genattr(sk);
+       if (secattr == NULL)
+               return;
 
-       /* Try to set the NetLabel on the socket to save time later, if we fail
-        * here we will pick up the pieces in later calls to
-        * selinux_netlbl_inode_permission(). */
-       selinux_netlbl_sock_setsid(sk, sksec->sid);
+       rc = netlbl_sock_setattr(sk, secattr);
+       switch (rc) {
+       case 0:
+               sksec->nlbl_state = NLBL_LABELED;
+               break;
+       case -EDESTADDRREQ:
+               /* no PF_INET6 support yet because we don't support any IPv6
+                * labeling protocols */
+               if (family != PF_INET) {
+                       sksec->nlbl_state = NLBL_UNSET;
+                       return;
+               }
+
+               addr.sin_family = family;
+               addr.sin_addr.s_addr = sk_inet->daddr;
+               if (netlbl_conn_setattr(sk, (struct sockaddr *)&addr,
+                                       secattr) != 0) {
+                       /* we failed to label the connected socket (could be
+                        * for a variety of reasons, the actual "why" isn't
+                        * important here) so we have to go to our backup plan,
+                        * labeling the packets individually in the netfilter
+                        * local output hook.  this is okay but we need to
+                        * adjust the MSS of the connection to take into
+                        * account any labeling overhead, since we don't know
+                        * the exact overhead at this point we'll use the worst
+                        * case value which is 40 bytes for IPv4 */
+                       struct inet_connection_sock *sk_conn = inet_csk(sk);
+                       sk_conn->icsk_ext_hdr_len += 40 -
+                                     (sk_inet->opt ? sk_inet->opt->optlen : 0);
+                       sk_conn->icsk_sync_mss(sk, sk_conn->icsk_pmtu_cookie);
+
+                       sksec->nlbl_state = NLBL_REQSKB;
+               } else
+                       sksec->nlbl_state = NLBL_CONNLABELED;
+               break;
+       default:
+               /* note that we are failing to label the socket which could be
+                * a bad thing since it means traffic could leave the system
+                * without the desired labeling, however, all is not lost as
+                * we have a check in selinux_netlbl_inode_permission() to
+                * pick up the pieces that we might drop here because we can't
+                * return an error code */
+               break;
+       }
 }
 
 /**
@@ -205,13 +361,7 @@ void selinux_netlbl_sock_graft(struct sock *sk, struct socket *sock)
  */
 int selinux_netlbl_socket_post_create(struct socket *sock)
 {
-       struct sock *sk = sock->sk;
-       struct sk_security_struct *sksec = sk->sk_security;
-
-       if (sksec->nlbl_state != NLBL_REQUIRE)
-               return 0;
-
-       return selinux_netlbl_sock_setsid(sk, sksec->sid);
+       return selinux_netlbl_sock_setsid(sock->sk);
 }
 
 /**
@@ -246,7 +396,7 @@ int selinux_netlbl_inode_permission(struct inode *inode, int mask)
        local_bh_disable();
        bh_lock_sock_nested(sk);
        if (likely(sksec->nlbl_state == NLBL_REQUIRE))
-               rc = selinux_netlbl_sock_setsid(sk, sksec->sid);
+               rc = selinux_netlbl_sock_setsid(sk);
        else
                rc = 0;
        bh_unlock_sock(sk);
@@ -307,7 +457,7 @@ int selinux_netlbl_sock_rcv_skb(struct sk_security_struct *sksec,
                return 0;
 
        if (nlbl_sid != SECINITSID_UNLABELED)
-               netlbl_skbuff_err(skb, rc);
+               netlbl_skbuff_err(skb, rc, 0);
        return rc;
 }
 
@@ -334,7 +484,8 @@ int selinux_netlbl_socket_setsockopt(struct socket *sock,
        struct netlbl_lsm_secattr secattr;
 
        if (level == IPPROTO_IP && optname == IP_OPTIONS &&
-           sksec->nlbl_state == NLBL_LABELED) {
+           (sksec->nlbl_state == NLBL_LABELED ||
+            sksec->nlbl_state == NLBL_CONNLABELED)) {
                netlbl_secattr_init(&secattr);
                lock_sock(sk);
                rc = netlbl_sock_getattr(sk, &secattr);
@@ -346,3 +497,50 @@ int selinux_netlbl_socket_setsockopt(struct socket *sock,
 
        return rc;
 }
+
+/**
+ * selinux_netlbl_socket_connect - Label a client-side socket on connect
+ * @sk: the socket to label
+ * @addr: the destination address
+ *
+ * Description:
+ * Attempt to label a connected socket with NetLabel using the given address.
+ * Returns zero values on success, negative values on failure.
+ *
+ */
+int selinux_netlbl_socket_connect(struct sock *sk, struct sockaddr *addr)
+{
+       int rc;
+       struct sk_security_struct *sksec = sk->sk_security;
+       struct netlbl_lsm_secattr *secattr;
+
+       if (sksec->nlbl_state != NLBL_REQSKB &&
+           sksec->nlbl_state != NLBL_CONNLABELED)
+               return 0;
+
+       local_bh_disable();
+       bh_lock_sock_nested(sk);
+
+       /* connected sockets are allowed to disconnect when the address family
+        * is set to AF_UNSPEC, if that is what is happening we want to reset
+        * the socket */
+       if (addr->sa_family == AF_UNSPEC) {
+               netlbl_sock_delattr(sk);
+               sksec->nlbl_state = NLBL_REQSKB;
+               rc = 0;
+               goto socket_connect_return;
+       }
+       secattr = selinux_netlbl_sock_genattr(sk);
+       if (secattr == NULL) {
+               rc = -ENOMEM;
+               goto socket_connect_return;
+       }
+       rc = netlbl_conn_setattr(sk, addr, secattr);
+       if (rc == 0)
+               sksec->nlbl_state = NLBL_CONNLABELED;
+
+socket_connect_return:
+       bh_unlock_sock(sk);
+       local_bh_enable();
+       return rc;
+}
index ab0cc0c..343c8ab 100644 (file)
@@ -2955,7 +2955,7 @@ netlbl_secattr_to_sid_return_cleanup:
  */
 int security_netlbl_sid_to_secattr(u32 sid, struct netlbl_lsm_secattr *secattr)
 {
-       int rc = -ENOENT;
+       int rc;
        struct context *ctx;
 
        if (!ss_initialized)
@@ -2963,11 +2963,18 @@ int security_netlbl_sid_to_secattr(u32 sid, struct netlbl_lsm_secattr *secattr)
 
        read_lock(&policy_rwlock);
        ctx = sidtab_search(&sidtab, sid);
-       if (ctx == NULL)
+       if (ctx == NULL) {
+               rc = -ENOENT;
                goto netlbl_sid_to_secattr_failure;
+       }
        secattr->domain = kstrdup(policydb.p_type_val_to_name[ctx->type - 1],
                                  GFP_ATOMIC);
-       secattr->flags |= NETLBL_SECATTR_DOMAIN_CPY;
+       if (secattr->domain == NULL) {
+               rc = -ENOMEM;
+               goto netlbl_sid_to_secattr_failure;
+       }
+       secattr->attr.secid = sid;
+       secattr->flags |= NETLBL_SECATTR_DOMAIN_CPY | NETLBL_SECATTR_SECID;
        mls_export_netlbl_lvl(ctx, secattr);
        rc = mls_export_netlbl_cat(ctx, secattr);
        if (rc != 0)
index 87d7541..6e2dc0b 100644 (file)
@@ -2179,7 +2179,10 @@ static int smack_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
         * This is the simplist possible security model
         * for networking.
         */
-       return smk_access(smack, ssp->smk_in, MAY_WRITE);
+       rc = smk_access(smack, ssp->smk_in, MAY_WRITE);
+       if (rc != 0)
+               netlbl_skbuff_err(skb, rc, 0);
+       return rc;
 }
 
 /**
index e7c6424..c21d8c8 100644 (file)
@@ -354,9 +354,11 @@ static void smk_cipso_doi(void)
                doip->tags[rc] = CIPSO_V4_TAG_INVALID;
 
        rc = netlbl_cfg_cipsov4_add_map(doip, NULL, &audit_info);
-       if (rc != 0)
+       if (rc != 0) {
                printk(KERN_WARNING "%s:%d add rc = %d\n",
                       __func__, __LINE__, rc);
+               kfree(doip);
+       }
 }
 
 /**