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 >
*
*/
/*
2008-10-10 18:16:32 +04:00
* ( c ) Copyright Hewlett - Packard Development Company , L . P . , 2007 , 2008
2007-02-28 23:14:22 +03:00
*
* 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>
2008-10-10 18:16:33 +04:00
# include <linux/ip.h>
# include <linux/ipv6.h>
2007-02-28 23:14:22 +03:00
# include <net/sock.h>
# include <net/netlabel.h>
2008-10-10 18:16:33 +04:00
# include <net/ip.h>
# include <net/ipv6.h>
2007-02-28 23:14:22 +03:00
# include "objsec.h"
# include "security.h"
2008-02-28 00:20:42 +03:00
# include "netlabel.h"
2007-02-28 23:14:22 +03:00
2008-01-29 16:44:18 +03:00
/**
* selinux_netlbl_sidlookup_cached - Cache a SID lookup
* @ skb : the packet
* @ secattr : the NetLabel security attributes
* @ sid : the SID
*
* Description :
* Query the SELinux security server to lookup the correct SID for the given
* security attributes . If the query is successful , cache the result to speed
* up future lookups . Returns zero on success , negative values on failure .
*
*/
static int selinux_netlbl_sidlookup_cached ( struct sk_buff * skb ,
struct netlbl_lsm_secattr * secattr ,
u32 * sid )
{
int rc ;
rc = security_netlbl_secattr_to_sid ( secattr , sid ) ;
if ( rc = = 0 & &
( secattr - > flags & NETLBL_SECATTR_CACHEABLE ) & &
( secattr - > flags & NETLBL_SECATTR_CACHE ) )
netlbl_cache_add ( skb , secattr ) ;
return rc ;
}
2008-10-10 18:16:33 +04:00
/**
* selinux_netlbl_sock_genattr - Generate the NetLabel socket secattr
* @ sk : the socket
*
* Description :
* Generate the NetLabel security attributes for a socket , making full use of
* the socket ' s attribute cache . Returns a pointer to the security attributes
* on success , NULL on failure .
*
*/
static struct netlbl_lsm_secattr * selinux_netlbl_sock_genattr ( struct sock * sk )
{
int rc ;
struct sk_security_struct * sksec = sk - > sk_security ;
struct netlbl_lsm_secattr * secattr ;
if ( sksec - > nlbl_secattr ! = NULL )
return sksec - > nlbl_secattr ;
secattr = netlbl_secattr_alloc ( GFP_ATOMIC ) ;
if ( secattr = = NULL )
return NULL ;
rc = security_netlbl_sid_to_secattr ( sksec - > sid , secattr ) ;
if ( rc ! = 0 ) {
netlbl_secattr_free ( secattr ) ;
return NULL ;
}
sksec - > nlbl_secattr = secattr ;
return secattr ;
}
2007-02-28 23:14:22 +03:00
/**
* 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 ( ) ;
}
2008-10-10 18:16:31 +04:00
/**
* selinux_netlbl_err - Handle a NetLabel packet error
* @ skb : the packet
* @ error : the error code
* @ gateway : true if host is acting as a gateway , false otherwise
*
* Description :
* When a packet is dropped due to a call to avc_has_perm ( ) pass the error
* code to the NetLabel subsystem so any protocol specific processing can be
* done . This is safe to call even if you are unsure if NetLabel labeling is
* present on the packet , NetLabel is smart enough to only act when it should .
*
*/
void selinux_netlbl_err ( struct sk_buff * skb , int error , int gateway )
{
netlbl_skbuff_err ( skb , error , gateway ) ;
}
2008-10-10 18:16:33 +04:00
/**
* selinux_netlbl_sk_security_free - Free the NetLabel fields
* @ sssec : the sk_security_struct
*
* Description :
* Free all of the memory in the NetLabel fields of a sk_security_struct .
*
*/
void selinux_netlbl_sk_security_free ( struct sk_security_struct * ssec )
{
if ( ssec - > nlbl_secattr ! = NULL )
netlbl_secattr_free ( ssec - > nlbl_secattr ) ;
}
2007-02-28 23:14:22 +03:00
/**
* 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 .
*
*/
2009-03-28 00:10:34 +03:00
void selinux_netlbl_sk_security_reset ( struct sk_security_struct * ssec )
2007-02-28 23:14:22 +03:00
{
2009-03-28 00:10:34 +03:00
ssec - > nlbl_state = NLBL_UNSET ;
2007-02-28 23:14:22 +03:00
}
/**
* 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
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 ,
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 ) ;
2008-01-29 16:44:18 +03:00
if ( rc = = 0 & & secattr . flags ! = NETLBL_SECATTR_NONE )
rc = selinux_netlbl_sidlookup_cached ( skb , & secattr , sid ) ;
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 ;
}
2008-10-10 18:16:32 +04:00
/**
* selinux_netlbl_skbuff_setsid - Set the NetLabel on a packet given a sid
* @ skb : the packet
* @ family : protocol family
* @ sid : the SID
*
* Description
* Call the NetLabel mechanism to set the label of a packet using @ sid .
* Returns zero on auccess , negative values on failure .
*
*/
int selinux_netlbl_skbuff_setsid ( struct sk_buff * skb ,
u16 family ,
u32 sid )
{
int rc ;
2008-10-10 18:16:33 +04:00
struct netlbl_lsm_secattr secattr_storage ;
struct netlbl_lsm_secattr * secattr = NULL ;
2008-10-10 18:16:32 +04:00
struct sock * sk ;
/* if this is a locally generated packet check to see if it is already
* being labeled by it ' s parent socket , if it is just exit */
sk = skb - > sk ;
if ( sk ! = NULL ) {
struct sk_security_struct * sksec = sk - > sk_security ;
if ( sksec - > nlbl_state ! = NLBL_REQSKB )
return 0 ;
2008-10-10 18:16:33 +04:00
secattr = sksec - > nlbl_secattr ;
}
if ( secattr = = NULL ) {
secattr = & secattr_storage ;
netlbl_secattr_init ( secattr ) ;
rc = security_netlbl_sid_to_secattr ( sid , secattr ) ;
if ( rc ! = 0 )
goto skbuff_setsid_return ;
2008-10-10 18:16:32 +04:00
}
2008-10-10 18:16:33 +04:00
rc = netlbl_skbuff_setattr ( skb , family , secattr ) ;
2008-10-10 18:16:32 +04:00
skbuff_setsid_return :
2008-10-10 18:16:33 +04:00
if ( secattr = = & secattr_storage )
netlbl_secattr_destroy ( secattr ) ;
2008-10-10 18:16:32 +04:00
return rc ;
}
2007-02-28 23:14:22 +03:00
/**
2009-03-28 00:10:34 +03:00
* selinux_netlbl_inet_conn_request - Label an incoming stream connection
* @ req : incoming connection request socket
2007-02-28 23:14:22 +03:00
*
* Description :
2009-03-28 00:10:34 +03:00
* A new incoming connection request is represented by @ req , we need to label
* the new request_sock here and the stack will ensure the on - the - wire label
* will get preserved when a full sock is created once the connection handshake
* is complete . Returns zero on success , negative values on failure .
2007-02-28 23:14:22 +03:00
*
*/
2009-03-28 00:10:34 +03:00
int selinux_netlbl_inet_conn_request ( struct request_sock * req , u16 family )
2007-02-28 23:14:22 +03:00
{
2008-10-10 18:16:33 +04:00
int rc ;
2009-03-28 00:10:34 +03:00
struct netlbl_lsm_secattr secattr ;
2008-10-10 18:16:33 +04:00
2009-03-28 00:10:34 +03:00
if ( family ! = PF_INET )
return 0 ;
2008-10-10 18:16:33 +04:00
2009-03-28 00:10:34 +03:00
netlbl_secattr_init ( & secattr ) ;
rc = security_netlbl_sid_to_secattr ( req - > secid , & secattr ) ;
if ( rc ! = 0 )
goto inet_conn_request_return ;
rc = netlbl_req_setattr ( req , & secattr ) ;
inet_conn_request_return :
netlbl_secattr_destroy ( & secattr ) ;
return rc ;
2007-02-28 23:14:22 +03:00
}
/**
2009-03-28 00:10:34 +03:00
* selinux_netlbl_inet_csk_clone - Initialize the newly created sock
* @ sk : the new sock
2007-02-28 23:14:22 +03:00
*
* Description :
2009-03-28 00:10:34 +03:00
* A new connection has been established using @ sk , we ' ve already labeled the
* socket via the request_sock struct in selinux_netlbl_inet_conn_request ( ) but
* we need to set the NetLabel state here since we now have a sock structure .
2007-02-28 23:14:22 +03:00
*
*/
2009-03-28 00:10:34 +03:00
void selinux_netlbl_inet_csk_clone ( struct sock * sk , u16 family )
2007-02-28 23:14:22 +03:00
{
2009-03-28 00:10:34 +03:00
struct sk_security_struct * sksec = sk - > sk_security ;
if ( family = = PF_INET )
sksec - > nlbl_state = NLBL_LABELED ;
else
sksec - > nlbl_state = NLBL_UNSET ;
2007-02-28 23:14:22 +03:00
}
/**
2009-03-28 00:10:34 +03:00
* selinux_netlbl_socket_post_create - Label a socket using NetLabel
* @ sock : the socket to label
* @ family : protocol family
2007-02-28 23:14:22 +03:00
*
* Description :
2009-03-28 00:10:34 +03:00
* Attempt to label a socket using the NetLabel mechanism using the given
* SID . Returns zero values on success , negative values on failure .
2007-02-28 23:14:22 +03:00
*
*/
2009-03-28 00:10:34 +03:00
int selinux_netlbl_socket_post_create ( struct sock * sk , u16 family )
2007-02-28 23:14:22 +03:00
{
int rc ;
2009-03-28 00:10:34 +03:00
struct sk_security_struct * sksec = sk - > sk_security ;
struct netlbl_lsm_secattr * secattr ;
2007-02-28 23:14:22 +03:00
2009-03-28 00:10:34 +03:00
if ( family ! = PF_INET )
2007-02-28 23:14:22 +03:00
return 0 ;
2008-02-25 19:40:33 +03:00
2009-03-28 00:10:34 +03:00
secattr = selinux_netlbl_sock_genattr ( sk ) ;
if ( secattr = = NULL )
return - ENOMEM ;
rc = netlbl_sock_setattr ( sk , family , secattr ) ;
switch ( rc ) {
case 0 :
sksec - > nlbl_state = NLBL_LABELED ;
break ;
case - EDESTADDRREQ :
sksec - > nlbl_state = NLBL_REQSKB ;
2008-02-25 19:40:33 +03:00
rc = 0 ;
2009-03-28 00:10:34 +03:00
break ;
}
2007-02-28 23:14:22 +03:00
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 ,
2009-07-14 20:14:09 +04:00
struct common_audit_data * ad )
2007-02-28 23:14:22 +03:00
{
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 ) ;
2008-01-29 16:44:18 +03:00
if ( rc = = 0 & & secattr . flags ! = NETLBL_SECATTR_NONE )
rc = selinux_netlbl_sidlookup_cached ( skb , & secattr , & nlbl_sid ) ;
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 )
2008-10-10 18:16:31 +04:00
netlbl_skbuff_err ( skb , rc , 0 ) ;
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 ;
if ( level = = IPPROTO_IP & & optname = = IP_OPTIONS & &
2008-10-10 18:16:33 +04:00
( sksec - > nlbl_state = = NLBL_LABELED | |
sksec - > nlbl_state = = NLBL_CONNLABELED ) ) {
2007-02-28 23:14:22 +03:00
netlbl_secattr_init ( & secattr ) ;
2007-06-08 05:37:15 +04:00
lock_sock ( sk ) ;
rc = netlbl_sock_getattr ( sk , & secattr ) ;
release_sock ( sk ) ;
2009-02-21 00:33:02 +03:00
if ( rc = = 0 )
2007-02-28 23:14:22 +03:00
rc = - EACCES ;
2009-02-21 00:33:02 +03:00
else if ( rc = = - ENOMSG )
rc = 0 ;
2007-02-28 23:14:22 +03:00
netlbl_secattr_destroy ( & secattr ) ;
}
return rc ;
}
2008-10-10 18:16:33 +04:00
/**
* selinux_netlbl_socket_connect - Label a client - side socket on connect
* @ sk : the socket to label
* @ addr : the destination address
*
* Description :
* Attempt to label a connected socket with NetLabel using the given address .
* Returns zero values on success , negative values on failure .
*
*/
int selinux_netlbl_socket_connect ( struct sock * sk , struct sockaddr * addr )
{
int rc ;
struct sk_security_struct * sksec = sk - > sk_security ;
2008-10-10 18:16:33 +04:00
struct netlbl_lsm_secattr * secattr ;
2008-10-10 18:16:33 +04:00
if ( sksec - > nlbl_state ! = NLBL_REQSKB & &
sksec - > nlbl_state ! = NLBL_CONNLABELED )
return 0 ;
local_bh_disable ( ) ;
bh_lock_sock_nested ( sk ) ;
/* connected sockets are allowed to disconnect when the address family
* is set to AF_UNSPEC , if that is what is happening we want to reset
* the socket */
if ( addr - > sa_family = = AF_UNSPEC ) {
netlbl_sock_delattr ( sk ) ;
sksec - > nlbl_state = NLBL_REQSKB ;
rc = 0 ;
goto socket_connect_return ;
}
2008-10-10 18:16:33 +04:00
secattr = selinux_netlbl_sock_genattr ( sk ) ;
if ( secattr = = NULL ) {
rc = - ENOMEM ;
2008-10-10 18:16:33 +04:00
goto socket_connect_return ;
2008-10-10 18:16:33 +04:00
}
rc = netlbl_conn_setattr ( sk , addr , secattr ) ;
if ( rc = = 0 )
sksec - > nlbl_state = NLBL_CONNLABELED ;
2008-10-10 18:16:33 +04:00
socket_connect_return :
bh_unlock_sock ( sk ) ;
local_bh_enable ( ) ;
return rc ;
}