2007-02-28 23:14:22 +03:00
/*
* SELinux NetLabel Support
*
* This file provides the necessary glue to tie NetLabel into the SELinux
* subsystem .
*
* Author : Paul Moore < paul . moore @ hp . com >
*
*/
/*
* ( c ) Copyright Hewlett - Packard Development Company , L . P . , 2007
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See
* the GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*
*/
# include <linux/spinlock.h>
# include <linux/rcupdate.h>
# include <net/sock.h>
# include <net/netlabel.h>
# include "objsec.h"
# include "security.h"
/**
2007-06-08 05:37:15 +04:00
* selinux_netlbl_sock_setsid - Label a socket using the NetLabel mechanism
* @ sk : the socket to label
2007-02-28 23:14:22 +03:00
* @ sid : the SID to use
*
* Description :
* Attempt to label a socket using the NetLabel mechanism using the given
* SID . Returns zero values on success , negative values on failure . The
* caller is responsibile for calling rcu_read_lock ( ) before calling this
* this function and rcu_read_unlock ( ) after this function returns .
*
*/
2007-06-08 05:37:15 +04:00
static int selinux_netlbl_sock_setsid ( struct sock * sk , u32 sid )
2007-02-28 23:14:22 +03:00
{
int rc ;
2007-06-08 05:37:15 +04:00
struct sk_security_struct * sksec = sk - > sk_security ;
2007-02-28 23:14:22 +03:00
struct netlbl_lsm_secattr secattr ;
2008-01-22 01:31:00 +03:00
netlbl_secattr_init ( & secattr ) ;
2007-02-28 23:14:22 +03:00
rc = security_netlbl_sid_to_secattr ( sid , & secattr ) ;
if ( rc ! = 0 )
2008-01-22 01:31:00 +03:00
goto sock_setsid_return ;
2007-06-08 05:37:15 +04:00
rc = netlbl_sock_setattr ( sk , & secattr ) ;
2007-02-28 23:14:22 +03:00
if ( rc = = 0 ) {
spin_lock_bh ( & sksec - > nlbl_lock ) ;
sksec - > nlbl_state = NLBL_LABELED ;
spin_unlock_bh ( & sksec - > nlbl_lock ) ;
}
2008-01-22 01:31:00 +03:00
sock_setsid_return :
netlbl_secattr_destroy ( & secattr ) ;
2007-02-28 23:14:22 +03:00
return rc ;
}
/**
* selinux_netlbl_cache_invalidate - Invalidate the NetLabel cache
*
* Description :
* Invalidate the NetLabel security attribute mapping cache .
*
*/
void selinux_netlbl_cache_invalidate ( void )
{
netlbl_cache_invalidate ( ) ;
}
/**
* selinux_netlbl_sk_security_reset - Reset the NetLabel fields
* @ ssec : the sk_security_struct
* @ family : the socket family
*
* Description :
* Called when the NetLabel state of a sk_security_struct needs to be reset .
* The caller is responsibile for all the NetLabel sk_security_struct locking .
*
*/
void selinux_netlbl_sk_security_reset ( struct sk_security_struct * ssec ,
int family )
{
if ( family = = PF_INET )
ssec - > nlbl_state = NLBL_REQUIRE ;
else
ssec - > nlbl_state = NLBL_UNSET ;
}
/**
* selinux_netlbl_sk_security_init - Setup the NetLabel fields
* @ ssec : the sk_security_struct
* @ family : the socket family
*
* Description :
* Called when a new sk_security_struct is allocated to initialize the NetLabel
* fields .
*
*/
void selinux_netlbl_sk_security_init ( struct sk_security_struct * ssec ,
int family )
{
/* No locking needed, we are the only one who has access to ssec */
selinux_netlbl_sk_security_reset ( ssec , family ) ;
spin_lock_init ( & ssec - > nlbl_lock ) ;
}
/**
* selinux_netlbl_sk_security_clone - Copy the NetLabel fields
* @ ssec : the original sk_security_struct
* @ newssec : the cloned sk_security_struct
*
* Description :
* Clone the NetLabel specific sk_security_struct fields from @ ssec to
* @ newssec .
*
*/
void selinux_netlbl_sk_security_clone ( struct sk_security_struct * ssec ,
struct sk_security_struct * newssec )
{
/* We don't need to take newssec->nlbl_lock because we are the only
* thread with access to newssec , but we do need to take the RCU read
* lock as other threads could have access to ssec */
rcu_read_lock ( ) ;
selinux_netlbl_sk_security_reset ( newssec , ssec - > sk - > sk_family ) ;
rcu_read_unlock ( ) ;
}
/**
* selinux_netlbl_skbuff_getsid - Get the sid of a packet using NetLabel
* @ skb : the packet
2008-01-29 16:38:04 +03:00
* @ family : protocol family
2007-02-28 23:14:22 +03:00
* @ base_sid : the SELinux SID to use as a context for MLS only attributes
2008-01-29 16:38:23 +03:00
* @ type : NetLabel labeling protocol type
2007-02-28 23:14:22 +03:00
* @ sid : the SID
*
* Description :
* Call the NetLabel mechanism to get the security attributes of the given
* packet and use those attributes to determine the correct context / SID to
* assign to the packet . Returns zero on success , negative values on failure .
*
*/
2008-01-29 16:38:04 +03:00
int selinux_netlbl_skbuff_getsid ( struct sk_buff * skb ,
u16 family ,
u32 base_sid ,
2008-01-29 16:38:23 +03:00
u32 * type ,
2008-01-29 16:38:04 +03:00
u32 * sid )
2007-02-28 23:14:22 +03:00
{
int rc ;
struct netlbl_lsm_secattr secattr ;
2007-07-18 20:28:45 +04:00
if ( ! netlbl_enabled ( ) ) {
* sid = SECSID_NULL ;
return 0 ;
}
2007-02-28 23:14:22 +03:00
netlbl_secattr_init ( & secattr ) ;
2008-01-29 16:38:04 +03:00
rc = netlbl_skbuff_getattr ( skb , family , & secattr ) ;
2007-07-31 00:33:26 +04:00
if ( rc = = 0 & & secattr . flags ! = NETLBL_SECATTR_NONE ) {
2007-07-18 20:28:46 +04:00
rc = security_netlbl_secattr_to_sid ( & secattr , base_sid , sid ) ;
2007-07-31 00:33:26 +04:00
if ( rc = = 0 & &
( secattr . flags & NETLBL_SECATTR_CACHEABLE ) & &
( secattr . flags & NETLBL_SECATTR_CACHE ) )
netlbl_cache_add ( skb , & secattr ) ;
} else
2007-02-28 23:14:22 +03:00
* sid = SECSID_NULL ;
2008-01-29 16:38:23 +03:00
* type = secattr . type ;
2007-02-28 23:14:22 +03:00
netlbl_secattr_destroy ( & secattr ) ;
return rc ;
}
/**
* selinux_netlbl_sock_graft - Netlabel the new socket
* @ sk : the new connection
* @ sock : the new socket
*
* Description :
* The connection represented by @ sk is being grafted onto @ sock so set the
* socket ' s NetLabel to match the SID of @ sk .
*
*/
void selinux_netlbl_sock_graft ( struct sock * sk , struct socket * sock )
{
struct sk_security_struct * sksec = sk - > sk_security ;
struct netlbl_lsm_secattr secattr ;
u32 nlbl_peer_sid ;
rcu_read_lock ( ) ;
if ( sksec - > nlbl_state ! = NLBL_REQUIRE ) {
rcu_read_unlock ( ) ;
return ;
}
netlbl_secattr_init ( & secattr ) ;
if ( netlbl_sock_getattr ( sk , & secattr ) = = 0 & &
secattr . flags ! = NETLBL_SECATTR_NONE & &
security_netlbl_secattr_to_sid ( & secattr ,
2007-07-18 20:28:46 +04:00
SECINITSID_NETMSG ,
2007-02-28 23:14:22 +03:00
& nlbl_peer_sid ) = = 0 )
sksec - > peer_sid = nlbl_peer_sid ;
netlbl_secattr_destroy ( & secattr ) ;
/* Try to set the NetLabel on the socket to save time later, if we fail
* here we will pick up the pieces in later calls to
* selinux_netlbl_inode_permission ( ) . */
2007-06-08 05:37:15 +04:00
selinux_netlbl_sock_setsid ( sk , sksec - > sid ) ;
2007-02-28 23:14:22 +03:00
rcu_read_unlock ( ) ;
}
/**
* selinux_netlbl_socket_post_create - Label a socket using NetLabel
* @ sock : the socket to label
*
* Description :
* Attempt to label a socket using the NetLabel mechanism using the given
* SID . Returns zero values on success , negative values on failure .
*
*/
int selinux_netlbl_socket_post_create ( struct socket * sock )
{
int rc = 0 ;
2007-06-08 05:37:15 +04:00
struct sock * sk = sock - > sk ;
struct sk_security_struct * sksec = sk - > sk_security ;
2007-02-28 23:14:22 +03:00
rcu_read_lock ( ) ;
if ( sksec - > nlbl_state = = NLBL_REQUIRE )
2007-06-08 05:37:15 +04:00
rc = selinux_netlbl_sock_setsid ( sk , sksec - > sid ) ;
2007-02-28 23:14:22 +03:00
rcu_read_unlock ( ) ;
return rc ;
}
/**
* selinux_netlbl_inode_permission - Verify the socket is NetLabel labeled
* @ inode : the file descriptor ' s inode
* @ mask : the permission mask
*
* Description :
* Looks at a file ' s inode and if it is marked as a socket protected by
* NetLabel then verify that the socket has been labeled , if not try to label
* the socket now with the inode ' s SID . Returns zero on success , negative
* values on failure .
*
*/
int selinux_netlbl_inode_permission ( struct inode * inode , int mask )
{
int rc ;
2007-06-08 05:37:15 +04:00
struct sock * sk ;
2007-02-28 23:14:22 +03:00
struct socket * sock ;
2007-06-08 05:37:15 +04:00
struct sk_security_struct * sksec ;
2007-02-28 23:14:22 +03:00
if ( ! S_ISSOCK ( inode - > i_mode ) | |
( ( mask & ( MAY_WRITE | MAY_APPEND ) ) = = 0 ) )
return 0 ;
sock = SOCKET_I ( inode ) ;
2007-06-08 05:37:15 +04:00
sk = sock - > sk ;
sksec = sk - > sk_security ;
2007-02-28 23:14:22 +03:00
rcu_read_lock ( ) ;
if ( sksec - > nlbl_state ! = NLBL_REQUIRE ) {
rcu_read_unlock ( ) ;
return 0 ;
}
local_bh_disable ( ) ;
2007-06-08 05:37:15 +04:00
bh_lock_sock_nested ( sk ) ;
rc = selinux_netlbl_sock_setsid ( sk , sksec - > sid ) ;
bh_unlock_sock ( sk ) ;
2007-02-28 23:14:22 +03:00
local_bh_enable ( ) ;
rcu_read_unlock ( ) ;
return rc ;
}
/**
* selinux_netlbl_sock_rcv_skb - Do an inbound access check using NetLabel
* @ sksec : the sock ' s sk_security_struct
* @ skb : the packet
2008-01-29 16:38:04 +03:00
* @ family : protocol family
2007-02-28 23:14:22 +03:00
* @ ad : the audit data
*
* Description :
* Fetch the NetLabel security attributes from @ skb and perform an access check
* against the receiving socket . Returns zero on success , negative values on
* error .
*
*/
int selinux_netlbl_sock_rcv_skb ( struct sk_security_struct * sksec ,
struct sk_buff * skb ,
2008-01-29 16:38:04 +03:00
u16 family ,
2007-02-28 23:14:22 +03:00
struct avc_audit_data * ad )
{
int rc ;
2007-07-18 20:28:46 +04:00
u32 nlbl_sid ;
u32 perm ;
struct netlbl_lsm_secattr secattr ;
2007-02-28 23:14:22 +03:00
2007-07-18 20:28:45 +04:00
if ( ! netlbl_enabled ( ) )
return 0 ;
2007-07-18 20:28:46 +04:00
netlbl_secattr_init ( & secattr ) ;
2008-01-29 16:38:04 +03:00
rc = netlbl_skbuff_getattr ( skb , family , & secattr ) ;
2007-07-31 00:33:26 +04:00
if ( rc = = 0 & & secattr . flags ! = NETLBL_SECATTR_NONE ) {
2007-07-18 20:28:46 +04:00
rc = security_netlbl_secattr_to_sid ( & secattr ,
SECINITSID_NETMSG ,
& nlbl_sid ) ;
2007-07-31 00:33:26 +04:00
if ( rc = = 0 & &
( secattr . flags & NETLBL_SECATTR_CACHEABLE ) & &
( secattr . flags & NETLBL_SECATTR_CACHE ) )
netlbl_cache_add ( skb , & secattr ) ;
} else
2007-07-18 20:28:46 +04:00
nlbl_sid = SECINITSID_UNLABELED ;
netlbl_secattr_destroy ( & secattr ) ;
2007-02-28 23:14:22 +03:00
if ( rc ! = 0 )
return rc ;
2007-07-14 03:53:18 +04:00
2007-02-28 23:14:22 +03:00
switch ( sksec - > sclass ) {
case SECCLASS_UDP_SOCKET :
2007-07-18 20:28:46 +04:00
perm = UDP_SOCKET__RECVFROM ;
2007-02-28 23:14:22 +03:00
break ;
case SECCLASS_TCP_SOCKET :
2007-07-18 20:28:46 +04:00
perm = TCP_SOCKET__RECVFROM ;
2007-02-28 23:14:22 +03:00
break ;
default :
2007-07-18 20:28:46 +04:00
perm = RAWIP_SOCKET__RECVFROM ;
2007-02-28 23:14:22 +03:00
}
2007-07-18 20:28:46 +04:00
rc = avc_has_perm ( sksec - > sid , nlbl_sid , sksec - > sclass , perm , ad ) ;
2007-02-28 23:14:22 +03:00
if ( rc = = 0 )
return 0 ;
2007-07-18 20:28:46 +04:00
if ( nlbl_sid ! = SECINITSID_UNLABELED )
netlbl_skbuff_err ( skb , rc ) ;
2007-02-28 23:14:22 +03:00
return rc ;
}
/**
* selinux_netlbl_socket_setsockopt - Do not allow users to remove a NetLabel
* @ sock : the socket
* @ level : the socket level or protocol
* @ optname : the socket option name
*
* Description :
* Check the setsockopt ( ) call and if the user is trying to replace the IP
* options on a socket and a NetLabel is in place for the socket deny the
* access ; otherwise allow the access . Returns zero when the access is
* allowed , - EACCES when denied , and other negative values on error .
*
*/
int selinux_netlbl_socket_setsockopt ( struct socket * sock ,
int level ,
int optname )
{
int rc = 0 ;
2007-06-08 05:37:15 +04:00
struct sock * sk = sock - > sk ;
struct sk_security_struct * sksec = sk - > sk_security ;
2007-02-28 23:14:22 +03:00
struct netlbl_lsm_secattr secattr ;
rcu_read_lock ( ) ;
if ( level = = IPPROTO_IP & & optname = = IP_OPTIONS & &
sksec - > nlbl_state = = NLBL_LABELED ) {
netlbl_secattr_init ( & secattr ) ;
2007-06-08 05:37:15 +04:00
lock_sock ( sk ) ;
rc = netlbl_sock_getattr ( sk , & secattr ) ;
release_sock ( sk ) ;
2007-02-28 23:14:22 +03:00
if ( rc = = 0 & & secattr . flags ! = NETLBL_SECATTR_NONE )
rc = - EACCES ;
netlbl_secattr_destroy ( & secattr ) ;
}
rcu_read_unlock ( ) ;
return rc ;
}