]>
Commit | Line | Data |
---|---|---|
5778eabd PM |
1 | /* |
2 | * SELinux NetLabel Support | |
3 | * | |
4 | * This file provides the necessary glue to tie NetLabel into the SELinux | |
5 | * subsystem. | |
6 | * | |
7 | * Author: Paul Moore <[email protected]> | |
8 | * | |
9 | */ | |
10 | ||
11 | /* | |
12 | * (c) Copyright Hewlett-Packard Development Company, L.P., 2007 | |
13 | * | |
14 | * This program is free software; you can redistribute it and/or modify | |
15 | * it under the terms of the GNU General Public License as published by | |
16 | * the Free Software Foundation; either version 2 of the License, or | |
17 | * (at your option) any later version. | |
18 | * | |
19 | * This program is distributed in the hope that it will be useful, | |
20 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
21 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See | |
22 | * the GNU General Public License for more details. | |
23 | * | |
24 | * You should have received a copy of the GNU General Public License | |
25 | * along with this program; if not, write to the Free Software | |
26 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
27 | * | |
28 | */ | |
29 | ||
30 | #include <linux/spinlock.h> | |
31 | #include <linux/rcupdate.h> | |
32 | #include <net/sock.h> | |
33 | #include <net/netlabel.h> | |
34 | ||
35 | #include "objsec.h" | |
36 | #include "security.h" | |
37 | ||
38 | /** | |
ba6ff9f2 PM |
39 | * selinux_netlbl_sock_setsid - Label a socket using the NetLabel mechanism |
40 | * @sk: the socket to label | |
5778eabd PM |
41 | * @sid: the SID to use |
42 | * | |
43 | * Description: | |
44 | * Attempt to label a socket using the NetLabel mechanism using the given | |
45 | * SID. Returns zero values on success, negative values on failure. The | |
46 | * caller is responsibile for calling rcu_read_lock() before calling this | |
47 | * this function and rcu_read_unlock() after this function returns. | |
48 | * | |
49 | */ | |
ba6ff9f2 | 50 | static int selinux_netlbl_sock_setsid(struct sock *sk, u32 sid) |
5778eabd PM |
51 | { |
52 | int rc; | |
ba6ff9f2 | 53 | struct sk_security_struct *sksec = sk->sk_security; |
5778eabd PM |
54 | struct netlbl_lsm_secattr secattr; |
55 | ||
56 | rc = security_netlbl_sid_to_secattr(sid, &secattr); | |
57 | if (rc != 0) | |
58 | return rc; | |
59 | ||
ba6ff9f2 | 60 | rc = netlbl_sock_setattr(sk, &secattr); |
5778eabd PM |
61 | if (rc == 0) { |
62 | spin_lock_bh(&sksec->nlbl_lock); | |
63 | sksec->nlbl_state = NLBL_LABELED; | |
64 | spin_unlock_bh(&sksec->nlbl_lock); | |
65 | } | |
66 | ||
67 | return rc; | |
68 | } | |
69 | ||
70 | /** | |
71 | * selinux_netlbl_cache_invalidate - Invalidate the NetLabel cache | |
72 | * | |
73 | * Description: | |
74 | * Invalidate the NetLabel security attribute mapping cache. | |
75 | * | |
76 | */ | |
77 | void selinux_netlbl_cache_invalidate(void) | |
78 | { | |
79 | netlbl_cache_invalidate(); | |
80 | } | |
81 | ||
82 | /** | |
83 | * selinux_netlbl_sk_security_reset - Reset the NetLabel fields | |
84 | * @ssec: the sk_security_struct | |
85 | * @family: the socket family | |
86 | * | |
87 | * Description: | |
88 | * Called when the NetLabel state of a sk_security_struct needs to be reset. | |
89 | * The caller is responsibile for all the NetLabel sk_security_struct locking. | |
90 | * | |
91 | */ | |
92 | void selinux_netlbl_sk_security_reset(struct sk_security_struct *ssec, | |
93 | int family) | |
94 | { | |
95 | if (family == PF_INET) | |
96 | ssec->nlbl_state = NLBL_REQUIRE; | |
97 | else | |
98 | ssec->nlbl_state = NLBL_UNSET; | |
99 | } | |
100 | ||
101 | /** | |
102 | * selinux_netlbl_sk_security_init - Setup the NetLabel fields | |
103 | * @ssec: the sk_security_struct | |
104 | * @family: the socket family | |
105 | * | |
106 | * Description: | |
107 | * Called when a new sk_security_struct is allocated to initialize the NetLabel | |
108 | * fields. | |
109 | * | |
110 | */ | |
111 | void selinux_netlbl_sk_security_init(struct sk_security_struct *ssec, | |
112 | int family) | |
113 | { | |
114 | /* No locking needed, we are the only one who has access to ssec */ | |
115 | selinux_netlbl_sk_security_reset(ssec, family); | |
116 | spin_lock_init(&ssec->nlbl_lock); | |
117 | } | |
118 | ||
119 | /** | |
120 | * selinux_netlbl_sk_security_clone - Copy the NetLabel fields | |
121 | * @ssec: the original sk_security_struct | |
122 | * @newssec: the cloned sk_security_struct | |
123 | * | |
124 | * Description: | |
125 | * Clone the NetLabel specific sk_security_struct fields from @ssec to | |
126 | * @newssec. | |
127 | * | |
128 | */ | |
129 | void selinux_netlbl_sk_security_clone(struct sk_security_struct *ssec, | |
130 | struct sk_security_struct *newssec) | |
131 | { | |
132 | /* We don't need to take newssec->nlbl_lock because we are the only | |
133 | * thread with access to newssec, but we do need to take the RCU read | |
134 | * lock as other threads could have access to ssec */ | |
135 | rcu_read_lock(); | |
136 | selinux_netlbl_sk_security_reset(newssec, ssec->sk->sk_family); | |
137 | newssec->sclass = ssec->sclass; | |
138 | rcu_read_unlock(); | |
139 | } | |
140 | ||
141 | /** | |
142 | * selinux_netlbl_skbuff_getsid - Get the sid of a packet using NetLabel | |
143 | * @skb: the packet | |
144 | * @base_sid: the SELinux SID to use as a context for MLS only attributes | |
145 | * @sid: the SID | |
146 | * | |
147 | * Description: | |
148 | * Call the NetLabel mechanism to get the security attributes of the given | |
149 | * packet and use those attributes to determine the correct context/SID to | |
150 | * assign to the packet. Returns zero on success, negative values on failure. | |
151 | * | |
152 | */ | |
153 | int selinux_netlbl_skbuff_getsid(struct sk_buff *skb, u32 base_sid, u32 *sid) | |
154 | { | |
155 | int rc; | |
156 | struct netlbl_lsm_secattr secattr; | |
157 | ||
23bcdc1a PM |
158 | if (!netlbl_enabled()) { |
159 | *sid = SECSID_NULL; | |
160 | return 0; | |
161 | } | |
162 | ||
5778eabd PM |
163 | netlbl_secattr_init(&secattr); |
164 | rc = netlbl_skbuff_getattr(skb, &secattr); | |
9534f71c | 165 | if (rc == 0 && secattr.flags != NETLBL_SECATTR_NONE) { |
f36158c4 | 166 | rc = security_netlbl_secattr_to_sid(&secattr, base_sid, sid); |
9534f71c PM |
167 | if (rc == 0 && |
168 | (secattr.flags & NETLBL_SECATTR_CACHEABLE) && | |
169 | (secattr.flags & NETLBL_SECATTR_CACHE)) | |
170 | netlbl_cache_add(skb, &secattr); | |
171 | } else | |
5778eabd PM |
172 | *sid = SECSID_NULL; |
173 | netlbl_secattr_destroy(&secattr); | |
174 | ||
175 | return rc; | |
176 | } | |
177 | ||
178 | /** | |
179 | * selinux_netlbl_sock_graft - Netlabel the new socket | |
180 | * @sk: the new connection | |
181 | * @sock: the new socket | |
182 | * | |
183 | * Description: | |
184 | * The connection represented by @sk is being grafted onto @sock so set the | |
185 | * socket's NetLabel to match the SID of @sk. | |
186 | * | |
187 | */ | |
188 | void selinux_netlbl_sock_graft(struct sock *sk, struct socket *sock) | |
189 | { | |
190 | struct inode_security_struct *isec = SOCK_INODE(sock)->i_security; | |
191 | struct sk_security_struct *sksec = sk->sk_security; | |
192 | struct netlbl_lsm_secattr secattr; | |
193 | u32 nlbl_peer_sid; | |
194 | ||
195 | sksec->sclass = isec->sclass; | |
196 | ||
197 | rcu_read_lock(); | |
198 | ||
199 | if (sksec->nlbl_state != NLBL_REQUIRE) { | |
200 | rcu_read_unlock(); | |
201 | return; | |
202 | } | |
203 | ||
204 | netlbl_secattr_init(&secattr); | |
205 | if (netlbl_sock_getattr(sk, &secattr) == 0 && | |
206 | secattr.flags != NETLBL_SECATTR_NONE && | |
207 | security_netlbl_secattr_to_sid(&secattr, | |
f36158c4 | 208 | SECINITSID_NETMSG, |
5778eabd PM |
209 | &nlbl_peer_sid) == 0) |
210 | sksec->peer_sid = nlbl_peer_sid; | |
211 | netlbl_secattr_destroy(&secattr); | |
212 | ||
213 | /* Try to set the NetLabel on the socket to save time later, if we fail | |
214 | * here we will pick up the pieces in later calls to | |
215 | * selinux_netlbl_inode_permission(). */ | |
ba6ff9f2 | 216 | selinux_netlbl_sock_setsid(sk, sksec->sid); |
5778eabd PM |
217 | |
218 | rcu_read_unlock(); | |
219 | } | |
220 | ||
221 | /** | |
222 | * selinux_netlbl_socket_post_create - Label a socket using NetLabel | |
223 | * @sock: the socket to label | |
224 | * | |
225 | * Description: | |
226 | * Attempt to label a socket using the NetLabel mechanism using the given | |
227 | * SID. Returns zero values on success, negative values on failure. | |
228 | * | |
229 | */ | |
230 | int selinux_netlbl_socket_post_create(struct socket *sock) | |
231 | { | |
232 | int rc = 0; | |
ba6ff9f2 | 233 | struct sock *sk = sock->sk; |
5778eabd | 234 | struct inode_security_struct *isec = SOCK_INODE(sock)->i_security; |
ba6ff9f2 | 235 | struct sk_security_struct *sksec = sk->sk_security; |
5778eabd PM |
236 | |
237 | sksec->sclass = isec->sclass; | |
238 | ||
239 | rcu_read_lock(); | |
240 | if (sksec->nlbl_state == NLBL_REQUIRE) | |
ba6ff9f2 | 241 | rc = selinux_netlbl_sock_setsid(sk, sksec->sid); |
5778eabd PM |
242 | rcu_read_unlock(); |
243 | ||
244 | return rc; | |
245 | } | |
246 | ||
247 | /** | |
248 | * selinux_netlbl_inode_permission - Verify the socket is NetLabel labeled | |
249 | * @inode: the file descriptor's inode | |
250 | * @mask: the permission mask | |
251 | * | |
252 | * Description: | |
253 | * Looks at a file's inode and if it is marked as a socket protected by | |
254 | * NetLabel then verify that the socket has been labeled, if not try to label | |
255 | * the socket now with the inode's SID. Returns zero on success, negative | |
256 | * values on failure. | |
257 | * | |
258 | */ | |
259 | int selinux_netlbl_inode_permission(struct inode *inode, int mask) | |
260 | { | |
261 | int rc; | |
ba6ff9f2 | 262 | struct sock *sk; |
5778eabd | 263 | struct socket *sock; |
ba6ff9f2 | 264 | struct sk_security_struct *sksec; |
5778eabd PM |
265 | |
266 | if (!S_ISSOCK(inode->i_mode) || | |
267 | ((mask & (MAY_WRITE | MAY_APPEND)) == 0)) | |
268 | return 0; | |
269 | sock = SOCKET_I(inode); | |
ba6ff9f2 PM |
270 | sk = sock->sk; |
271 | sksec = sk->sk_security; | |
5778eabd PM |
272 | |
273 | rcu_read_lock(); | |
274 | if (sksec->nlbl_state != NLBL_REQUIRE) { | |
275 | rcu_read_unlock(); | |
276 | return 0; | |
277 | } | |
278 | local_bh_disable(); | |
ba6ff9f2 PM |
279 | bh_lock_sock_nested(sk); |
280 | rc = selinux_netlbl_sock_setsid(sk, sksec->sid); | |
281 | bh_unlock_sock(sk); | |
5778eabd PM |
282 | local_bh_enable(); |
283 | rcu_read_unlock(); | |
284 | ||
285 | return rc; | |
286 | } | |
287 | ||
288 | /** | |
289 | * selinux_netlbl_sock_rcv_skb - Do an inbound access check using NetLabel | |
290 | * @sksec: the sock's sk_security_struct | |
291 | * @skb: the packet | |
292 | * @ad: the audit data | |
293 | * | |
294 | * Description: | |
295 | * Fetch the NetLabel security attributes from @skb and perform an access check | |
296 | * against the receiving socket. Returns zero on success, negative values on | |
297 | * error. | |
298 | * | |
299 | */ | |
300 | int selinux_netlbl_sock_rcv_skb(struct sk_security_struct *sksec, | |
301 | struct sk_buff *skb, | |
302 | struct avc_audit_data *ad) | |
303 | { | |
304 | int rc; | |
f36158c4 PM |
305 | u32 nlbl_sid; |
306 | u32 perm; | |
307 | struct netlbl_lsm_secattr secattr; | |
5778eabd | 308 | |
23bcdc1a PM |
309 | if (!netlbl_enabled()) |
310 | return 0; | |
311 | ||
f36158c4 PM |
312 | netlbl_secattr_init(&secattr); |
313 | rc = netlbl_skbuff_getattr(skb, &secattr); | |
9534f71c | 314 | if (rc == 0 && secattr.flags != NETLBL_SECATTR_NONE) { |
f36158c4 PM |
315 | rc = security_netlbl_secattr_to_sid(&secattr, |
316 | SECINITSID_NETMSG, | |
317 | &nlbl_sid); | |
9534f71c PM |
318 | if (rc == 0 && |
319 | (secattr.flags & NETLBL_SECATTR_CACHEABLE) && | |
320 | (secattr.flags & NETLBL_SECATTR_CACHE)) | |
321 | netlbl_cache_add(skb, &secattr); | |
322 | } else | |
f36158c4 PM |
323 | nlbl_sid = SECINITSID_UNLABELED; |
324 | netlbl_secattr_destroy(&secattr); | |
5778eabd PM |
325 | if (rc != 0) |
326 | return rc; | |
8d9107e8 | 327 | |
5778eabd PM |
328 | switch (sksec->sclass) { |
329 | case SECCLASS_UDP_SOCKET: | |
f36158c4 | 330 | perm = UDP_SOCKET__RECVFROM; |
5778eabd PM |
331 | break; |
332 | case SECCLASS_TCP_SOCKET: | |
f36158c4 | 333 | perm = TCP_SOCKET__RECVFROM; |
5778eabd PM |
334 | break; |
335 | default: | |
f36158c4 | 336 | perm = RAWIP_SOCKET__RECVFROM; |
5778eabd PM |
337 | } |
338 | ||
f36158c4 | 339 | rc = avc_has_perm(sksec->sid, nlbl_sid, sksec->sclass, perm, ad); |
5778eabd PM |
340 | if (rc == 0) |
341 | return 0; | |
342 | ||
f36158c4 PM |
343 | if (nlbl_sid != SECINITSID_UNLABELED) |
344 | netlbl_skbuff_err(skb, rc); | |
5778eabd PM |
345 | return rc; |
346 | } | |
347 | ||
348 | /** | |
349 | * selinux_netlbl_socket_setsockopt - Do not allow users to remove a NetLabel | |
350 | * @sock: the socket | |
351 | * @level: the socket level or protocol | |
352 | * @optname: the socket option name | |
353 | * | |
354 | * Description: | |
355 | * Check the setsockopt() call and if the user is trying to replace the IP | |
356 | * options on a socket and a NetLabel is in place for the socket deny the | |
357 | * access; otherwise allow the access. Returns zero when the access is | |
358 | * allowed, -EACCES when denied, and other negative values on error. | |
359 | * | |
360 | */ | |
361 | int selinux_netlbl_socket_setsockopt(struct socket *sock, | |
362 | int level, | |
363 | int optname) | |
364 | { | |
365 | int rc = 0; | |
ba6ff9f2 PM |
366 | struct sock *sk = sock->sk; |
367 | struct sk_security_struct *sksec = sk->sk_security; | |
5778eabd PM |
368 | struct netlbl_lsm_secattr secattr; |
369 | ||
370 | rcu_read_lock(); | |
371 | if (level == IPPROTO_IP && optname == IP_OPTIONS && | |
372 | sksec->nlbl_state == NLBL_LABELED) { | |
373 | netlbl_secattr_init(&secattr); | |
ba6ff9f2 PM |
374 | lock_sock(sk); |
375 | rc = netlbl_sock_getattr(sk, &secattr); | |
376 | release_sock(sk); | |
5778eabd PM |
377 | if (rc == 0 && secattr.flags != NETLBL_SECATTR_NONE) |
378 | rc = -EACCES; | |
379 | netlbl_secattr_destroy(&secattr); | |
380 | } | |
381 | rcu_read_unlock(); | |
382 | ||
383 | return rc; | |
384 | } |