SELinux: Correct the NetLabel locking for the sk_security_struct
[safe/jmp/linux-2.6] / security / selinux / netlabel.c
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 /**
39  * selinux_netlbl_sidlookup_cached - Cache a SID lookup
40  * @skb: the packet
41  * @secattr: the NetLabel security attributes
42  * @sid: the SID
43  *
44  * Description:
45  * Query the SELinux security server to lookup the correct SID for the given
46  * security attributes.  If the query is successful, cache the result to speed
47  * up future lookups.  Returns zero on success, negative values on failure.
48  *
49  */
50 static int selinux_netlbl_sidlookup_cached(struct sk_buff *skb,
51                                            struct netlbl_lsm_secattr *secattr,
52                                            u32 *sid)
53 {
54         int rc;
55
56         rc = security_netlbl_secattr_to_sid(secattr, sid);
57         if (rc == 0 &&
58             (secattr->flags & NETLBL_SECATTR_CACHEABLE) &&
59             (secattr->flags & NETLBL_SECATTR_CACHE))
60                 netlbl_cache_add(skb, secattr);
61
62         return rc;
63 }
64
65 /**
66  * selinux_netlbl_sock_setsid - Label a socket using the NetLabel mechanism
67  * @sk: the socket to label
68  * @sid: the SID to use
69  *
70  * Description:
71  * Attempt to label a socket using the NetLabel mechanism using the given
72  * SID.  Returns zero values on success, negative values on failure.
73  *
74  */
75 static int selinux_netlbl_sock_setsid(struct sock *sk, u32 sid)
76 {
77         int rc;
78         struct sk_security_struct *sksec = sk->sk_security;
79         struct netlbl_lsm_secattr secattr;
80
81         netlbl_secattr_init(&secattr);
82
83         rc = security_netlbl_sid_to_secattr(sid, &secattr);
84         if (rc != 0)
85                 goto sock_setsid_return;
86         rc = netlbl_sock_setattr(sk, &secattr);
87         if (rc == 0)
88                 sksec->nlbl_state = NLBL_LABELED;
89
90 sock_setsid_return:
91         netlbl_secattr_destroy(&secattr);
92         return rc;
93 }
94
95 /**
96  * selinux_netlbl_cache_invalidate - Invalidate the NetLabel cache
97  *
98  * Description:
99  * Invalidate the NetLabel security attribute mapping cache.
100  *
101  */
102 void selinux_netlbl_cache_invalidate(void)
103 {
104         netlbl_cache_invalidate();
105 }
106
107 /**
108  * selinux_netlbl_sk_security_reset - Reset the NetLabel fields
109  * @ssec: the sk_security_struct
110  * @family: the socket family
111  *
112  * Description:
113  * Called when the NetLabel state of a sk_security_struct needs to be reset.
114  * The caller is responsibile for all the NetLabel sk_security_struct locking.
115  *
116  */
117 void selinux_netlbl_sk_security_reset(struct sk_security_struct *ssec,
118                                       int family)
119 {
120         if (family == PF_INET)
121                 ssec->nlbl_state = NLBL_REQUIRE;
122         else
123                 ssec->nlbl_state = NLBL_UNSET;
124 }
125
126 /**
127  * selinux_netlbl_skbuff_getsid - Get the sid of a packet using NetLabel
128  * @skb: the packet
129  * @family: protocol family
130  * @type: NetLabel labeling protocol type
131  * @sid: the SID
132  *
133  * Description:
134  * Call the NetLabel mechanism to get the security attributes of the given
135  * packet and use those attributes to determine the correct context/SID to
136  * assign to the packet.  Returns zero on success, negative values on failure.
137  *
138  */
139 int selinux_netlbl_skbuff_getsid(struct sk_buff *skb,
140                                  u16 family,
141                                  u32 *type,
142                                  u32 *sid)
143 {
144         int rc;
145         struct netlbl_lsm_secattr secattr;
146
147         if (!netlbl_enabled()) {
148                 *sid = SECSID_NULL;
149                 return 0;
150         }
151
152         netlbl_secattr_init(&secattr);
153         rc = netlbl_skbuff_getattr(skb, family, &secattr);
154         if (rc == 0 && secattr.flags != NETLBL_SECATTR_NONE)
155                 rc = selinux_netlbl_sidlookup_cached(skb, &secattr, sid);
156         else
157                 *sid = SECSID_NULL;
158         *type = secattr.type;
159         netlbl_secattr_destroy(&secattr);
160
161         return rc;
162 }
163
164 /**
165  * selinux_netlbl_sock_graft - Netlabel the new socket
166  * @sk: the new connection
167  * @sock: the new socket
168  *
169  * Description:
170  * The connection represented by @sk is being grafted onto @sock so set the
171  * socket's NetLabel to match the SID of @sk.
172  *
173  */
174 void selinux_netlbl_sock_graft(struct sock *sk, struct socket *sock)
175 {
176         struct sk_security_struct *sksec = sk->sk_security;
177         struct netlbl_lsm_secattr secattr;
178         u32 nlbl_peer_sid;
179
180         if (sksec->nlbl_state != NLBL_REQUIRE)
181                 return;
182
183         netlbl_secattr_init(&secattr);
184         if (netlbl_sock_getattr(sk, &secattr) == 0 &&
185             secattr.flags != NETLBL_SECATTR_NONE &&
186             security_netlbl_secattr_to_sid(&secattr, &nlbl_peer_sid) == 0)
187                 sksec->peer_sid = nlbl_peer_sid;
188         netlbl_secattr_destroy(&secattr);
189
190         /* Try to set the NetLabel on the socket to save time later, if we fail
191          * here we will pick up the pieces in later calls to
192          * selinux_netlbl_inode_permission(). */
193         selinux_netlbl_sock_setsid(sk, sksec->sid);
194 }
195
196 /**
197  * selinux_netlbl_socket_post_create - Label a socket using NetLabel
198  * @sock: the socket to label
199  *
200  * Description:
201  * Attempt to label a socket using the NetLabel mechanism using the given
202  * SID.  Returns zero values on success, negative values on failure.
203  *
204  */
205 int selinux_netlbl_socket_post_create(struct socket *sock)
206 {
207         struct sock *sk = sock->sk;
208         struct sk_security_struct *sksec = sk->sk_security;
209
210         if (sksec->nlbl_state != NLBL_REQUIRE)
211                 return 0;
212
213         return selinux_netlbl_sock_setsid(sk, sksec->sid);
214 }
215
216 /**
217  * selinux_netlbl_inode_permission - Verify the socket is NetLabel labeled
218  * @inode: the file descriptor's inode
219  * @mask: the permission mask
220  *
221  * Description:
222  * Looks at a file's inode and if it is marked as a socket protected by
223  * NetLabel then verify that the socket has been labeled, if not try to label
224  * the socket now with the inode's SID.  Returns zero on success, negative
225  * values on failure.
226  *
227  */
228 int selinux_netlbl_inode_permission(struct inode *inode, int mask)
229 {
230         int rc;
231         struct sock *sk;
232         struct socket *sock;
233         struct sk_security_struct *sksec;
234
235         if (!S_ISSOCK(inode->i_mode) ||
236             ((mask & (MAY_WRITE | MAY_APPEND)) == 0))
237                 return 0;
238
239         sock = SOCKET_I(inode);
240         sk = sock->sk;
241         sksec = sk->sk_security;
242         if (sksec->nlbl_state != NLBL_REQUIRE)
243                 return 0;
244
245         local_bh_disable();
246         bh_lock_sock_nested(sk);
247         if (likely(sksec->nlbl_state == NLBL_REQUIRE))
248                 rc = selinux_netlbl_sock_setsid(sk, sksec->sid);
249         else
250                 rc = 0;
251         bh_unlock_sock(sk);
252         local_bh_enable();
253
254         return rc;
255 }
256
257 /**
258  * selinux_netlbl_sock_rcv_skb - Do an inbound access check using NetLabel
259  * @sksec: the sock's sk_security_struct
260  * @skb: the packet
261  * @family: protocol family
262  * @ad: the audit data
263  *
264  * Description:
265  * Fetch the NetLabel security attributes from @skb and perform an access check
266  * against the receiving socket.  Returns zero on success, negative values on
267  * error.
268  *
269  */
270 int selinux_netlbl_sock_rcv_skb(struct sk_security_struct *sksec,
271                                 struct sk_buff *skb,
272                                 u16 family,
273                                 struct avc_audit_data *ad)
274 {
275         int rc;
276         u32 nlbl_sid;
277         u32 perm;
278         struct netlbl_lsm_secattr secattr;
279
280         if (!netlbl_enabled())
281                 return 0;
282
283         netlbl_secattr_init(&secattr);
284         rc = netlbl_skbuff_getattr(skb, family, &secattr);
285         if (rc == 0 && secattr.flags != NETLBL_SECATTR_NONE)
286                 rc = selinux_netlbl_sidlookup_cached(skb, &secattr, &nlbl_sid);
287         else
288                 nlbl_sid = SECINITSID_UNLABELED;
289         netlbl_secattr_destroy(&secattr);
290         if (rc != 0)
291                 return rc;
292
293         switch (sksec->sclass) {
294         case SECCLASS_UDP_SOCKET:
295                 perm = UDP_SOCKET__RECVFROM;
296                 break;
297         case SECCLASS_TCP_SOCKET:
298                 perm = TCP_SOCKET__RECVFROM;
299                 break;
300         default:
301                 perm = RAWIP_SOCKET__RECVFROM;
302         }
303
304         rc = avc_has_perm(sksec->sid, nlbl_sid, sksec->sclass, perm, ad);
305         if (rc == 0)
306                 return 0;
307
308         if (nlbl_sid != SECINITSID_UNLABELED)
309                 netlbl_skbuff_err(skb, rc);
310         return rc;
311 }
312
313 /**
314  * selinux_netlbl_socket_setsockopt - Do not allow users to remove a NetLabel
315  * @sock: the socket
316  * @level: the socket level or protocol
317  * @optname: the socket option name
318  *
319  * Description:
320  * Check the setsockopt() call and if the user is trying to replace the IP
321  * options on a socket and a NetLabel is in place for the socket deny the
322  * access; otherwise allow the access.  Returns zero when the access is
323  * allowed, -EACCES when denied, and other negative values on error.
324  *
325  */
326 int selinux_netlbl_socket_setsockopt(struct socket *sock,
327                                      int level,
328                                      int optname)
329 {
330         int rc = 0;
331         struct sock *sk = sock->sk;
332         struct sk_security_struct *sksec = sk->sk_security;
333         struct netlbl_lsm_secattr secattr;
334
335         if (level == IPPROTO_IP && optname == IP_OPTIONS &&
336             sksec->nlbl_state == NLBL_LABELED) {
337                 netlbl_secattr_init(&secattr);
338                 lock_sock(sk);
339                 rc = netlbl_sock_getattr(sk, &secattr);
340                 release_sock(sk);
341                 if (rc == 0 && secattr.flags != NETLBL_SECATTR_NONE)
342                         rc = -EACCES;
343                 netlbl_secattr_destroy(&secattr);
344         }
345
346         return rc;
347 }