diff options
author | Paul Moore <paul.moore@hp.com> | 2008-10-10 10:16:33 -0400 |
---|---|---|
committer | Paul Moore <paul.moore@hp.com> | 2008-10-10 10:16:33 -0400 |
commit | 014ab19a69c325f52d7bae54ceeda73d6307ae0c (patch) | |
tree | 8a69c490accb7d5454bdfeb8c078d846729aeb60 /security/selinux/netlabel.c | |
parent | 948bf85c1bc9a84754786a9d5dd99b7ecc46451e (diff) |
selinux: Set socket NetLabel based on connection endpoint
Previous work enabled the use of address based NetLabel selectors, which while
highly useful, brought the potential for additional per-packet overhead when
used. This patch attempts to solve that by applying NetLabel socket labels
when sockets are connect()'d. This should alleviate the per-packet NetLabel
labeling for all connected sockets (yes, it even works for connected DGRAM
sockets).
Signed-off-by: Paul Moore <paul.moore@hp.com>
Reviewed-by: James Morris <jmorris@namei.org>
Diffstat (limited to 'security/selinux/netlabel.c')
-rw-r--r-- | security/selinux/netlabel.c | 147 |
1 files changed, 119 insertions, 28 deletions
diff --git a/security/selinux/netlabel.c b/security/selinux/netlabel.c index 090404d6e512..b22b7dafa0e3 100644 --- a/security/selinux/netlabel.c +++ b/security/selinux/netlabel.c | |||
@@ -29,10 +29,12 @@ | |||
29 | 29 | ||
30 | #include <linux/spinlock.h> | 30 | #include <linux/spinlock.h> |
31 | #include <linux/rcupdate.h> | 31 | #include <linux/rcupdate.h> |
32 | #include <linux/ip.h> | ||
33 | #include <linux/ipv6.h> | ||
32 | #include <net/sock.h> | 34 | #include <net/sock.h> |
33 | #include <net/netlabel.h> | 35 | #include <net/netlabel.h> |
34 | #include <net/inet_sock.h> | 36 | #include <net/ip.h> |
35 | #include <net/inet_connection_sock.h> | 37 | #include <net/ipv6.h> |
36 | 38 | ||
37 | #include "objsec.h" | 39 | #include "objsec.h" |
38 | #include "security.h" | 40 | #include "security.h" |
@@ -79,8 +81,6 @@ static int selinux_netlbl_sock_setsid(struct sock *sk) | |||
79 | int rc; | 81 | int rc; |
80 | struct sk_security_struct *sksec = sk->sk_security; | 82 | struct sk_security_struct *sksec = sk->sk_security; |
81 | struct netlbl_lsm_secattr secattr; | 83 | struct netlbl_lsm_secattr secattr; |
82 | struct inet_sock *sk_inet; | ||
83 | struct inet_connection_sock *sk_conn; | ||
84 | 84 | ||
85 | if (sksec->nlbl_state != NLBL_REQUIRE) | 85 | if (sksec->nlbl_state != NLBL_REQUIRE) |
86 | return 0; | 86 | return 0; |
@@ -96,20 +96,6 @@ static int selinux_netlbl_sock_setsid(struct sock *sk) | |||
96 | sksec->nlbl_state = NLBL_LABELED; | 96 | sksec->nlbl_state = NLBL_LABELED; |
97 | break; | 97 | break; |
98 | case -EDESTADDRREQ: | 98 | case -EDESTADDRREQ: |
99 | /* we are going to possibly end up labeling the individual | ||
100 | * packets later which is problematic for stream sockets | ||
101 | * because of the additional IP header size, our solution is to | ||
102 | * allow for the maximum IP header length (40 bytes for IPv4, | ||
103 | * we don't have to worry about IPv6 yet) just in case */ | ||
104 | sk_inet = inet_sk(sk); | ||
105 | if (sk_inet->is_icsk) { | ||
106 | sk_conn = inet_csk(sk); | ||
107 | if (sk_inet->opt) | ||
108 | sk_conn->icsk_ext_hdr_len -= | ||
109 | sk_inet->opt->optlen; | ||
110 | sk_conn->icsk_ext_hdr_len += 40; | ||
111 | sk_conn->icsk_sync_mss(sk, sk_conn->icsk_pmtu_cookie); | ||
112 | } | ||
113 | sksec->nlbl_state = NLBL_REQSKB; | 99 | sksec->nlbl_state = NLBL_REQSKB; |
114 | rc = 0; | 100 | rc = 0; |
115 | break; | 101 | break; |
@@ -247,21 +233,77 @@ skbuff_setsid_return: | |||
247 | } | 233 | } |
248 | 234 | ||
249 | /** | 235 | /** |
250 | * selinux_netlbl_sock_graft - Netlabel the new socket | 236 | * selinux_netlbl_inet_conn_established - Netlabel the newly accepted connection |
251 | * @sk: the new connection | 237 | * @sk: the new connection |
252 | * @sock: the new socket | ||
253 | * | 238 | * |
254 | * Description: | 239 | * Description: |
255 | * The connection represented by @sk is being grafted onto @sock so set the | 240 | * A new connection has been established on @sk so make sure it is labeled |
256 | * socket's NetLabel to match the SID of @sk. | 241 | * correctly with the NetLabel susbsystem. |
257 | * | 242 | * |
258 | */ | 243 | */ |
259 | void selinux_netlbl_sock_graft(struct sock *sk, struct socket *sock) | 244 | void selinux_netlbl_inet_conn_established(struct sock *sk, u16 family) |
260 | { | 245 | { |
261 | /* Try to set the NetLabel on the socket to save time later, if we fail | 246 | int rc; |
262 | * here we will pick up the pieces in later calls to | 247 | struct sk_security_struct *sksec = sk->sk_security; |
263 | * selinux_netlbl_inode_permission(). */ | 248 | struct netlbl_lsm_secattr secattr; |
264 | selinux_netlbl_sock_setsid(sk); | 249 | struct inet_sock *sk_inet = inet_sk(sk); |
250 | struct sockaddr_in addr; | ||
251 | |||
252 | if (sksec->nlbl_state != NLBL_REQUIRE) | ||
253 | return; | ||
254 | |||
255 | netlbl_secattr_init(&secattr); | ||
256 | if (security_netlbl_sid_to_secattr(sksec->sid, &secattr) != 0) | ||
257 | goto inet_conn_established_return; | ||
258 | |||
259 | rc = netlbl_sock_setattr(sk, &secattr); | ||
260 | switch (rc) { | ||
261 | case 0: | ||
262 | sksec->nlbl_state = NLBL_LABELED; | ||
263 | break; | ||
264 | case -EDESTADDRREQ: | ||
265 | /* no PF_INET6 support yet because we don't support any IPv6 | ||
266 | * labeling protocols */ | ||
267 | if (family != PF_INET) { | ||
268 | sksec->nlbl_state = NLBL_UNSET; | ||
269 | goto inet_conn_established_return; | ||
270 | } | ||
271 | |||
272 | addr.sin_family = family; | ||
273 | addr.sin_addr.s_addr = sk_inet->daddr; | ||
274 | if (netlbl_conn_setattr(sk, (struct sockaddr *)&addr, | ||
275 | &secattr) != 0) { | ||
276 | /* we failed to label the connected socket (could be | ||
277 | * for a variety of reasons, the actual "why" isn't | ||
278 | * important here) so we have to go to our backup plan, | ||
279 | * labeling the packets individually in the netfilter | ||
280 | * local output hook. this is okay but we need to | ||
281 | * adjust the MSS of the connection to take into | ||
282 | * account any labeling overhead, since we don't know | ||
283 | * the exact overhead at this point we'll use the worst | ||
284 | * case value which is 40 bytes for IPv4 */ | ||
285 | struct inet_connection_sock *sk_conn = inet_csk(sk); | ||
286 | sk_conn->icsk_ext_hdr_len += 40 - | ||
287 | (sk_inet->opt ? sk_inet->opt->optlen : 0); | ||
288 | sk_conn->icsk_sync_mss(sk, sk_conn->icsk_pmtu_cookie); | ||
289 | |||
290 | sksec->nlbl_state = NLBL_REQSKB; | ||
291 | } else | ||
292 | sksec->nlbl_state = NLBL_CONNLABELED; | ||
293 | break; | ||
294 | default: | ||
295 | /* note that we are failing to label the socket which could be | ||
296 | * a bad thing since it means traffic could leave the system | ||
297 | * without the desired labeling, however, all is not lost as | ||
298 | * we have a check in selinux_netlbl_inode_permission() to | ||
299 | * pick up the pieces that we might drop here because we can't | ||
300 | * return an error code */ | ||
301 | break; | ||
302 | } | ||
303 | |||
304 | inet_conn_established_return: | ||
305 | netlbl_secattr_destroy(&secattr); | ||
306 | return; | ||
265 | } | 307 | } |
266 | 308 | ||
267 | /** | 309 | /** |
@@ -398,7 +440,8 @@ int selinux_netlbl_socket_setsockopt(struct socket *sock, | |||
398 | struct netlbl_lsm_secattr secattr; | 440 | struct netlbl_lsm_secattr secattr; |
399 | 441 | ||
400 | if (level == IPPROTO_IP && optname == IP_OPTIONS && | 442 | if (level == IPPROTO_IP && optname == IP_OPTIONS && |
401 | sksec->nlbl_state == NLBL_LABELED) { | 443 | (sksec->nlbl_state == NLBL_LABELED || |
444 | sksec->nlbl_state == NLBL_CONNLABELED)) { | ||
402 | netlbl_secattr_init(&secattr); | 445 | netlbl_secattr_init(&secattr); |
403 | lock_sock(sk); | 446 | lock_sock(sk); |
404 | rc = netlbl_sock_getattr(sk, &secattr); | 447 | rc = netlbl_sock_getattr(sk, &secattr); |
@@ -410,3 +453,51 @@ int selinux_netlbl_socket_setsockopt(struct socket *sock, | |||
410 | 453 | ||
411 | return rc; | 454 | return rc; |
412 | } | 455 | } |
456 | |||
457 | /** | ||
458 | * selinux_netlbl_socket_connect - Label a client-side socket on connect | ||
459 | * @sk: the socket to label | ||
460 | * @addr: the destination address | ||
461 | * | ||
462 | * Description: | ||
463 | * Attempt to label a connected socket with NetLabel using the given address. | ||
464 | * Returns zero values on success, negative values on failure. | ||
465 | * | ||
466 | */ | ||
467 | int selinux_netlbl_socket_connect(struct sock *sk, struct sockaddr *addr) | ||
468 | { | ||
469 | int rc; | ||
470 | struct sk_security_struct *sksec = sk->sk_security; | ||
471 | struct netlbl_lsm_secattr secattr; | ||
472 | |||
473 | if (sksec->nlbl_state != NLBL_REQSKB && | ||
474 | sksec->nlbl_state != NLBL_CONNLABELED) | ||
475 | return 0; | ||
476 | |||
477 | netlbl_secattr_init(&secattr); | ||
478 | local_bh_disable(); | ||
479 | bh_lock_sock_nested(sk); | ||
480 | |||
481 | /* connected sockets are allowed to disconnect when the address family | ||
482 | * is set to AF_UNSPEC, if that is what is happening we want to reset | ||
483 | * the socket */ | ||
484 | if (addr->sa_family == AF_UNSPEC) { | ||
485 | netlbl_sock_delattr(sk); | ||
486 | sksec->nlbl_state = NLBL_REQSKB; | ||
487 | rc = 0; | ||
488 | goto socket_connect_return; | ||
489 | } | ||
490 | rc = security_netlbl_sid_to_secattr(sksec->sid, &secattr); | ||
491 | if (rc != 0) | ||
492 | goto socket_connect_return; | ||
493 | rc = netlbl_conn_setattr(sk, addr, &secattr); | ||
494 | if (rc != 0) | ||
495 | goto socket_connect_return; | ||
496 | sksec->nlbl_state = NLBL_CONNLABELED; | ||
497 | |||
498 | socket_connect_return: | ||
499 | bh_unlock_sock(sk); | ||
500 | local_bh_enable(); | ||
501 | netlbl_secattr_destroy(&secattr); | ||
502 | return rc; | ||
503 | } | ||