diff options
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 | } | ||
