rxrpc: rxgk: Implement connection rekeying
Implement rekeying of connections with the RxGK security class. This involves regenerating the keys with a different key number as part of the input data after a certain amount of time or a certain amount of bytes encrypted. Rekeying may be triggered by either end. The LSW of the key number is inserted into the security-specific field in the RX header, and we try and expand it to 32-bits to make it last longer. Signed-off-by: David Howells <dhowells@redhat.com> cc: Marc Dionne <marc.dionne@auristor.com> cc: Herbert Xu <herbert@gondor.apana.org.au> cc: Chuck Lever <chuck.lever@oracle.com> cc: Simon Horman <horms@kernel.org> cc: linux-afs@lists.infradead.org Link: https://patch.msgid.link/20250411095303.2316168-10-dhowells@redhat.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>pull/1112/head
parent
9d1d2b5934
commit
7a7513a308
|
|
@ -2725,6 +2725,30 @@ TRACE_EVENT(rxrpc_rack_timer,
|
|||
ktime_to_us(__entry->delay))
|
||||
);
|
||||
|
||||
TRACE_EVENT(rxrpc_rxgk_rekey,
|
||||
TP_PROTO(struct rxrpc_connection *conn,
|
||||
unsigned int current_key, unsigned int requested_key),
|
||||
|
||||
TP_ARGS(conn, current_key, requested_key),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field(unsigned int, conn)
|
||||
__field(unsigned int, current_key)
|
||||
__field(unsigned int, requested_key)
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__entry->conn = conn->debug_id;
|
||||
__entry->current_key = current_key;
|
||||
__entry->requested_key = requested_key;
|
||||
),
|
||||
|
||||
TP_printk("C=%08x cur=%x req=%x",
|
||||
__entry->conn,
|
||||
__entry->current_key,
|
||||
__entry->requested_key)
|
||||
);
|
||||
|
||||
#undef EM
|
||||
#undef E_
|
||||
|
||||
|
|
|
|||
|
|
@ -565,13 +565,16 @@ struct rxrpc_connection {
|
|||
u32 nonce; /* response re-use preventer */
|
||||
} rxkad;
|
||||
struct {
|
||||
struct rxgk_context *keys[1];
|
||||
struct rxgk_context *keys[4]; /* (Re-)keying buffer */
|
||||
u64 start_time; /* The start time for TK derivation */
|
||||
u8 nonce[20]; /* Response re-use preventer */
|
||||
u32 enctype; /* Kerberos 5 encoding type */
|
||||
u32 key_number; /* Current key number */
|
||||
} rxgk;
|
||||
};
|
||||
rwlock_t security_use_lock; /* Security use/modification lock */
|
||||
struct sk_buff *tx_response; /* Response packet to be transmitted */
|
||||
|
||||
unsigned long flags;
|
||||
unsigned long events;
|
||||
unsigned long idle_timestamp; /* Time at which last became idle */
|
||||
|
|
|
|||
|
|
@ -73,6 +73,7 @@ struct rxrpc_connection *rxrpc_alloc_connection(struct rxrpc_net *rxnet,
|
|||
skb_queue_head_init(&conn->rx_queue);
|
||||
conn->rxnet = rxnet;
|
||||
conn->security = &rxrpc_no_security;
|
||||
rwlock_init(&conn->security_use_lock);
|
||||
spin_lock_init(&conn->state_lock);
|
||||
conn->debug_id = atomic_inc_return(&rxrpc_debug_id);
|
||||
conn->idle_timestamp = jiffies;
|
||||
|
|
|
|||
160
net/rxrpc/rxgk.c
160
net/rxrpc/rxgk.c
|
|
@ -76,11 +76,155 @@ static void rxgk_describe_server_key(const struct key *key, struct seq_file *m)
|
|||
seq_printf(m, ": %s", krb5->name);
|
||||
}
|
||||
|
||||
static struct rxgk_context *rxgk_get_key(struct rxrpc_connection *conn,
|
||||
u16 *specific_key_number)
|
||||
/*
|
||||
* Handle rekeying the connection when we see our limits overrun or when the
|
||||
* far side decided to rekey.
|
||||
*
|
||||
* Returns a ref on the context if successful or -ESTALE if the key is out of
|
||||
* date.
|
||||
*/
|
||||
static struct rxgk_context *rxgk_rekey(struct rxrpc_connection *conn,
|
||||
const u16 *specific_key_number)
|
||||
{
|
||||
refcount_inc(&conn->rxgk.keys[0]->usage);
|
||||
return conn->rxgk.keys[0];
|
||||
struct rxgk_context *gk, *dead = NULL;
|
||||
unsigned int key_number, current_key, mask = ARRAY_SIZE(conn->rxgk.keys) - 1;
|
||||
bool crank = false;
|
||||
|
||||
_enter("%d", specific_key_number ? *specific_key_number : -1);
|
||||
|
||||
mutex_lock(&conn->security_lock);
|
||||
|
||||
current_key = conn->rxgk.key_number;
|
||||
if (!specific_key_number) {
|
||||
key_number = current_key;
|
||||
} else {
|
||||
if (*specific_key_number == (u16)current_key)
|
||||
key_number = current_key;
|
||||
else if (*specific_key_number == (u16)(current_key - 1))
|
||||
key_number = current_key - 1;
|
||||
else if (*specific_key_number == (u16)(current_key + 1))
|
||||
goto crank_window;
|
||||
else
|
||||
goto bad_key;
|
||||
}
|
||||
|
||||
gk = conn->rxgk.keys[key_number & mask];
|
||||
if (!gk)
|
||||
goto generate_key;
|
||||
if (!specific_key_number &&
|
||||
test_bit(RXGK_TK_NEEDS_REKEY, &gk->flags))
|
||||
goto crank_window;
|
||||
|
||||
grab:
|
||||
refcount_inc(&gk->usage);
|
||||
mutex_unlock(&conn->security_lock);
|
||||
rxgk_put(dead);
|
||||
return gk;
|
||||
|
||||
crank_window:
|
||||
trace_rxrpc_rxgk_rekey(conn, current_key,
|
||||
specific_key_number ? *specific_key_number : -1);
|
||||
if (current_key == UINT_MAX)
|
||||
goto bad_key;
|
||||
if (current_key + 1 == UINT_MAX)
|
||||
set_bit(RXRPC_CONN_DONT_REUSE, &conn->flags);
|
||||
|
||||
key_number = current_key + 1;
|
||||
if (WARN_ON(conn->rxgk.keys[key_number & mask]))
|
||||
goto bad_key;
|
||||
crank = true;
|
||||
|
||||
generate_key:
|
||||
gk = conn->rxgk.keys[current_key & mask];
|
||||
gk = rxgk_generate_transport_key(conn, gk->key, key_number, GFP_NOFS);
|
||||
if (IS_ERR(gk)) {
|
||||
mutex_unlock(&conn->security_lock);
|
||||
return gk;
|
||||
}
|
||||
|
||||
write_lock(&conn->security_use_lock);
|
||||
if (crank) {
|
||||
current_key++;
|
||||
conn->rxgk.key_number = current_key;
|
||||
dead = conn->rxgk.keys[(current_key - 2) & mask];
|
||||
conn->rxgk.keys[(current_key - 2) & mask] = NULL;
|
||||
}
|
||||
conn->rxgk.keys[current_key & mask] = gk;
|
||||
write_unlock(&conn->security_use_lock);
|
||||
goto grab;
|
||||
|
||||
bad_key:
|
||||
mutex_unlock(&conn->security_lock);
|
||||
return ERR_PTR(-ESTALE);
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the specified keying context.
|
||||
*
|
||||
* Returns a ref on the context if successful or -ESTALE if the key is out of
|
||||
* date.
|
||||
*/
|
||||
static struct rxgk_context *rxgk_get_key(struct rxrpc_connection *conn,
|
||||
const u16 *specific_key_number)
|
||||
{
|
||||
struct rxgk_context *gk;
|
||||
unsigned int key_number, current_key, mask = ARRAY_SIZE(conn->rxgk.keys) - 1;
|
||||
|
||||
_enter("{%u},%d",
|
||||
conn->rxgk.key_number, specific_key_number ? *specific_key_number : -1);
|
||||
|
||||
read_lock(&conn->security_use_lock);
|
||||
|
||||
current_key = conn->rxgk.key_number;
|
||||
if (!specific_key_number) {
|
||||
key_number = current_key;
|
||||
} else {
|
||||
/* Only the bottom 16 bits of the key number are exposed in the
|
||||
* header, so we try and keep the upper 16 bits in step. The
|
||||
* whole 32 bits are used to generate the TK.
|
||||
*/
|
||||
if (*specific_key_number == (u16)current_key)
|
||||
key_number = current_key;
|
||||
else if (*specific_key_number == (u16)(current_key - 1))
|
||||
key_number = current_key - 1;
|
||||
else if (*specific_key_number == (u16)(current_key + 1))
|
||||
goto rekey;
|
||||
else
|
||||
goto bad_key;
|
||||
}
|
||||
|
||||
gk = conn->rxgk.keys[key_number & mask];
|
||||
if (!gk)
|
||||
goto slow_path;
|
||||
if (!specific_key_number &&
|
||||
key_number < UINT_MAX) {
|
||||
if (time_after(jiffies, gk->expiry) ||
|
||||
gk->bytes_remaining < 0) {
|
||||
set_bit(RXGK_TK_NEEDS_REKEY, &gk->flags);
|
||||
goto slow_path;
|
||||
}
|
||||
|
||||
if (test_bit(RXGK_TK_NEEDS_REKEY, &gk->flags))
|
||||
goto slow_path;
|
||||
}
|
||||
|
||||
refcount_inc(&gk->usage);
|
||||
read_unlock(&conn->security_use_lock);
|
||||
return gk;
|
||||
|
||||
rekey:
|
||||
_debug("rekey");
|
||||
if (current_key == UINT_MAX)
|
||||
goto bad_key;
|
||||
gk = conn->rxgk.keys[current_key & mask];
|
||||
if (gk)
|
||||
set_bit(RXGK_TK_NEEDS_REKEY, &gk->flags);
|
||||
slow_path:
|
||||
read_unlock(&conn->security_use_lock);
|
||||
return rxgk_rekey(conn, specific_key_number);
|
||||
bad_key:
|
||||
read_unlock(&conn->security_use_lock);
|
||||
return ERR_PTR(-ESTALE);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -92,7 +236,8 @@ static int rxgk_init_connection_security(struct rxrpc_connection *conn,
|
|||
struct rxgk_context *gk;
|
||||
int ret;
|
||||
|
||||
_enter("{%d},{%x}", conn->debug_id, key_serial(conn->key));
|
||||
_enter("{%d,%u},{%x}",
|
||||
conn->debug_id, conn->rxgk.key_number, key_serial(conn->key));
|
||||
|
||||
conn->security_ix = token->security_index;
|
||||
conn->security_level = token->rxgk->level;
|
||||
|
|
@ -102,11 +247,12 @@ static int rxgk_init_connection_security(struct rxrpc_connection *conn,
|
|||
do_div(conn->rxgk.start_time, 100);
|
||||
}
|
||||
|
||||
gk = rxgk_generate_transport_key(conn, token->rxgk, 0, GFP_NOFS);
|
||||
gk = rxgk_generate_transport_key(conn, token->rxgk, conn->rxgk.key_number,
|
||||
GFP_NOFS);
|
||||
if (IS_ERR(gk))
|
||||
return PTR_ERR(gk);
|
||||
conn->rxgk.enctype = gk->krb5->etype;
|
||||
conn->rxgk.keys[0] = gk;
|
||||
conn->rxgk.keys[gk->key_number & 3] = gk;
|
||||
|
||||
switch (conn->security_level) {
|
||||
case RXRPC_SECURITY_PLAIN:
|
||||
|
|
|
|||
Loading…
Reference in New Issue