Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 63c41688 authored by Paul Moore's avatar Paul Moore
Browse files

netlabel: Add network address selectors to the NetLabel/LSM domain mapping



This patch extends the NetLabel traffic labeling capabilities to individual
packets based not only on the LSM domain but the by the destination address
as well.  The changes here only affect the core NetLabel infrastructre,
changes to the NetLabel KAPI and individial protocol engines are also
required but are split out into a different patch to ease review.

Signed-off-by: default avatarPaul Moore <paul.moore@hp.com>
Reviewed-by: default avatarJames Morris <jmorris@namei.org>
parent 61e10682
Loading
Loading
Loading
Loading
+5 −2
Original line number Diff line number Diff line
@@ -9,7 +9,7 @@
 */

/*
 * (c) Copyright Hewlett-Packard Development Company, L.P., 2006
 * (c) Copyright Hewlett-Packard Development Company, L.P., 2006, 2008
 *
 * This program is free software;  you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
@@ -72,8 +72,9 @@ struct cipso_v4_doi;
/* NetLabel NETLINK protocol version
 *  1: initial version
 *  2: added static labels for unlabeled connections
 *  3: network selectors added to the NetLabel/LSM domain mapping
 */
#define NETLBL_PROTO_VERSION            2
#define NETLBL_PROTO_VERSION            3

/* NetLabel NETLINK types/families */
#define NETLBL_NLTYPE_NONE              0
@@ -87,6 +88,8 @@ struct cipso_v4_doi;
#define NETLBL_NLTYPE_CIPSOV6_NAME      "NLBL_CIPSOv6"
#define NETLBL_NLTYPE_UNLABELED         5
#define NETLBL_NLTYPE_UNLABELED_NAME    "NLBL_UNLBL"
#define NETLBL_NLTYPE_ADDRSELECT        6
#define NETLBL_NLTYPE_ADDRSELECT_NAME   "NLBL_ADRSEL"

/*
 * NetLabel - Kernel API for accessing the network packet label mappings.
+130 −0
Original line number Diff line number Diff line
@@ -39,6 +39,7 @@
#include <linux/ipv6.h>
#include <net/ip.h>
#include <net/ipv6.h>
#include <linux/audit.h>

#include "netlabel_addrlist.h"

@@ -69,6 +70,32 @@ struct netlbl_af4list *netlbl_af4list_search(__be32 addr,
	return NULL;
}

/**
 * netlbl_af4list_search_exact - Search for an exact IPv4 address entry
 * @addr: IPv4 address
 * @mask: IPv4 address mask
 * @head: the list head
 *
 * Description:
 * Searches the IPv4 address list given by @head.  If an exact match if found
 * it is returned, otherwise NULL is returned.  The caller is responsible for
 * calling the rcu_read_[un]lock() functions.
 *
 */
struct netlbl_af4list *netlbl_af4list_search_exact(__be32 addr,
						   __be32 mask,
						   struct list_head *head)
{
	struct netlbl_af4list *iter;

	list_for_each_entry_rcu(iter, head, list)
		if (iter->valid && iter->addr == addr && iter->mask == mask)
			return iter;

	return NULL;
}


#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
/**
 * netlbl_af6list_search - Search for a matching IPv6 address entry
@@ -93,6 +120,33 @@ struct netlbl_af6list *netlbl_af6list_search(const struct in6_addr *addr,

	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 */

/**
@@ -256,3 +310,79 @@ struct netlbl_af6list *netlbl_af6list_remove(const struct in6_addr *addr,
	return NULL;
}
#endif /* IPv6 */

/*
 * Audit Helper Functions
 */

/**
 * netlbl_af4list_audit_addr - Audit an IPv4 address
 * @audit_buf: audit buffer
 * @src: true if source address, false if destination
 * @dev: network interface
 * @addr: IP address
 * @mask: IP address mask
 *
 * Description:
 * Write the IPv4 address and address mask, if necessary, to @audit_buf.
 *
 */
void netlbl_af4list_audit_addr(struct audit_buffer *audit_buf,
					int src, const char *dev,
					__be32 addr, __be32 mask)
{
	u32 mask_val = ntohl(mask);
	char *dir = (src ? "src" : "dst");

	if (dev != NULL)
		audit_log_format(audit_buf, " netif=%s", dev);
	audit_log_format(audit_buf, " %s=" NIPQUAD_FMT, dir, NIPQUAD(addr));
	if (mask_val != 0xffffffff) {
		u32 mask_len = 0;
		while (mask_val > 0) {
			mask_val <<= 1;
			mask_len++;
		}
		audit_log_format(audit_buf, " %s_prefixlen=%d", dir, mask_len);
	}
}

