aboutsummaryrefslogtreecommitdiffstats
path: root/security/selinux/netlabel.c
diff options
context:
space:
mode:
authorPaul Moore <paul.moore@hp.com>2009-03-27 17:10:34 -0400
committerJames Morris <jmorris@namei.org>2009-03-28 00:01:36 -0400
commit389fb800ac8be2832efedd19978a2b8ced37eb61 (patch)
treefa0bc16050dfb491aa05f76b54fa4c167de96376 /security/selinux/netlabel.c
parent284904aa79466a4736f4c775fdbe5c7407fa136c (diff)
netlabel: Label incoming TCP connections correctly in SELinux
The current NetLabel/SELinux behavior for incoming TCP connections works but only through a series of happy coincidences that rely on the limited nature of standard CIPSO (only able to convey MLS attributes) and the write equality imposed by the SELinux MLS constraints. The problem is that network sockets created as the result of an incoming TCP connection were not on-the-wire labeled based on the security attributes of the parent socket but rather based on the wire label of the remote peer. The issue had to do with how IP options were managed as part of the network stack and where the LSM hooks were in relation to the code which set the IP options on these newly created child sockets. While NetLabel/SELinux did correctly set the socket's on-the-wire label it was promptly cleared by the network stack and reset based on the IP options of the remote peer. This patch, in conjunction with a prior patch that adjusted the LSM hook locations, works to set the correct on-the-wire label format for new incoming connections through the security_inet_conn_request() hook. Besides the correct behavior there are many advantages to this change, the most significant is that all of the NetLabel socket labeling code in SELinux now lives in hooks which can return error codes to the core stack which allows us to finally get ride of the selinux_netlbl_inode_permission() logic which greatly simplfies the NetLabel/SELinux glue code. In the process of developing this patch I also ran into a small handful of AF_INET6 cleanliness issues that have been fixed which should make the code safer and easier to extend in the future. Signed-off-by: Paul Moore <paul.moore@hp.com> Acked-by: Casey Schaufler <casey@schaufler-ca.com> Signed-off-by: James Morris <jmorris@namei.org>
Diffstat (limited to 'security/selinux/netlabel.c')
-rw-r--r--security/selinux/netlabel.c186
1 files changed, 53 insertions, 133 deletions
diff --git a/security/selinux/netlabel.c b/security/selinux/netlabel.c
index 350794ab9b42..2e984413c7b2 100644
--- a/security/selinux/netlabel.c
+++ b/security/selinux/netlabel.c
@@ -100,41 +100,6 @@ static struct netlbl_lsm_secattr *selinux_netlbl_sock_genattr(struct sock *sk)
100} 100}
101 101
102/** 102/**
103 * selinux_netlbl_sock_setsid - Label a socket using the NetLabel mechanism
104 * @sk: the socket to label
105 *
106 * Description:
107 * Attempt to label a socket using the NetLabel mechanism. Returns zero values
108 * on success, negative values on failure.
109 *
110 */
111static int selinux_netlbl_sock_setsid(struct sock *sk)
112{
113 int rc;
114 struct sk_security_struct *sksec = sk->sk_security;
115 struct netlbl_lsm_secattr *secattr;
116
117 if (sksec->nlbl_state != NLBL_REQUIRE)
118 return 0;
119
120 secattr = selinux_netlbl_sock_genattr(sk);
121 if (secattr == NULL)
122 return -ENOMEM;
123 rc = netlbl_sock_setattr(sk, secattr);
124 switch (rc) {
125 case 0:
126 sksec->nlbl_state = NLBL_LABELED;
127 break;
128 case -EDESTADDRREQ:
129 sksec->nlbl_state = NLBL_REQSKB;
130 rc = 0;
131 break;
132 }
133
134 return rc;
135}
136
137/**
138 * selinux_netlbl_cache_invalidate - Invalidate the NetLabel cache 103 * selinux_netlbl_cache_invalidate - Invalidate the NetLabel cache
139 * 104 *
140 * Description: 105 * Description:
@@ -188,13 +153,9 @@ void selinux_netlbl_sk_security_free(struct sk_security_struct *ssec)
188 * The caller is responsibile for all the NetLabel sk_security_struct locking. 153 * The caller is responsibile for all the NetLabel sk_security_struct locking.
189 * 154 *
190 */ 155 */
191void selinux_netlbl_sk_security_reset(struct sk_security_struct *ssec, 156void selinux_netlbl_sk_security_reset(struct sk_security_struct *ssec)
192 int family)
193{ 157{
194 if (family == PF_INET) 158 ssec->nlbl_state = NLBL_UNSET;
195 ssec->nlbl_state = NLBL_REQUIRE;
196 else
197 ssec->nlbl_state = NLBL_UNSET;
198} 159}
199 160
200/** 161/**
@@ -281,127 +242,86 @@ skbuff_setsid_return:
281} 242}
282 243
283/** 244/**
284 * selinux_netlbl_inet_conn_established - Netlabel the newly accepted connection 245 * selinux_netlbl_inet_conn_request - Label an incoming stream connection
285 * @sk: the new connection 246 * @req: incoming connection request socket
286 * 247 *
287 * Description: 248 * Description:
288 * A new connection has been established on @sk so make sure it is labeled 249 * A new incoming connection request is represented by @req, we need to label
289 * correctly with the NetLabel susbsystem. 250 * the new request_sock here and the stack will ensure the on-the-wire label
251 * will get preserved when a full sock is created once the connection handshake
252 * is complete. Returns zero on success, negative values on failure.
290 * 253 *
291 */ 254 */
292void selinux_netlbl_inet_conn_established(struct sock *sk, u16 family) 255int selinux_netlbl_inet_conn_request(struct request_sock *req, u16 family)
293{ 256{
294 int rc; 257 int rc;
295 struct sk_security_struct *sksec = sk->sk_security; 258 struct netlbl_lsm_secattr secattr;
296 struct netlbl_lsm_secattr *secattr;
297 struct inet_sock *sk_inet = inet_sk(sk);
298 struct sockaddr_in addr;
299
300 if (sksec->nlbl_state != NLBL_REQUIRE)
301 return;
302 259
303 secattr = selinux_netlbl_sock_genattr(sk); 260 if (family != PF_INET)
304 if (secattr == NULL) 261 return 0;
305 return;
306 262
307 rc = netlbl_sock_setattr(sk, secattr); 263 netlbl_secattr_init(&secattr);
308 switch (rc) { 264 rc = security_netlbl_sid_to_secattr(req->secid, &secattr);
309 case 0: 265 if (rc != 0)
310 sksec->nlbl_state = NLBL_LABELED; 266 goto inet_conn_request_return;
311 break; 267 rc = netlbl_req_setattr(req, &secattr);
312 case -EDESTADDRREQ: 268inet_conn_request_return:
313 /* no PF_INET6 support yet because we don't support any IPv6 269 netlbl_secattr_destroy(&secattr);
314 * labeling protocols */ 270 return rc;
315 if (family != PF_INET) {
316 sksec->nlbl_state = NLBL_UNSET;
317 return;
318 }
319
320 addr.sin_family = family;
321 addr.sin_addr.s_addr = sk_inet->daddr;
322 if (netlbl_conn_setattr(sk, (struct sockaddr *)&addr,
323 secattr) != 0) {
324 /* we failed to label the connected socket (could be
325 * for a variety of reasons, the actual "why" isn't
326 * important here) so we have to go to our backup plan,
327 * labeling the packets individually in the netfilter
328 * local output hook. this is okay but we need to
329 * adjust the MSS of the connection to take into
330 * account any labeling overhead, since we don't know
331 * the exact overhead at this point we'll use the worst
332 * case value which is 40 bytes for IPv4 */
333 struct inet_connection_sock *sk_conn = inet_csk(sk);
334 sk_conn->icsk_ext_hdr_len += 40 -
335 (sk_inet->opt ? sk_inet->opt->optlen : 0);
336 sk_conn->icsk_sync_mss(sk, sk_conn->icsk_pmtu_cookie);
337
338 sksec->nlbl_state = NLBL_REQSKB;
339 } else
340 sksec->nlbl_state = NLBL_CONNLABELED;
341 break;
342 default:
343 /* note that we are failing to label the socket which could be
344 * a bad thing since it means traffic could leave the system
345 * without the desired labeling, however, all is not lost as
346 * we have a check in selinux_netlbl_inode_permission() to
347 * pick up the pieces that we might drop here because we can't
348 * return an error code */
349 break;
350 }
351} 271}
352 272
353/** 273/**
354 * selinux_netlbl_socket_post_create - Label a socket using NetLabel 274 * selinux_netlbl_inet_csk_clone - Initialize the newly created sock
355 * @sock: the socket to label 275 * @sk: the new sock
356 * 276 *
357 * Description: 277 * Description:
358 * Attempt to label a socket using the NetLabel mechanism using the given 278 * A new connection has been established using @sk, we've already labeled the
359 * SID. Returns zero values on success, negative values on failure. 279 * socket via the request_sock struct in selinux_netlbl_inet_conn_request() but
280 * we need to set the NetLabel state here since we now have a sock structure.
360 * 281 *
361 */ 282 */
362int selinux_netlbl_socket_post_create(struct socket *sock) 283void selinux_netlbl_inet_csk_clone(struct sock *sk, u16 family)
363{ 284{
364 return selinux_netlbl_sock_setsid(sock->sk); 285 struct sk_security_struct *sksec = sk->sk_security;
286
287 if (family == PF_INET)
288 sksec->nlbl_state = NLBL_LABELED;
289 else
290 sksec->nlbl_state = NLBL_UNSET;
365} 291}
366 292
367/** 293/**
368 * selinux_netlbl_inode_permission - Verify the socket is NetLabel labeled 294 * selinux_netlbl_socket_post_create - Label a socket using NetLabel
369 * @inode: the file descriptor's inode 295 * @sock: the socket to label
370 * @mask: the permission mask 296 * @family: protocol family
371 * 297 *
372 * Description: 298 * Description:
373 * Looks at a file's inode and if it is marked as a socket protected by 299 * Attempt to label a socket using the NetLabel mechanism using the given
374 * NetLabel then verify that the socket has been labeled, if not try to label 300 * SID. Returns zero values on success, negative values on failure.
375 * the socket now with the inode's SID. Returns zero on success, negative
376 * values on failure.
377 * 301 *
378 */ 302 */
379int selinux_netlbl_inode_permission(struct inode *inode, int mask) 303int selinux_netlbl_socket_post_create(struct sock *sk, u16 family)
380{ 304{
381 int rc; 305 int rc;
382 struct sock *sk; 306 struct sk_security_struct *sksec = sk->sk_security;
383 struct socket *sock; 307 struct netlbl_lsm_secattr *secattr;
384 struct sk_security_struct *sksec;
385 308
386 if (!S_ISSOCK(inode->i_mode) || 309 if (family != PF_INET)
387 ((mask & (MAY_WRITE | MAY_APPEND)) == 0))
388 return 0;
389 sock = SOCKET_I(inode);
390 sk = sock->sk;
391 if (sk == NULL)
392 return 0;
393 sksec = sk->sk_security;
394 if (sksec == NULL || sksec->nlbl_state != NLBL_REQUIRE)
395 return 0; 310 return 0;
396 311
397 local_bh_disable(); 312 secattr = selinux_netlbl_sock_genattr(sk);
398 bh_lock_sock_nested(sk); 313 if (secattr == NULL)
399 if (likely(sksec->nlbl_state == NLBL_REQUIRE)) 314 return -ENOMEM;
400 rc = selinux_netlbl_sock_setsid(sk); 315 rc = netlbl_sock_setattr(sk, family, secattr);
401 else 316 switch (rc) {
317 case 0:
318 sksec->nlbl_state = NLBL_LABELED;
319 break;
320 case -EDESTADDRREQ:
321 sksec->nlbl_state = NLBL_REQSKB;
402 rc = 0; 322 rc = 0;
403 bh_unlock_sock(sk); 323 break;
404 local_bh_enable(); 324 }
405 325
406 return rc; 326 return rc;
407} 327}