aboutsummaryrefslogtreecommitdiffstats
path: root/security/selinux
diff options
context:
space:
mode:
authorPaul Moore <paul.moore@hp.com>2008-10-10 10:16:33 -0400
committerPaul Moore <paul.moore@hp.com>2008-10-10 10:16:33 -0400
commit014ab19a69c325f52d7bae54ceeda73d6307ae0c (patch)
tree8a69c490accb7d5454bdfeb8c078d846729aeb60 /security/selinux
parent948bf85c1bc9a84754786a9d5dd99b7ecc46451e (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')
-rw-r--r--security/selinux/hooks.c11
-rw-r--r--security/selinux/include/netlabel.h19
-rw-r--r--security/selinux/include/objsec.h1
-rw-r--r--security/selinux/netlabel.c147
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
3795static int selinux_socket_connect(struct socket *sock, struct sockaddr *address, int addrlen) 3795static 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
3844out: 3846out:
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
4297static int selinux_inet_conn_request(struct sock *sk, struct sk_buff *skb, 4297static 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
4345static void selinux_inet_conn_established(struct sock *sk, 4345static 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
4358static void selinux_req_classify_flow(const struct request_sock *req, 4359static 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
55void selinux_netlbl_sock_graft(struct sock *sk, struct socket *sock); 55void selinux_netlbl_inet_conn_established(struct sock *sk, u16 family);
56int selinux_netlbl_socket_post_create(struct socket *sock); 56int selinux_netlbl_socket_post_create(struct socket *sock);
57int selinux_netlbl_inode_permission(struct inode *inode, int mask); 57int selinux_netlbl_inode_permission(struct inode *inode, int mask);
58int selinux_netlbl_sock_rcv_skb(struct sk_security_struct *sksec, 58int 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,
62int selinux_netlbl_socket_setsockopt(struct socket *sock, 62int selinux_netlbl_socket_setsockopt(struct socket *sock,
63 int level, 63 int level,
64 int optname); 64 int optname);
65int selinux_netlbl_socket_connect(struct sock *sk, struct sockaddr *addr);
66
65#else 67#else
66static inline void selinux_netlbl_cache_invalidate(void) 68static 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
101static inline void selinux_netlbl_sock_graft(struct sock *sk, 103static inline int selinux_netlbl_conn_setsid(struct sock *sk,
102 struct socket *sock) 104 struct sockaddr *addr)
105{
106 return 0;
107}
108
109static 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}
136static 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 */
259void selinux_netlbl_sock_graft(struct sock *sk, struct socket *sock) 244void 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
304inet_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 */
467int 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
498socket_connect_return:
499 bh_unlock_sock(sk);
500 local_bh_enable();
501 netlbl_secattr_destroy(&secattr);
502 return rc;
503}