Blame view

security/selinux/netnode.c 7.93 KB
224dfbd81   Paul Moore   SELinux: Add a ne...
1
2
3
4
5
6
7
8
  /*
   * Network node table
   *
   * SELinux must keep a mapping of network nodes to labels/SIDs.  This
   * mapping is maintained as part of the normal policy but a fast cache is
   * needed to reduce the lookup overhead since most of these queries happen on
   * a per-packet basis.
   *
82c21bfab   Paul Moore   doc: Update the e...
9
   * Author: Paul Moore <paul@paul-moore.com>
224dfbd81   Paul Moore   SELinux: Add a ne...
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
   *
   * This code is heavily based on the "netif" concept originally developed by
   * James Morris <jmorris@redhat.com>
   *   (see security/selinux/netif.c for more information)
   *
   */
  
  /*
   * (c) Copyright Hewlett-Packard Development Company, L.P., 2007
   *
   * This program is free software: you can redistribute it and/or modify
   * it under the terms of version 2 of the GNU General Public License as
   * published by the Free Software Foundation.
   *
   * 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.
   *
   */
  
  #include <linux/types.h>
  #include <linux/rcupdate.h>
  #include <linux/list.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
34
  #include <linux/slab.h>
224dfbd81   Paul Moore   SELinux: Add a ne...
35
36
37
38
39
40
41
  #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>
224dfbd81   Paul Moore   SELinux: Add a ne...
42

a639e7ca8   Paul Moore   SELinux: Made net...
43
  #include "netnode.h"
224dfbd81   Paul Moore   SELinux: Add a ne...
44
45
46
47
  #include "objsec.h"
  
  #define SEL_NETNODE_HASH_SIZE       256
  #define SEL_NETNODE_HASH_BKT_LIMIT   16
a639e7ca8   Paul Moore   SELinux: Made net...
48
49
50
51
  struct sel_netnode_bkt {
  	unsigned int size;
  	struct list_head list;
  };
224dfbd81   Paul Moore   SELinux: Add a ne...
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
  struct sel_netnode {
  	struct netnode_security_struct nsec;
  
  	struct list_head list;
  	struct rcu_head rcu;
  };
  
  /* NOTE: we are using a combined hash table for both IPv4 and IPv6, the reason
   * for this is that I suspect most users will not make heavy use of both
   * address families at the same time so one table will usually end up wasted,
   * if this becomes a problem we can always add a hash table for each address
   * family later */
  
  static LIST_HEAD(sel_netnode_list);
  static DEFINE_SPINLOCK(sel_netnode_lock);
a639e7ca8   Paul Moore   SELinux: Made net...
67
  static struct sel_netnode_bkt sel_netnode_hash[SEL_NETNODE_HASH_SIZE];
224dfbd81   Paul Moore   SELinux: Add a ne...
68
69
  
  /**
224dfbd81   Paul Moore   SELinux: Add a ne...
70
71
72
73
74
75
76
77
   * sel_netnode_hashfn_ipv4 - IPv4 hashing function for the node table
   * @addr: IPv4 address
   *
   * Description:
   * This is the IPv4 hashing function for the node interface table, it returns
   * the bucket number for the given IP address.
   *
   */
a639e7ca8   Paul Moore   SELinux: Made net...
78
  static unsigned int sel_netnode_hashfn_ipv4(__be32 addr)
224dfbd81   Paul Moore   SELinux: Add a ne...
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
  {
  	/* at some point we should determine if the mismatch in byte order
  	 * affects the hash function dramatically */
  	return (addr & (SEL_NETNODE_HASH_SIZE - 1));
  }
  
  /**
   * sel_netnode_hashfn_ipv6 - IPv6 hashing function for the node table
   * @addr: IPv6 address
   *
   * Description:
   * This is the IPv6 hashing function for the node interface table, it returns
   * the bucket number for the given IP address.
   *
   */
a639e7ca8   Paul Moore   SELinux: Made net...
94
  static unsigned int sel_netnode_hashfn_ipv6(const struct in6_addr *addr)