#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
/**
 * netlbl_af6list_audit_addr - Audit an IPv6 address
 * @audit_buf: audit buffer
 * @src: true if source address, false if destination
 * @dev: network interface
 * @addr: IP address
 * @mask: IP address mask
 *
 * Description:
 * Write the IPv6 address and address mask, if necessary, to @audit_buf.
 *
 */
void netlbl_af6list_audit_addr(struct audit_buffer *audit_buf,
				 int src,
				 const char *dev,
				 const struct in6_addr *addr,
				 const struct in6_addr *mask)
{
	char *dir = (src ? "src" : "dst");

	if (dev != NULL)
		audit_log_format(audit_buf, " netif=%s", dev);
	audit_log_format(audit_buf, " %s=" NIP6_FMT, dir, NIP6(*addr));
	if (ntohl(mask->s6_addr32[3]) != 0xffffffff) {
		u32 mask_len = 0;
		u32 mask_val;
		int iter = -1;
		while (ntohl(mask->s6_addr32[++iter]) == 0xffffffff)
			mask_len += 32;
		mask_val = ntohl(mask->s6_addr32[iter]);
		while (mask_val > 0) {
			mask_val <<= 1;
			mask_len++;
		}
		audit_log_format(audit_buf, " %s_prefixlen=%d", dir, mask_len);
	}
}
#endif /* IPv6 */
+15 −0
Original line number Diff line number Diff line
@@ -36,6 +36,7 @@
#include <linux/rcupdate.h>
#include <linux/list.h>
#include <linux/in6.h>
#include <linux/audit.h>

/**
 * struct netlbl_af4list - NetLabel IPv4 address list
@@ -116,6 +117,12 @@ struct netlbl_af4list *netlbl_af4list_remove(__be32 addr, __be32 mask,
void netlbl_af4list_remove_entry(struct netlbl_af4list *entry);
struct netlbl_af4list *netlbl_af4list_search(__be32 addr,
					     struct list_head *head);
struct netlbl_af4list *netlbl_af4list_search_exact(__be32 addr,
						   __be32 mask,
						   struct list_head *head);
void netlbl_af4list_audit_addr(struct audit_buffer *audit_buf,
			       int src, const char *dev,
			       __be32 addr, __be32 mask);

#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)

@@ -169,6 +176,14 @@ struct netlbl_af6list *netlbl_af6list_remove(const struct in6_addr *addr,
void netlbl_af6list_remove_entry(struct netlbl_af6list *entry);
struct netlbl_af6list *netlbl_af6list_search(const struct in6_addr *addr,
					     struct list_head *head);
struct netlbl_af6list *netlbl_af6list_search_exact(const struct in6_addr *addr,
						   const struct in6_addr *mask,
						   struct list_head *head);
void netlbl_af6list_audit_addr(struct audit_buffer *audit_buf,
			       int src,
			       const char *dev,
			       const struct in6_addr *addr,
			       const struct in6_addr *mask);
#endif /* IPV6 */

#endif
+253 −37
Original line number Diff line number Diff line
@@ -11,7 +11,7 @@
 */

/*
 * (c) Copyright Hewlett-Packard Development Company, L.P., 2006
 * (c) Copyright Hewlett-Packard Development Company, L.P., 2006, 2008
 *
 * This program is free software;  you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
@@ -40,6 +40,7 @@
#include <asm/bug.h>

#include "netlabel_mgmt.h"
#include "netlabel_addrlist.h"
#include "netlabel_domainhash.h"
#include "netlabel_user.h"

@@ -72,8 +73,28 @@ static struct netlbl_dom_map *netlbl_domhsh_def = NULL;
static void netlbl_domhsh_free_entry(struct rcu_head *entry)
{
	struct netlbl_dom_map *ptr;
	struct netlbl_af4list *iter4;
	struct netlbl_af4list *tmp4;
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
	struct netlbl_af6list *iter6;
	struct netlbl_af6list *tmp6;
#endif /* IPv6 */

	ptr = container_of(entry, struct netlbl_dom_map, rcu);
	if (ptr->type == NETLBL_NLTYPE_ADDRSELECT) {
		netlbl_af4list_foreach_safe(iter4, tmp4,
					    &ptr->type_def.addrsel->list4) {
			netlbl_af4list_remove_entry(iter4);
			kfree(netlbl_domhsh_addr4_entry(iter4));
		}
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
		netlbl_af6list_foreach_safe(iter6, tmp6,
					    &ptr->type_def.addrsel->list6) {
			netlbl_af6list_remove_entry(iter6);
			kfree(netlbl_domhsh_addr6_entry(iter6));
		}
#endif /* IPv6 */
	}
	kfree(ptr->domain);
	kfree(ptr);
}
@@ -156,6 +177,69 @@ static struct netlbl_dom_map *netlbl_domhsh_search_def(const char *domain)
	return entry;
}

