aboutsummaryrefslogtreecommitdiff
path: root/net/netlabel
diff options
context:
space:
mode:
Diffstat (limited to 'net/netlabel')
-rw-r--r--net/netlabel/Makefile5
-rw-r--r--net/netlabel/netlabel_addrlist.c383
-rw-r--r--net/netlabel/netlabel_addrlist.h208
-rw-r--r--net/netlabel/netlabel_cipso_v4.c222
-rw-r--r--net/netlabel/netlabel_cipso_v4.h16
-rw-r--r--net/netlabel/netlabel_domainhash.c560
-rw-r--r--net/netlabel/netlabel_domainhash.h55
-rw-r--r--net/netlabel/netlabel_kapi.c668
-rw-r--r--net/netlabel/netlabel_mgmt.c453
-rw-r--r--net/netlabel/netlabel_mgmt.h67
-rw-r--r--net/netlabel/netlabel_unlabeled.c698
-rw-r--r--net/netlabel/netlabel_unlabeled.h21
-rw-r--r--net/netlabel/netlabel_user.c10
-rw-r--r--net/netlabel/netlabel_user.h10
14 files changed, 2405 insertions, 971 deletions
diff --git a/net/netlabel/Makefile b/net/netlabel/Makefile
index 8af18c0a47d..d2732fc952e 100644
--- a/net/netlabel/Makefile
+++ b/net/netlabel/Makefile
@@ -1,11 +1,10 @@
#
# Makefile for the NetLabel subsystem.
#
-# Feb 9, 2006, Paul Moore <paul.moore@hp.com>
-#
# 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
index 00000000000..d0a3acfa574
--- /dev/null
+++ b/net/netlabel/netlabel_addrlist.c
@@ -0,0 +1,383 @@
+/*
+ * 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@paul-moore.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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#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 IS_ENABLED(CONFIG_IPV6)
+/**
+ * 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 IS_ENABLED(CONFIG_IPV6)
+/**
+ * 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_exact(addr, mask, head);
+ if (entry == NULL)
+ return NULL;
+ netlbl_af4list_remove_entry(entry);
+ return entry;
+}
+
+#if IS_ENABLED(CONFIG_IPV6)
+/**
+ * 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_exact(addr, mask, head);
+ if (entry == NULL)
+ return NULL;
+ netlbl_af6list_remove_entry(entry);
+ return entry;
+}
+#endif /* IPv6 */
+
+/*
+ * Audit Helper Functions
+ */
+
+#ifdef CONFIG_AUDIT
+/**
+ * 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=%pI4", dir, &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 IS_ENABLED(CONFIG_IPV6)
+/**
+ * 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=%pI6", dir, 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 */
+#endif /* CONFIG_AUDIT */
diff --git a/net/netlabel/netlabel_addrlist.h b/net/netlabel/netlabel_addrlist.h
new file mode 100644
index 00000000000..d0f38bc9af6
--- /dev/null
+++ b/net/netlabel/netlabel_addrlist.h
@@ -0,0 +1,208 @@
+/*
+ * 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@paul-moore.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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#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); \
+ &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); \
+ &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);
+
+#ifdef CONFIG_AUDIT
+void netlbl_af4list_audit_addr(struct audit_buffer *audit_buf,
+ int src, const char *dev,
+ __be32 addr, __be32 mask);
+#else
+static inline void netlbl_af4list_audit_addr(struct audit_buffer *audit_buf,
+ int src, const char *dev,
+ __be32 addr, __be32 mask)
+{
+}
+#endif
+
+#if IS_ENABLED(CONFIG_IPV6)
+
+#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); \
+ &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); \
+ &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);
+
+#ifdef CONFIG_AUDIT
+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);
+#else
+static inline 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
+#endif /* IPV6 */
+
+#endif
diff --git a/net/netlabel/netlabel_cipso_v4.c b/net/netlabel/netlabel_cipso_v4.c
index fdc14a0d21a..c2f2a53a487 100644
--- a/net/netlabel/netlabel_cipso_v4.c
+++ b/net/netlabel/netlabel_cipso_v4.c
@@ -5,7 +5,7 @@
* NetLabel system manages static and dynamic label mappings for network
* protocols such as CIPSO and RIPSO.
*
- * Author: Paul Moore <paul.moore@hp.com>
+ * Author: Paul Moore <paul@paul-moore.com>
*
*/
@@ -23,8 +23,7 @@
* 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
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
*
*/
@@ -33,16 +32,18 @@
#include <linux/string.h>
#include <linux/skbuff.h>
#include <linux/audit.h>
+#include <linux/slab.h>
#include <net/sock.h>
#include <net/netlink.h>
#include <net/genetlink.h>
#include <net/netlabel.h>
#include <net/cipso_ipv4.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
#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,
@@ -81,32 +88,6 @@ static const struct nla_policy netlbl_cipsov4_genl_policy[NLBL_CIPSOV4_A_MAX + 1
*/
/**
- * 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
* @doi_def: the CIPSO V4 DOI definition
@@ -149,14 +130,16 @@ static int netlbl_cipsov4_add_common(struct genl_info *info,
/**
* netlbl_cipsov4_add_std - Adds a CIPSO V4 DOI definition
* @info: the Generic NETLINK info block
+ * @audit_info: NetLabel audit information
*
* 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)
+static int netlbl_cipsov4_add_std(struct genl_info *info,
+ struct netlbl_audit *audit_info)
{
int ret_val = -EINVAL;
struct cipso_v4_doi *doi_def = NULL;
@@ -183,7 +166,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)
@@ -335,20 +318,21 @@ static int netlbl_cipsov4_add_std(struct genl_info *info)
}
}
- ret_val = cipso_v4_doi_add(doi_def);
+ ret_val = cipso_v4_doi_add(doi_def, audit_info);
if (ret_val != 0)
goto add_std_failure;
return 0;
add_std_failure:
if (doi_def)
- netlbl_cipsov4_doi_free(&doi_def->rcu);
+ cipso_v4_doi_free(doi_def);
return ret_val;
}
/**
* netlbl_cipsov4_add_pass - Adds a CIPSO V4 DOI definition
* @info: the Generic NETLINK info block
+ * @audit_info: NetLabel audit information
*
* Description:
* Create a new CIPSO_V4_MAP_PASS DOI definition based on the given ADD message
@@ -356,7 +340,8 @@ add_std_failure:
* error.
*
*/
-static int netlbl_cipsov4_add_pass(struct genl_info *info)
+static int netlbl_cipsov4_add_pass(struct genl_info *info,
+ struct netlbl_audit *audit_info)
{
int ret_val;
struct cipso_v4_doi *doi_def = NULL;
@@ -373,13 +358,52 @@ static int netlbl_cipsov4_add_pass(struct genl_info *info)
if (ret_val != 0)
goto add_pass_failure;
- ret_val = cipso_v4_doi_add(doi_def);
+ ret_val = cipso_v4_doi_add(doi_def, audit_info);
if (ret_val != 0)
goto add_pass_failure;
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
+ * @audit_info: NetLabel audit information
+ *
+ * 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,
+ struct netlbl_audit *audit_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, audit_info);
+ if (ret_val != 0)
+ goto add_local_failure;
+ return 0;
+
+add_local_failure:
+ cipso_v4_doi_free(doi_def);
return ret_val;
}
@@ -397,44 +421,27 @@ static int netlbl_cipsov4_add(struct sk_buff *skb, struct genl_info *info)
{
int ret_val = -EINVAL;
- u32 type;
- u32 doi;
- const char *type_str = "(unknown)";
- struct audit_buffer *audit_buf;
struct netlbl_audit audit_info;
if (!info->attrs[NLBL_CIPSOV4_A_DOI] ||
!info->attrs[NLBL_CIPSOV4_A_MTYPE])
return -EINVAL;
- doi = nla_get_u32(info->attrs[NLBL_CIPSOV4_A_DOI]);
netlbl_netlink_auditinfo(skb, &audit_info);
-
- type = nla_get_u32(info->attrs[NLBL_CIPSOV4_A_MTYPE]);
- switch (type) {
- case CIPSO_V4_MAP_STD:
- type_str = "std";
- ret_val = netlbl_cipsov4_add_std(info);
+ switch (nla_get_u32(info->attrs[NLBL_CIPSOV4_A_MTYPE])) {
+ case CIPSO_V4_MAP_TRANS:
+ ret_val = netlbl_cipsov4_add_std(info, &audit_info);
break;
case CIPSO_V4_MAP_PASS:
- type_str = "pass";
- ret_val = netlbl_cipsov4_add_pass(info);
+ ret_val = netlbl_cipsov4_add_pass(info, &audit_info);
+ break;
+ case CIPSO_V4_MAP_LOCAL:
+ ret_val = netlbl_cipsov4_add_local(info, &audit_info);
break;
}
if (ret_val == 0)
atomic_inc(&netlabel_mgmt_protocount);
- audit_buf = netlbl_audit_start_common(AUDIT_MAC_CIPSOV4_ADD,
- &audit_info);
- if (audit_buf != NULL) {
- 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;
}
@@ -491,7 +498,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 +523,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;
@@ -584,19 +591,14 @@ list_start:
rcu_read_unlock();
genlmsg_end(ans_skb, data);
-
- ret_val = genlmsg_reply(ans_skb, info);
- if (ret_val != 0)
- goto list_failure;
-
- return 0;
+ return genlmsg_reply(ans_skb, info);
list_retry:
/* XXX - this limit is a guesstimate */
if (nlsze_mult < 4) {
rcu_read_unlock();
kfree_skb(ans_skb);
- nlsze_mult++;
+ nlsze_mult *= 2;
goto list_start;
}
list_failure_lock:
@@ -624,7 +626,7 @@ static int netlbl_cipsov4_listall_cb(struct cipso_v4_doi *doi_def, void *arg)
struct netlbl_cipsov4_doiwalk_arg *cb_arg = arg;
void *data;
- data = genlmsg_put(cb_arg->skb, NETLINK_CB(cb_arg->nl_cb->skb).pid,
+ data = genlmsg_put(cb_arg->skb, NETLINK_CB(cb_arg->nl_cb->skb).portid,
cb_arg->seq, &netlbl_cipsov4_gnl_family,
NLM_F_MULTI, NLBL_CIPSOV4_C_LISTALL);
if (data == NULL)
@@ -660,7 +662,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;
@@ -673,6 +675,29 @@ static int netlbl_cipsov4_listall(struct sk_buff *skb,
}
/**
+ * 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->def.type == NETLBL_NLTYPE_CIPSOV4 &&
+ entry->def.cipso->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
* @info: the Generic NETLINK info block
@@ -685,30 +710,23 @@ static int netlbl_cipsov4_listall(struct sk_buff *skb,
static int netlbl_cipsov4_remove(struct sk_buff *skb, struct genl_info *info)
{
int ret_val = -EINVAL;
- u32 doi = 0;
- struct audit_buffer *audit_buf;
+ struct netlbl_domhsh_walk_arg cb_arg;
struct netlbl_audit audit_info;
+ u32 skip_bkt = 0;
+ u32 skip_chain = 0;
if (!info->attrs[NLBL_CIPSOV4_A_DOI])
return -EINVAL;
- 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);
-
- audit_buf = netlbl_audit_start_common(AUDIT_MAC_CIPSOV4_DEL,
- &audit_info);
- if (audit_buf != NULL) {
- audit_log_format(audit_buf,
- " cipso_doi=%u res=%u",
- doi,
- ret_val == 0 ? 1 : 0);
- audit_log_end(audit_buf);
+ cb_arg.doi = nla_get_u32(info->attrs[NLBL_CIPSOV4_A_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(cb_arg.doi, &audit_info);
+ if (ret_val == 0)
+ atomic_dec(&netlabel_mgmt_protocount);
}
return ret_val;
@@ -718,7 +736,7 @@ static int netlbl_cipsov4_remove(struct sk_buff *skb, struct genl_info *info)
* NetLabel Generic NETLINK Command Definitions
*/
-static struct genl_ops netlbl_cipsov4_ops[] = {
+static const struct genl_ops netlbl_cipsov4_ops[] = {
{
.cmd = NLBL_CIPSOV4_C_ADD,
.flags = GENL_ADMIN_PERM,
@@ -763,18 +781,6 @@ static struct genl_ops netlbl_cipsov4_ops[] = {
*/
int __init netlbl_cipsov4_genl_init(void)
{
- int ret_val, i;
-
- ret_val = genl_register_family(&netlbl_cipsov4_gnl_family);
- if (ret_val != 0)
- return ret_val;
-
- for (i = 0; i < ARRAY_SIZE(netlbl_cipsov4_ops); i++) {
- ret_val = genl_register_ops(&netlbl_cipsov4_gnl_family,
- &netlbl_cipsov4_ops[i]);
- if (ret_val != 0)
- return ret_val;
- }
-
- return 0;
+ return genl_register_family_with_ops(&netlbl_cipsov4_gnl_family,
+ netlbl_cipsov4_ops);
}
diff --git a/net/netlabel/netlabel_cipso_v4.h b/net/netlabel/netlabel_cipso_v4.h
index 220cb9d06b4..875826808b0 100644
--- a/net/netlabel/netlabel_cipso_v4.h
+++ b/net/netlabel/netlabel_cipso_v4.h
@@ -5,7 +5,7 @@
* NetLabel system manages static and dynamic label mappings for network
* protocols such as CIPSO and RIPSO.
*
- * Author: Paul Moore <paul.moore@hp.com>
+ * Author: Paul Moore <paul@paul-moore.com>
*
*/
@@ -23,8 +23,7 @@
* 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
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
*
*/
@@ -45,12 +44,13 @@
* 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
@@ -76,12 +76,13 @@
* 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
@@ -105,7 +106,6 @@ enum {
NLBL_CIPSOV4_C_LISTALL,
__NLBL_CIPSOV4_C_MAX,
};
-#define NLBL_CIPSOV4_C_MAX (__NLBL_CIPSOV4_C_MAX - 1)
/* NetLabel CIPSOv4 attributes */
enum {
diff --git a/net/netlabel/netlabel_domainhash.c b/net/netlabel/netlabel_domainhash.c
index 02c2f7c0b25..f0cb92f3dda 100644
--- a/net/netlabel/netlabel_domainhash.c
+++ b/net/netlabel/netlabel_domainhash.c
@@ -6,12 +6,12 @@
* system manages static and dynamic label mappings for network protocols such
* as CIPSO and RIPSO.
*
- * Author: Paul Moore <paul.moore@hp.com>
+ * Author: Paul Moore <paul@paul-moore.com>
*
*/
/*
- * (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
@@ -24,23 +24,23 @@
* 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
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
*
*/
#include <linux/types.h>
-#include <linux/rcupdate.h>
-#include <linux/list.h>
+#include <linux/rculist.h>
#include <linux/skbuff.h>
#include <linux/spinlock.h>
#include <linux/string.h>
#include <linux/audit.h>
+#include <linux/slab.h>
#include <net/netlabel.h>
#include <net/cipso_ipv4.h>
#include <asm/bug.h>
#include "netlabel_mgmt.h"
+#include "netlabel_addrlist.h"
#include "netlabel_domainhash.h"
#include "netlabel_user.h"
@@ -50,9 +50,11 @@ struct netlbl_domhsh_tbl {
};
/* Domain hash table */
-/* XXX - updates should be so rare that having one spinlock for the entire
- * hash table should be okay */
+/* updates should be so rare that having one spinlock for the entire hash table
+ * should be okay */
static DEFINE_SPINLOCK(netlbl_domhsh_lock);
+#define netlbl_domhsh_rcu_deref(p) \
+ rcu_dereference_check(p, lockdep_is_held(&netlbl_domhsh_lock))
static struct netlbl_domhsh_tbl *netlbl_domhsh = NULL;
static struct netlbl_dom_map *netlbl_domhsh_def = NULL;
@@ -73,8 +75,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 IS_ENABLED(CONFIG_IPV6)
+ struct netlbl_af6list *iter6;
+ struct netlbl_af6list *tmp6;
+#endif /* IPv6 */
ptr = container_of(entry, struct netlbl_dom_map, rcu);
+ if (ptr->def.type == NETLBL_NLTYPE_ADDRSELECT) {
+ netlbl_af4list_foreach_safe(iter4, tmp4,
+ &ptr->def.addrsel->list4) {
+ netlbl_af4list_remove_entry(iter4);
+ kfree(netlbl_domhsh_addr4_entry(iter4));
+ }
+#if IS_ENABLED(CONFIG_IPV6)
+ netlbl_af6list_foreach_safe(iter6, tmp6,
+ &ptr->def.addrsel->list6) {
+ netlbl_af6list_remove_entry(iter6);
+ kfree(netlbl_domhsh_addr6_entry(iter6));
+ }
+#endif /* IPv6 */
+ }
kfree(ptr->domain);
kfree(ptr);
}
@@ -85,8 +107,9 @@ static void netlbl_domhsh_free_entry(struct rcu_head *entry)
*
* Description:
* This is the hashing function for the domain hash table, it returns the
- * correct bucket number for the domain. The caller is responsibile for
- * calling the rcu_read_[un]lock() functions.
+ * correct bucket number for the domain. The caller is responsible for
+ * ensuring that the hash table is protected with either a RCU read lock or the
+ * hash table lock.
*
*/
static u32 netlbl_domhsh_hash(const char *key)
@@ -100,7 +123,7 @@ static u32 netlbl_domhsh_hash(const char *key)
for (iter = 0, val = 0, len = strlen(key); iter < len; iter++)
val = (val << 4 | (val >> (8 * sizeof(u32) - 4))) ^ key[iter];
- return val & (rcu_dereference(netlbl_domhsh)->size - 1);
+ return val & (netlbl_domhsh_rcu_deref(netlbl_domhsh)->size - 1);
}
/**
@@ -109,20 +132,21 @@ static u32 netlbl_domhsh_hash(const char *key)
*
* Description:
* Searches the domain hash table and returns a pointer to the hash table
- * entry if found, otherwise NULL is returned. The caller is responsibile for
- * the rcu hash table locks (i.e. the caller much call rcu_read_[un]lock()).
+ * entry if found, otherwise NULL is returned. The caller is responsible for
+ * ensuring that the hash table is protected with either a RCU read lock or the
+ * hash table lock.
*
*/
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 = &netlbl_domhsh_rcu_deref(netlbl_domhsh)->tbl[bkt];
+ list_for_each_entry_rcu(iter, bkt_list, list)
if (iter->valid && strcmp(iter->domain, domain) == 0)
return iter;
}
@@ -139,8 +163,8 @@ static struct netlbl_dom_map *netlbl_domhsh_search(const char *domain)
* Searches the domain hash table and returns a pointer to the hash table
* entry if an exact match is found, if an exact match is not present in the
* hash table then the default entry is returned if valid otherwise NULL is
- * returned. The caller is responsibile for the rcu hash table locks
- * (i.e. the caller much call rcu_read_[un]lock()).
+ * returned. The caller is responsible ensuring that the hash table is
+ * protected with either a RCU read lock or the hash table lock.
*
*/
static struct netlbl_dom_map *netlbl_domhsh_search_def(const char *domain)
@@ -149,7 +173,7 @@ static struct netlbl_dom_map *netlbl_domhsh_search_def(const char *domain)
entry = netlbl_domhsh_search(domain);
if (entry == NULL) {
- entry = rcu_dereference(netlbl_domhsh_def);
+ entry = netlbl_domhsh_rcu_deref(netlbl_domhsh_def);
if (entry != NULL && !entry->valid)
entry = NULL;
}
@@ -157,6 +181,133 @@ 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 responsible 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->def.type;
+ cipsov4 = map4->def.cipso;
+ netlbl_af4list_audit_addr(audit_buf, 0, NULL,
+ addr4->addr, addr4->mask);
+#if IS_ENABLED(CONFIG_IPV6)
+ } else if (addr6 != NULL) {
+ struct netlbl_domaddr6_map *map6;
+ map6 = netlbl_domhsh_addr6_entry(addr6);
+ type = map6->def.type;
+ netlbl_af6list_audit_addr(audit_buf, 0, NULL,
+ &addr6->addr, &addr6->mask);
+#endif /* IPv6 */
+ } else {
+ type = entry->def.type;
+ cipsov4 = entry->def.cipso;
+ }
+ 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);
+ }
+}
+
+/**
+ * netlbl_domhsh_validate - Validate a new domain mapping entry
+ * @entry: the entry to validate
+ *
+ * This function validates the new domain mapping entry to ensure that it is
+ * a valid entry. Returns zero on success, negative values on failure.
+ *
+ */
+static int netlbl_domhsh_validate(const struct netlbl_dom_map *entry)
+{
+ struct netlbl_af4list *iter4;
+ struct netlbl_domaddr4_map *map4;
+#if IS_ENABLED(CONFIG_IPV6)
+ struct netlbl_af6list *iter6;
+ struct netlbl_domaddr6_map *map6;
+#endif /* IPv6 */
+
+ if (entry == NULL)
+ return -EINVAL;
+
+ switch (entry->def.type) {
+ case NETLBL_NLTYPE_UNLABELED:
+ if (entry->def.cipso != NULL || entry->def.addrsel != NULL)
+ return -EINVAL;
+ break;
+ case NETLBL_NLTYPE_CIPSOV4:
+ if (entry->def.cipso == NULL)
+ return -EINVAL;
+ break;
+ case NETLBL_NLTYPE_ADDRSELECT:
+ netlbl_af4list_foreach(iter4, &entry->def.addrsel->list4) {
+ map4 = netlbl_domhsh_addr4_entry(iter4);
+ switch (map4->def.type) {
+ case NETLBL_NLTYPE_UNLABELED:
+ if (map4->def.cipso != NULL)
+ return -EINVAL;
+ break;
+ case NETLBL_NLTYPE_CIPSOV4:
+ if (map4->def.cipso == NULL)
+ return -EINVAL;
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
+#if IS_ENABLED(CONFIG_IPV6)
+ netlbl_af6list_foreach(iter6, &entry->def.addrsel->list6) {
+ map6 = netlbl_domhsh_addr6_entry(iter6);
+ switch (map6->def.type) {
+ case NETLBL_NLTYPE_UNLABELED:
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
+#endif /* IPv6 */
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
/*
* Domain Hash Table Functions
*/
@@ -214,74 +365,110 @@ 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;
- }
+ int ret_val = 0;
+ struct netlbl_dom_map *entry_old;
+ struct netlbl_af4list *iter4;
+ struct netlbl_af4list *tmp4;
+#if IS_ENABLED(CONFIG_IPV6)
+ struct netlbl_af6list *iter6;
+ struct netlbl_af6list *tmp6;
+#endif /* IPv6 */
+
+ ret_val = netlbl_domhsh_validate(entry);
if (ret_val != 0)
return ret_val;
- entry->valid = 1;
- INIT_RCU_HEAD(&entry->rcu);
-
+ /* XXX - we can remove this RCU read lock as the spinlock protects the
+ * entire function, but before we do we need to fixup the
+ * netlbl_af[4,6]list RCU functions to do "the right thing" with
+ * respect to rcu_dereference() when only a spinlock is held. */
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;
+
+ 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->def.type == NETLBL_NLTYPE_ADDRSELECT) {
+ netlbl_af4list_foreach_rcu(iter4,
+ &entry->def.addrsel->list4)
+ netlbl_domhsh_audit_add(entry, iter4, NULL,
+ ret_val, audit_info);
+#if IS_ENABLED(CONFIG_IPV6)
+ netlbl_af6list_foreach_rcu(iter6,
+ &entry->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->def.type == NETLBL_NLTYPE_ADDRSELECT &&
+ entry->def.type == NETLBL_NLTYPE_ADDRSELECT) {
+ struct list_head *old_list4;
+ struct list_head *old_list6;
+
+ old_list4 = &entry_old->def.addrsel->list4;
+ old_list6 = &entry_old->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->def.addrsel->list4)
+ if (netlbl_af4list_search_exact(iter4->addr,
+ iter4->mask,
+ old_list4)) {
+ ret_val = -EEXIST;
+ goto add_return;
+ }
+#if IS_ENABLED(CONFIG_IPV6)
+ netlbl_af6list_foreach_rcu(iter6, &entry->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->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 IS_ENABLED(CONFIG_IPV6)
+ netlbl_af6list_foreach_safe(iter6, tmp6,
+ &entry->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;
}
@@ -303,44 +490,35 @@ 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;
if (entry != rcu_dereference(netlbl_domhsh_def))
list_del_rcu(&entry->list);
else
- rcu_assign_pointer(netlbl_domhsh_def, NULL);
- ret_val = 0;
- }
+ RCU_INIT_POINTER(netlbl_domhsh_def, NULL);
+ } else
+ ret_val = -ENOENT;
spin_unlock(&netlbl_domhsh_lock);
audit_buf = netlbl_audit_start_common(AUDIT_MAC_MAP_DEL, audit_info);
@@ -352,10 +530,122 @@ 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->def.type) {
+ case NETLBL_NLTYPE_ADDRSELECT:
+ netlbl_af4list_foreach_rcu(iter4,
+ &entry->def.addrsel->list4) {
+ map4 = netlbl_domhsh_addr4_entry(iter4);
+ cipso_v4_doi_putdef(map4->def.cipso);
+ }
+ /* 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->def.cipso);
+ break;
+ }
call_rcu(&entry->rcu, netlbl_domhsh_free_entry);
+ }
+
+ return ret_val;
+}
+
+/**
+ * netlbl_domhsh_remove_af4 - Removes an address selector entry
+ * @domain: the domain
+ * @addr: IPv4 address
+ * @mask: IPv4 address mask
+ * @audit_info: NetLabel audit information
+ *
+ * Description:
+ * Removes an individual address selector from a domain mapping and potentially
+ * the entire mapping if it is empty. Returns zero on success, negative values
+ * on failure.
+ *
+ */
+int netlbl_domhsh_remove_af4(const char *domain,
+ const struct in_addr *addr,
+ const struct in_addr *mask,
+ struct netlbl_audit *audit_info)
+{
+ struct netlbl_dom_map *entry_map;
+ struct netlbl_af4list *entry_addr;
+ struct netlbl_af4list *iter4;
+#if IS_ENABLED(CONFIG_IPV6)
+ struct netlbl_af6list *iter6;
+#endif /* IPv6 */
+ struct netlbl_domaddr4_map *entry;
+
+ rcu_read_lock();
+
+ if (domain)
+ entry_map = netlbl_domhsh_search(domain);
+ else
+ entry_map = netlbl_domhsh_search_def(domain);
+ if (entry_map == NULL ||
+ entry_map->def.type != NETLBL_NLTYPE_ADDRSELECT)
+ goto remove_af4_failure;
+
+ spin_lock(&netlbl_domhsh_lock);
+ entry_addr = netlbl_af4list_remove(addr->s_addr, mask->s_addr,
+ &entry_map->def.addrsel->list4);
+ spin_unlock(&netlbl_domhsh_lock);
+
+ if (entry_addr == NULL)
+ goto remove_af4_failure;
+ netlbl_af4list_foreach_rcu(iter4, &entry_map->def.addrsel->list4)
+ goto remove_af4_single_addr;
+#if IS_ENABLED(CONFIG_IPV6)
+ netlbl_af6list_foreach_rcu(iter6, &entry_map->def.addrsel->list6)
+ goto remove_af4_single_addr;
+#endif /* IPv6 */
+ /* the domain mapping is empty so remove it from the mapping table */
+ netlbl_domhsh_remove_entry(entry_map, audit_info);
+
+remove_af4_single_addr:
+ rcu_read_unlock();
+ /* yick, we can't use call_rcu here because we don't have a rcu head
+ * pointer but hopefully this should be a rare case so the pause
+ * shouldn't be a problem */
+ synchronize_rcu();
+ entry = netlbl_domhsh_addr4_entry(entry_addr);
+ cipso_v4_doi_putdef(entry->def.cipso);
+ kfree(entry);
+ return 0;
+
+remove_af4_failure:
+ rcu_read_unlock();
+ return -ENOENT;
+}
+
+/**
+ * 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;
}
@@ -380,7 +670,7 @@ int netlbl_domhsh_remove_default(struct netlbl_audit *audit_info)
*
* Description:
* Look through the domain hash table searching for an entry to match @domain,
- * return a pointer to a copy of the entry or NULL. The caller is responsibile
+ * return a pointer to a copy of the entry or NULL. The caller is responsible
* for ensuring that rcu_read_[un]lock() is called.
*
*/
@@ -390,6 +680,66 @@ struct netlbl_dom_map *netlbl_domhsh_getentry(const char *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_dommap_def *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->def.type != NETLBL_NLTYPE_ADDRSELECT)
+ return &dom_iter->def;
+ addr_iter = netlbl_af4list_search(addr, &dom_iter->def.addrsel->list4);
+ if (addr_iter == NULL)
+ return NULL;
+ return &(netlbl_domhsh_addr4_entry(addr_iter)->def);
+}
+
+#if IS_ENABLED(CONFIG_IPV6)
+/**
+ * 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_dommap_def *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->def.type != NETLBL_NLTYPE_ADDRSELECT)
+ return &dom_iter->def;
+ addr_iter = netlbl_af6list_search(addr, &dom_iter->def.addrsel->list6);
+ if (addr_iter == NULL)
+ return NULL;
+ return &(netlbl_domhsh_addr6_entry(addr_iter)->def);
+}
+#endif /* IPv6 */
+
+/**
* netlbl_domhsh_walk - Iterate through the domain mapping hash table
* @skip_bkt: the number of buckets to skip at the start
* @skip_chain: the number of entries to skip in the first iterated bucket
@@ -401,7 +751,7 @@ struct netlbl_dom_map *netlbl_domhsh_getentry(const char *domain)
* buckets and @skip_chain entries. For each entry in the table call
* @callback, if @callback returns a negative value stop 'walking' through the
* table and return. Updates the values in @skip_bkt and @skip_chain on
- * return. Returns zero on succcess, negative values on failure.
+ * return. Returns zero on success, negative values on failure.
*
*/
int netlbl_domhsh_walk(u32 *skip_bkt,
@@ -411,6 +761,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;
@@ -418,9 +769,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;
diff --git a/net/netlabel/netlabel_domainhash.h b/net/netlabel/netlabel_domainhash.h
index 8220990ceb9..680caf4dff5 100644
--- a/net/netlabel/netlabel_domainhash.h
+++ b/net/netlabel/netlabel_domainhash.h
@@ -6,12 +6,12 @@
* system manages static and dynamic label mappings for network protocols such
* as CIPSO and RIPSO.
*
- * Author: Paul Moore <paul.moore@hp.com>
+ * Author: Paul Moore <paul@paul-moore.com>
*
*/
/*
- * (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
@@ -24,8 +24,7 @@
* 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
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
*
*/
@@ -36,17 +35,42 @@
#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 */
-struct netlbl_dom_map {
- char *domain;
+/* Domain mapping definition structures */
+struct netlbl_domaddr_map {
+ struct list_head list4;
+ struct list_head list6;
+};
+struct netlbl_dommap_def {
u32 type;
union {
- struct cipso_v4_doi *cipsov4;
- } type_def;
+ struct netlbl_domaddr_map *addrsel;
+ struct cipso_v4_doi *cipso;
+ };
+};
+#define netlbl_domhsh_addr4_entry(iter) \
+ container_of(iter, struct netlbl_domaddr4_map, list)
+struct netlbl_domaddr4_map {
+ struct netlbl_dommap_def def;
+
+ struct netlbl_af4list list;
+};
+#define netlbl_domhsh_addr6_entry(iter) \
+ container_of(iter, struct netlbl_domaddr6_map, list)
+struct netlbl_domaddr6_map {
+ struct netlbl_dommap_def def;
+
+ struct netlbl_af6list list;
+};
+
+struct netlbl_dom_map {
+ char *domain;
+ struct netlbl_dommap_def def;
u32 valid;
struct list_head list;
@@ -61,9 +85,22 @@ 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_af4(const char *domain,
+ const struct in_addr *addr,
+ const struct in_addr *mask,
+ 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_dommap_def *netlbl_domhsh_getentry_af4(const char *domain,
+ __be32 addr);
+#if IS_ENABLED(CONFIG_IPV6)
+struct netlbl_dommap_def *netlbl_domhsh_getentry_af6(const char *domain,
+ const struct in6_addr *addr);
+#endif /* IPv6 */
+
int netlbl_domhsh_walk(u32 *skip_bkt,
u32 *skip_chain,
int (*callback) (struct netlbl_dom_map *entry, void *arg),
diff --git a/net/netlabel/netlabel_kapi.c b/net/netlabel/netlabel_kapi.c
index 39793a1a93a..3045a964f39 100644
--- a/net/netlabel/netlabel_kapi.c
+++ b/net/netlabel/netlabel_kapi.c
@@ -5,12 +5,12 @@
* system manages static and dynamic label mappings for network protocols such
* as CIPSO and RIPSO.
*
- * Author: Paul Moore <paul.moore@hp.com>
+ * Author: Paul Moore <paul@paul-moore.com>
*
*/
/*
- * (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
@@ -23,25 +23,29 @@
* 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
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
*
*/
#include <linux/init.h>
#include <linux/types.h>
+#include <linux/slab.h>
#include <linux/audit.h>
+#include <linux/in.h>
+#include <linux/in6.h>
#include <net/ip.h>
+#include <net/ipv6.h>
#include <net/netlabel.h>
#include <net/cipso_ipv4.h>
#include <asm/bug.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
#include "netlabel_domainhash.h"
#include "netlabel_unlabeled.h"
#include "netlabel_cipso_v4.h"
#include "netlabel_user.h"
#include "netlabel_mgmt.h"
+#include "netlabel_addrlist.h"
/*
* Configuration Functions
@@ -50,6 +54,9 @@
/**
* netlbl_cfg_map_del - Remove a NetLabel/LSM domain mapping
* @domain: the domain mapping to remove
+ * @family: address family
+ * @addr: IP address
+ * @mask: IP address mask
* @audit_info: NetLabel audit information
*
* Description:
@@ -58,14 +65,32 @@
* values on failure.
*
*/
-int netlbl_cfg_map_del(const char *domain, struct netlbl_audit *audit_info)
+int netlbl_cfg_map_del(const char *domain,
+ u16 family,
+ const void *addr,
+ const void *mask,
+ struct netlbl_audit *audit_info)
{
- return netlbl_domhsh_remove(domain, audit_info);
+ if (addr == NULL && mask == NULL) {
+ return netlbl_domhsh_remove(domain, audit_info);
+ } else if (addr != NULL && mask != NULL) {
+ switch (family) {
+ case AF_INET:
+ return netlbl_domhsh_remove_af4(domain, addr, mask,
+ audit_info);
+ default:
+ return -EPFNOSUPPORT;
+ }
+ } else
+ return -EINVAL;
}
/**
- * netlbl_cfg_unlbl_add_map - Add an unlabeled NetLabel/LSM domain mapping
+ * netlbl_cfg_unlbl_map_add - Add a new unlabeled mapping
* @domain: the domain mapping to add
+ * @family: address family
+ * @addr: IP address
+ * @mask: IP address mask
* @audit_info: NetLabel audit information
*
* Description:
@@ -74,150 +99,308 @@ int netlbl_cfg_map_del(const char *domain, struct netlbl_audit *audit_info)
* negative values on failure.
*
*/
-int netlbl_cfg_unlbl_add_map(const char *domain,
+int netlbl_cfg_unlbl_map_add(const char *domain,
+ u16 family,
+ const void *addr,
+ const void *mask,
struct netlbl_audit *audit_info)
{
int ret_val = -ENOMEM;
struct netlbl_dom_map *entry;
+ struct netlbl_domaddr_map *addrmap = NULL;
+ struct netlbl_domaddr4_map *map4 = NULL;
+ struct netlbl_domaddr6_map *map6 = NULL;
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)
- goto cfg_unlbl_add_map_failure;
+ goto cfg_unlbl_map_add_failure;
+ }
+
+ if (addr == NULL && mask == NULL)
+ entry->def.type = NETLBL_NLTYPE_UNLABELED;
+ else if (addr != NULL && mask != NULL) {
+ addrmap = kzalloc(sizeof(*addrmap), GFP_ATOMIC);
+ if (addrmap == NULL)
+ goto cfg_unlbl_map_add_failure;
+ INIT_LIST_HEAD(&addrmap->list4);
+ INIT_LIST_HEAD(&addrmap->list6);
+
+ switch (family) {
+ case AF_INET: {
+ const struct in_addr *addr4 = addr;
+ const struct in_addr *mask4 = mask;
+ map4 = kzalloc(sizeof(*map4), GFP_ATOMIC);
+ if (map4 == NULL)
+ goto cfg_unlbl_map_add_failure;
+ map4->def.type = NETLBL_NLTYPE_UNLABELED;
+ map4->list.addr = addr4->s_addr & mask4->s_addr;
+ map4->list.mask = mask4->s_addr;
+ map4->list.valid = 1;
+ ret_val = netlbl_af4list_add(&map4->list,
+ &addrmap->list4);
+ if (ret_val != 0)
+ goto cfg_unlbl_map_add_failure;
+ break;
+ }
+#if IS_ENABLED(CONFIG_IPV6)
+ case AF_INET6: {
+ const struct in6_addr *addr6 = addr;
+ const struct in6_addr *mask6 = mask;
+ map6 = kzalloc(sizeof(*map6), GFP_ATOMIC);
+ if (map6 == NULL)
+ goto cfg_unlbl_map_add_failure;
+ map6->def.type = NETLBL_NLTYPE_UNLABELED;
+ map6->list.addr = *addr6;
+ map6->list.addr.s6_addr32[0] &= mask6->s6_addr32[0];
+ map6->list.addr.s6_addr32[1] &= mask6->s6_addr32[1];
+ map6->list.addr.s6_addr32[2] &= mask6->s6_addr32[2];
+ map6->list.addr.s6_addr32[3] &= mask6->s6_addr32[3];
+ map6->list.mask = *mask6;
+ map6->list.valid = 1;
+ ret_val = netlbl_af6list_add(&map6->list,
+ &addrmap->list6);
+ if (ret_val != 0)
+ goto cfg_unlbl_map_add_failure;
+ break;
+ }
+#endif /* IPv6 */
+ default:
+ goto cfg_unlbl_map_add_failure;
+ break;
+ }
+
+ entry->def.addrsel = addrmap;
+ entry->def.type = NETLBL_NLTYPE_ADDRSELECT;
+ } else {
+ ret_val = -EINVAL;
+ goto cfg_unlbl_map_add_failure;
}
- entry->type = NETLBL_NLTYPE_UNLABELED;
ret_val = netlbl_domhsh_add(entry, audit_info);
if (ret_val != 0)
- goto cfg_unlbl_add_map_failure;
+ goto cfg_unlbl_map_add_failure;
return 0;
-cfg_unlbl_add_map_failure:
- if (entry != NULL)
- kfree(entry->domain);
+cfg_unlbl_map_add_failure:
+ kfree(entry->domain);
kfree(entry);
+ kfree(addrmap);
+ kfree(map4);
+ kfree(map6);
return ret_val;
}
+
/**
- * netlbl_cfg_cipsov4_add - Add a new CIPSOv4 DOI definition
- * @doi_def: the DOI definition
+ * netlbl_cfg_unlbl_static_add - Adds a new static label
+ * @net: network namespace
+ * @dev_name: interface name
+ * @addr: IP address in network byte order (struct in[6]_addr)
+ * @mask: address mask in network byte order (struct in[6]_addr)
+ * @family: address family
+ * @secid: LSM secid value for the entry
* @audit_info: NetLabel audit information
*
* Description:
- * Add a new CIPSOv4 DOI definition to the NetLabel subsystem. Returns zero on
- * success, negative values on failure.
+ * Adds a new NetLabel static label to be used when protocol provided labels
+ * are not present on incoming traffic. If @dev_name is NULL then the default
+ * interface will be used. 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 netlbl_cfg_unlbl_static_add(struct net *net,
+ const char *dev_name,
+ const void *addr,
+ const void *mask,
+ u16 family,
+ u32 secid,
+ struct netlbl_audit *audit_info)
{
- int ret_val;
- const char *type_str;
- struct audit_buffer *audit_buf;
+ u32 addr_len;
- ret_val = cipso_v4_doi_add(doi_def);
+ switch (family) {
+ case AF_INET:
+ addr_len = sizeof(struct in_addr);
+ break;
+#if IS_ENABLED(CONFIG_IPV6)
+ case AF_INET6:
+ addr_len = sizeof(struct in6_addr);
+ break;
+#endif /* IPv6 */
+ default:
+ return -EPFNOSUPPORT;
+ }
- 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 netlbl_unlhsh_add(net,
+ dev_name, addr, mask, addr_len,
+ secid, audit_info);
+}
+
+/**
+ * netlbl_cfg_unlbl_static_del - Removes an existing static label
+ * @net: network namespace
+ * @dev_name: interface name
+ * @addr: IP address in network byte order (struct in[6]_addr)
+ * @mask: address mask in network byte order (struct in[6]_addr)
+ * @family: address family
+ * @secid: LSM secid value for the entry
+ * @audit_info: NetLabel audit information
+ *
+ * Description:
+ * Removes an existing NetLabel static label used when protocol provided labels
+ * are not present on incoming traffic. If @dev_name is NULL then the default
+ * interface will be used. Returns zero on success, negative values on failure.
+ *
+ */
+int netlbl_cfg_unlbl_static_del(struct net *net,
+ const char *dev_name,
+ const void *addr,
+ const void *mask,
+ u16 family,
+ struct netlbl_audit *audit_info)
+{
+ u32 addr_len;
+
+ switch (family) {
+ case AF_INET:
+ addr_len = sizeof(struct in_addr);
+ break;
+#if IS_ENABLED(CONFIG_IPV6)
+ case AF_INET6:
+ addr_len = sizeof(struct in6_addr);
+ break;
+#endif /* IPv6 */
+ default:
+ return -EPFNOSUPPORT;
}
- return ret_val;
+ return netlbl_unlhsh_remove(net,
+ dev_name, addr, mask, addr_len,
+ audit_info);
+}
+
+/**
+ * netlbl_cfg_cipsov4_add - Add a new CIPSOv4 DOI definition
+ * @doi_def: CIPSO DOI definition
+ * @audit_info: NetLabel audit information
+ *
+ * Description:
+ * Add a new CIPSO DOI definition as defined by @doi_def. Returns zero on
+ * success and negative values on failure.
+ *
+ */
+int netlbl_cfg_cipsov4_add(struct cipso_v4_doi *doi_def,
+ struct netlbl_audit *audit_info)
+{
+ return cipso_v4_doi_add(doi_def, audit_info);
}
/**
- * netlbl_cfg_cipsov4_add_map - Add a new CIPSOv4 DOI definition and mapping
- * @doi_def: the DOI definition
+ * netlbl_cfg_cipsov4_del - Remove an existing CIPSOv4 DOI definition
+ * @doi: CIPSO DOI
+ * @audit_info: NetLabel audit information
+ *
+ * Description:
+ * Remove an existing CIPSO DOI definition matching @doi. Returns zero on
+ * success and negative values on failure.
+ *
+ */
+void netlbl_cfg_cipsov4_del(u32 doi, struct netlbl_audit *audit_info)
+{
+ cipso_v4_doi_remove(doi, audit_info);
+}
+
+/**
+ * netlbl_cfg_cipsov4_map_add - Add a new CIPSOv4 DOI mapping
+ * @doi: the CIPSO DOI
* @domain: the domain mapping to add
+ * @addr: IP address
+ * @mask: IP address mask
* @audit_info: NetLabel audit information
*
* Description:
- * Add a new CIPSOv4 DOI definition and NetLabel/LSM domain mapping for this
- * new DOI definition to the NetLabel subsystem. A @domain value of NULL adds
- * a new default domain mapping. Returns zero on success, negative values on
- * failure.
+ * Add a new NetLabel/LSM domain mapping for the given CIPSO DOI to the NetLabel
+ * subsystem. A @domain value of NULL adds a new default domain mapping.
+ * Returns zero on success, negative values on failure.
*
*/
-int netlbl_cfg_cipsov4_add_map(struct cipso_v4_doi *doi_def,
+int netlbl_cfg_cipsov4_map_add(u32 doi,
const char *domain,
+ const struct in_addr *addr,
+ const struct in_addr *mask,
struct netlbl_audit *audit_info)
{
int ret_val = -ENOMEM;
+ struct cipso_v4_doi *doi_def;
struct netlbl_dom_map *entry;
+ struct netlbl_domaddr_map *addrmap = NULL;
+ struct netlbl_domaddr4_map *addrinfo = NULL;
+
+ doi_def = cipso_v4_doi_getdef(doi);
+ if (doi_def == NULL)
+ return -ENOENT;
entry = kzalloc(sizeof(*entry), GFP_ATOMIC);
if (entry == NULL)
- goto cfg_cipsov4_add_map_failure;
+ goto out_entry;
if (domain != NULL) {
entry->domain = kstrdup(domain, GFP_ATOMIC);
if (entry->domain == NULL)
- goto cfg_cipsov4_add_map_failure;
+ goto out_domain;
}
- 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. */
+ if (addr == NULL && mask == NULL) {
+ entry->def.cipso = doi_def;
+ entry->def.type = NETLBL_NLTYPE_CIPSOV4;
+ } else if (addr != NULL && mask != NULL) {
+ addrmap = kzalloc(sizeof(*addrmap), GFP_ATOMIC);
+ if (addrmap == NULL)
+ goto out_addrmap;
+ INIT_LIST_HEAD(&addrmap->list4);
+ INIT_LIST_HEAD(&addrmap->list6);
+
+ addrinfo = kzalloc(sizeof(*addrinfo), GFP_ATOMIC);
+ if (addrinfo == NULL)
+ goto out_addrinfo;
+ addrinfo->def.cipso = doi_def;
+ addrinfo->def.type = NETLBL_NLTYPE_CIPSOV4;
+ addrinfo->list.addr = addr->s_addr & mask->s_addr;
+ addrinfo->list.mask = mask->s_addr;
+ addrinfo->list.valid = 1;
+ ret_val = netlbl_af4list_add(&addrinfo->list, &addrmap->list4);
+ if (ret_val != 0)
+ goto cfg_cipsov4_map_add_failure;
+
+ entry->def.addrsel = addrmap;
+ entry->def.type = NETLBL_NLTYPE_ADDRSELECT;
+ } else {
+ ret_val = -EINVAL;
+ goto out_addrmap;
+ }
- rcu_read_lock();
- ret_val = netlbl_cfg_cipsov4_add(doi_def, audit_info);
- if (ret_val != 0)
- goto cfg_cipsov4_add_map_failure_unlock;
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_map_add_failure;
return 0;
-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();
-cfg_cipsov4_add_map_failure:
- if (entry != NULL)
- kfree(entry->domain);
+cfg_cipsov4_map_add_failure:
+ kfree(addrinfo);
+out_addrinfo:
+ kfree(addrmap);
+out_addrmap:
+ kfree(entry->domain);
+out_domain:
kfree(entry);
+out_entry:
+ cipso_v4_doi_putdef(doi_def);
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);
-}
-
/*
* Security Attribute Functions
*/
@@ -413,7 +596,7 @@ int netlbl_secattr_catmap_setrng(struct netlbl_lsm_secattr_catmap *catmap,
iter = iter->next;
iter_max_spot = iter->startbit + NETLBL_CATMAP_SIZE;
}
- ret_val = netlbl_secattr_catmap_setbit(iter, spot, GFP_ATOMIC);
+ ret_val = netlbl_secattr_catmap_setbit(iter, spot, flags);
}
return ret_val;
@@ -444,38 +627,60 @@ int netlbl_enabled(void)
}
/**
- * netlbl_socket_setattr - Label a socket using the correct protocol
+ * netlbl_sock_setattr - Label a socket using the correct protocol
* @sk: the socket to label
+ * @family: protocol family
* @secattr: the security attributes
*
* Description:
* 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,
+ u16 family,
const struct netlbl_lsm_secattr *secattr)
{
- int ret_val = -ENOENT;
+ int ret_val;
struct netlbl_dom_map *dom_entry;
rcu_read_lock();
dom_entry = netlbl_domhsh_getentry(secattr->domain);
- if (dom_entry == NULL)
+ if (dom_entry == NULL) {
+ ret_val = -ENOENT;
goto socket_setattr_return;
- switch (dom_entry->type) {
- case NETLBL_NLTYPE_CIPSOV4:
- ret_val = cipso_v4_sock_setattr(sk,
- dom_entry->type_def.cipsov4,
- secattr);
+ }
+ switch (family) {
+ case AF_INET:
+ switch (dom_entry->def.type) {
+ case NETLBL_NLTYPE_ADDRSELECT:
+ ret_val = -EDESTADDRREQ;
+ break;
+ case NETLBL_NLTYPE_CIPSOV4:
+ ret_val = cipso_v4_sock_setattr(sk,
+ dom_entry->def.cipso,
+ secattr);
+ break;
+ case NETLBL_NLTYPE_UNLABELED:
+ ret_val = 0;
+ break;
+ default:
+ ret_val = -ENOENT;
+ }
break;
- case NETLBL_NLTYPE_UNLABELED:
+#if IS_ENABLED(CONFIG_IPV6)
+ 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 = -ENOENT;
+ ret_val = -EPROTONOSUPPORT;
}
socket_setattr_return:
@@ -484,6 +689,20 @@ socket_setattr_return:
}
/**
+ * 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
* @secattr: the security attributes
@@ -495,9 +714,213 @@ socket_setattr_return:
* on failure.
*
*/
-int netlbl_sock_getattr(struct sock *sk, struct netlbl_lsm_secattr *secattr)
+int netlbl_sock_getattr(struct sock *sk,
+ struct netlbl_lsm_secattr *secattr)
+{
+ int ret_val;
+
+ switch (sk->sk_family) {
+ case AF_INET:
+ ret_val = cipso_v4_sock_getattr(sk, secattr);
+ break;
+#if IS_ENABLED(CONFIG_IPV6)
+ case AF_INET6:
+ ret_val = -ENOMSG;
+ break;
+#endif /* IPv6 */
+ default:
+ ret_val = -EPROTONOSUPPORT;
+ }
+
+ return ret_val;
+}
+
+/**
+ * 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)
{
- return cipso_v4_sock_getattr(sk, secattr);
+ int ret_val;
+ struct sockaddr_in *addr4;
+ struct netlbl_dommap_def *entry;
+
+ rcu_read_lock();
+ switch (addr->sa_family) {
+ case AF_INET:
+ addr4 = (struct sockaddr_in *)addr;
+ entry = netlbl_domhsh_getentry_af4(secattr->domain,
+ addr4->sin_addr.s_addr);
+ if (entry == NULL) {
+ ret_val = -ENOENT;
+ goto conn_setattr_return;
+ }
+ switch (entry->type) {
+ case NETLBL_NLTYPE_CIPSOV4:
+ ret_val = cipso_v4_sock_setattr(sk,
+ entry->cipso, 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 IS_ENABLED(CONFIG_IPV6)
+ 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 = -EPROTONOSUPPORT;
+ }
+
+conn_setattr_return:
+ rcu_read_unlock();
+ return ret_val;
+}
+
+/**
+ * netlbl_req_setattr - Label a request socket using the correct protocol
+ * @req: the request socket to label
+ * @secattr: the security attributes
+ *
+ * Description:
+ * Attach the correct label to the given socket using the security attributes
+ * specified in @secattr. Returns zero on success, negative values on failure.
+ *
+ */
+int netlbl_req_setattr(struct request_sock *req,
+ const struct netlbl_lsm_secattr *secattr)
+{
+ int ret_val;
+ struct netlbl_dommap_def *entry;
+
+ rcu_read_lock();
+ switch (req->rsk_ops->family) {
+ case AF_INET:
+ entry = netlbl_domhsh_getentry_af4(secattr->domain,
+ inet_rsk(req)->ir_rmt_addr);
+ if (entry == NULL) {
+ ret_val = -ENOENT;
+ goto req_setattr_return;
+ }
+ switch (entry->type) {
+ case NETLBL_NLTYPE_CIPSOV4:
+ ret_val = cipso_v4_req_setattr(req,
+ entry->cipso, 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_req_delattr(req);
+ ret_val = 0;
+ break;
+ default:
+ ret_val = -ENOENT;
+ }
+ break;
+#if IS_ENABLED(CONFIG_IPV6)
+ 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 = -EPROTONOSUPPORT;
+ }
+
+req_setattr_return:
+ rcu_read_unlock();
+ return ret_val;
+}
+
+/**
+* netlbl_req_delattr - Delete all the NetLabel labels on a socket
+* @req: the socket
+*
+* Description:
+* Remove all the NetLabel labeling from @req.
+*
+*/
+void netlbl_req_delattr(struct request_sock *req)
+{
+ cipso_v4_req_delattr(req);
+}
+
+/**
+ * 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_dommap_def *entry;
+
+ rcu_read_lock();
+ switch (family) {
+ case AF_INET:
+ hdr4 = ip_hdr(skb);
+ entry = netlbl_domhsh_getentry_af4(secattr->domain,hdr4->daddr);
+ if (entry == NULL) {
+ ret_val = -ENOENT;
+ goto skbuff_setattr_return;
+ }
+ switch (entry->type) {
+ case NETLBL_NLTYPE_CIPSOV4:
+ ret_val = cipso_v4_skbuff_setattr(skb, entry->cipso,
+ 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 IS_ENABLED(CONFIG_IPV6)
+ 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 = -EPROTONOSUPPORT;
+ }
+
+skbuff_setattr_return:
+ rcu_read_unlock();
+ return ret_val;
}
/**
@@ -517,9 +940,17 @@ int netlbl_skbuff_getattr(const struct sk_buff *skb,
u16 family,
struct netlbl_lsm_secattr *secattr)
{
- if (CIPSO_V4_OPTEXIST(skb) &&
- cipso_v4_skbuff_getattr(skb, secattr) == 0)
- return 0;
+ switch (family) {
+ case AF_INET:
+ if (CIPSO_V4_OPTEXIST(skb) &&
+ cipso_v4_skbuff_getattr(skb, secattr) == 0)
+ return 0;
+ break;
+#if IS_ENABLED(CONFIG_IPV6)
+ case AF_INET6:
+ break;
+#endif /* IPv6 */
+ }
return netlbl_unlabel_getattr(skb, family, secattr);
}
@@ -528,6 +959,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 +967,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);
}
/**
@@ -579,6 +1011,28 @@ int netlbl_cache_add(const struct sk_buff *skb,
}
/*
+ * Protocol Engine Functions
+ */
+
+/**
+ * netlbl_audit_start - Start an audit message
+ * @type: audit message type
+ * @audit_info: NetLabel audit information
+ *
+ * Description:
+ * Start an audit message using the type specified in @type and fill the audit
+ * message with some fields common to all NetLabel audit messages. This
+ * function should only be used by protocol engines, not LSMs. Returns a
+ * pointer to the audit buffer on success, NULL on failure.
+ *
+ */
+struct audit_buffer *netlbl_audit_start(int type,
+ struct netlbl_audit *audit_info)
+{
+ return netlbl_audit_start_common(type, audit_info);
+}
+
+/*
* Setup Functions
*/
diff --git a/net/netlabel/netlabel_mgmt.c b/net/netlabel/netlabel_mgmt.c
index 22c19126780..e66e977ef2f 100644
--- a/net/netlabel/netlabel_mgmt.c
+++ b/net/netlabel/netlabel_mgmt.c
@@ -5,12 +5,12 @@
* NetLabel system manages static and dynamic label mappings for network
* protocols such as CIPSO and RIPSO.
*
- * Author: Paul Moore <paul.moore@hp.com>
+ * Author: Paul Moore <paul@paul-moore.com>
*
*/
/*
- * (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
@@ -23,8 +23,7 @@
* 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
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
*
*/
@@ -32,12 +31,17 @@
#include <linux/socket.h>
#include <linux/string.h>
#include <linux/skbuff.h>
+#include <linux/in.h>
+#include <linux/in6.h>
+#include <linux/slab.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>
+#include <linux/atomic.h>
#include "netlabel_domainhash.h"
#include "netlabel_user.h"
@@ -71,86 +75,335 @@ 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->def.type = nla_get_u32(info->attrs[NLBL_MGMT_A_PROTOCOL]);
+ 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);
}
- entry->type = nla_get_u32(info->attrs[NLBL_MGMT_A_PROTOCOL]);
- nla_strlcpy(entry->domain, info->attrs[NLBL_MGMT_A_DOMAIN], tmp_size);
- switch (entry->type) {
+ /* NOTE: internally we allow/use a entry->def.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->def.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->def.cipso = 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->def.type = entry->def.type;
+ if (cipsov4)
+ map->def.cipso = cipsov4;
+
+ ret_val = netlbl_af4list_add(&map->list, &addrmap->list4);
+ if (ret_val != 0) {
+ kfree(map);
+ goto add_failure;
+ }
+
+ entry->def.type = NETLBL_NLTYPE_ADDRSELECT;
+ entry->def.addrsel = addrmap;
+#if IS_ENABLED(CONFIG_IPV6)
+ } 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;
+ }
+ 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];
+ map->list.mask = *mask;
+ map->list.valid = 1;
+ map->def.type = entry->def.type;
+
+ ret_val = netlbl_af6list_add(&map->list, &addrmap->list6);
+ if (ret_val != 0) {
+ kfree(map);
+ goto add_failure;
+ }
+
+ entry->def.type = NETLBL_NLTYPE_ADDRSELECT;
+ entry->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 responsible 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 = 0;
+ struct nlattr *nla_a;
+ struct nlattr *nla_b;
+ struct netlbl_af4list *iter4;
+#if IS_ENABLED(CONFIG_IPV6)
+ 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->def.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->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->def.type);
+ if (ret_val != 0)
+ return ret_val;
+ switch (map4->def.type) {
+ case NETLBL_NLTYPE_CIPSOV4:
+ ret_val = nla_put_u32(skb, NLBL_MGMT_A_CV4DOI,
+ map4->def.cipso->doi);
+ if (ret_val != 0)
+ return ret_val;
+ break;
+ }
+
+ nla_nest_end(skb, nla_b);
+ }
+#if IS_ENABLED(CONFIG_IPV6)
+ netlbl_af6list_foreach_rcu(iter6, &entry->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->def.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->def.type);
+ break;
+ case NETLBL_NLTYPE_CIPSOV4:
+ ret_val = nla_put_u32(skb,NLBL_MGMT_A_PROTOCOL,entry->def.type);
+ if (ret_val != 0)
+ return ret_val;
+ ret_val = nla_put_u32(skb, NLBL_MGMT_A_CV4DOI,
+ entry->def.cipso->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
* @info: the Generic NETLINK info block
@@ -192,29 +445,15 @@ static int netlbl_mgmt_listall_cb(struct netlbl_dom_map *entry, void *arg)
struct netlbl_domhsh_walk_arg *cb_arg = arg;
void *data;
- data = genlmsg_put(cb_arg->skb, NETLINK_CB(cb_arg->nl_cb->skb).pid,
+ data = genlmsg_put(cb_arg->skb, NETLINK_CB(cb_arg->nl_cb->skb).portid,
cb_arg->seq, &netlbl_mgmt_gnl_family,
NLM_F_MULTI, NLBL_MGMT_C_LISTALL);
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 +507,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,26 +576,13 @@ 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();
-
- genlmsg_end(ans_skb, data);
-
- ret_val = genlmsg_reply(ans_skb, info);
if (ret_val != 0)
goto listdef_failure;
- return 0;
+
+ genlmsg_end(ans_skb, data);
+ return genlmsg_reply(ans_skb, info);
listdef_failure_lock:
rcu_read_unlock();
@@ -402,7 +594,6 @@ listdef_failure:
/**
* netlbl_mgmt_protocols_cb - Write an individual PROTOCOL message response
* @skb: the skb to write to
- * @seq: the NETLINK sequence number
* @cb: the NETLINK callback
* @protocol: the NetLabel protocol to use in the message
*
@@ -419,7 +610,7 @@ static int netlbl_mgmt_protocols_cb(struct sk_buff *skb,
int ret_val = -ENOMEM;
void *data;
- data = genlmsg_put(skb, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq,
+ data = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq,
&netlbl_mgmt_gnl_family, NLM_F_MULTI,
NLBL_MGMT_C_PROTOCOLS);
if (data == NULL)
@@ -501,11 +692,7 @@ static int netlbl_mgmt_version(struct sk_buff *skb, struct genl_info *info)
goto version_failure;
genlmsg_end(ans_skb, data);
-
- ret_val = genlmsg_reply(ans_skb, info);
- if (ret_val != 0)
- goto version_failure;
- return 0;
+ return genlmsg_reply(ans_skb, info);
version_failure:
kfree_skb(ans_skb);
@@ -517,7 +704,7 @@ version_failure:
* NetLabel Generic NETLINK Command Definitions
*/
-static struct genl_ops netlbl_mgmt_genl_ops[] = {
+static const struct genl_ops netlbl_mgmt_genl_ops[] = {
{
.cmd = NLBL_MGMT_C_ADD,
.flags = GENL_ADMIN_PERM,
@@ -590,18 +777,6 @@ static struct genl_ops netlbl_mgmt_genl_ops[] = {
*/
int __init netlbl_mgmt_genl_init(void)
{
- int ret_val, i;
-
- ret_val = genl_register_family(&netlbl_mgmt_gnl_family);
- if (ret_val != 0)
- return ret_val;
-
- for (i = 0; i < ARRAY_SIZE(netlbl_mgmt_genl_ops); i++) {
- ret_val = genl_register_ops(&netlbl_mgmt_gnl_family,
- &netlbl_mgmt_genl_ops[i]);
- if (ret_val != 0)
- return ret_val;
- }
-
- return 0;
+ return genl_register_family_with_ops(&netlbl_mgmt_gnl_family,
+ netlbl_mgmt_genl_ops);
}
diff --git a/net/netlabel/netlabel_mgmt.h b/net/netlabel/netlabel_mgmt.h
index a43bff169d6..8b6e1ab62b4 100644
--- a/net/netlabel/netlabel_mgmt.h
+++ b/net/netlabel/netlabel_mgmt.h
@@ -5,7 +5,7 @@
* NetLabel system manages static and dynamic label mappings for network
* protocols such as CIPSO and RIPSO.
*
- * Author: Paul Moore <paul.moore@hp.com>
+ * Author: Paul Moore <paul@paul-moore.com>
*
*/
@@ -23,8 +23,7 @@
* 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
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
*
*/
@@ -32,7 +31,7 @@
#define _NETLABEL_MGMT_H
#include <net/netlabel.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
/*
* The following NetLabel payloads are supported by the management interface.
@@ -45,6 +44,16 @@
* 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
@@ -68,13 +77,24 @@
* 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
@@ -100,15 +120,23 @@
* 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
@@ -144,7 +172,6 @@ enum {
NLBL_MGMT_C_VERSION,
__NLBL_MGMT_C_MAX,
};
-#define NLBL_MGMT_C_MAX (__NLBL_MGMT_C_MAX - 1)
/* NetLabel Management attributes */
enum {
@@ -162,6 +189,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)
diff --git a/net/netlabel/netlabel_unlabeled.c b/net/netlabel/netlabel_unlabeled.c
index 4478f2f6079..78a63c18779 100644
--- a/net/netlabel/netlabel_unlabeled.c
+++ b/net/netlabel/netlabel_unlabeled.c
@@ -5,12 +5,12 @@
* NetLabel system. The NetLabel system manages static and dynamic label
* mappings for network protocols such as CIPSO and RIPSO.
*
- * Author: Paul Moore <paul.moore@hp.com>
+ * Author: Paul Moore <paul@paul-moore.com>
*
*/
/*
- * (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
@@ -23,8 +23,7 @@
* 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
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
*
*/
@@ -43,6 +42,7 @@
#include <linux/notifier.h>
#include <linux/netdevice.h>
#include <linux/security.h>
+#include <linux/slab.h>
#include <net/sock.h>
#include <net/netlink.h>
#include <net/genetlink.h>
@@ -51,9 +51,10 @@
#include <net/net_namespace.h>
#include <net/netlabel.h>
#include <asm/bug.h>
-#include <asm/atomic.h>
+#include <linux/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 {
@@ -115,6 +114,8 @@ struct netlbl_unlhsh_walk_arg {
/* updates should be so rare that having one spinlock for the entire
* hash table should be okay */
static DEFINE_SPINLOCK(netlbl_unlhsh_lock);
+#define netlbl_unlhsh_rcu_deref(p) \
+ rcu_dereference_check(p, lockdep_is_held(&netlbl_unlhsh_lock))
static struct netlbl_unlhsh_tbl *netlbl_unlhsh = NULL;
static struct netlbl_unlhsh_iface *netlbl_unlhsh_def = NULL;
@@ -147,118 +148,10 @@ static const struct nla_policy netlbl_unlabel_genl_policy[NLBL_UNLABEL_A_MAX + 1
};
/*
- * 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
*/
/**
- * netlbl_unlhsh_free_addr4 - Frees an IPv4 address entry from the hash table
- * @entry: the entry's RCU field
- *
- * Description:
- * This function is designed to be used as a callback to the call_rcu()
- * function so that memory allocated to a hash table address entry can be
- * released safely.
- *
- */
-static void netlbl_unlhsh_free_addr4(struct rcu_head *entry)
-{
- struct netlbl_unlhsh_addr4 *ptr;
-
- ptr = container_of(entry, struct netlbl_unlhsh_addr4, rcu);
- kfree(ptr);
-}
-
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-/**
- * netlbl_unlhsh_free_addr6 - Frees an IPv6 address entry from the hash table
- * @entry: the entry's RCU field
- *
- * Description:
- * This function is designed to be used as a callback to the call_rcu()
- * function so that memory allocated to a hash table address entry can be
- * released safely.
- *
- */
-static void netlbl_unlhsh_free_addr6(struct rcu_head *entry)
-{
- struct netlbl_unlhsh_addr6 *ptr;
-
- ptr = container_of(entry, struct netlbl_unlhsh_addr6, rcu);
- kfree(ptr);
-}
-#endif /* IPv6 */
-
-/**
* netlbl_unlhsh_free_iface - Frees an interface entry from the hash table
* @entry: the entry's RCU field
*
@@ -274,26 +167,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 IS_ENABLED(CONFIG_IPV6)
+ 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 IS_ENABLED(CONFIG_IPV6)
+ netlbl_af6list_foreach_safe(iter6, tmp6, &iface->addr6_list) {
+ netlbl_af6list_remove_entry(iter6);
+ kfree(netlbl_unlhsh_addr6_entry(iter6));
+ }
+#endif /* IPv6 */
kfree(iface);
}
@@ -304,69 +199,14 @@ static void netlbl_unlhsh_free_iface(struct rcu_head *entry)
* Description:
* This is the hashing function for the unlabeled hash table, it returns the
* bucket number for the given device/interface. The caller is responsible for
- * calling the rcu_read_[un]lock() functions.
+ * ensuring that the hash table is protected with either a RCU read lock or
+ * the hash table lock.
*
*/
static u32 netlbl_unlhsh_hash(int ifindex)
{
- /* this is taken _almost_ directly from
- * security/selinux/netif.c:sel_netif_hasfn() as they do pretty much
- * the same thing */
- 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;
+ return ifindex & (netlbl_unlhsh_rcu_deref(netlbl_unlhsh)->size - 1);
}
-#endif /* IPv6 */
/**
* netlbl_unlhsh_search_iface - Search for a matching interface entry
@@ -375,18 +215,19 @@ static struct netlbl_unlhsh_addr6 *netlbl_unlhsh_search_addr6(
* Description:
* Searches the unlabeled connection hash table and returns a pointer to the
* interface entry which matches @ifindex, otherwise NULL is returned. The
- * caller is responsible for calling the rcu_read_[un]lock() functions.
+ * caller is responsible for ensuring that the hash table is protected with
+ * either a RCU read lock or the hash table lock.
*
*/
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 = &netlbl_unlhsh_rcu_deref(netlbl_unlhsh)->tbl[bkt];
+ list_for_each_entry_rcu(iter, bkt_list, list)
if (iter->valid && iter->ifindex == ifindex)
return iter;
@@ -394,33 +235,6 @@ static struct netlbl_unlhsh_iface *netlbl_unlhsh_search_iface(int ifindex)
}
/**
- * netlbl_unlhsh_search_iface_def - Search for a matching interface entry
- * @ifindex: the network interface
- *
- * Description:
- * Searches the unlabeled connection hash table and returns a pointer to the
- * interface entry which matches @ifindex. If an exact match can not be found
- * and there is a valid default entry, the default entry is returned, otherwise
- * NULL is returned. The caller is responsible for calling the
- * rcu_read_[un]lock() functions.
- *
- */
-static struct netlbl_unlhsh_iface *netlbl_unlhsh_search_iface_def(int ifindex)
-{
- struct netlbl_unlhsh_iface *entry;
-
- entry = netlbl_unlhsh_search_iface(ifindex);
- if (entry != NULL)
- return entry;
-
- entry = rcu_dereference(netlbl_unlhsh_def);
- if (entry != NULL && entry->valid)
- return entry;
-
- return NULL;
-}
-
-/**
* netlbl_unlhsh_add_addr4 - Add a new IPv4 address entry to the hash table
* @iface: the associated interface entry
* @addr: IPv4 address in network byte order
@@ -430,8 +244,7 @@ static struct netlbl_unlhsh_iface *netlbl_unlhsh_search_iface_def(int ifindex)
* Description:
* Add a new address entry into the unlabeled connection hash table using the
* interface entry specified by @iface. On success zero is returned, otherwise
- * a negative value is returned. The caller is responsible for calling the
- * rcu_read_[un]lock() functions.
+ * a negative value is returned.
*
*/
static int netlbl_unlhsh_add_addr4(struct netlbl_unlhsh_iface *iface,
@@ -439,46 +252,28 @@ 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->list.addr = addr->s_addr & mask->s_addr;
+ entry->list.mask = mask->s_addr;
+ entry->list.valid = 1;
entry->secid = secid;
- entry->valid = 1;
- INIT_RCU_HEAD(&entry->rcu);
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)
+#if IS_ENABLED(CONFIG_IPV6)
/**
* netlbl_unlhsh_add_addr6 - Add a new IPv6 address entry to the hash table
* @iface: the associated interface entry
@@ -489,8 +284,7 @@ static int netlbl_unlhsh_add_addr4(struct netlbl_unlhsh_iface *iface,
* Description:
* Add a new address entry into the unlabeled connection hash table using the
* interface entry specified by @iface. On success zero is returned, otherwise
- * a negative value is returned. The caller is responsible for calling the
- * rcu_read_[un]lock() functions.
+ * a negative value is returned.
*
*/
static int netlbl_unlhsh_add_addr6(struct netlbl_unlhsh_iface *iface,
@@ -498,47 +292,28 @@ 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->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];
+ entry->list.mask = *mask;
+ entry->list.valid = 1;
entry->secid = secid;
- entry->valid = 1;
- INIT_RCU_HEAD(&entry->rcu);
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 */
@@ -550,8 +325,7 @@ static int netlbl_unlhsh_add_addr6(struct netlbl_unlhsh_iface *iface,
* Description:
* Add a new, empty, interface entry into the unlabeled connection hash table.
* On success a pointer to the new interface entry is returned, on failure NULL
- * is returned. The caller is responsible for calling the rcu_read_[un]lock()
- * functions.
+ * is returned.
*
*/
static struct netlbl_unlhsh_iface *netlbl_unlhsh_add_iface(int ifindex)
@@ -567,7 +341,6 @@ static struct netlbl_unlhsh_iface *netlbl_unlhsh_add_iface(int ifindex)
INIT_LIST_HEAD(&iface->addr4_list);
INIT_LIST_HEAD(&iface->addr6_list);
iface->valid = 1;
- INIT_RCU_HEAD(&iface->rcu);
spin_lock(&netlbl_unlhsh_lock);
if (ifindex > 0) {
@@ -575,10 +348,10 @@ static struct netlbl_unlhsh_iface *netlbl_unlhsh_add_iface(int ifindex)
if (netlbl_unlhsh_search_iface(ifindex) != NULL)
goto add_iface_failure;
list_add_tail_rcu(&iface->list,
- &rcu_dereference(netlbl_unlhsh)->tbl[bkt]);
+ &netlbl_unlhsh_rcu_deref(netlbl_unlhsh)->tbl[bkt]);
} else {
INIT_LIST_HEAD(&iface->list);
- if (rcu_dereference(netlbl_unlhsh_def) != NULL)
+ if (netlbl_unlhsh_rcu_deref(netlbl_unlhsh_def) != NULL)
goto add_iface_failure;
rcu_assign_pointer(netlbl_unlhsh_def, iface);
}
@@ -607,13 +380,13 @@ add_iface_failure:
* success, negative values on failure.
*
*/
-static int netlbl_unlhsh_add(struct net *net,
- const char *dev_name,
- const void *addr,
- const void *mask,
- u32 addr_len,
- u32 secid,
- struct netlbl_audit *audit_info)
+int netlbl_unlhsh_add(struct net *net,
+ const char *dev_name,
+ const void *addr,
+ const void *mask,
+ u32 addr_len,
+ u32 secid,
+ struct netlbl_audit *audit_info)
{
int ret_val;
int ifindex;
@@ -629,13 +402,12 @@ static int netlbl_unlhsh_add(struct net *net,
rcu_read_lock();
if (dev_name != NULL) {
- dev = dev_get_by_name(net, dev_name);
+ dev = dev_get_by_name_rcu(net, dev_name);
if (dev == NULL) {
ret_val = -ENODEV;
goto unlhsh_add_return;
}
ifindex = dev->ifindex;
- dev_put(dev);
iface = netlbl_unlhsh_search_iface(ifindex);
} else {
ifindex = 0;
@@ -652,29 +424,27 @@ static int netlbl_unlhsh_add(struct net *net,
audit_info);
switch (addr_len) {
case sizeof(struct in_addr): {
- struct in_addr *addr4, *mask4;
+ const struct in_addr *addr4 = addr;
+ const struct in_addr *mask4 = mask;
- addr4 = (struct in_addr *)addr;
- 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)
+#if IS_ENABLED(CONFIG_IPV6)
case sizeof(struct in6_addr): {
- struct in6_addr *addr6, *mask6;
+ const struct in6_addr *addr6 = addr;
+ const struct in6_addr *mask6 = mask;
- addr6 = (struct in6_addr *)addr;
- 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 */
@@ -709,8 +479,7 @@ unlhsh_add_return:
*
* Description:
* Remove an IP address entry from the unlabeled connection hash table.
- * Returns zero on success, negative values on failure. The caller is
- * responsible for calling the rcu_read_[un]lock() functions.
+ * Returns zero on success, negative values on failure.
*
*/
static int netlbl_unlhsh_remove_addr4(struct net *net,
@@ -719,48 +488,49 @@ static int netlbl_unlhsh_remove_addr4(struct net *net,
const struct in_addr *mask,
struct netlbl_audit *audit_info)
{
- int ret_val = -ENOENT;
+ 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)
+ entry = netlbl_unlhsh_addr4_entry(list_entry);
+ else
+ entry = NULL;
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 != NULL &&
+ 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);
}
- audit_log_format(audit_buf, " res=%u", ret_val == 0 ? 1 : 0);
+ audit_log_format(audit_buf, " res=%u", entry != NULL ? 1 : 0);
audit_log_end(audit_buf);
}
- if (ret_val == 0)
- call_rcu(&entry->rcu, netlbl_unlhsh_free_addr4);
- return ret_val;
+ if (entry == NULL)
+ return -ENOENT;
+
+ kfree_rcu(entry, rcu);
+ return 0;
}
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+#if IS_ENABLED(CONFIG_IPV6)
/**
* netlbl_unlhsh_remove_addr6 - Remove an IPv6 address entry
* @net: network namespace
@@ -771,8 +541,7 @@ static int netlbl_unlhsh_remove_addr4(struct net *net,
*
* Description:
* Remove an IP address entry from the unlabeled connection hash table.
- * Returns zero on success, negative values on failure. The caller is
- * responsible for calling the rcu_read_[un]lock() functions.
+ * Returns zero on success, negative values on failure.
*
*/
static int netlbl_unlhsh_remove_addr6(struct net *net,
@@ -781,46 +550,45 @@ static int netlbl_unlhsh_remove_addr6(struct net *net,
const struct in6_addr *mask,
struct netlbl_audit *audit_info)
{
- int ret_val = -ENOENT;
+ 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)
+ entry = netlbl_unlhsh_addr6_entry(list_entry);
+ else
+ entry = NULL;
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 != NULL &&
+ 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);
}
- audit_log_format(audit_buf, " res=%u", ret_val == 0 ? 1 : 0);
+ audit_log_format(audit_buf, " res=%u", entry != NULL ? 1 : 0);
audit_log_end(audit_buf);
}
- if (ret_val == 0)
- call_rcu(&entry->rcu, netlbl_unlhsh_free_addr6);
- return ret_val;
+ if (entry == NULL)
+ return -ENOENT;
+
+ kfree_rcu(entry, rcu);
+ return 0;
}
#endif /* IPv6 */
@@ -836,21 +604,23 @@ 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 IS_ENABLED(CONFIG_IPV6)
+ 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 IS_ENABLED(CONFIG_IPV6)
+ 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);
else
- rcu_assign_pointer(netlbl_unlhsh_def, NULL);
+ RCU_INIT_POINTER(netlbl_unlhsh_def, NULL);
spin_unlock(&netlbl_unlhsh_lock);
call_rcu(&iface->rcu, netlbl_unlhsh_free_iface);
@@ -858,7 +628,6 @@ static void netlbl_unlhsh_condremove_iface(struct netlbl_unlhsh_iface *iface)
unlhsh_condremove_failure:
spin_unlock(&netlbl_unlhsh_lock);
- return;
}
/**
@@ -875,12 +644,12 @@ unlhsh_condremove_failure:
* Returns zero on success, negative values on failure.
*
*/
-static int netlbl_unlhsh_remove(struct net *net,
- const char *dev_name,
- const void *addr,
- const void *mask,
- u32 addr_len,
- struct netlbl_audit *audit_info)
+int netlbl_unlhsh_remove(struct net *net,
+ const char *dev_name,
+ const void *addr,
+ const void *mask,
+ u32 addr_len,
+ struct netlbl_audit *audit_info)
{
int ret_val;
struct net_device *dev;
@@ -892,13 +661,12 @@ static int netlbl_unlhsh_remove(struct net *net,
rcu_read_lock();
if (dev_name != NULL) {
- dev = dev_get_by_name(net, dev_name);
+ dev = dev_get_by_name_rcu(net, dev_name);
if (dev == NULL) {
ret_val = -ENODEV;
goto unlhsh_remove_return;
}
iface = netlbl_unlhsh_search_iface(dev->ifindex);
- dev_put(dev);
} else
iface = rcu_dereference(netlbl_unlhsh_def);
if (iface == NULL) {
@@ -911,7 +679,7 @@ static int netlbl_unlhsh_remove(struct net *net,
iface, addr, mask,
audit_info);
break;
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+#if IS_ENABLED(CONFIG_IPV6)
case sizeof(struct in6_addr):
ret_val = netlbl_unlhsh_remove_addr6(net,
iface, addr, mask,
@@ -939,7 +707,7 @@ unlhsh_remove_return:
* netlbl_unlhsh_netdev_handler - Network device notification handler
* @this: notifier block
* @event: the event
- * @ptr: the network device (cast to void)
+ * @ptr: the netdevice notifier info (cast to void)
*
* Description:
* Handle network device events, although at present all we care about is a
@@ -948,13 +716,12 @@ unlhsh_remove_return:
*
*/
static int netlbl_unlhsh_netdev_handler(struct notifier_block *this,
- unsigned long event,
- void *ptr)
+ unsigned long event, void *ptr)
{
- struct net_device *dev = ptr;
+ struct net_device *dev = netdev_notifier_info_to_dev(ptr);
struct netlbl_unlhsh_iface *iface = NULL;
- if (dev->nd_net != &init_net)
+ if (!net_eq(dev_net(dev), &init_net))
return NOTIFY_DONE;
/* XXX - should this be a check for NETDEV_DOWN or _UNREGISTER? */
@@ -1107,11 +874,7 @@ static int netlbl_unlabel_list(struct sk_buff *skb, struct genl_info *info)
goto list_failure;
genlmsg_end(ans_skb, data);
-
- ret_val = genlmsg_reply(ans_skb, info);
- if (ret_val != 0)
- goto list_failure;
- return 0;
+ return genlmsg_reply(ans_skb, info);
list_failure:
kfree_skb(ans_skb);
@@ -1331,7 +1094,7 @@ static int netlbl_unlabel_staticlist_gen(u32 cmd,
char *secctx;
u32 secctx_len;
- data = genlmsg_put(cb_arg->skb, NETLINK_CB(cb_arg->nl_cb->skb).pid,
+ data = genlmsg_put(cb_arg->skb, NETLINK_CB(cb_arg->nl_cb->skb).portid,
cb_arg->seq, &netlbl_unlabel_gnl_family,
NLM_F_MULTI, cmd);
if (data == NULL)
@@ -1339,6 +1102,10 @@ static int netlbl_unlabel_staticlist_gen(u32 cmd,
if (iface->ifindex > 0) {
dev = dev_get_by_index(&init_net, iface->ifindex);
+ if (!dev) {
+ ret_val = -ENODEV;
+ goto list_cb_failure;
+ }
ret_val = nla_put_string(cb_arg->skb,
NLBL_UNLABEL_A_IFACE, dev->name);
dev_put(dev);
@@ -1349,7 +1116,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 +1124,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 +1137,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;
@@ -1420,13 +1187,14 @@ static int netlbl_unlabel_staticlist(struct sk_buff *skb,
struct netlbl_unlhsh_walk_arg cb_arg;
u32 skip_bkt = cb->args[0];
u32 skip_chain = cb->args[1];
- u32 skip_addr4 = cb->args[2];
- u32 skip_addr6 = cb->args[3];
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 IS_ENABLED(CONFIG_IPV6)
+ struct netlbl_af6list *addr6;
+#endif
cb_arg.nl_cb = cb;
cb_arg.skb = skb;
@@ -1436,53 +1204,52 @@ 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++ < cb->args[2])
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 IS_ENABLED(CONFIG_IPV6)
+ netlbl_af6list_foreach_rcu(addr6,
+ &iface->addr6_list) {
+ if (iter_addr6++ < cb->args[3])
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 */
}
}
unlabel_staticlist_return:
rcu_read_unlock();
- cb->args[0] = skip_bkt;
- cb->args[1] = skip_chain;
- cb->args[2] = skip_addr4;
- cb->args[3] = skip_addr6;
+ cb->args[0] = iter_bkt;
+ cb->args[1] = iter_chain;
+ cb->args[2] = iter_addr4;
+ cb->args[3] = iter_addr6;
return skb->len;
}
@@ -1502,11 +1269,11 @@ static int netlbl_unlabel_staticlistdef(struct sk_buff *skb,
{
struct netlbl_unlhsh_walk_arg cb_arg;
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;
+ struct netlbl_af4list *addr4;
+#if IS_ENABLED(CONFIG_IPV6)
+ struct netlbl_af6list *addr6;
+#endif
cb_arg.nl_cb = cb;
cb_arg.skb = skb;
@@ -1517,35 +1284,37 @@ 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++ < cb->args[0])
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 IS_ENABLED(CONFIG_IPV6)
+ netlbl_af6list_foreach_rcu(addr6, &iface->addr6_list) {
+ if (iter_addr6++ < cb->args[1])
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();
- cb->args[0] = skip_addr4;
- cb->args[1] = skip_addr6;
+ cb->args[0] = iter_addr4;
+ cb->args[1] = iter_addr6;
return skb->len;
}
@@ -1553,7 +1322,7 @@ unlabel_staticlistdef_return:
* NetLabel Generic NETLINK Command Definitions
*/
-static struct genl_ops netlbl_unlabel_genl_ops[] = {
+static const struct genl_ops netlbl_unlabel_genl_ops[] = {
{
.cmd = NLBL_UNLABEL_C_STATICADD,
.flags = GENL_ADMIN_PERM,
@@ -1626,20 +1395,8 @@ static struct genl_ops netlbl_unlabel_genl_ops[] = {
*/
int __init netlbl_unlabel_genl_init(void)
{
- int ret_val, i;
-
- ret_val = genl_register_family(&netlbl_unlabel_gnl_family);
- if (ret_val != 0)
- return ret_val;
-
- for (i = 0; i < ARRAY_SIZE(netlbl_unlabel_genl_ops); i++) {
- ret_val = genl_register_ops(&netlbl_unlabel_gnl_family,
- &netlbl_unlabel_genl_ops[i]);
- if (ret_val != 0)
- return ret_val;
- }
-
- return 0;
+ return genl_register_family_with_ops(&netlbl_unlabel_gnl_family,
+ netlbl_unlabel_genl_ops);
}
/*
@@ -1683,11 +1440,9 @@ int __init netlbl_unlabel_init(u32 size)
for (iter = 0; iter < hsh_tbl->size; iter++)
INIT_LIST_HEAD(&hsh_tbl->tbl[iter]);
- rcu_read_lock();
spin_lock(&netlbl_unlhsh_lock);
rcu_assign_pointer(netlbl_unlhsh, hsh_tbl);
spin_unlock(&netlbl_unlhsh_lock);
- rcu_read_unlock();
register_netdevice_notifier(&netlbl_unlhsh_netdev_notifier);
@@ -1712,31 +1467,35 @@ int netlbl_unlabel_getattr(const struct sk_buff *skb,
struct netlbl_unlhsh_iface *iface;
rcu_read_lock();
- iface = netlbl_unlhsh_search_iface_def(skb->iif);
+ iface = netlbl_unlhsh_search_iface(skb->skb_iif);
if (iface == NULL)
+ iface = rcu_dereference(netlbl_unlhsh_def);
+ if (iface == NULL || !iface->valid)
goto unlabel_getattr_nolabel;
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)
+#if IS_ENABLED(CONFIG_IPV6)
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 */
@@ -1775,12 +1534,13 @@ int __init netlbl_unlabel_defconf(void)
* it is called is at bootup before the audit subsystem is reporting
* messages so don't worry to much about these values. */
security_task_getsecid(current, &audit_info.secid);
- audit_info.loginuid = 0;
+ audit_info.loginuid = GLOBAL_ROOT_UID;
+ audit_info.sessionid = 0;
entry = kzalloc(sizeof(*entry), GFP_KERNEL);
if (entry == NULL)
return -ENOMEM;
- entry->type = NETLBL_NLTYPE_UNLABELED;
+ entry->def.type = NETLBL_NLTYPE_UNLABELED;
ret_val = netlbl_domhsh_add_default(entry, &audit_info);
if (ret_val != 0)
return ret_val;
diff --git a/net/netlabel/netlabel_unlabeled.h b/net/netlabel/netlabel_unlabeled.h
index 06b1301ac07..3a9e5dc9511 100644
--- a/net/netlabel/netlabel_unlabeled.h
+++ b/net/netlabel/netlabel_unlabeled.h
@@ -5,7 +5,7 @@
* NetLabel system. The NetLabel system manages static and dynamic label
* mappings for network protocols such as CIPSO and RIPSO.
*
- * Author: Paul Moore <paul.moore@hp.com>
+ * Author: Paul Moore <paul@paul-moore.com>
*
*/
@@ -23,8 +23,7 @@
* 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
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
*
*/
@@ -180,7 +179,6 @@ enum {
NLBL_UNLABEL_C_STATICLISTDEF,
__NLBL_UNLABEL_C_MAX,
};
-#define NLBL_UNLABEL_C_MAX (__NLBL_UNLABEL_C_MAX - 1)
/* NetLabel Unlabeled attributes */
enum {
@@ -221,6 +219,21 @@ int netlbl_unlabel_genl_init(void);
/* General Unlabeled init function */
int netlbl_unlabel_init(u32 size);
+/* Static/Fallback label management functions */
+int netlbl_unlhsh_add(struct net *net,
+ const char *dev_name,
+ const void *addr,
+ const void *mask,
+ u32 addr_len,
+ u32 secid,
+ struct netlbl_audit *audit_info);
+int netlbl_unlhsh_remove(struct net *net,
+ const char *dev_name,
+ const void *addr,
+ const void *mask,
+ u32 addr_len,
+ struct netlbl_audit *audit_info);
+
/* Process Unlabeled incoming network packets */
int netlbl_unlabel_getattr(const struct sk_buff *skb,
u16 family,
diff --git a/net/netlabel/netlabel_user.c b/net/netlabel/netlabel_user.c
index b17d4203806..1e779bb7fa4 100644
--- a/net/netlabel/netlabel_user.c
+++ b/net/netlabel/netlabel_user.c
@@ -5,7 +5,7 @@
* NetLabel system manages static and dynamic label mappings for network
* protocols such as CIPSO and RIPSO.
*
- * Author: Paul Moore <paul.moore@hp.com>
+ * Author: Paul Moore <paul@paul-moore.com>
*
*/
@@ -23,8 +23,7 @@
* 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
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
*
*/
@@ -35,6 +34,7 @@
#include <linux/audit.h>
#include <linux/tty.h>
#include <linux/security.h>
+#include <linux/gfp.h>
#include <net/sock.h>
#include <net/netlink.h>
#include <net/genetlink.h>
@@ -107,7 +107,9 @@ struct audit_buffer *netlbl_audit_start_common(int type,
if (audit_buf == NULL)
return NULL;
- audit_log_format(audit_buf, "netlabel: auid=%u", audit_info->loginuid);
+ audit_log_format(audit_buf, "netlabel: auid=%u ses=%u",
+ from_kuid(&init_user_ns, audit_info->loginuid),
+ audit_info->sessionid);
if (audit_info->secid != 0 &&
security_secid_to_secctx(audit_info->secid,
diff --git a/net/netlabel/netlabel_user.h b/net/netlabel/netlabel_user.h
index 6d7f4ab46c2..4a397cde1a4 100644
--- a/net/netlabel/netlabel_user.h
+++ b/net/netlabel/netlabel_user.h
@@ -5,7 +5,7 @@
* NetLabel system manages static and dynamic label mappings for network
* protocols such as CIPSO and RIPSO.
*
- * Author: Paul Moore <paul.moore@hp.com>
+ * Author: Paul Moore <paul@paul-moore.com>
*
*/
@@ -23,8 +23,7 @@
* 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
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
*
*/
@@ -49,8 +48,9 @@
static inline void netlbl_netlink_auditinfo(struct sk_buff *skb,
struct netlbl_audit *audit_info)
{
- audit_info->secid = NETLINK_CB(skb).sid;
- audit_info->loginuid = NETLINK_CB(skb).loginuid;
+ security_task_getsecid(current, &audit_info->secid);
+ audit_info->loginuid = audit_get_loginuid(current);
+ audit_info->sessionid = audit_get_sessionid(current);
}
/* NetLabel NETLINK I/O functions */