2019-05-27 08:55:01 +02:00
// SPDX-License-Identifier: GPL-2.0-or-later
2007-04-26 15:48:28 -07:00
/* RxRPC security handling
*
* Copyright ( C ) 2007 Red Hat , Inc . All Rights Reserved .
* Written by David Howells ( dhowells @ redhat . com )
*/
# 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 01:17:35 +00:00
# include <keys/rxrpc-type.h>
2007-04-26 15:48:28 -07:00
# include "ar-internal.h"
2016-04-07 17:23:51 +01:00
static const struct rxrpc_security * rxrpc_security_types [ ] = {
2016-04-07 17:23:58 +01:00
[ RXRPC_SECURITY_NONE ] = & rxrpc_no_security ,
2016-04-07 17:23:51 +01:00
# ifdef CONFIG_RXKAD
[ RXRPC_SECURITY_RXKAD ] = & rxkad ,
# endif
} ;
2007-04-26 15:48:28 -07:00
2016-04-07 17:23:51 +01:00
int __init rxrpc_init_security ( void )
2007-04-26 15:48:28 -07:00
{
2016-04-07 17:23:51 +01:00
int i , ret ;
2007-04-26 15:48:28 -07:00
2016-04-07 17:23:51 +01:00
for ( i = 0 ; i < ARRAY_SIZE ( rxrpc_security_types ) ; i + + ) {
if ( rxrpc_security_types [ i ] ) {
ret = rxrpc_security_types [ i ] - > init ( ) ;
if ( ret < 0 )
goto failed ;
2007-04-26 15:48:28 -07:00
}
}
2016-04-07 17:23:51 +01:00
return 0 ;
failed :
for ( i - - ; i > = 0 ; i - - )
if ( rxrpc_security_types [ i ] )
rxrpc_security_types [ i ] - > exit ( ) ;
return ret ;
2007-04-26 15:48:28 -07:00
}
2016-04-07 17:23:51 +01:00
void rxrpc_exit_security ( void )
2007-04-26 15:48:28 -07:00
{
2016-04-07 17:23:51 +01:00
int i ;
2007-04-26 15:48:28 -07:00
2016-04-07 17:23:51 +01:00
for ( i = 0 ; i < ARRAY_SIZE ( rxrpc_security_types ) ; i + + )
if ( rxrpc_security_types [ i ] )
rxrpc_security_types [ i ] - > exit ( ) ;
2007-04-26 15:48:28 -07:00
}
2016-04-07 17:23:51 +01:00
/*
* look up an rxrpc security module
2007-04-26 15:48:28 -07:00
*/
2020-09-16 08:37:29 +01:00
const struct rxrpc_security * rxrpc_security_lookup ( u8 security_index )
2007-04-26 15:48:28 -07:00
{
2016-04-07 17:23:51 +01:00
if ( security_index > = ARRAY_SIZE ( rxrpc_security_types ) )
return NULL ;
return rxrpc_security_types [ security_index ] ;
2007-04-26 15:48:28 -07:00
}
2022-10-20 21:58:36 +01:00
/*
* Initialise the security on a client call .
*/
int rxrpc_init_client_call_security ( struct rxrpc_call * call )
{
2022-12-15 16:19:56 +00:00
const struct rxrpc_security * sec = & rxrpc_no_security ;
2022-10-20 21:58:36 +01:00
struct rxrpc_key_token * token ;
struct key * key = call - > key ;
int ret ;
if ( ! key )
2022-12-15 16:19:56 +00:00
goto found ;
2022-10-20 21:58:36 +01:00
ret = key_validate ( key ) ;
if ( ret < 0 )
return ret ;
for ( token = key - > payload . data [ 0 ] ; token ; token = token - > next ) {
sec = rxrpc_security_lookup ( token - > security_index ) ;
if ( sec )
goto found ;
}
return - EKEYREJECTED ;
found :
call - > security = sec ;
2022-12-15 16:19:56 +00:00
call - > security_ix = sec - > security_index ;
2022-10-20 21:58:36 +01:00
return 0 ;
}
2007-04-26 15:48:28 -07:00
/*
* initialise the security on a client connection
*/
int rxrpc_init_client_conn_security ( struct rxrpc_connection * conn )
{
2009-09-14 01:17:35 +00:00
struct rxrpc_key_token * token ;
2022-10-19 13:49:02 +01:00
struct key * key = conn - > key ;
2022-10-19 09:45:43 +01:00
int ret = 0 ;
2007-04-26 15:48:28 -07:00
_enter ( " {%d},{%x} " , conn - > debug_id , key_serial ( key ) ) ;
2020-09-16 08:19:12 +01:00
for ( token = key - > payload . data [ 0 ] ; token ; token = token - > next ) {
2022-10-19 09:45:43 +01:00
if ( token - > security_index = = conn - > security - > security_index )
2020-09-16 08:19:12 +01:00
goto found ;
}
return - EKEYREJECTED ;
2009-09-14 01:17:35 +00:00
2020-09-16 08:19:12 +01:00
found :
2022-10-19 09:45:43 +01:00
mutex_lock ( & conn - > security_lock ) ;
if ( conn - > state = = RXRPC_CONN_CLIENT_UNSECURED ) {
ret = conn - > security - > init_connection_security ( conn , token ) ;
if ( ret = = 0 ) {
spin_lock ( & conn - > state_lock ) ;
if ( conn - > state = = RXRPC_CONN_CLIENT_UNSECURED )
conn - > state = RXRPC_CONN_CLIENT ;
spin_unlock ( & conn - > state_lock ) ;
}
2007-04-26 15:48:28 -07:00
}
2022-10-19 09:45:43 +01:00
mutex_unlock ( & conn - > security_lock ) ;
return ret ;
2007-04-26 15:48:28 -07:00
}
/*
2020-09-16 08:00:44 +01:00
* Set the ops a server connection .
2007-04-26 15:48:28 -07:00
*/
2020-09-16 08:00:44 +01:00
const struct rxrpc_security * rxrpc_get_incoming_security ( struct rxrpc_sock * rx ,
struct sk_buff * skb )
2007-04-26 15:48:28 -07:00
{
2016-04-07 17:23:51 +01:00
const struct rxrpc_security * sec ;
2019-12-20 16:17:16 +00:00
struct rxrpc_skb_priv * sp = rxrpc_skb ( skb ) ;
2007-04-26 15:48:28 -07:00
_enter ( " " ) ;
2019-12-20 16:17:16 +00:00
sec = rxrpc_security_lookup ( sp - > hdr . securityIndex ) ;
2007-04-26 15:48:28 -07:00
if ( ! sec ) {
2022-10-06 21:45:42 +01:00
rxrpc_direct_abort ( skb , rxrpc_abort_unsupported_security ,
RX_INVALID_OPERATION , - EKEYREJECTED ) ;
2020-09-16 08:00:44 +01:00
return NULL ;
2007-04-26 15:48:28 -07:00
}
2020-09-16 08:00:44 +01:00
if ( sp - > hdr . securityIndex ! = RXRPC_SECURITY_NONE & &
! rx - > securities ) {
2022-10-06 21:45:42 +01:00
rxrpc_direct_abort ( skb , rxrpc_abort_no_service_key ,
sec - > no_key_abort , - EKEYREJECTED ) ;
2020-09-16 08:00:44 +01:00
return NULL ;
2007-04-26 15:48:28 -07:00
}
2020-09-16 08:00:44 +01:00
return sec ;
}
/*
* Find the security key for a server connection .
*/
struct key * rxrpc_look_up_server_security ( struct rxrpc_connection * conn ,
struct sk_buff * skb ,
u32 kvno , u32 enctype )
{
struct rxrpc_skb_priv * sp = rxrpc_skb ( skb ) ;
struct rxrpc_sock * rx ;
struct key * key = ERR_PTR ( - EKEYREJECTED ) ;
key_ref_t kref = NULL ;
char kdesc [ 5 + 1 + 3 + 1 + 12 + 1 + 12 + 1 ] ;
int ret ;
_enter ( " " ) ;
if ( enctype )
sprintf ( kdesc , " %u:%u:%u:%u " ,
sp - > hdr . serviceId , sp - > hdr . securityIndex , kvno , enctype ) ;
else if ( kvno )
sprintf ( kdesc , " %u:%u:%u " ,
sp - > hdr . serviceId , sp - > hdr . securityIndex , kvno ) ;
else
sprintf ( kdesc , " %u:%u " ,
sp - > hdr . serviceId , sp - > hdr . securityIndex ) ;
rxrpc: Fix incoming call setup race
An incoming call can race with rxrpc socket destruction, leading to a
leaked call. This may result in an oops when the call timer eventually
expires:
BUG: kernel NULL pointer dereference, address: 0000000000000874
RIP: 0010:_raw_spin_lock_irqsave+0x2a/0x50
Call Trace:
<IRQ>
try_to_wake_up+0x59/0x550
? __local_bh_enable_ip+0x37/0x80
? rxrpc_poke_call+0x52/0x110 [rxrpc]
? rxrpc_poke_call+0x110/0x110 [rxrpc]
? rxrpc_poke_call+0x110/0x110 [rxrpc]
call_timer_fn+0x24/0x120
with a warning in the kernel log looking something like:
rxrpc: Call 00000000ba5e571a still in use (1,SvAwtACK,1061d,0)!
incurred during rmmod of rxrpc. The 1061d is the call flags:
RECVMSG_READ_ALL, RX_HEARD, BEGAN_RX_TIMER, RX_LAST, EXPOSED,
IS_SERVICE, RELEASED
but no DISCONNECTED flag (0x800), so it's an incoming (service) call and
it's still connected.
The race appears to be that:
(1) rxrpc_new_incoming_call() consults the service struct, checks sk_state
and allocates a call - then pauses, possibly for an interrupt.
(2) rxrpc_release_sock() sets RXRPC_CLOSE, nulls the service pointer,
discards the prealloc and releases all calls attached to the socket.
(3) rxrpc_new_incoming_call() resumes, launching the new call, including
its timer and attaching it to the socket.
Fix this by read-locking local->services_lock to access the AF_RXRPC socket
providing the service rather than RCU in rxrpc_new_incoming_call().
There's no real need to use RCU here as local->services_lock is only
write-locked by the socket side in two places: when binding and when
shutting down.
Fixes: 5e6ef4f1017c ("rxrpc: Make the I/O thread take over the call and local processor work")
Reported-by: Marc Dionne <marc.dionne@auristor.com>
Signed-off-by: David Howells <dhowells@redhat.com>
cc: linux-afs@lists.infradead.org
2023-01-06 13:03:18 +00:00
read_lock ( & conn - > local - > services_lock ) ;
2020-09-16 08:00:44 +01:00
rxrpc: Fix incoming call setup race
An incoming call can race with rxrpc socket destruction, leading to a
leaked call. This may result in an oops when the call timer eventually
expires:
BUG: kernel NULL pointer dereference, address: 0000000000000874
RIP: 0010:_raw_spin_lock_irqsave+0x2a/0x50
Call Trace:
<IRQ>
try_to_wake_up+0x59/0x550
? __local_bh_enable_ip+0x37/0x80
? rxrpc_poke_call+0x52/0x110 [rxrpc]
? rxrpc_poke_call+0x110/0x110 [rxrpc]
? rxrpc_poke_call+0x110/0x110 [rxrpc]
call_timer_fn+0x24/0x120
with a warning in the kernel log looking something like:
rxrpc: Call 00000000ba5e571a still in use (1,SvAwtACK,1061d,0)!
incurred during rmmod of rxrpc. The 1061d is the call flags:
RECVMSG_READ_ALL, RX_HEARD, BEGAN_RX_TIMER, RX_LAST, EXPOSED,
IS_SERVICE, RELEASED
but no DISCONNECTED flag (0x800), so it's an incoming (service) call and
it's still connected.
The race appears to be that:
(1) rxrpc_new_incoming_call() consults the service struct, checks sk_state
and allocates a call - then pauses, possibly for an interrupt.
(2) rxrpc_release_sock() sets RXRPC_CLOSE, nulls the service pointer,
discards the prealloc and releases all calls attached to the socket.
(3) rxrpc_new_incoming_call() resumes, launching the new call, including
its timer and attaching it to the socket.
Fix this by read-locking local->services_lock to access the AF_RXRPC socket
providing the service rather than RCU in rxrpc_new_incoming_call().
There's no real need to use RCU here as local->services_lock is only
write-locked by the socket side in two places: when binding and when
shutting down.
Fixes: 5e6ef4f1017c ("rxrpc: Make the I/O thread take over the call and local processor work")
Reported-by: Marc Dionne <marc.dionne@auristor.com>
Signed-off-by: David Howells <dhowells@redhat.com>
cc: linux-afs@lists.infradead.org
2023-01-06 13:03:18 +00:00
rx = conn - > local - > service ;
2020-09-16 08:00:44 +01:00
if ( ! rx )
goto out ;
2007-04-26 15:48:28 -07:00
/* look through the service's keyring */
kref = keyring_search ( make_key_ref ( rx - > securities , 1UL ) ,
2019-06-26 21:02:32 +01:00
& key_type_rxrpc_s , kdesc , true ) ;
2007-04-26 15:48:28 -07:00
if ( IS_ERR ( kref ) ) {
2020-09-16 08:00:44 +01:00
key = ERR_CAST ( kref ) ;
goto out ;
}
key = key_ref_to_ptr ( kref ) ;
ret = key_validate ( key ) ;
if ( ret < 0 ) {
key_put ( key ) ;
key = ERR_PTR ( ret ) ;
goto out ;
2007-04-26 15:48:28 -07:00
}
2019-12-20 16:17:16 +00:00
out :
rxrpc: Fix incoming call setup race
An incoming call can race with rxrpc socket destruction, leading to a
leaked call. This may result in an oops when the call timer eventually
expires:
BUG: kernel NULL pointer dereference, address: 0000000000000874
RIP: 0010:_raw_spin_lock_irqsave+0x2a/0x50
Call Trace:
<IRQ>
try_to_wake_up+0x59/0x550
? __local_bh_enable_ip+0x37/0x80
? rxrpc_poke_call+0x52/0x110 [rxrpc]
? rxrpc_poke_call+0x110/0x110 [rxrpc]
? rxrpc_poke_call+0x110/0x110 [rxrpc]
call_timer_fn+0x24/0x120
with a warning in the kernel log looking something like:
rxrpc: Call 00000000ba5e571a still in use (1,SvAwtACK,1061d,0)!
incurred during rmmod of rxrpc. The 1061d is the call flags:
RECVMSG_READ_ALL, RX_HEARD, BEGAN_RX_TIMER, RX_LAST, EXPOSED,
IS_SERVICE, RELEASED
but no DISCONNECTED flag (0x800), so it's an incoming (service) call and
it's still connected.
The race appears to be that:
(1) rxrpc_new_incoming_call() consults the service struct, checks sk_state
and allocates a call - then pauses, possibly for an interrupt.
(2) rxrpc_release_sock() sets RXRPC_CLOSE, nulls the service pointer,
discards the prealloc and releases all calls attached to the socket.
(3) rxrpc_new_incoming_call() resumes, launching the new call, including
its timer and attaching it to the socket.
Fix this by read-locking local->services_lock to access the AF_RXRPC socket
providing the service rather than RCU in rxrpc_new_incoming_call().
There's no real need to use RCU here as local->services_lock is only
write-locked by the socket side in two places: when binding and when
shutting down.
Fixes: 5e6ef4f1017c ("rxrpc: Make the I/O thread take over the call and local processor work")
Reported-by: Marc Dionne <marc.dionne@auristor.com>
Signed-off-by: David Howells <dhowells@redhat.com>
cc: linux-afs@lists.infradead.org
2023-01-06 13:03:18 +00:00
read_unlock ( & conn - > local - > services_lock ) ;
2020-09-16 08:00:44 +01:00
return key ;
2007-04-26 15:48:28 -07:00
}