Root/
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 <paul.moore@hp.com> |
9 | * |
10 | * This code is heavily based on the "netif" concept originally developed by |
11 | * James Morris <jmorris@redhat.com> |
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 | |
48 | struct sel_netport_bkt { |
49 | int size; |
50 | struct list_head list; |
51 | }; |
52 | |
53 | struct sel_netport { |
54 | struct netport_security_struct psec; |
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 | |
70 | /** |
71 | * sel_netport_free - Frees a port entry |
72 | * @p: the entry's RCU field |
73 | * |
74 | * Description: |
75 | * This function is designed to be used as a callback to the call_rcu() |
76 | * function so that memory allocated to a hash table port entry can be |
77 | * released safely. |
78 | * |
79 | */ |
80 | static void sel_netport_free(struct rcu_head *p) |
81 | { |
82 | struct sel_netport *port = container_of(p, struct sel_netport, rcu); |
83 | kfree(port); |
84 | } |
85 | |
86 | /** |
87 | * sel_netport_hashfn - Hashing function for the port table |
88 | * @pnum: port number |
89 | * |
90 | * Description: |
91 | * This is the hashing function for the port table, it returns the bucket |
92 | * number for the given port. |
93 | * |
94 | */ |
95 | static unsigned int sel_netport_hashfn(u16 pnum) |
96 | { |
97 | return (pnum & (SEL_NETPORT_HASH_SIZE - 1)); |
98 | } |
99 | |
100 | /** |
101 | * sel_netport_find - Search for a port record |
102 | * @protocol: protocol |
103 | * @port: pnum |
104 | * |
105 | * Description: |
106 | * Search the network port table and return the matching record. If an entry |
107 | * can not be found in the table return NULL. |
108 | * |
109 | */ |
110 | static struct sel_netport *sel_netport_find(u8 protocol, u16 pnum) |
111 | { |
112 | unsigned int idx; |
113 | struct sel_netport *port; |
114 | |
115 | idx = sel_netport_hashfn(pnum); |
116 | list_for_each_entry_rcu(port, &sel_netport_hash[idx].list, list) |
117 | if (port->psec.port == pnum && port->psec.protocol == protocol) |
118 | return port; |
119 | |
120 | return NULL; |
121 | } |
122 | |
123 | /** |
124 | * sel_netport_insert - Insert a new port into the table |
125 | * @port: the new port record |
126 | * |
127 | * Description: |
128 | * Add a new port record to the network address hash table. |
129 | * |
130 | */ |
131 | static void sel_netport_insert(struct sel_netport *port) |
132 | { |
133 | unsigned int idx; |
134 | |
135 | /* we need to impose a limit on the growth of the hash table so check |
136 | * this bucket to make sure it is within the specified bounds */ |
137 | idx = sel_netport_hashfn(port->psec.port); |
138 | list_add_rcu(&port->list, &sel_netport_hash[idx].list); |
139 | if (sel_netport_hash[idx].size == SEL_NETPORT_HASH_BKT_LIMIT) { |
140 | struct sel_netport *tail; |
141 | tail = list_entry( |
142 | rcu_dereference(sel_netport_hash[idx].list.prev), |
143 | struct sel_netport, list); |
144 | list_del_rcu(&tail->list); |
145 | call_rcu(&tail->rcu, sel_netport_free); |
146 | } else |
147 | sel_netport_hash[idx].size++; |
148 | } |
149 | |
150 | /** |
151 | * sel_netport_sid_slow - Lookup the SID of a network address using the policy |
152 | * @protocol: protocol |
153 | * @pnum: port |
154 | * @sid: port SID |
155 | * |
156 | * Description: |
157 | * This function determines the SID of a network port by quering the security |
158 | * policy. The result is added to the network port table to speedup future |
159 | * queries. Returns zero on success, negative values on failure. |
160 | * |
161 | */ |
162 | static int sel_netport_sid_slow(u8 protocol, u16 pnum, u32 *sid) |
163 | { |
164 | int ret = -ENOMEM; |
165 | struct sel_netport *port; |
166 | struct sel_netport *new = NULL; |
167 | |
168 | spin_lock_bh(&sel_netport_lock); |
169 | port = sel_netport_find(protocol, pnum); |
170 | if (port != NULL) { |
171 | *sid = port->psec.sid; |
172 | spin_unlock_bh(&sel_netport_lock); |
173 | return 0; |
174 | } |
175 | new = kzalloc(sizeof(*new), GFP_ATOMIC); |
176 | if (new == NULL) |
177 | goto out; |
178 | ret = security_port_sid(protocol, pnum, sid); |
179 | if (ret != 0) |
180 | goto out; |
181 | |
182 | new->psec.port = pnum; |
183 | new->psec.protocol = protocol; |
184 | new->psec.sid = *sid; |
185 | sel_netport_insert(new); |
186 | |
187 | out: |
188 | spin_unlock_bh(&sel_netport_lock); |
189 | if (unlikely(ret)) { |
190 | printk(KERN_WARNING |
191 | "SELinux: failure in sel_netport_sid_slow()," |
192 | " unable to determine network port label\n"); |
193 | kfree(new); |
194 | } |
195 | return ret; |
196 | } |
197 | |
198 | /** |
199 | * sel_netport_sid - Lookup the SID of a network port |
200 | * @protocol: protocol |
201 | * @pnum: port |
202 | * @sid: port SID |
203 | * |
204 | * Description: |
205 | * This function determines the SID of a network port using the fastest method |
206 | * possible. First the port table is queried, but if an entry can't be found |
207 | * then the policy is queried and the result is added to the table to speedup |
208 | * future queries. Returns zero on success, negative values on failure. |
209 | * |
210 | */ |
211 | int sel_netport_sid(u8 protocol, u16 pnum, u32 *sid) |
212 | { |
213 | struct sel_netport *port; |
214 | |
215 | rcu_read_lock(); |
216 | port = sel_netport_find(protocol, pnum); |
217 | if (port != NULL) { |
218 | *sid = port->psec.sid; |
219 | rcu_read_unlock(); |
220 | return 0; |
221 | } |
222 | rcu_read_unlock(); |
223 | |
224 | return sel_netport_sid_slow(protocol, pnum, sid); |
225 | } |
226 | |
227 | /** |
228 | * sel_netport_flush - Flush the entire network port table |
229 | * |
230 | * Description: |
231 | * Remove all entries from the network address table. |
232 | * |
233 | */ |
234 | static void sel_netport_flush(void) |
235 | { |
236 | unsigned int idx; |
237 | struct sel_netport *port, *port_tmp; |
238 | |
239 | spin_lock_bh(&sel_netport_lock); |
240 | for (idx = 0; idx < SEL_NETPORT_HASH_SIZE; idx++) { |
241 | list_for_each_entry_safe(port, port_tmp, |
242 | &sel_netport_hash[idx].list, list) { |
243 | list_del_rcu(&port->list); |
244 | call_rcu(&port->rcu, sel_netport_free); |
245 | } |
246 | sel_netport_hash[idx].size = 0; |
247 | } |
248 | spin_unlock_bh(&sel_netport_lock); |
249 | } |
250 | |
251 | static int sel_netport_avc_callback(u32 event, u32 ssid, u32 tsid, |
252 | u16 class, u32 perms, u32 *retained) |
253 | { |
254 | if (event == AVC_CALLBACK_RESET) { |
255 | sel_netport_flush(); |
256 | synchronize_net(); |
257 | } |
258 | return 0; |
259 | } |
260 | |
261 | static __init int sel_netport_init(void) |
262 | { |
263 | int iter; |
264 | int ret; |
265 | |
266 | if (!selinux_enabled) |
267 | return 0; |
268 | |
269 | for (iter = 0; iter < SEL_NETPORT_HASH_SIZE; iter++) { |
270 | INIT_LIST_HEAD(&sel_netport_hash[iter].list); |
271 | sel_netport_hash[iter].size = 0; |
272 | } |
273 | |
274 | ret = avc_add_callback(sel_netport_avc_callback, AVC_CALLBACK_RESET, |
275 | SECSID_NULL, SECSID_NULL, SECCLASS_NULL, 0); |
276 | if (ret != 0) |
277 | panic("avc_add_callback() failed, error %d\n", ret); |
278 | |
279 | return ret; |
280 | } |
281 | |
282 | __initcall(sel_netport_init); |
283 |
Branches:
ben-wpan
ben-wpan-stefan
javiroman/ks7010
jz-2.6.34
jz-2.6.34-rc5
jz-2.6.34-rc6
jz-2.6.34-rc7
jz-2.6.35
jz-2.6.36
jz-2.6.37
jz-2.6.38
jz-2.6.39
jz-3.0
jz-3.1
jz-3.11
jz-3.12
jz-3.13
jz-3.15
jz-3.16
jz-3.18-dt
jz-3.2
jz-3.3
jz-3.4
jz-3.5
jz-3.6
jz-3.6-rc2-pwm
jz-3.9
jz-3.9-clk
jz-3.9-rc8
jz47xx
jz47xx-2.6.38
master
Tags:
od-2011-09-04
od-2011-09-18
v2.6.34-rc5
v2.6.34-rc6
v2.6.34-rc7
v3.9