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 <paul.moore@hp.com> | |
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 | ||
158 | netlbl_secattr_init(&secattr); | |
159 | rc = netlbl_skbuff_getattr(skb, &secattr); | |
160 | if (rc == 0 && secattr.flags != NETLBL_SECATTR_NONE) | |
8d9107e8 LT |
161 | rc = security_netlbl_secattr_to_sid(&secattr, |
162 | base_sid, | |
163 | sid); | |
5778eabd PM |
164 | else |
165 | *sid = SECSID_NULL; | |
166 | netlbl_secattr_destroy(&secattr); | |
167 | ||
168 | return rc; | |
169 | } | |
170 | ||
171 | /** | |
172 | * selinux_netlbl_sock_graft - Netlabel the new socket | |
173 | * @sk: the new connection | |
174 | * @sock: the new socket | |
175 | * | |
176 | * Description: | |
177 | * The connection represented by @sk is being grafted onto @sock so set the | |
178 | * socket's NetLabel to match the SID of @sk. | |
179 | * | |
180 | */ | |
181 | void selinux_netlbl_sock_graft(struct sock *sk, struct socket *sock) | |
182 | { | |
183 | struct inode_security_struct *isec = SOCK_INODE(sock)->i_security; | |
184 | struct sk_security_struct *sksec = sk->sk_security; | |
185 | struct netlbl_lsm_secattr secattr; | |
186 | u32 nlbl_peer_sid; | |
187 | ||
188 | sksec->sclass = isec->sclass; | |
189 | ||
190 | rcu_read_lock(); | |
191 | ||
192 | if (sksec->nlbl_state != NLBL_REQUIRE) { | |
193 | rcu_read_unlock(); | |
194 | return; | |
195 | } | |
196 | ||
197 | netlbl_secattr_init(&secattr); | |
198 | if (netlbl_sock_getattr(sk, &secattr) == 0 && | |
199 | secattr.flags != NETLBL_SECATTR_NONE && | |
200 | security_netlbl_secattr_to_sid(&secattr, | |
8d9107e8 | 201 | SECINITSID_UNLABELED, |
5778eabd PM |
202 | &nlbl_peer_sid) == 0) |
203 | sksec->peer_sid = nlbl_peer_sid; | |
204 | netlbl_secattr_destroy(&secattr); | |
205 | ||
206 | /* Try to set the NetLabel on the socket to save time later, if we fail | |
207 | * here we will pick up the pieces in later calls to | |
208 | * selinux_netlbl_inode_permission(). */ | |
ba6ff9f2 | 209 | selinux_netlbl_sock_setsid(sk, sksec->sid); |
5778eabd PM |
210 | |
211 | rcu_read_unlock(); | |
212 | } | |
213 | ||
214 | /** | |
215 | * selinux_netlbl_socket_post_create - Label a socket using NetLabel | |
216 | * @sock: the socket to label | |
217 | * | |
218 | * Description: | |
219 | * Attempt to label a socket using the NetLabel mechanism using the given | |
220 | * SID. Returns zero values on success, negative values on failure. | |
221 | * | |
222 | */ | |
223 | int selinux_netlbl_socket_post_create(struct socket *sock) | |
224 | { | |
225 | int rc = 0; | |
ba6ff9f2 | 226 | struct sock *sk = sock->sk; |
5778eabd | 227 | struct inode_security_struct *isec = SOCK_INODE(sock)->i_security; |
ba6ff9f2 | 228 | struct sk_security_struct *sksec = sk->sk_security; |
5778eabd PM |
229 | |
230 | sksec->sclass = isec->sclass; | |
231 | ||
232 | rcu_read_lock(); | |
233 | if (sksec->nlbl_state == NLBL_REQUIRE) | |
ba6ff9f2 | 234 | rc = selinux_netlbl_sock_setsid(sk, sksec->sid); |
5778eabd PM |
235 | rcu_read_unlock(); |
236 | ||
237 | return rc; | |
238 | } | |
239 | ||
240 | /** | |
241 | * selinux_netlbl_inode_permission - Verify the socket is NetLabel labeled | |
242 | * @inode: the file descriptor's inode | |
243 | * @mask: the permission mask | |
244 | * | |
245 | * Description: | |
246 | * Looks at a file's inode and if it is marked as a socket protected by | |
247 | * NetLabel then verify that the socket has been labeled, if not try to label | |
248 | * the socket now with the inode's SID. Returns zero on success, negative | |
249 | * values on failure. | |
250 | * | |
251 | */ | |
252 | int selinux_netlbl_inode_permission(struct inode *inode, int mask) | |
253 | { | |
254 | int rc; | |
ba6ff9f2 | 255 | struct sock *sk; |
5778eabd | 256 | struct socket *sock; |
ba6ff9f2 | 257 | struct sk_security_struct *sksec; |
5778eabd PM |
258 | |
259 | if (!S_ISSOCK(inode->i_mode) || | |
260 | ((mask & (MAY_WRITE | MAY_APPEND)) == 0)) | |
261 | return 0; | |
262 | sock = SOCKET_I(inode); | |
ba6ff9f2 PM |
263 | sk = sock->sk; |
264 | sksec = sk->sk_security; | |
5778eabd PM |
265 | |
266 | rcu_read_lock(); | |
267 | if (sksec->nlbl_state != NLBL_REQUIRE) { | |
268 | rcu_read_unlock(); | |
269 | return 0; | |
270 | } | |
271 | local_bh_disable(); | |
ba6ff9f2 PM |
272 | bh_lock_sock_nested(sk); |
273 | rc = selinux_netlbl_sock_setsid(sk, sksec->sid); | |
274 | bh_unlock_sock(sk); | |
5778eabd PM |
275 | local_bh_enable(); |
276 | rcu_read_unlock(); | |
277 | ||
278 | return rc; | |
279 | } | |
280 | ||
281 | /** | |
282 | * selinux_netlbl_sock_rcv_skb - Do an inbound access check using NetLabel | |
283 | * @sksec: the sock's sk_security_struct | |
284 | * @skb: the packet | |
285 | * @ad: the audit data | |
286 | * | |
287 | * Description: | |
288 | * Fetch the NetLabel security attributes from @skb and perform an access check | |
289 | * against the receiving socket. Returns zero on success, negative values on | |
290 | * error. | |
291 | * | |
292 | */ | |
293 | int selinux_netlbl_sock_rcv_skb(struct sk_security_struct *sksec, | |
294 | struct sk_buff *skb, | |
295 | struct avc_audit_data *ad) | |
296 | { | |
297 | int rc; | |
8d9107e8 LT |
298 | u32 netlbl_sid; |
299 | u32 recv_perm; | |
5778eabd | 300 | |
8d9107e8 LT |
301 | rc = selinux_netlbl_skbuff_getsid(skb, |
302 | SECINITSID_UNLABELED, | |
303 | &netlbl_sid); | |
5778eabd PM |
304 | if (rc != 0) |
305 | return rc; | |
8d9107e8 LT |
306 | |
307 | if (netlbl_sid == SECSID_NULL) | |
308 | return 0; | |
5778eabd PM |
309 | |
310 | switch (sksec->sclass) { | |
311 | case SECCLASS_UDP_SOCKET: | |
8d9107e8 | 312 | recv_perm = UDP_SOCKET__RECVFROM; |
5778eabd PM |
313 | break; |
314 | case SECCLASS_TCP_SOCKET: | |
8d9107e8 | 315 | recv_perm = TCP_SOCKET__RECVFROM; |
5778eabd PM |
316 | break; |
317 | default: | |
8d9107e8 | 318 | recv_perm = RAWIP_SOCKET__RECVFROM; |
5778eabd PM |
319 | } |
320 | ||
8d9107e8 LT |
321 | rc = avc_has_perm(sksec->sid, |
322 | netlbl_sid, | |
323 | sksec->sclass, | |
324 | recv_perm, | |
325 | ad); | |
5778eabd PM |
326 | if (rc == 0) |
327 | return 0; | |
328 | ||
8d9107e8 | 329 | netlbl_skbuff_err(skb, rc); |
5778eabd PM |
330 | return rc; |
331 | } | |
332 | ||
333 | /** | |
334 | * selinux_netlbl_socket_setsockopt - Do not allow users to remove a NetLabel | |
335 | * @sock: the socket | |
336 | * @level: the socket level or protocol | |
337 | * @optname: the socket option name | |
338 | * | |
339 | * Description: | |
340 | * Check the setsockopt() call and if the user is trying to replace the IP | |
341 | * options on a socket and a NetLabel is in place for the socket deny the | |
342 | * access; otherwise allow the access. Returns zero when the access is | |
343 | * allowed, -EACCES when denied, and other negative values on error. | |
344 | * | |
345 | */ | |
346 | int selinux_netlbl_socket_setsockopt(struct socket *sock, | |
347 | int level, | |
348 | int optname) | |
349 | { | |
350 | int rc = 0; | |
ba6ff9f2 PM |
351 | struct sock *sk = sock->sk; |
352 | struct sk_security_struct *sksec = sk->sk_security; | |
5778eabd PM |
353 | struct netlbl_lsm_secattr secattr; |
354 | ||
355 | rcu_read_lock(); | |
356 | if (level == IPPROTO_IP && optname == IP_OPTIONS && | |
357 | sksec->nlbl_state == NLBL_LABELED) { | |
358 | netlbl_secattr_init(&secattr); | |
ba6ff9f2 PM |
359 | lock_sock(sk); |
360 | rc = netlbl_sock_getattr(sk, &secattr); | |
361 | release_sock(sk); | |
5778eabd PM |
362 | if (rc == 0 && secattr.flags != NETLBL_SECATTR_NONE) |
363 | rc = -EACCES; | |
364 | netlbl_secattr_destroy(&secattr); | |
365 | } | |
366 | rcu_read_unlock(); | |
367 | ||
368 | return rc; | |
369 | } |