224dfbd81   Paul Moore   SELinux: Add a ne...
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
  {
  	/* just hash the least significant 32 bits to keep things fast (they
  	 * are the most likely to be different anyway), we can revisit this
  	 * later if needed */
  	return (addr->s6_addr32[3] & (SEL_NETNODE_HASH_SIZE - 1));
  }
  
  /**
   * sel_netnode_find - Search for a node record
   * @addr: IP address
   * @family: address family
   *
   * Description:
   * Search the network node table and return the record matching @addr.  If an
   * entry can not be found in the table return NULL.
   *
   */
  static struct sel_netnode *sel_netnode_find(const void *addr, u16 family)
  {
a639e7ca8   Paul Moore   SELinux: Made net...
114
  	unsigned int idx;
224dfbd81   Paul Moore   SELinux: Add a ne...
115
116
117
118
119
120
121
122
123
124
125
  	struct sel_netnode *node;
  
  	switch (family) {
  	case PF_INET:
  		idx = sel_netnode_hashfn_ipv4(*(__be32 *)addr);
  		break;
  	case PF_INET6:
  		idx = sel_netnode_hashfn_ipv6(addr);
  		break;
  	default:
  		BUG();
a35c6c836   Eric Paris   SELinux: silence ...
126
  		return NULL;
224dfbd81   Paul Moore   SELinux: Add a ne...
127
  	}
a639e7ca8   Paul Moore   SELinux: Made net...
128
  	list_for_each_entry_rcu(node, &sel_netnode_hash[idx].list, list)
224dfbd81   Paul Moore   SELinux: Add a ne...
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
  		if (node->nsec.family == family)
  			switch (family) {
  			case PF_INET:
  				if (node->nsec.addr.ipv4 == *(__be32 *)addr)
  					return node;
  				break;
  			case PF_INET6:
  				if (ipv6_addr_equal(&node->nsec.addr.ipv6,
  						    addr))
  					return node;
  				break;
  			}
  
  	return NULL;
  }
  
  /**
   * sel_netnode_insert - Insert a new node into the table
   * @node: the new node record
   *
   * Description:
a639e7ca8   Paul Moore   SELinux: Made net...
150
   * Add a new node record to the network address hash table.
224dfbd81   Paul Moore   SELinux: Add a ne...
151
152
   *
   */
a639e7ca8   Paul Moore   SELinux: Made net...
153
  static void sel_netnode_insert(struct sel_netnode *node)