/**
 * netlbl_domhsh_audit_add - Generate an audit entry for an add event
 * @entry: the entry being added
 * @addr4: the IPv4 address information
 * @addr6: the IPv6 address information
 * @result: the result code
 * @audit_info: NetLabel audit information
 *
 * Description:
 * Generate an audit record for adding a new NetLabel/LSM mapping entry with
 * the given information.  Caller is responsibile for holding the necessary
 * locks.
 *
 */
static void netlbl_domhsh_audit_add(struct netlbl_dom_map *entry,
				    struct netlbl_af4list *addr4,
				    struct netlbl_af6list *addr6,
				    int result,
				    struct netlbl_audit *audit_info)
{
	struct audit_buffer *audit_buf;
	struct cipso_v4_doi *cipsov4 = NULL;
	u32 type;

	audit_buf = netlbl_audit_start_common(AUDIT_MAC_MAP_ADD, audit_info);
	if (audit_buf != NULL) {
		audit_log_format(audit_buf, " nlbl_domain=%s",
				 entry->domain ? entry->domain : "(default)");
		if (addr4 != NULL) {
			struct netlbl_domaddr4_map *map4;
			map4 = netlbl_domhsh_addr4_entry(addr4);
			type = map4->type;
			cipsov4 = map4->type_def.cipsov4;
			netlbl_af4list_audit_addr(audit_buf, 0, NULL,
						  addr4->addr, addr4->mask);
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
		} else if (addr6 != NULL) {
			struct netlbl_domaddr6_map *map6;
			map6 = netlbl_domhsh_addr6_entry(addr6);
			type = map6->type;
			netlbl_af6list_audit_addr(audit_buf, 0, NULL,
						  &addr6->addr, &addr6->mask);
#endif /* IPv6 */
		} else {
			type = entry->type;
			cipsov4 = entry->type_def.cipsov4;
		}
		switch (type) {
		case NETLBL_NLTYPE_UNLABELED:
			audit_log_format(audit_buf, " nlbl_protocol=unlbl");
			break;
		case NETLBL_NLTYPE_CIPSOV4:
			BUG_ON(cipsov4 == NULL);
			audit_log_format(audit_buf,
					 " nlbl_protocol=cipsov4 cipso_doi=%u",
					 cipsov4->doi);
			break;
		}
		audit_log_format(audit_buf, " res=%u", result == 0 ? 1 : 0);
		audit_log_end(audit_buf);
	}
}

/*
 * Domain Hash Table Functions
 */
@@ -213,50 +297,106 @@ int __init netlbl_domhsh_init(u32 size)
int netlbl_domhsh_add(struct netlbl_dom_map *entry,
		      struct netlbl_audit *audit_info)
{
	int ret_val;
	u32 bkt;
	struct audit_buffer *audit_buf;
	int ret_val = 0;
	struct netlbl_dom_map *entry_old;
	struct netlbl_af4list *iter4;
	struct netlbl_af4list *tmp4;
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
	struct netlbl_af6list *iter6;
	struct netlbl_af6list *tmp6;
#endif /* IPv6 */

	rcu_read_lock();

