1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Network port table |
4 | * |
5 | * SELinux must keep a mapping of network ports 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. |
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 | * (c) Copyright Hewlett-Packard Development Company, L.P., 2008 |
18 | */ |
19 | |
20 | #include <linux/types.h> |
21 | #include <linux/rcupdate.h> |
22 | #include <linux/list.h> |
23 | #include <linux/slab.h> |
24 | #include <linux/spinlock.h> |
25 | #include <linux/in.h> |
26 | #include <linux/in6.h> |
27 | #include <linux/ip.h> |
28 | #include <linux/ipv6.h> |
29 | #include <net/ip.h> |
30 | #include <net/ipv6.h> |
31 | |
32 | #include "netport.h" |
33 | #include "objsec.h" |
34 | |
35 | #define SEL_NETPORT_HASH_SIZE 256 |
36 | #define SEL_NETPORT_HASH_BKT_LIMIT 16 |
37 | |
38 | struct sel_netport_bkt { |
39 | int size; |
40 | struct list_head list; |
41 | }; |
42 | |
43 | struct sel_netport { |
44 | struct netport_security_struct psec; |
45 | |
46 | struct list_head list; |
47 | struct rcu_head rcu; |
48 | }; |
49 | |
50 | /* NOTE: we are using a combined hash table for both IPv4 and IPv6, the reason |
51 | * for this is that I suspect most users will not make heavy use of both |
52 | * address families at the same time so one table will usually end up wasted, |
53 | * if this becomes a problem we can always add a hash table for each address |
54 | * family later */ |
55 | |
56 | static DEFINE_SPINLOCK(sel_netport_lock); |
57 | static struct sel_netport_bkt sel_netport_hash[SEL_NETPORT_HASH_SIZE]; |
58 | |
59 | /** |
60 | * sel_netport_hashfn - Hashing function for the port table |
61 | * @pnum: port number |
62 | * |
63 | * Description: |
64 | * This is the hashing function for the port table, it returns the bucket |
65 | * number for the given port. |
66 | * |
67 | */ |
68 | static unsigned int sel_netport_hashfn(u16 pnum) |
69 | { |
70 | return (pnum & (SEL_NETPORT_HASH_SIZE - 1)); |
71 | } |
72 | |
73 | /** |
74 | * sel_netport_find - Search for a port record |
75 | * @protocol: protocol |
76 | * @pnum: port |
77 | * |
78 | * Description: |
79 | * Search the network port table and return the matching record. If an entry |
80 | * can not be found in the table return NULL. |
81 | * |
82 | */ |
83 | static struct sel_netport *sel_netport_find(u8 protocol, u16 pnum) |
84 | { |
85 | unsigned int idx; |
86 | struct sel_netport *port; |
87 | |
88 | idx = sel_netport_hashfn(pnum); |
89 | list_for_each_entry_rcu(port, &sel_netport_hash[idx].list, list) |
90 | if (port->psec.port == pnum && port->psec.protocol == protocol) |
91 | return port; |
92 | |
93 | return NULL; |
94 | } |
95 | |
96 | /** |
97 | * sel_netport_insert - Insert a new port into the table |
98 | * @port: the new port record |
99 | * |
100 | * Description: |
101 | * Add a new port record to the network address hash table. |
102 | * |
103 | */ |
104 | static void sel_netport_insert(struct sel_netport *port) |
105 | { |
106 | unsigned int idx; |
107 | |
108 | /* we need to impose a limit on the growth of the hash table so check |
109 | * this bucket to make sure it is within the specified bounds */ |
110 | idx = sel_netport_hashfn(pnum: port->psec.port); |
111 | list_add_rcu(new: &port->list, head: &sel_netport_hash[idx].list); |
112 | if (sel_netport_hash[idx].size == SEL_NETPORT_HASH_BKT_LIMIT) { |
113 | struct sel_netport *tail; |
114 | tail = list_entry( |
115 | rcu_dereference_protected( |
116 | list_tail_rcu(&sel_netport_hash[idx].list), |
117 | lockdep_is_held(&sel_netport_lock)), |
118 | struct sel_netport, list); |
119 | list_del_rcu(entry: &tail->list); |
120 | kfree_rcu(tail, rcu); |
121 | } else |
122 | sel_netport_hash[idx].size++; |
123 | } |
124 | |
125 | /** |
126 | * sel_netport_sid_slow - Lookup the SID of a network address using the policy |
127 | * @protocol: protocol |
128 | * @pnum: port |
129 | * @sid: port SID |
130 | * |
131 | * Description: |
132 | * This function determines the SID of a network port by querying the security |
133 | * policy. The result is added to the network port table to speedup future |
134 | * queries. Returns zero on success, negative values on failure. |
135 | * |
136 | */ |
137 | static int sel_netport_sid_slow(u8 protocol, u16 pnum, u32 *sid) |
138 | { |
139 | int ret; |
140 | struct sel_netport *port; |
141 | struct sel_netport *new; |
142 | |
143 | spin_lock_bh(lock: &sel_netport_lock); |
144 | port = sel_netport_find(protocol, pnum); |
145 | if (port != NULL) { |
146 | *sid = port->psec.sid; |
147 | spin_unlock_bh(lock: &sel_netport_lock); |
148 | return 0; |
149 | } |
150 | |
151 | ret = security_port_sid(protocol, pnum, sid); |
152 | if (ret != 0) |
153 | goto out; |
154 | new = kzalloc(size: sizeof(*new), GFP_ATOMIC); |
155 | if (new) { |
156 | new->psec.port = pnum; |
157 | new->psec.protocol = protocol; |
158 | new->psec.sid = *sid; |
159 | sel_netport_insert(port: new); |
160 | } |
161 | |
162 | out: |
163 | spin_unlock_bh(lock: &sel_netport_lock); |
164 | if (unlikely(ret)) |
165 | pr_warn("SELinux: failure in %s(), unable to determine network port label\n" , |
166 | __func__); |
167 | return ret; |
168 | } |
169 | |
170 | /** |
171 | * sel_netport_sid - Lookup the SID of a network port |
172 | * @protocol: protocol |
173 | * @pnum: port |
174 | * @sid: port SID |
175 | * |
176 | * Description: |
177 | * This function determines the SID of a network port using the fastest method |
178 | * possible. First the port table is queried, but if an entry can't be found |
179 | * then the policy is queried and the result is added to the table to speedup |
180 | * future queries. Returns zero on success, negative values on failure. |
181 | * |
182 | */ |
183 | int sel_netport_sid(u8 protocol, u16 pnum, u32 *sid) |
184 | { |
185 | struct sel_netport *port; |
186 | |
187 | rcu_read_lock(); |
188 | port = sel_netport_find(protocol, pnum); |
189 | if (port != NULL) { |
190 | *sid = port->psec.sid; |
191 | rcu_read_unlock(); |
192 | return 0; |
193 | } |
194 | rcu_read_unlock(); |
195 | |
196 | return sel_netport_sid_slow(protocol, pnum, sid); |
197 | } |
198 | |
199 | /** |
200 | * sel_netport_flush - Flush the entire network port table |
201 | * |
202 | * Description: |
203 | * Remove all entries from the network address table. |
204 | * |
205 | */ |
206 | void sel_netport_flush(void) |
207 | { |
208 | unsigned int idx; |
209 | struct sel_netport *port, *port_tmp; |
210 | |
211 | spin_lock_bh(lock: &sel_netport_lock); |
212 | for (idx = 0; idx < SEL_NETPORT_HASH_SIZE; idx++) { |
213 | list_for_each_entry_safe(port, port_tmp, |
214 | &sel_netport_hash[idx].list, list) { |
215 | list_del_rcu(entry: &port->list); |
216 | kfree_rcu(port, rcu); |
217 | } |
218 | sel_netport_hash[idx].size = 0; |
219 | } |
220 | spin_unlock_bh(lock: &sel_netport_lock); |
221 | } |
222 | |
223 | static __init int sel_netport_init(void) |
224 | { |
225 | int iter; |
226 | |
227 | if (!selinux_enabled_boot) |
228 | return 0; |
229 | |
230 | for (iter = 0; iter < SEL_NETPORT_HASH_SIZE; iter++) { |
231 | INIT_LIST_HEAD(list: &sel_netport_hash[iter].list); |
232 | sel_netport_hash[iter].size = 0; |
233 | } |
234 | |
235 | return 0; |
236 | } |
237 | |
238 | __initcall(sel_netport_init); |
239 | |