Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
netport.c
Go to the documentation of this file.
1 /*
2  * Network port table
3  *
4  * SELinux must keep a mapping of network ports 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.
7  *
8  * Author: Paul Moore <[email protected]>
9  *
10  * This code is heavily based on the "netif" concept originally developed by
11  * James Morris <[email protected]>
12  * (see security/selinux/netif.c for more information)
13  *
14  */
15 
16 /*
17  * (c) Copyright Hewlett-Packard Development Company, L.P., 2008
18  *
19  * This program is free software: you can redistribute it and/or modify
20  * it under the terms of version 2 of the GNU General Public License as
21  * published by the Free Software Foundation.
22  *
23  * This program is distributed in the hope that it will be useful,
24  * but WITHOUT ANY WARRANTY; without even the implied warranty of
25  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26  * GNU General Public License for more details.
27  *
28  */
29 
30 #include <linux/types.h>
31 #include <linux/rcupdate.h>
32 #include <linux/list.h>
33 #include <linux/slab.h>
34 #include <linux/spinlock.h>
35 #include <linux/in.h>
36 #include <linux/in6.h>
37 #include <linux/ip.h>
38 #include <linux/ipv6.h>
39 #include <net/ip.h>
40 #include <net/ipv6.h>
41 
42 #include "netport.h"
43 #include "objsec.h"
44 
45 #define SEL_NETPORT_HASH_SIZE 256
46 #define SEL_NETPORT_HASH_BKT_LIMIT 16
47 
49  int size;
50  struct list_head list;
51 };
52 
53 struct sel_netport {
55 
56  struct list_head list;
57  struct rcu_head rcu;
58 };
59 
60 /* NOTE: we are using a combined hash table for both IPv4 and IPv6, the reason
61  * for this is that I suspect most users will not make heavy use of both
62  * address families at the same time so one table will usually end up wasted,
63  * if this becomes a problem we can always add a hash table for each address
64  * family later */
65 
66 static LIST_HEAD(sel_netport_list);
67 static DEFINE_SPINLOCK(sel_netport_lock);
68 static struct sel_netport_bkt sel_netport_hash[SEL_NETPORT_HASH_SIZE];
69 
79 static unsigned int sel_netport_hashfn(u16 pnum)
80 {
81  return (pnum & (SEL_NETPORT_HASH_SIZE - 1));
82 }
83 
94 static struct sel_netport *sel_netport_find(u8 protocol, u16 pnum)
95 {
96  unsigned int idx;
97  struct sel_netport *port;
98 
99  idx = sel_netport_hashfn(pnum);
100  list_for_each_entry_rcu(port, &sel_netport_hash[idx].list, list)
101  if (port->psec.port == pnum && port->psec.protocol == protocol)
102  return port;
103 
104  return NULL;
105 }
106 
115 static void sel_netport_insert(struct sel_netport *port)
116 {
117  unsigned int idx;
118 
119  /* we need to impose a limit on the growth of the hash table so check
120  * this bucket to make sure it is within the specified bounds */
121  idx = sel_netport_hashfn(port->psec.port);
122  list_add_rcu(&port->list, &sel_netport_hash[idx].list);
123  if (sel_netport_hash[idx].size == SEL_NETPORT_HASH_BKT_LIMIT) {
124  struct sel_netport *tail;
125  tail = list_entry(
127  sel_netport_hash[idx].list.prev,
128  lockdep_is_held(&sel_netport_lock)),
129  struct sel_netport, list);
130  list_del_rcu(&tail->list);
131  kfree_rcu(tail, rcu);
132  } else
133  sel_netport_hash[idx].size++;
134 }
135 
148 static int sel_netport_sid_slow(u8 protocol, u16 pnum, u32 *sid)
149 {
150  int ret = -ENOMEM;
151  struct sel_netport *port;
152  struct sel_netport *new = NULL;
153 
154  spin_lock_bh(&sel_netport_lock);
155  port = sel_netport_find(protocol, pnum);
156  if (port != NULL) {
157  *sid = port->psec.sid;
158  spin_unlock_bh(&sel_netport_lock);
159  return 0;
160  }
161  new = kzalloc(sizeof(*new), GFP_ATOMIC);
162  if (new == NULL)
163  goto out;
164  ret = security_port_sid(protocol, pnum, sid);
165  if (ret != 0)
166  goto out;
167 
168  new->psec.port = pnum;
169  new->psec.protocol = protocol;
170  new->psec.sid = *sid;
171  sel_netport_insert(new);
172 
173 out:
174  spin_unlock_bh(&sel_netport_lock);
175  if (unlikely(ret)) {
177  "SELinux: failure in sel_netport_sid_slow(),"
178  " unable to determine network port label\n");
179  kfree(new);
180  }
181  return ret;
182 }
183 
197 int sel_netport_sid(u8 protocol, u16 pnum, u32 *sid)
198 {
199  struct sel_netport *port;
200 
201  rcu_read_lock();
202  port = sel_netport_find(protocol, pnum);
203  if (port != NULL) {
204  *sid = port->psec.sid;
205  rcu_read_unlock();
206  return 0;
207  }
208  rcu_read_unlock();
209 
210  return sel_netport_sid_slow(protocol, pnum, sid);
211 }
212 
220 static void sel_netport_flush(void)
221 {
222  unsigned int idx;
223  struct sel_netport *port, *port_tmp;
224 
225  spin_lock_bh(&sel_netport_lock);
226  for (idx = 0; idx < SEL_NETPORT_HASH_SIZE; idx++) {
227  list_for_each_entry_safe(port, port_tmp,
228  &sel_netport_hash[idx].list, list) {
229  list_del_rcu(&port->list);
230  kfree_rcu(port, rcu);
231  }
232  sel_netport_hash[idx].size = 0;
233  }
234  spin_unlock_bh(&sel_netport_lock);
235 }
236 
237 static int sel_netport_avc_callback(u32 event)
238 {
239  if (event == AVC_CALLBACK_RESET) {
240  sel_netport_flush();
241  synchronize_net();
242  }
243  return 0;
244 }
245 
246 static __init int sel_netport_init(void)
247 {
248  int iter;
249  int ret;
250 
251  if (!selinux_enabled)
252  return 0;
253 
254  for (iter = 0; iter < SEL_NETPORT_HASH_SIZE; iter++) {
255  INIT_LIST_HEAD(&sel_netport_hash[iter].list);
256  sel_netport_hash[iter].size = 0;
257  }
258 
259  ret = avc_add_callback(sel_netport_avc_callback, AVC_CALLBACK_RESET);
260  if (ret != 0)
261  panic("avc_add_callback() failed, error %d\n", ret);
262 
263  return ret;
264 }
265 
266 __initcall(sel_netport_init);