	spin_lock(&netlbl_domhsh_lock);
	if (entry->domain != NULL)
		entry_old = netlbl_domhsh_search(entry->domain);
	else
		entry_old = netlbl_domhsh_search_def(entry->domain);
	if (entry_old == NULL) {
		entry->valid = 1;
		INIT_RCU_HEAD(&entry->rcu);

	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)
			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)
			rcu_assign_pointer(netlbl_domhsh_def, entry);
		else
		}

		if (entry->type == NETLBL_NLTYPE_ADDRSELECT) {
			netlbl_af4list_foreach_rcu(iter4,
					       &entry->type_def.addrsel->list4)
				netlbl_domhsh_audit_add(entry, iter4, NULL,
							ret_val, audit_info);
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
			netlbl_af6list_foreach_rcu(iter6,
					       &entry->type_def.addrsel->list6)
				netlbl_domhsh_audit_add(entry, NULL, iter6,
							ret_val, audit_info);
#endif /* IPv6 */
		} else
			netlbl_domhsh_audit_add(entry, NULL, NULL,
						ret_val, audit_info);
	} else if (entry_old->type == NETLBL_NLTYPE_ADDRSELECT &&
		   entry->type == NETLBL_NLTYPE_ADDRSELECT) {
		struct list_head *old_list4;
		struct list_head *old_list6;

		old_list4 = &entry_old->type_def.addrsel->list4;
		old_list6 = &entry_old->type_def.addrsel->list6;

		/* we only allow the addition of address selectors if all of
		 * the selectors do not exist in the existing domain map */
		netlbl_af4list_foreach_rcu(iter4,
					   &entry->type_def.addrsel->list4)
			if (netlbl_af4list_search_exact(iter4->addr,
							iter4->mask,
							old_list4)) {
				ret_val = -EEXIST;
				goto add_return;
			}
	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;
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
		netlbl_af6list_foreach_rcu(iter6,
					   &entry->type_def.addrsel->list6)
			if (netlbl_af6list_search_exact(&iter6->addr,
							&iter6->mask,
							old_list6)) {
				ret_val = -EEXIST;
				goto add_return;
			}
		audit_log_format(audit_buf, " res=%u", ret_val == 0 ? 1 : 0);
		audit_log_end(audit_buf);
#endif /* IPv6 */

		netlbl_af4list_foreach_safe(iter4, tmp4,
					    &entry->type_def.addrsel->list4) {
			netlbl_af4list_remove_entry(iter4);
			iter4->valid = 1;
			ret_val = netlbl_af4list_add(iter4, old_list4);
			netlbl_domhsh_audit_add(entry_old, iter4, NULL,
						ret_val, audit_info);
			if (ret_val != 0)
				goto add_return;
		}
	rcu_read_unlock();
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
		netlbl_af6list_foreach_safe(iter6, tmp6,
					    &entry->type_def.addrsel->list6) {
			netlbl_af6list_remove_entry(iter6);
			iter6->valid = 1;
			ret_val = netlbl_af6list_add(iter6, old_list6);
			netlbl_domhsh_audit_add(entry_old, NULL, iter6,
						ret_val, audit_info);
			if (ret_val != 0)
				goto add_return;
		}
#endif /* IPv6 */
	} else
		ret_val = -EINVAL;

add_return:
	spin_unlock(&netlbl_domhsh_lock);
	rcu_read_unlock();
	return ret_val;
}

