2016-06-27 22:02:46 +03:00
/*
* NetLabel CALIPSO / IPv6 Support
*
* This file defines the CALIPSO / IPv6 functions for the NetLabel system . The
* NetLabel system manages static and dynamic label mappings for network
* protocols such as CIPSO and CALIPSO .
*
* Authors : Paul Moore < paul @ paul - moore . com >
* Huw Davies < huw @ codeweavers . com >
*
*/
/* (c) Copyright Hewlett-Packard Development Company, L.P., 2006
* ( c ) Copyright Huw Davies < huw @ codeweavers . com > , 2015
*
* 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 , see < http : //www.gnu.org/licenses/>.
*
*/
# include <linux/types.h>
# include <linux/socket.h>
# include <linux/string.h>
# include <linux/skbuff.h>
# include <linux/audit.h>
# include <linux/slab.h>
# include <net/sock.h>
# include <net/netlink.h>
# include <net/genetlink.h>
# include <net/netlabel.h>
# include <net/calipso.h>
# include <linux/atomic.h>
# include "netlabel_user.h"
# include "netlabel_calipso.h"
# include "netlabel_mgmt.h"
# include "netlabel_domainhash.h"
2016-06-27 22:02:48 +03:00
/* Argument struct for calipso_doi_walk() */
struct netlbl_calipso_doiwalk_arg {
struct netlink_callback * nl_cb ;
struct sk_buff * skb ;
u32 seq ;
} ;
2016-06-27 22:02:49 +03:00
/* Argument struct for netlbl_domhsh_walk() */
struct netlbl_domhsh_walk_arg {
struct netlbl_audit * audit_info ;
u32 doi ;
} ;
2016-06-27 22:02:46 +03:00
/* NetLabel Generic NETLINK CALIPSO family */
static struct genl_family netlbl_calipso_gnl_family = {
. id = GENL_ID_GENERATE ,
. hdrsize = 0 ,
. name = NETLBL_NLTYPE_CALIPSO_NAME ,
. version = NETLBL_PROTO_VERSION ,
. maxattr = NLBL_CALIPSO_A_MAX ,
} ;
/* NetLabel Netlink attribute policy */
static const struct nla_policy calipso_genl_policy [ NLBL_CALIPSO_A_MAX + 1 ] = {
[ NLBL_CALIPSO_A_DOI ] = { . type = NLA_U32 } ,
[ NLBL_CALIPSO_A_MTYPE ] = { . type = NLA_U32 } ,
} ;
/* NetLabel Command Handlers
*/
/**
* netlbl_calipso_add_pass - Adds a CALIPSO pass DOI definition
* @ info : the Generic NETLINK info block
* @ audit_info : NetLabel audit information
*
* Description :
* Create a new CALIPSO_MAP_PASS DOI definition based on the given ADD message
* and add it to the CALIPSO engine . Return zero on success and non - zero on
* error .
*
*/
static int netlbl_calipso_add_pass ( struct genl_info * info ,
struct netlbl_audit * audit_info )
{
int ret_val ;
struct calipso_doi * doi_def = NULL ;
doi_def = kmalloc ( sizeof ( * doi_def ) , GFP_KERNEL ) ;
if ( ! doi_def )
return - ENOMEM ;
doi_def - > type = CALIPSO_MAP_PASS ;
doi_def - > doi = nla_get_u32 ( info - > attrs [ NLBL_CALIPSO_A_DOI ] ) ;
ret_val = calipso_doi_add ( doi_def , audit_info ) ;
if ( ret_val ! = 0 )
calipso_doi_free ( doi_def ) ;
return ret_val ;
}
/**
* netlbl_calipso_add - Handle an ADD message
* @ skb : the NETLINK buffer
* @ info : the Generic NETLINK info block
*
* Description :
* Create a new DOI definition based on the given ADD message and add it to the
* CALIPSO engine . Returns zero on success , negative values on failure .
*
*/
static int netlbl_calipso_add ( struct sk_buff * skb , struct genl_info * info )
{
int ret_val = - EINVAL ;
struct netlbl_audit audit_info ;
if ( ! info - > attrs [ NLBL_CALIPSO_A_DOI ] | |
! info - > attrs [ NLBL_CALIPSO_A_MTYPE ] )
return - EINVAL ;
netlbl_netlink_auditinfo ( skb , & audit_info ) ;
switch ( nla_get_u32 ( info - > attrs [ NLBL_CALIPSO_A_MTYPE ] ) ) {
case CALIPSO_MAP_PASS :
ret_val = netlbl_calipso_add_pass ( info , & audit_info ) ;
break ;
}
if ( ret_val = = 0 )
atomic_inc ( & netlabel_mgmt_protocount ) ;
return ret_val ;
}
2016-06-27 22:02:47 +03:00
/**
* netlbl_calipso_list - Handle a LIST message
* @ skb : the NETLINK buffer
* @ info : the Generic NETLINK info block
*
* Description :
* Process a user generated LIST message and respond accordingly .
* Returns zero on success and negative values on error .
*
*/
static int netlbl_calipso_list ( struct sk_buff * skb , struct genl_info * info )
{
int ret_val ;
struct sk_buff * ans_skb = NULL ;
void * data ;
u32 doi ;
struct calipso_doi * doi_def ;
if ( ! info - > attrs [ NLBL_CALIPSO_A_DOI ] ) {
ret_val = - EINVAL ;
goto list_failure ;
}
doi = nla_get_u32 ( info - > attrs [ NLBL_CALIPSO_A_DOI ] ) ;
doi_def = calipso_doi_getdef ( doi ) ;
if ( ! doi_def ) {
ret_val = - EINVAL ;
goto list_failure ;
}
ans_skb = nlmsg_new ( NLMSG_DEFAULT_SIZE , GFP_KERNEL ) ;
if ( ! ans_skb ) {
ret_val = - ENOMEM ;
goto list_failure_put ;
}
data = genlmsg_put_reply ( ans_skb , info , & netlbl_calipso_gnl_family ,
0 , NLBL_CALIPSO_C_LIST ) ;
if ( ! data ) {
ret_val = - ENOMEM ;
goto list_failure_put ;
}
ret_val = nla_put_u32 ( ans_skb , NLBL_CALIPSO_A_MTYPE , doi_def - > type ) ;
if ( ret_val ! = 0 )
goto list_failure_put ;
calipso_doi_putdef ( doi_def ) ;
genlmsg_end ( ans_skb , data ) ;
return genlmsg_reply ( ans_skb , info ) ;
list_failure_put :
calipso_doi_putdef ( doi_def ) ;
list_failure :
kfree_skb ( ans_skb ) ;
return ret_val ;
}
2016-06-27 22:02:48 +03:00
/**
* netlbl_calipso_listall_cb - calipso_doi_walk ( ) callback for LISTALL
* @ doi_def : the CALIPSO DOI definition
* @ arg : the netlbl_calipso_doiwalk_arg structure
*
* Description :
* This function is designed to be used as a callback to the
* calipso_doi_walk ( ) function for use in generating a response for a LISTALL
* message . Returns the size of the message on success , negative values on
* failure .
*
*/
static int netlbl_calipso_listall_cb ( struct calipso_doi * doi_def , void * arg )
{
int ret_val = - ENOMEM ;
struct netlbl_calipso_doiwalk_arg * cb_arg = arg ;
void * data ;
data = genlmsg_put ( cb_arg - > skb , NETLINK_CB ( cb_arg - > nl_cb - > skb ) . portid ,
cb_arg - > seq , & netlbl_calipso_gnl_family ,
NLM_F_MULTI , NLBL_CALIPSO_C_LISTALL ) ;
if ( ! data )
goto listall_cb_failure ;
ret_val = nla_put_u32 ( cb_arg - > skb , NLBL_CALIPSO_A_DOI , doi_def - > doi ) ;
if ( ret_val ! = 0 )
goto listall_cb_failure ;
ret_val = nla_put_u32 ( cb_arg - > skb ,
NLBL_CALIPSO_A_MTYPE ,
doi_def - > type ) ;
if ( ret_val ! = 0 )
goto listall_cb_failure ;
genlmsg_end ( cb_arg - > skb , data ) ;
return 0 ;
listall_cb_failure :
genlmsg_cancel ( cb_arg - > skb , data ) ;
return ret_val ;
}
/**
* netlbl_calipso_listall - Handle a LISTALL message
* @ skb : the NETLINK buffer
* @ cb : the NETLINK callback
*
* Description :
* Process a user generated LISTALL message and respond accordingly . Returns
* zero on success and negative values on error .
*
*/
static int netlbl_calipso_listall ( struct sk_buff * skb ,
struct netlink_callback * cb )
{
struct netlbl_calipso_doiwalk_arg cb_arg ;
u32 doi_skip = cb - > args [ 0 ] ;
cb_arg . nl_cb = cb ;
cb_arg . skb = skb ;
cb_arg . seq = cb - > nlh - > nlmsg_seq ;
calipso_doi_walk ( & doi_skip , netlbl_calipso_listall_cb , & cb_arg ) ;
cb - > args [ 0 ] = doi_skip ;
return skb - > len ;
}
2016-06-27 22:02:49 +03:00
/**
* netlbl_calipso_remove_cb - netlbl_calipso_remove ( ) callback for REMOVE
* @ entry : LSM domain mapping entry
* @ arg : the netlbl_domhsh_walk_arg structure
*
* Description :
* This function is intended for use by netlbl_calipso_remove ( ) as the callback
* for the netlbl_domhsh_walk ( ) function ; it removes LSM domain map entries
* which are associated with the CALIPSO DOI specified in @ arg . Returns zero on
* success , negative values on failure .
*
*/
static int netlbl_calipso_remove_cb ( struct netlbl_dom_map * entry , void * arg )
{
struct netlbl_domhsh_walk_arg * cb_arg = arg ;
if ( entry - > def . type = = NETLBL_NLTYPE_CALIPSO & &
entry - > def . calipso - > doi = = cb_arg - > doi )
return netlbl_domhsh_remove_entry ( entry , cb_arg - > audit_info ) ;
return 0 ;
}
/**
* netlbl_calipso_remove - Handle a REMOVE message
* @ skb : the NETLINK buffer
* @ info : the Generic NETLINK info block
*
* Description :
* Process a user generated REMOVE message and respond accordingly . Returns
* zero on success , negative values on failure .
*
*/
static int netlbl_calipso_remove ( struct sk_buff * skb , struct genl_info * info )
{
int ret_val = - EINVAL ;
struct netlbl_domhsh_walk_arg cb_arg ;
struct netlbl_audit audit_info ;
u32 skip_bkt = 0 ;
u32 skip_chain = 0 ;
if ( ! info - > attrs [ NLBL_CALIPSO_A_DOI ] )
return - EINVAL ;
netlbl_netlink_auditinfo ( skb , & audit_info ) ;
cb_arg . doi = nla_get_u32 ( info - > attrs [ NLBL_CALIPSO_A_DOI ] ) ;
cb_arg . audit_info = & audit_info ;
ret_val = netlbl_domhsh_walk ( & skip_bkt , & skip_chain ,
netlbl_calipso_remove_cb , & cb_arg ) ;
if ( ret_val = = 0 | | ret_val = = - ENOENT ) {
ret_val = calipso_doi_remove ( cb_arg . doi , & audit_info ) ;
if ( ret_val = = 0 )
atomic_dec ( & netlabel_mgmt_protocount ) ;
}
return ret_val ;
}
2016-06-27 22:02:46 +03:00
/* NetLabel Generic NETLINK Command Definitions
*/
static const struct genl_ops netlbl_calipso_ops [ ] = {
{
. cmd = NLBL_CALIPSO_C_ADD ,
. flags = GENL_ADMIN_PERM ,
. policy = calipso_genl_policy ,
. doit = netlbl_calipso_add ,
. dumpit = NULL ,
} ,
2016-06-27 22:02:47 +03:00
{
2016-06-27 22:02:49 +03:00
. cmd = NLBL_CALIPSO_C_REMOVE ,
. flags = GENL_ADMIN_PERM ,
. policy = calipso_genl_policy ,
. doit = netlbl_calipso_remove ,
. dumpit = NULL ,
} ,
{
2016-06-27 22:02:47 +03:00
. cmd = NLBL_CALIPSO_C_LIST ,
. flags = 0 ,
. policy = calipso_genl_policy ,
. doit = netlbl_calipso_list ,
. dumpit = NULL ,
} ,
2016-06-27 22:02:48 +03:00
{
. cmd = NLBL_CALIPSO_C_LISTALL ,
. flags = 0 ,
. policy = calipso_genl_policy ,
. doit = NULL ,
. dumpit = netlbl_calipso_listall ,
} ,
2016-06-27 22:02:46 +03:00
} ;
/* NetLabel Generic NETLINK Protocol Functions
*/
/**
* netlbl_calipso_genl_init - Register the CALIPSO NetLabel component
*
* Description :
* Register the CALIPSO packet NetLabel component with the Generic NETLINK
* mechanism . Returns zero on success , negative values on failure .
*
*/
int __init netlbl_calipso_genl_init ( void )
{
return genl_register_family_with_ops ( & netlbl_calipso_gnl_family ,
netlbl_calipso_ops ) ;
}
static const struct netlbl_calipso_ops * calipso_ops ;
/**
* netlbl_calipso_ops_register - Register the CALIPSO operations
*
* Description :
* Register the CALIPSO packet engine operations .
*
*/
const struct netlbl_calipso_ops *
netlbl_calipso_ops_register ( const struct netlbl_calipso_ops * ops )
{
return xchg ( & calipso_ops , ops ) ;
}
EXPORT_SYMBOL ( netlbl_calipso_ops_register ) ;
static const struct netlbl_calipso_ops * netlbl_calipso_ops_get ( void )
{
return ACCESS_ONCE ( calipso_ops ) ;
}
/**
* calipso_doi_add - Add a new DOI to the CALIPSO protocol engine
* @ doi_def : the DOI structure
* @ audit_info : NetLabel audit information
*
* Description :
* The caller defines a new DOI for use by the CALIPSO engine and calls this
* function to add it to the list of acceptable domains . The caller must
* ensure that the mapping table specified in @ doi_def - > map meets all of the
* requirements of the mapping type ( see calipso . h for details ) . Returns
* zero on success and non - zero on failure .
*
*/
int calipso_doi_add ( struct calipso_doi * doi_def ,
struct netlbl_audit * audit_info )
{
int ret_val = - ENOMSG ;
const struct netlbl_calipso_ops * ops = netlbl_calipso_ops_get ( ) ;
if ( ops )
ret_val = ops - > doi_add ( doi_def , audit_info ) ;
return ret_val ;
}
/**
* calipso_doi_free - Frees a DOI definition
* @ doi_def : the DOI definition
*
* Description :
* This function frees all of the memory associated with a DOI definition .
*
*/
void calipso_doi_free ( struct calipso_doi * doi_def )
{
const struct netlbl_calipso_ops * ops = netlbl_calipso_ops_get ( ) ;
if ( ops )
ops - > doi_free ( doi_def ) ;
}
2016-06-27 22:02:47 +03:00
2016-06-27 22:02:49 +03:00
/**
* calipso_doi_remove - Remove an existing DOI from the CALIPSO protocol engine
* @ doi : the DOI value
* @ audit_secid : the LSM secid to use in the audit message
*
* Description :
* Removes a DOI definition from the CALIPSO engine . The NetLabel routines will
* be called to release their own LSM domain mappings as well as our own
* domain list . Returns zero on success and negative values on failure .
*
*/
int calipso_doi_remove ( u32 doi , struct netlbl_audit * audit_info )
{
int ret_val = - ENOMSG ;
const struct netlbl_calipso_ops * ops = netlbl_calipso_ops_get ( ) ;
if ( ops )
ret_val = ops - > doi_remove ( doi , audit_info ) ;
return ret_val ;
}
2016-06-27 22:02:47 +03:00
/**
* calipso_doi_getdef - Returns a reference to a valid DOI definition
* @ doi : the DOI value
*
* Description :
* Searches for a valid DOI definition and if one is found it is returned to
* the caller . Otherwise NULL is returned . The caller must ensure that
* calipso_doi_putdef ( ) is called when the caller is done .
*
*/
struct calipso_doi * calipso_doi_getdef ( u32 doi )
{
struct calipso_doi * ret_val = NULL ;
const struct netlbl_calipso_ops * ops = netlbl_calipso_ops_get ( ) ;
if ( ops )
ret_val = ops - > doi_getdef ( doi ) ;
return ret_val ;
}
/**
* calipso_doi_putdef - Releases a reference for the given DOI definition
* @ doi_def : the DOI definition
*
* Description :
* Releases a DOI definition reference obtained from calipso_doi_getdef ( ) .
*
*/
void calipso_doi_putdef ( struct calipso_doi * doi_def )
{
const struct netlbl_calipso_ops * ops = netlbl_calipso_ops_get ( ) ;
if ( ops )
ops - > doi_putdef ( doi_def ) ;
}
2016-06-27 22:02:48 +03:00
/**
* calipso_doi_walk - Iterate through the DOI definitions
* @ skip_cnt : skip past this number of DOI definitions , updated
* @ callback : callback for each DOI definition
* @ cb_arg : argument for the callback function
*
* Description :
* Iterate over the DOI definition list , skipping the first @ skip_cnt entries .
* For each entry call @ callback , if @ callback returns a negative value stop
* ' walking ' through the list and return . Updates the value in @ skip_cnt upon
* return . Returns zero on success , negative values on failure .
*
*/
int calipso_doi_walk ( u32 * skip_cnt ,
int ( * callback ) ( struct calipso_doi * doi_def , void * arg ) ,
void * cb_arg )
{
int ret_val = - ENOMSG ;
const struct netlbl_calipso_ops * ops = netlbl_calipso_ops_get ( ) ;
if ( ops )
ret_val = ops - > doi_walk ( skip_cnt , callback , cb_arg ) ;
return ret_val ;
}