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 | |
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')
-rw-r--r-- | security/selinux/hooks.c | 11 | ||||
-rw-r--r-- | security/selinux/include/netlabel.h | 19 | ||||
-rw-r--r-- | security/selinux/include/objsec.h | 1 | ||||
-rw-r--r-- | security/selinux/netlabel.c | 147 |
4 files changed, 142 insertions, 36 deletions
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 7432bdd5d367..632ac3e80a61 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c | |||
@@ -3794,6 +3794,7 @@ out: | |||
3794 | 3794 | ||
3795 | static int selinux_socket_connect(struct socket *sock, struct sockaddr *address, int addrlen) | 3795 | static int selinux_socket_connect(struct socket *sock, struct sockaddr *address, int addrlen) |
3796 | { | 3796 | { |
3797 | struct sock *sk = sock->sk; | ||
3797 | struct inode_security_struct *isec; | 3798 | struct inode_security_struct *isec; |
3798 | int err; | 3799 | int err; |
3799 | 3800 | ||
@@ -3807,7 +3808,6 @@ static int selinux_socket_connect(struct socket *sock, struct sockaddr *address, | |||
3807 | isec = SOCK_INODE(sock)->i_security; | 3808 | isec = SOCK_INODE(sock)->i_security; |
3808 | if (isec->sclass == SECCLASS_TCP_SOCKET || | 3809 | if (isec->sclass == SECCLASS_TCP_SOCKET || |
3809 | isec->sclass == SECCLASS_DCCP_SOCKET) { | 3810 | isec->sclass == SECCLASS_DCCP_SOCKET) { |
3810 | struct sock *sk = sock->sk; | ||
3811 | struct avc_audit_data ad; | 3811 | struct avc_audit_data ad; |
3812 | struct sockaddr_in *addr4 = NULL; | 3812 | struct sockaddr_in *addr4 = NULL; |
3813 | struct sockaddr_in6 *addr6 = NULL; | 3813 | struct sockaddr_in6 *addr6 = NULL; |
@@ -3841,6 +3841,8 @@ static int selinux_socket_connect(struct socket *sock, struct sockaddr *address, | |||
3841 | goto out; | 3841 | goto out; |
3842 | } | 3842 | } |
3843 | 3843 | ||
3844 | err = selinux_netlbl_socket_connect(sk, address); | ||
3845 | |||
3844 | out: | 3846 | out: |
3845 | return err; | 3847 | return err; |
3846 | } | 3848 | } |
@@ -4290,8 +4292,6 @@ static void selinux_sock_graft(struct sock *sk, struct socket *parent) | |||
4290 | sk->sk_family == PF_UNIX) | 4292 | sk->sk_family == PF_UNIX) |
4291 | isec->sid = sksec->sid; | 4293 | isec->sid = sksec->sid; |
4292 | sksec->sclass = isec->sclass; | 4294 | sksec->sclass = isec->sclass; |
4293 | |||
4294 | selinux_netlbl_sock_graft(sk, parent); | ||
4295 | } | 4295 | } |
4296 | 4296 | ||
4297 | static int selinux_inet_conn_request(struct sock *sk, struct sk_buff *skb, | 4297 | static int selinux_inet_conn_request(struct sock *sk, struct sk_buff *skb, |
@@ -4342,8 +4342,7 @@ static void selinux_inet_csk_clone(struct sock *newsk, | |||
4342 | selinux_netlbl_sk_security_reset(newsksec, req->rsk_ops->family); | 4342 | selinux_netlbl_sk_security_reset(newsksec, req->rsk_ops->family); |
4343 | } | 4343 | } |
4344 | 4344 | ||
4345 | static void selinux_inet_conn_established(struct sock *sk, | 4345 | static void selinux_inet_conn_established(struct sock *sk, struct sk_buff *skb) |
4346 | struct sk_buff *skb) | ||
4347 | { | 4346 | { |
4348 | u16 family = sk->sk_family; | 4347 | u16 family = sk->sk_family; |
4349 | struct sk_security_struct *sksec = sk->sk_security; | 4348 | struct sk_security_struct *sksec = sk->sk_security; |
@@ -4353,6 +4352,8 @@ static void selinux_inet_conn_established(struct sock *sk, | |||
4353 | family = PF_INET; | 4352 | family = PF_INET; |
4354 | 4353 | ||
4355 | selinux_skb_peerlbl_sid(skb, family, &sksec->peer_sid); | 4354 | selinux_skb_peerlbl_sid(skb, family, &sksec->peer_sid); |
4355 | |||
4356 | selinux_netlbl_inet_conn_established(sk, family); | ||
4356 | } | 4357 | } |
4357 | 4358 | ||
4358 | static void selinux_req_classify_flow(const struct request_sock *req, | 4359 | static void selinux_req_classify_flow(const struct request_sock *req, |
diff --git a/security/selinux/include/netlabel.h b/security/selinux/include/netlabel.h index b3e6ae071fc3..982bac0ac328 100644 --- a/security/selinux/include/netlabel.h +++ b/security/selinux/include/netlabel.h | |||
@@ -52,7 +52,7 @@ int selinux_netlbl_skbuff_setsid(struct sk_buff *skb, | |||
52 | u16 family, | 52 | u16 family, |
53 | u32 sid); | 53 | u32 sid); |
54 | 54 | ||
55 | void selinux_netlbl_sock_graft(struct sock *sk, struct socket *sock); | 55 | void selinux_netlbl_inet_conn_established(struct sock *sk, u16 family); |
56 | int selinux_netlbl_socket_post_create(struct socket *sock); | 56 | int selinux_netlbl_socket_post_create(struct socket *sock); |
57 | int selinux_netlbl_inode_permission(struct inode *inode, int mask); | 57 | int selinux_netlbl_inode_permission(struct inode *inode, int mask); |
58 | int selinux_netlbl_sock_rcv_skb(struct sk_security_struct *sksec, | 58 | int selinux_netlbl_sock_rcv_skb(struct sk_security_struct *sksec, |
@@ -62,6 +62,8 @@ int selinux_netlbl_sock_rcv_skb(struct sk_security_struct *sksec, | |||
62 | int selinux_netlbl_socket_setsockopt(struct socket *sock, | 62 | int selinux_netlbl_socket_setsockopt(struct socket *sock, |
63 | int level, | 63 | int level, |
64 | int optname); | 64 | int optname); |
65 | int selinux_netlbl_socket_connect(struct sock *sk, struct sockaddr *addr); | ||
66 | |||
65 | #else | 67 | #else |
66 | static inline void selinux_netlbl_cache_invalidate(void) | 68 | static inline void selinux_netlbl_cache_invalidate(void) |
67 | { | 69 | { |
@@ -98,8 +100,14 @@ static inline int selinux_netlbl_skbuff_setsid(struct sk_buff *skb, | |||
98 | return 0; | 100 | return 0; |
99 | } | 101 | } |
100 | 102 | ||
101 | static inline void selinux_netlbl_sock_graft(struct sock *sk, | 103 | static inline int selinux_netlbl_conn_setsid(struct sock *sk, |
102 | struct socket *sock) | 104 | struct sockaddr *addr) |
105 | { | ||
106 | return 0; | ||
107 | } | ||
108 | |||
109 | static inline void selinux_netlbl_inet_conn_established(struct sock *sk, | ||
110 | u16 family) | ||
103 | { | 111 | { |
104 | return; | 112 | return; |
105 | } | 113 | } |
@@ -125,6 +133,11 @@ static inline int selinux_netlbl_socket_setsockopt(struct socket *sock, | |||
125 | { | 133 | { |
126 | return 0; | 134 | return 0; |
127 | } | 135 | } |
136 | static inline int selinux_netlbl_socket_connect(struct sock *sk, | ||
137 | struct sockaddr *addr) | ||
138 | { | ||
139 | return 0; | ||
140 | } | ||
128 | #endif /* CONFIG_NETLABEL */ | 141 | #endif /* CONFIG_NETLABEL */ |
129 | 142 | ||
130 | #endif | 143 | #endif |
diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h index f46dd1c3d01c..ad34787c6c02 100644 --- a/security/selinux/include/objsec.h +++ b/security/selinux/include/objsec.h | |||
@@ -118,6 +118,7 @@ struct sk_security_struct { | |||
118 | NLBL_REQUIRE, | 118 | NLBL_REQUIRE, |
119 | NLBL_LABELED, | 119 | NLBL_LABELED, |
120 | NLBL_REQSKB, | 120 | NLBL_REQSKB, |
121 | NLBL_CONNLABELED, | ||
121 | } nlbl_state; | 122 | } nlbl_state; |
122 | #endif | 123 | #endif |
123 | }; | 124 | }; |
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 | } | ||