Branch data Line data Source code
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@paul-moore.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_hashfn - Hashing function for the port table
72 : : * @pnum: port number
73 : : *
74 : : * Description:
75 : : * This is the hashing function for the port table, it returns the bucket
76 : : * number for the given port.
77 : : *
78 : : */
79 : : static unsigned int sel_netport_hashfn(u16 pnum)
80 : : {
81 : 0 : return (pnum & (SEL_NETPORT_HASH_SIZE - 1));
82 : : }
83 : :
84 : : /**
85 : : * sel_netport_find - Search for a port record
86 : : * @protocol: protocol
87 : : * @port: pnum
88 : : *
89 : : * Description:
90 : : * Search the network port table and return the matching record. If an entry
91 : : * can not be found in the table return NULL.
92 : : *
93 : : */
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 [ # # ][ # # ]: 0 : list_for_each_entry_rcu(port, &sel_netport_hash[idx].list, list)
101 [ # # ][ # # ]: 0 : if (port->psec.port == pnum && port->psec.protocol == protocol)
[ # # ][ # # ]
102 : : return port;
103 : :
104 : : return NULL;
105 : : }
106 : :
107 : : /**
108 : : * sel_netport_insert - Insert a new port into the table
109 : : * @port: the new port record
110 : : *
111 : : * Description:
112 : : * Add a new port record to the network address hash table.
113 : : *
114 : : */
115 : 0 : 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 : 0 : idx = sel_netport_hashfn(port->psec.port);
122 : 0 : list_add_rcu(&port->list, &sel_netport_hash[idx].list);
123 [ # # ]: 0 : if (sel_netport_hash[idx].size == SEL_NETPORT_HASH_BKT_LIMIT) {
124 : : struct sel_netport *tail;
125 : 0 : tail = list_entry(
126 : : rcu_dereference_protected(
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 : 0 : kfree_rcu(tail, rcu);
132 : : } else
133 : 0 : sel_netport_hash[idx].size++;
134 : 0 : }
135 : :
136 : : /**
137 : : * sel_netport_sid_slow - Lookup the SID of a network address using the policy
138 : : * @protocol: protocol
139 : : * @pnum: port
140 : : * @sid: port SID
141 : : *
142 : : * Description:
143 : : * This function determines the SID of a network port by quering the security
144 : : * policy. The result is added to the network port table to speedup future
145 : : * queries. Returns zero on success, negative values on failure.
146 : : *
147 : : */
148 : 0 : 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 [ # # ]: 0 : if (port != NULL) {
157 : 0 : *sid = port->psec.sid;
158 : : spin_unlock_bh(&sel_netport_lock);
159 : 0 : return 0;
160 : : }
161 : : new = kzalloc(sizeof(*new), GFP_ATOMIC);
162 [ # # ]: 0 : if (new == NULL)
163 : : goto out;
164 : 0 : ret = security_port_sid(protocol, pnum, sid);
165 [ # # ]: 0 : if (ret != 0)
166 : : goto out;
167 : :
168 : 0 : new->psec.port = pnum;
169 : 0 : new->psec.protocol = protocol;
170 : 0 : new->psec.sid = *sid;
171 : 0 : sel_netport_insert(new);
172 : :
173 : : out:
174 : : spin_unlock_bh(&sel_netport_lock);
175 [ # # ]: 0 : if (unlikely(ret)) {
176 : 0 : printk(KERN_WARNING
177 : : "SELinux: failure in sel_netport_sid_slow(),"
178 : : " unable to determine network port label\n");
179 : 0 : kfree(new);
180 : : }
181 : 0 : return ret;
182 : : }
183 : :
184 : : /**
185 : : * sel_netport_sid - Lookup the SID of a network port
186 : : * @protocol: protocol
187 : : * @pnum: port
188 : : * @sid: port SID
189 : : *
190 : : * Description:
191 : : * This function determines the SID of a network port using the fastest method
192 : : * possible. First the port table is queried, but if an entry can't be found
193 : : * then the policy is queried and the result is added to the table to speedup
194 : : * future queries. Returns zero on success, negative values on failure.
195 : : *
196 : : */
197 : 0 : 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 [ # # ]: 0 : if (port != NULL) {
204 : 0 : *sid = port->psec.sid;
205 : : rcu_read_unlock();
206 : 0 : return 0;
207 : : }
208 : : rcu_read_unlock();
209 : :
210 : 0 : return sel_netport_sid_slow(protocol, pnum, sid);
211 : : }
212 : :
213 : : /**
214 : : * sel_netport_flush - Flush the entire network port table
215 : : *
216 : : * Description:
217 : : * Remove all entries from the network address table.
218 : : *
219 : : */
220 : 0 : 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 [ # # ]: 0 : for (idx = 0; idx < SEL_NETPORT_HASH_SIZE; idx++) {
227 [ # # ]: 0 : list_for_each_entry_safe(port, port_tmp,
228 : : &sel_netport_hash[idx].list, list) {
229 : : list_del_rcu(&port->list);
230 : 0 : kfree_rcu(port, rcu);
231 : : }
232 : 0 : sel_netport_hash[idx].size = 0;
233 : : }
234 : : spin_unlock_bh(&sel_netport_lock);
235 : 0 : }
236 : :
237 : 0 : static int sel_netport_avc_callback(u32 event)
238 : : {
239 [ # # ]: 0 : if (event == AVC_CALLBACK_RESET) {
240 : 0 : sel_netport_flush();
241 : 0 : synchronize_net();
242 : : }
243 : 0 : return 0;
244 : : }
245 : :
246 : 0 : static __init int sel_netport_init(void)
247 : : {
248 : : int iter;
249 : : int ret;
250 : :
251 [ # # ]: 0 : if (!selinux_enabled)
252 : : return 0;
253 : :
254 [ # # ]: 0 : for (iter = 0; iter < SEL_NETPORT_HASH_SIZE; iter++) {
255 : 0 : INIT_LIST_HEAD(&sel_netport_hash[iter].list);
256 : 0 : sel_netport_hash[iter].size = 0;
257 : : }
258 : :
259 : 0 : ret = avc_add_callback(sel_netport_avc_callback, AVC_CALLBACK_RESET);
260 [ # # ]: 0 : if (ret != 0)
261 : 0 : panic("avc_add_callback() failed, error %d\n", ret);
262 : :
263 : : return ret;
264 : : }
265 : :
266 : : __initcall(sel_netport_init);
|