Linux Audio

Check our new training course

Loading...
v4.6
 
  1/*
  2 * Network node table
  3 *
  4 * SELinux must keep a mapping of network nodes to labels/SIDs.  This
  5 * mapping is maintained as part of the normal policy but a fast cache is
  6 * needed to reduce the lookup overhead since most of these queries happen on
  7 * a per-packet basis.
  8 *
  9 * Author: Paul Moore <paul@paul-moore.com>
 10 *
 11 * This code is heavily based on the "netif" concept originally developed by
 12 * James Morris <jmorris@redhat.com>
 13 *   (see security/selinux/netif.c for more information)
 14 *
 15 */
 16
 17/*
 18 * (c) Copyright Hewlett-Packard Development Company, L.P., 2007
 19 *
 20 * This program is free software: you can redistribute it and/or modify
 21 * it under the terms of version 2 of the GNU General Public License as
 22 * published by the Free Software Foundation.
 23 *
 24 * This program is distributed in the hope that it will be useful,
 25 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 27 * GNU General Public License for more details.
 28 *
 29 */
 30
 31#include <linux/types.h>
 32#include <linux/rcupdate.h>
 33#include <linux/list.h>
 34#include <linux/slab.h>
 35#include <linux/spinlock.h>
 36#include <linux/in.h>
 37#include <linux/in6.h>
 38#include <linux/ip.h>
 39#include <linux/ipv6.h>
 40#include <net/ip.h>
 41#include <net/ipv6.h>
 42
 43#include "netnode.h"
 44#include "objsec.h"
 45
 46#define SEL_NETNODE_HASH_SIZE       256
 47#define SEL_NETNODE_HASH_BKT_LIMIT   16
 48
 49struct sel_netnode_bkt {
 50	unsigned int size;
 51	struct list_head list;
 52};
 53
 54struct sel_netnode {
 55	struct netnode_security_struct nsec;
 56
 57	struct list_head list;
 58	struct rcu_head rcu;
 59};
 60
 61/* NOTE: we are using a combined hash table for both IPv4 and IPv6, the reason
 62 * for this is that I suspect most users will not make heavy use of both
 63 * address families at the same time so one table will usually end up wasted,
 64 * if this becomes a problem we can always add a hash table for each address
 65 * family later */
 66
 67static LIST_HEAD(sel_netnode_list);
 68static DEFINE_SPINLOCK(sel_netnode_lock);
 69static struct sel_netnode_bkt sel_netnode_hash[SEL_NETNODE_HASH_SIZE];
 70
 71/**
 72 * sel_netnode_hashfn_ipv4 - IPv4 hashing function for the node table
 73 * @addr: IPv4 address
 74 *
 75 * Description:
 76 * This is the IPv4 hashing function for the node interface table, it returns
 77 * the bucket number for the given IP address.
 78 *
 79 */
 80static unsigned int sel_netnode_hashfn_ipv4(__be32 addr)
 81{
 82	/* at some point we should determine if the mismatch in byte order
 83	 * affects the hash function dramatically */
 84	return (addr & (SEL_NETNODE_HASH_SIZE - 1));
 85}
 86
 87/**
 88 * sel_netnode_hashfn_ipv6 - IPv6 hashing function for the node table
 89 * @addr: IPv6 address
 90 *
 91 * Description:
 92 * This is the IPv6 hashing function for the node interface table, it returns
 93 * the bucket number for the given IP address.
 94 *
 95 */
 96static unsigned int sel_netnode_hashfn_ipv6(const struct in6_addr *addr)
 97{
 98	/* just hash the least significant 32 bits to keep things fast (they
 99	 * are the most likely to be different anyway), we can revisit this
100	 * later if needed */
101	return (addr->s6_addr32[3] & (SEL_NETNODE_HASH_SIZE - 1));
102}
103
104/**
105 * sel_netnode_find - Search for a node record
106 * @addr: IP address
107 * @family: address family
108 *
109 * Description:
110 * Search the network node table and return the record matching @addr.  If an
111 * entry can not be found in the table return NULL.
112 *
113 */
114static struct sel_netnode *sel_netnode_find(const void *addr, u16 family)
115{
116	unsigned int idx;
117	struct sel_netnode *node;
118
119	switch (family) {
120	case PF_INET:
121		idx = sel_netnode_hashfn_ipv4(*(__be32 *)addr);
122		break;
123	case PF_INET6:
124		idx = sel_netnode_hashfn_ipv6(addr);
125		break;
126	default:
127		BUG();
128		return NULL;
129	}
130
131	list_for_each_entry_rcu(node, &sel_netnode_hash[idx].list, list)
132		if (node->nsec.family == family)
133			switch (family) {
134			case PF_INET:
135				if (node->nsec.addr.ipv4 == *(__be32 *)addr)
136					return node;
137				break;
138			case PF_INET6:
139				if (ipv6_addr_equal(&node->nsec.addr.ipv6,
140						    addr))
141					return node;
142				break;
143			}
144
145	return NULL;
146}
147
148/**
149 * sel_netnode_insert - Insert a new node into the table
150 * @node: the new node record
151 *
152 * Description:
153 * Add a new node record to the network address hash table.
154 *
155 */
156static void sel_netnode_insert(struct sel_netnode *node)
157{
158	unsigned int idx;
159
160	switch (node->nsec.family) {
161	case PF_INET:
162		idx = sel_netnode_hashfn_ipv4(node->nsec.addr.ipv4);
163		break;
164	case PF_INET6:
165		idx = sel_netnode_hashfn_ipv6(&node->nsec.addr.ipv6);
166		break;
167	default:
168		BUG();
169		return;
170	}
171
172	/* we need to impose a limit on the growth of the hash table so check
173	 * this bucket to make sure it is within the specified bounds */
174	list_add_rcu(&node->list, &sel_netnode_hash[idx].list);
175	if (sel_netnode_hash[idx].size == SEL_NETNODE_HASH_BKT_LIMIT) {
176		struct sel_netnode *tail;
177		tail = list_entry(
178			rcu_dereference_protected(sel_netnode_hash[idx].list.prev,
179						  lockdep_is_held(&sel_netnode_lock)),
 
180			struct sel_netnode, list);
181		list_del_rcu(&tail->list);
182		kfree_rcu(tail, rcu);
183	} else
184		sel_netnode_hash[idx].size++;
185}
186
187/**
188 * sel_netnode_sid_slow - Lookup the SID of a network address using the policy
189 * @addr: the IP address
190 * @family: the address family
191 * @sid: node SID
192 *
193 * Description:
194 * This function determines the SID of a network address by quering the
195 * security policy.  The result is added to the network address table to
196 * speedup future queries.  Returns zero on success, negative values on
197 * failure.
198 *
199 */
200static int sel_netnode_sid_slow(void *addr, u16 family, u32 *sid)
201{
202	int ret = -ENOMEM;
203	struct sel_netnode *node;
204	struct sel_netnode *new = NULL;
205
206	spin_lock_bh(&sel_netnode_lock);
207	node = sel_netnode_find(addr, family);
208	if (node != NULL) {
209		*sid = node->nsec.sid;
210		spin_unlock_bh(&sel_netnode_lock);
211		return 0;
212	}
 
213	new = kzalloc(sizeof(*new), GFP_ATOMIC);
214	if (new == NULL)
215		goto out;
216	switch (family) {
217	case PF_INET:
218		ret = security_node_sid(PF_INET,
219					addr, sizeof(struct in_addr), sid);
220		new->nsec.addr.ipv4 = *(__be32 *)addr;
 
221		break;
222	case PF_INET6:
223		ret = security_node_sid(PF_INET6,
224					addr, sizeof(struct in6_addr), sid);
225		new->nsec.addr.ipv6 = *(struct in6_addr *)addr;
 
226		break;
227	default:
228		BUG();
229		ret = -EINVAL;
230	}
231	if (ret != 0)
232		goto out;
233
234	new->nsec.family = family;
235	new->nsec.sid = *sid;
236	sel_netnode_insert(new);
237
238out:
239	spin_unlock_bh(&sel_netnode_lock);
240	if (unlikely(ret)) {
241		printk(KERN_WARNING
242		       "SELinux: failure in sel_netnode_sid_slow(),"
243		       " unable to determine network node label\n");
244		kfree(new);
245	}
246	return ret;
247}
248
249/**
250 * sel_netnode_sid - Lookup the SID of a network address
251 * @addr: the IP address
252 * @family: the address family
253 * @sid: node SID
254 *
255 * Description:
256 * This function determines the SID of a network address using the fastest
257 * method possible.  First the address table is queried, but if an entry
258 * can't be found then the policy is queried and the result is added to the
259 * table to speedup future queries.  Returns zero on success, negative values
260 * on failure.
261 *
262 */
263int sel_netnode_sid(void *addr, u16 family, u32 *sid)
264{
265	struct sel_netnode *node;
266
267	rcu_read_lock();
268	node = sel_netnode_find(addr, family);
269	if (node != NULL) {
270		*sid = node->nsec.sid;
271		rcu_read_unlock();
272		return 0;
273	}
274	rcu_read_unlock();
275
276	return sel_netnode_sid_slow(addr, family, sid);
277}
278
279/**
280 * sel_netnode_flush - Flush the entire network address table
281 *
282 * Description:
283 * Remove all entries from the network address table.
284 *
285 */
286void sel_netnode_flush(void)
287{
288	unsigned int idx;
289	struct sel_netnode *node, *node_tmp;
290
291	spin_lock_bh(&sel_netnode_lock);
292	for (idx = 0; idx < SEL_NETNODE_HASH_SIZE; idx++) {
293		list_for_each_entry_safe(node, node_tmp,
294					 &sel_netnode_hash[idx].list, list) {
295				list_del_rcu(&node->list);
296				kfree_rcu(node, rcu);
297		}
298		sel_netnode_hash[idx].size = 0;
299	}
300	spin_unlock_bh(&sel_netnode_lock);
301}
302
303static __init int sel_netnode_init(void)
304{
305	int iter;
306
307	if (!selinux_enabled)
308		return 0;
309
310	for (iter = 0; iter < SEL_NETNODE_HASH_SIZE; iter++) {
311		INIT_LIST_HEAD(&sel_netnode_hash[iter].list);
312		sel_netnode_hash[iter].size = 0;
313	}
314
315	return 0;
316}
317
318__initcall(sel_netnode_init);
v6.13.7
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * Network node table
  4 *
  5 * SELinux must keep a mapping of network nodes to labels/SIDs.  This
  6 * mapping is maintained as part of the normal policy but a fast cache is
  7 * needed to reduce the lookup overhead since most of these queries happen on
  8 * a per-packet basis.
  9 *
 10 * Author: Paul Moore <paul@paul-moore.com>
 11 *
 12 * This code is heavily based on the "netif" concept originally developed by
 13 * James Morris <jmorris@redhat.com>
 14 *   (see security/selinux/netif.c for more information)
 
 15 */
 16
 17/*
 18 * (c) Copyright Hewlett-Packard Development Company, L.P., 2007
 
 
 
 
 
 
 
 
 
 
 19 */
 20
 21#include <linux/types.h>
 22#include <linux/rcupdate.h>
 23#include <linux/list.h>
 24#include <linux/slab.h>
 25#include <linux/spinlock.h>
 26#include <linux/in.h>
 27#include <linux/in6.h>
 28#include <linux/ip.h>
 29#include <linux/ipv6.h>
 30#include <net/ip.h>
 31#include <net/ipv6.h>
 32
 33#include "netnode.h"
 34#include "objsec.h"
 35
 36#define SEL_NETNODE_HASH_SIZE       256
 37#define SEL_NETNODE_HASH_BKT_LIMIT   16
 38
 39struct sel_netnode_bkt {
 40	unsigned int size;
 41	struct list_head list;
 42};
 43
 44struct sel_netnode {
 45	struct netnode_security_struct nsec;
 46
 47	struct list_head list;
 48	struct rcu_head rcu;
 49};
 50
 51/* NOTE: we are using a combined hash table for both IPv4 and IPv6, the reason
 52 * for this is that I suspect most users will not make heavy use of both
 53 * address families at the same time so one table will usually end up wasted,
 54 * if this becomes a problem we can always add a hash table for each address
 55 * family later */
 56
 
 57static DEFINE_SPINLOCK(sel_netnode_lock);
 58static struct sel_netnode_bkt sel_netnode_hash[SEL_NETNODE_HASH_SIZE];
 59
 60/**
 61 * sel_netnode_hashfn_ipv4 - IPv4 hashing function for the node table
 62 * @addr: IPv4 address
 63 *
 64 * Description:
 65 * This is the IPv4 hashing function for the node interface table, it returns
 66 * the bucket number for the given IP address.
 67 *
 68 */
 69static unsigned int sel_netnode_hashfn_ipv4(__be32 addr)
 70{
 71	/* at some point we should determine if the mismatch in byte order
 72	 * affects the hash function dramatically */
 73	return (addr & (SEL_NETNODE_HASH_SIZE - 1));
 74}
 75
 76/**
 77 * sel_netnode_hashfn_ipv6 - IPv6 hashing function for the node table
 78 * @addr: IPv6 address
 79 *
 80 * Description:
 81 * This is the IPv6 hashing function for the node interface table, it returns
 82 * the bucket number for the given IP address.
 83 *
 84 */
 85static unsigned int sel_netnode_hashfn_ipv6(const struct in6_addr *addr)
 86{
 87	/* just hash the least significant 32 bits to keep things fast (they
 88	 * are the most likely to be different anyway), we can revisit this
 89	 * later if needed */
 90	return (addr->s6_addr32[3] & (SEL_NETNODE_HASH_SIZE - 1));
 91}
 92
 93/**
 94 * sel_netnode_find - Search for a node record
 95 * @addr: IP address
 96 * @family: address family
 97 *
 98 * Description:
 99 * Search the network node table and return the record matching @addr.  If an
100 * entry can not be found in the table return NULL.
101 *
102 */
103static struct sel_netnode *sel_netnode_find(const void *addr, u16 family)
104{
105	unsigned int idx;
106	struct sel_netnode *node;
107
108	switch (family) {
109	case PF_INET:
110		idx = sel_netnode_hashfn_ipv4(*(const __be32 *)addr);
111		break;
112	case PF_INET6:
113		idx = sel_netnode_hashfn_ipv6(addr);
114		break;
115	default:
116		BUG();
117		return NULL;
118	}
119
120	list_for_each_entry_rcu(node, &sel_netnode_hash[idx].list, list)
121		if (node->nsec.family == family)
122			switch (family) {
123			case PF_INET:
124				if (node->nsec.addr.ipv4 == *(const __be32 *)addr)
125					return node;
126				break;
127			case PF_INET6:
128				if (ipv6_addr_equal(&node->nsec.addr.ipv6,
129						    addr))
130					return node;
131				break;
132			}
133
134	return NULL;
135}
136
137/**
138 * sel_netnode_insert - Insert a new node into the table
139 * @node: the new node record
140 *
141 * Description:
142 * Add a new node record to the network address hash table.
143 *
144 */
145static void sel_netnode_insert(struct sel_netnode *node)
146{
147	unsigned int idx;
148
149	switch (node->nsec.family) {
150	case PF_INET:
151		idx = sel_netnode_hashfn_ipv4(node->nsec.addr.ipv4);
152		break;
153	case PF_INET6:
154		idx = sel_netnode_hashfn_ipv6(&node->nsec.addr.ipv6);
155		break;
156	default:
157		BUG();
158		return;
159	}
160
161	/* we need to impose a limit on the growth of the hash table so check
162	 * this bucket to make sure it is within the specified bounds */
163	list_add_rcu(&node->list, &sel_netnode_hash[idx].list);
164	if (sel_netnode_hash[idx].size == SEL_NETNODE_HASH_BKT_LIMIT) {
165		struct sel_netnode *tail;
166		tail = list_entry(
167			rcu_dereference_protected(
168				list_tail_rcu(&sel_netnode_hash[idx].list),
169				lockdep_is_held(&sel_netnode_lock)),
170			struct sel_netnode, list);
171		list_del_rcu(&tail->list);
172		kfree_rcu(tail, rcu);
173	} else
174		sel_netnode_hash[idx].size++;
175}
176
177/**
178 * sel_netnode_sid_slow - Lookup the SID of a network address using the policy
179 * @addr: the IP address
180 * @family: the address family
181 * @sid: node SID
182 *
183 * Description:
184 * This function determines the SID of a network address by querying the
185 * security policy.  The result is added to the network address table to
186 * speedup future queries.  Returns zero on success, negative values on
187 * failure.
188 *
189 */
190static int sel_netnode_sid_slow(void *addr, u16 family, u32 *sid)
191{
192	int ret;
193	struct sel_netnode *node;
194	struct sel_netnode *new;
195
196	spin_lock_bh(&sel_netnode_lock);
197	node = sel_netnode_find(addr, family);
198	if (node != NULL) {
199		*sid = node->nsec.sid;
200		spin_unlock_bh(&sel_netnode_lock);
201		return 0;
202	}
203
204	new = kzalloc(sizeof(*new), GFP_ATOMIC);
 
 
205	switch (family) {
206	case PF_INET:
207		ret = security_node_sid(PF_INET,
208					addr, sizeof(struct in_addr), sid);
209		if (new)
210			new->nsec.addr.ipv4 = *(__be32 *)addr;
211		break;
212	case PF_INET6:
213		ret = security_node_sid(PF_INET6,
214					addr, sizeof(struct in6_addr), sid);
215		if (new)
216			new->nsec.addr.ipv6 = *(struct in6_addr *)addr;
217		break;
218	default:
219		BUG();
220		ret = -EINVAL;
221	}
222	if (ret == 0 && new) {
223		new->nsec.family = family;
224		new->nsec.sid = *sid;
225		sel_netnode_insert(new);
226	} else
227		kfree(new);
228
 
229	spin_unlock_bh(&sel_netnode_lock);
230	if (unlikely(ret))
231		pr_warn("SELinux: failure in %s(), unable to determine network node label\n",
232			__func__);
 
 
 
233	return ret;
234}
235
236/**
237 * sel_netnode_sid - Lookup the SID of a network address
238 * @addr: the IP address
239 * @family: the address family
240 * @sid: node SID
241 *
242 * Description:
243 * This function determines the SID of a network address using the fastest
244 * method possible.  First the address table is queried, but if an entry
245 * can't be found then the policy is queried and the result is added to the
246 * table to speedup future queries.  Returns zero on success, negative values
247 * on failure.
248 *
249 */
250int sel_netnode_sid(void *addr, u16 family, u32 *sid)
251{
252	struct sel_netnode *node;
253
254	rcu_read_lock();
255	node = sel_netnode_find(addr, family);
256	if (node != NULL) {
257		*sid = node->nsec.sid;
258		rcu_read_unlock();
259		return 0;
260	}
261	rcu_read_unlock();
262
263	return sel_netnode_sid_slow(addr, family, sid);
264}
265
266/**
267 * sel_netnode_flush - Flush the entire network address table
268 *
269 * Description:
270 * Remove all entries from the network address table.
271 *
272 */
273void sel_netnode_flush(void)
274{
275	unsigned int idx;
276	struct sel_netnode *node, *node_tmp;
277
278	spin_lock_bh(&sel_netnode_lock);
279	for (idx = 0; idx < SEL_NETNODE_HASH_SIZE; idx++) {
280		list_for_each_entry_safe(node, node_tmp,
281					 &sel_netnode_hash[idx].list, list) {
282				list_del_rcu(&node->list);
283				kfree_rcu(node, rcu);
284		}
285		sel_netnode_hash[idx].size = 0;
286	}
287	spin_unlock_bh(&sel_netnode_lock);
288}
289
290static __init int sel_netnode_init(void)
291{
292	int iter;
293
294	if (!selinux_enabled_boot)
295		return 0;
296
297	for (iter = 0; iter < SEL_NETNODE_HASH_SIZE; iter++) {
298		INIT_LIST_HEAD(&sel_netnode_hash[iter].list);
299		sel_netnode_hash[iter].size = 0;
300	}
301
302	return 0;
303}
304
305__initcall(sel_netnode_init);