224dfbd81   Paul Moore   SELinux: Add a ne...
154
  {
a639e7ca8   Paul Moore   SELinux: Made net...
155
  	unsigned int idx;
224dfbd81   Paul Moore   SELinux: Add a ne...
156
157
158
159
160
161
162
163
164
165
  
  	switch (node->nsec.family) {
  	case PF_INET:
  		idx = sel_netnode_hashfn_ipv4(node->nsec.addr.ipv4);
  		break;
  	case PF_INET6:
  		idx = sel_netnode_hashfn_ipv6(&node->nsec.addr.ipv6);
  		break;
  	default:
  		BUG();
b04eea886   Paul Moore   selinux: fix prob...
166
  		return;
224dfbd81   Paul Moore   SELinux: Add a ne...
167
  	}
a639e7ca8   Paul Moore   SELinux: Made net...
168

224dfbd81   Paul Moore   SELinux: Add a ne...
169
170
  	/* we need to impose a limit on the growth of the hash table so check
  	 * this bucket to make sure it is within the specified bounds */
a639e7ca8   Paul Moore   SELinux: Made net...
171
172
173
174
  	list_add_rcu(&node->list, &sel_netnode_hash[idx].list);
  	if (sel_netnode_hash[idx].size == SEL_NETNODE_HASH_BKT_LIMIT) {
  		struct sel_netnode *tail;
  		tail = list_entry(
88a693b5c   Dave Jones   selinux: fix sel_...
175
176
  			rcu_dereference_protected(sel_netnode_hash[idx].list.prev,
  						  lockdep_is_held(&sel_netnode_lock)),
a639e7ca8   Paul Moore   SELinux: Made net...
177
178
  			struct sel_netnode, list);
  		list_del_rcu(&tail->list);
9801c60e9   Lai Jiangshan   security,rcu: Con...
179
  		kfree_rcu(tail, rcu);
a639e7ca8   Paul Moore   SELinux: Made net...
180
181
  	} else
  		sel_netnode_hash[idx].size++;
224dfbd81   Paul Moore   SELinux: Add a ne...
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
  }
  
  /**
   * sel_netnode_sid_slow - Lookup the SID of a network address using the policy
   * @addr: the IP address
   * @family: the address family
   * @sid: node SID
   *
   * Description:
   * This function determines the SID of a network address by quering the
   * security policy.  The result is added to the network address table to
   * speedup future queries.  Returns zero on success, negative values on
   * failure.
   *
   */
  static int sel_netnode_sid_slow(void *addr, u16 family, u32 *sid)
  {
a639e7ca8   Paul Moore   SELinux: Made net...
199
  	int ret = -ENOMEM;
224dfbd81   Paul Moore   SELinux: Add a ne...
200
201
202
203
204
205
206
  	struct sel_netnode *node;
  	struct sel_netnode *new = NULL;
  
  	spin_lock_bh(&sel_netnode_lock);
  	node = sel_netnode_find(addr, family);
  	if (node != NULL) {
  		*sid = node->nsec.sid;
a639e7ca8   Paul Moore   SELinux: Made net...
207
208
  		spin_unlock_bh(&sel_netnode_lock);
  		return 0;
224dfbd81   Paul Moore   SELinux: Add a ne...
209
210
  	}
  	new = kzalloc(sizeof(*new), GFP_ATOMIC);
a639e7ca8   Paul Moore   SELinux: Made net...
211
  	if (new == NULL)
224dfbd81   Paul Moore   SELinux: Add a ne...
212
  		goto out;
224dfbd81   Paul Moore   SELinux: Add a ne...
213
214
215
  	switch (family) {
  	case PF_INET:
  		ret = security_node_sid(PF_INET,
a639e7ca8   Paul Moore   SELinux: Made net...
216
  					addr, sizeof(struct in_addr), sid);
224dfbd81   Paul Moore   SELinux: Add a ne...
217
218
219
220
  		new->nsec.addr.ipv4 = *(__be32 *)addr;
  		break;
  	case PF_INET6:
  		ret = security_node_sid(PF_INET6,
a639e7ca8   Paul Moore   SELinux: Made net...
221
  					addr, sizeof(struct in6_addr), sid);
4e3fd7a06   Alexey Dobriyan   net: remove ipv6_...
222
  		new->nsec.addr.ipv6 = *(struct in6_addr *)addr;
224dfbd81   Paul Moore   SELinux: Add a ne...
223
224
225
  		break;
  	default:
  		BUG();
b04eea886   Paul Moore   selinux: fix prob...
226
  		ret = -EINVAL;
224dfbd81   Paul Moore   SELinux: Add a ne...
227
228
229
  	}
  	if (ret != 0)
  		goto out;
a639e7ca8   Paul Moore   SELinux: Made net...
230

224dfbd81   Paul Moore   SELinux: Add a ne...
231
  	new->nsec.family = family;
a639e7ca8   Paul Moore   SELinux: Made net...
232
233
  	new->nsec.sid = *sid;
  	sel_netnode_insert(new);
224dfbd81   Paul Moore   SELinux: Add a ne...
234
235
236
  
  out:
  	spin_unlock_bh(&sel_netnode_lock);
71f1cb05f   Paul Moore   SELinux: Add warn...
237
238
239
240
241
  	if (unlikely(ret)) {
  		printk(KERN_WARNING
  		       "SELinux: failure in sel_netnode_sid_slow(),"
  		       " unable to determine network node label
  ");
224dfbd81   Paul Moore   SELinux: Add a ne...
242
  		kfree(new);
71f1cb05f   Paul Moore   SELinux: Add warn...
243
  	}
224dfbd81   Paul Moore   SELinux: Add a ne...
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
  	return ret;
  }
  
  /**
   * sel_netnode_sid - Lookup the SID of a network address
   * @addr: the IP address
   * @family: the address family
   * @sid: node SID
   *
   * Description:
   * This function determines the SID of a network address using the fastest
   * method possible.  First the address table is queried, but if an entry
   * can't be found then the policy is queried and the result is added to the
   * table to speedup future queries.  Returns zero on success, negative values
   * on failure.
   *
   */
  int sel_netnode_sid(void *addr, u16 family, u32 *sid)
  {
  	struct sel_netnode *node;
  
  	rcu_read_lock();
  	node = sel_netnode_find(addr, family);
  	if (node != NULL) {
  		*sid = node->nsec.sid;
  		rcu_read_unlock();
  		return 0;
  	}
  	rcu_read_unlock();
  
  	return sel_netnode_sid_slow(addr, family, sid);
  }
  
  /**
   * sel_netnode_flush - Flush the entire network address table
   *
   * Description:
   * Remove all entries from the network address table.
   *
   */
615e51fdd   Paul Moore   selinux: reduce t...
284
  void sel_netnode_flush(void)
224dfbd81   Paul Moore   SELinux: Add a ne...
285
  {
a639e7ca8   Paul Moore   SELinux: Made net...
286
287
  	unsigned int idx;
  	struct sel_netnode *node, *node_tmp;
224dfbd81   Paul Moore   SELinux: Add a ne...
288
289
  
  	spin_lock_bh(&sel_netnode_lock);
a639e7ca8   Paul Moore   SELinux: Made net...
290
291
292
293
  	for (idx = 0; idx < SEL_NETNODE_HASH_SIZE; idx++) {
  		list_for_each_entry_safe(node, node_tmp,
  					 &sel_netnode_hash[idx].list, list) {
  				list_del_rcu(&node->list);
9801c60e9   Lai Jiangshan   security,rcu: Con...
294
  				kfree_rcu(node, rcu);
a639e7ca8   Paul Moore   SELinux: Made net...
295
296
297
  		}
  		sel_netnode_hash[idx].size = 0;
  	}
224dfbd81   Paul Moore   SELinux: Add a ne...
298
299
  	spin_unlock_bh(&sel_netnode_lock);
  }
224dfbd81   Paul Moore   SELinux: Add a ne...
300
301
302
  static __init int sel_netnode_init(void)
  {
  	int iter;
224dfbd81   Paul Moore   SELinux: Add a ne...
303
304
305
  
  	if (!selinux_enabled)
  		return 0;
a639e7ca8   Paul Moore   SELinux: Made net...
306
307
308
309
  	for (iter = 0; iter < SEL_NETNODE_HASH_SIZE; iter++) {
  		INIT_LIST_HEAD(&sel_netnode_hash[iter].list);
  		sel_netnode_hash[iter].size = 0;
  	}
224dfbd81   Paul Moore   SELinux: Add a ne...
310

942ba3646   Paul Moore   selinux: remove u...
311
  	return 0;
224dfbd81   Paul Moore   SELinux: Add a ne...
312
313
314
  }
  
  __initcall(sel_netnode_init);