@@ -319,7 +459,19 @@ int netlbl_domhsh_remove_entry(struct netlbl_dom_map *entry,
	}

	if (ret_val == 0) {
		struct netlbl_af4list *iter4;
		struct netlbl_domaddr4_map *map4;

		switch (entry->type) {
		case NETLBL_NLTYPE_ADDRSELECT:
			netlbl_af4list_foreach_rcu(iter4,
					     &entry->type_def.addrsel->list4) {
				map4 = netlbl_domhsh_addr4_entry(iter4);
				cipso_v4_doi_putdef(map4->type_def.cipsov4);
			}
			/* no need to check the IPv6 list since we currently
			 * support only unlabeled protocols for IPv6 */
			break;
		case NETLBL_NLTYPE_CIPSOV4:
			cipso_v4_doi_putdef(entry->type_def.cipsov4);
			break;
@@ -387,6 +539,70 @@ struct netlbl_dom_map *netlbl_domhsh_getentry(const char *domain)
	return netlbl_domhsh_search_def(domain);
}

/**
 * netlbl_domhsh_getentry_af4 - Get an entry from the domain hash table
 * @domain: the domain name to search for
 * @addr: the IP address to search for
 *
 * Description:
 * Look through the domain hash table searching for an entry to match @domain
 * and @addr, return a pointer to a copy of the entry or NULL.  The caller is
 * responsible for ensuring that rcu_read_[un]lock() is called.
 *
 */
struct netlbl_domaddr4_map *netlbl_domhsh_getentry_af4(const char *domain,
						       __be32 addr)
{
	struct netlbl_dom_map *dom_iter;
	struct netlbl_af4list *addr_iter;

	dom_iter = netlbl_domhsh_search_def(domain);
	if (dom_iter == NULL)
		return NULL;
	if (dom_iter->type != NETLBL_NLTYPE_ADDRSELECT)
		return NULL;

	addr_iter = netlbl_af4list_search(addr,
					  &dom_iter->type_def.addrsel->list4);
	if (addr_iter == NULL)
		return NULL;

	return netlbl_domhsh_addr4_entry(addr_iter);
}

#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
/**
 * netlbl_domhsh_getentry_af6 - Get an entry from the domain hash table
 * @domain: the domain name to search for
 * @addr: the IP address to search for
 *
 * Description:
 * Look through the domain hash table searching for an entry to match @domain
 * and @addr, return a pointer to a copy of the entry or NULL.  The caller is
 * responsible for ensuring that rcu_read_[un]lock() is called.
 *
 */
struct netlbl_domaddr6_map *netlbl_domhsh_getentry_af6(const char *domain,
						   const struct in6_addr *addr)
{
	struct netlbl_dom_map *dom_iter;
	struct netlbl_af6list *addr_iter;

	dom_iter = netlbl_domhsh_search_def(domain);
	if (dom_iter == NULL)
		return NULL;
	if (dom_iter->type != NETLBL_NLTYPE_ADDRSELECT)
		return NULL;

	addr_iter = netlbl_af6list_search(addr,
					  &dom_iter->type_def.addrsel->list6);
	if (addr_iter == NULL)
		return NULL;

	return netlbl_domhsh_addr6_entry(addr_iter);
}
#endif /* IPv6 */

/**
 * netlbl_domhsh_walk - Iterate through the domain mapping hash table
 * @skip_bkt: the number of buckets to skip at the start
+36 −2
Original line number Diff line number Diff line
@@ -11,7 +11,7 @@
 */

/*
 * (c) Copyright Hewlett-Packard Development Company, L.P., 2006
 * (c) Copyright Hewlett-Packard Development Company, L.P., 2006, 2008
 *
 * This program is free software;  you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
@@ -36,16 +36,43 @@
#include <linux/rcupdate.h>
#include <linux/list.h>

#include "netlabel_addrlist.h"

/* Domain hash table size */
/* XXX - currently this number is an uneducated guess */
#define NETLBL_DOMHSH_BITSIZE       7

/* Domain mapping definition struct */
/* Domain mapping definition structures */
#define netlbl_domhsh_addr4_entry(iter) \
	container_of(iter, struct netlbl_domaddr4_map, list)
struct netlbl_domaddr4_map {
	u32 type;
	union {
		struct cipso_v4_doi *cipsov4;
	} type_def;

	struct netlbl_af4list list;
};
#define netlbl_domhsh_addr6_entry(iter) \
	container_of(iter, struct netlbl_domaddr6_map, list)
struct netlbl_domaddr6_map {
	u32 type;

	/* NOTE: no 'type_def' union needed at present since we don't currently
	 *       support any IPv6 labeling protocols */

	struct netlbl_af6list list;
};
struct netlbl_domaddr_map {
	struct list_head list4;
	struct list_head list6;
};
struct netlbl_dom_map {
	char *domain;
	u32 type;
	union {
		struct cipso_v4_doi *cipsov4;
		struct netlbl_domaddr_map *addrsel;
	} type_def;

	u32 valid;
@@ -66,9 +93,16 @@ int netlbl_domhsh_remove_entry(struct netlbl_dom_map *entry,
int netlbl_domhsh_remove(const char *domain, struct netlbl_audit *audit_info);
int netlbl_domhsh_remove_default(struct netlbl_audit *audit_info);
struct netlbl_dom_map *netlbl_domhsh_getentry(const char *domain);
struct netlbl_domaddr4_map *netlbl_domhsh_getentry_af4(const char *domain,
						       __be32 addr);
int netlbl_domhsh_walk(u32 *skip_bkt,
		     u32 *skip_chain,
		     int (*callback) (struct netlbl_dom_map *entry, void *arg),
		     void *cb_arg);

#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
struct netlbl_domaddr6_map *netlbl_domhsh_getentry_af6(const char *domain,
						  const struct in6_addr *addr);
#endif /* IPv6 */

#endif
Loading