2007-04-27 02:48:28 +04:00
/* RxRPC security handling
*
* Copyright ( C ) 2007 Red Hat , Inc . All Rights Reserved .
* Written by David Howells ( dhowells @ redhat . com )
*
* 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 .
*/
# include <linux/module.h>
# include <linux/net.h>
# include <linux/skbuff.h>
# include <linux/udp.h>
# include <linux/crypto.h>
# include <net/sock.h>
# include <net/af_rxrpc.h>
2009-09-14 05:17:35 +04:00
# include <keys/rxrpc-type.h>
2007-04-27 02:48:28 +04:00
# include "ar-internal.h"
static LIST_HEAD ( rxrpc_security_methods ) ;
static DECLARE_RWSEM ( rxrpc_security_sem ) ;
/*
* get an RxRPC security module
*/
static struct rxrpc_security * rxrpc_security_get ( struct rxrpc_security * sec )
{
return try_module_get ( sec - > owner ) ? sec : NULL ;
}
/*
* release an RxRPC security module
*/
static void rxrpc_security_put ( struct rxrpc_security * sec )
{
module_put ( sec - > owner ) ;
}
/*
* look up an rxrpc security module
*/
2008-12-11 02:18:31 +03:00
static struct rxrpc_security * rxrpc_security_lookup ( u8 security_index )
2007-04-27 02:48:28 +04:00
{
struct rxrpc_security * sec = NULL ;
_enter ( " " ) ;
down_read ( & rxrpc_security_sem ) ;
list_for_each_entry ( sec , & rxrpc_security_methods , link ) {
if ( sec - > security_index = = security_index ) {
if ( unlikely ( ! rxrpc_security_get ( sec ) ) )
break ;
goto out ;
}
}
sec = NULL ;
out :
up_read ( & rxrpc_security_sem ) ;
_leave ( " = %p [%s] " , sec , sec ? sec - > name : " " ) ;
return sec ;
}
/**
* rxrpc_register_security - register an RxRPC security handler
* @ sec : security module
*
* register an RxRPC security handler for use by RxRPC
*/
int rxrpc_register_security ( struct rxrpc_security * sec )
{
struct rxrpc_security * psec ;
int ret ;
_enter ( " " ) ;
down_write ( & rxrpc_security_sem ) ;
ret = - EEXIST ;
list_for_each_entry ( psec , & rxrpc_security_methods , link ) {
if ( psec - > security_index = = sec - > security_index )
goto out ;
}
list_add ( & sec - > link , & rxrpc_security_methods ) ;
printk ( KERN_NOTICE " RxRPC: Registered security type %d '%s' \n " ,
sec - > security_index , sec - > name ) ;
ret = 0 ;
out :
up_write ( & rxrpc_security_sem ) ;
_leave ( " = %d " , ret ) ;
return ret ;
}
EXPORT_SYMBOL_GPL ( rxrpc_register_security ) ;
/**
* rxrpc_unregister_security - unregister an RxRPC security handler
* @ sec : security module
*
* unregister an RxRPC security handler
*/
void rxrpc_unregister_security ( struct rxrpc_security * sec )
{
_enter ( " " ) ;
down_write ( & rxrpc_security_sem ) ;
list_del_init ( & sec - > link ) ;
up_write ( & rxrpc_security_sem ) ;
printk ( KERN_NOTICE " RxRPC: Unregistered security type %d '%s' \n " ,
sec - > security_index , sec - > name ) ;
}
EXPORT_SYMBOL_GPL ( rxrpc_unregister_security ) ;
/*
* initialise the security on a client connection
*/
int rxrpc_init_client_conn_security ( struct rxrpc_connection * conn )
{
2009-09-14 05:17:35 +04:00
struct rxrpc_key_token * token ;
2007-04-27 02:48:28 +04:00
struct rxrpc_security * sec ;
struct key * key = conn - > key ;
int ret ;
_enter ( " {%d},{%x} " , conn - > debug_id , key_serial ( key ) ) ;
if ( ! key )
return 0 ;
ret = key_validate ( key ) ;
if ( ret < 0 )
return ret ;
2009-09-14 05:17:35 +04:00
if ( ! key - > payload . data )
return - EKEYREJECTED ;
token = key - > payload . data ;
sec = rxrpc_security_lookup ( token - > security_index ) ;
2007-04-27 02:48:28 +04:00
if ( ! sec )
return - EKEYREJECTED ;
conn - > security = sec ;
ret = conn - > security - > init_connection_security ( conn ) ;
if ( ret < 0 ) {
rxrpc_security_put ( conn - > security ) ;
conn - > security = NULL ;
return ret ;
}
_leave ( " = 0 " ) ;
return 0 ;
}
/*
* initialise the security on a server connection
*/
int rxrpc_init_server_conn_security ( struct rxrpc_connection * conn )
{
struct rxrpc_security * sec ;
struct rxrpc_local * local = conn - > trans - > local ;
struct rxrpc_sock * rx ;
struct key * key ;
key_ref_t kref ;
char kdesc [ 5 + 1 + 3 + 1 ] ;
_enter ( " " ) ;
sprintf ( kdesc , " %u:%u " , ntohs ( conn - > service_id ) , conn - > security_ix ) ;
sec = rxrpc_security_lookup ( conn - > security_ix ) ;
if ( ! sec ) {
_leave ( " = -ENOKEY [lookup] " ) ;
return - ENOKEY ;
}
/* find the service */
read_lock_bh ( & local - > services_lock ) ;
list_for_each_entry ( rx , & local - > services , listen_link ) {
if ( rx - > service_id = = conn - > service_id )
goto found_service ;
}
/* the service appears to have died */
read_unlock_bh ( & local - > services_lock ) ;
rxrpc_security_put ( sec ) ;
_leave ( " = -ENOENT " ) ;
return - ENOENT ;
found_service :
if ( ! rx - > securities ) {
read_unlock_bh ( & local - > services_lock ) ;
rxrpc_security_put ( sec ) ;
_leave ( " = -ENOKEY " ) ;
return - ENOKEY ;
}
/* look through the service's keyring */
kref = keyring_search ( make_key_ref ( rx - > securities , 1UL ) ,
& key_type_rxrpc_s , kdesc ) ;
if ( IS_ERR ( kref ) ) {
read_unlock_bh ( & local - > services_lock ) ;
rxrpc_security_put ( sec ) ;
_leave ( " = %ld [search] " , PTR_ERR ( kref ) ) ;
return PTR_ERR ( kref ) ;
}
key = key_ref_to_ptr ( kref ) ;
read_unlock_bh ( & local - > services_lock ) ;
conn - > server_key = key ;
conn - > security = sec ;
_leave ( " = 0 " ) ;
return 0 ;
}
/*
* secure a packet prior to transmission
*/
int rxrpc_secure_packet ( const struct rxrpc_call * call ,
struct sk_buff * skb ,
size_t data_size ,
void * sechdr )
{
if ( call - > conn - > security )
return call - > conn - > security - > secure_packet (
call , skb , data_size , sechdr ) ;
return 0 ;
}
/*
* secure a packet prior to transmission
*/
int rxrpc_verify_packet ( const struct rxrpc_call * call , struct sk_buff * skb ,
u32 * _abort_code )
{
if ( call - > conn - > security )
return call - > conn - > security - > verify_packet (
call , skb , _abort_code ) ;
return 0 ;
}
/*
* clear connection security
*/
void rxrpc_clear_conn_security ( struct rxrpc_connection * conn )
{
_enter ( " {%d} " , conn - > debug_id ) ;
if ( conn - > security ) {
conn - > security - > clear ( conn ) ;
rxrpc_security_put ( conn - > security ) ;
conn - > security = NULL ;
}
key_put ( conn - > key ) ;
key_put ( conn - > server_key ) ;
}