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
David Howells 2025-04-11 10:52:54 +01:00 committed by Jakub Kicinski
parent 9d1d2b5934
commit 7a7513a308
4 changed files with 182 additions and 8 deletions

View File

@ -2725,6 +2725,30 @@ TRACE_EVENT(rxrpc_rack_timer,
ktime_to_us(__entry->delay)) 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 EM
#undef E_ #undef E_

View File

@ -565,13 +565,16 @@ struct rxrpc_connection {
u32 nonce; /* response re-use preventer */ u32 nonce; /* response re-use preventer */
} rxkad; } rxkad;
struct { struct {
struct rxgk_context *keys[1]; struct rxgk_context *keys[4]; /* (Re-)keying buffer */
u64 start_time; /* The start time for TK derivation */ u64 start_time; /* The start time for TK derivation */
u8 nonce[20]; /* Response re-use preventer */ u8 nonce[20]; /* Response re-use preventer */
u32 enctype; /* Kerberos 5 encoding type */ u32 enctype; /* Kerberos 5 encoding type */
u32 key_number; /* Current key number */
} rxgk; } rxgk;
}; };
rwlock_t security_use_lock; /* Security use/modification lock */
struct sk_buff *tx_response; /* Response packet to be transmitted */ struct sk_buff *tx_response; /* Response packet to be transmitted */
unsigned long flags; unsigned long flags;
unsigned long events; unsigned long events;
unsigned long idle_timestamp; /* Time at which last became idle */ unsigned long idle_timestamp; /* Time at which last became idle */

View File

@ -73,6 +73,7 @@ struct rxrpc_connection *rxrpc_alloc_connection(struct rxrpc_net *rxnet,
skb_queue_head_init(&conn->rx_queue); skb_queue_head_init(&conn->rx_queue);
conn->rxnet = rxnet; conn->rxnet = rxnet;
conn->security = &rxrpc_no_security; conn->security = &rxrpc_no_security;
rwlock_init(&conn->security_use_lock);
spin_lock_init(&conn->state_lock); spin_lock_init(&conn->state_lock);
conn->debug_id = atomic_inc_return(&rxrpc_debug_id); conn->debug_id = atomic_inc_return(&rxrpc_debug_id);
conn->idle_timestamp = jiffies; conn->idle_timestamp = jiffies;

View File

@ -76,11 +76,155 @@ static void rxgk_describe_server_key(const struct key *key, struct seq_file *m)
seq_printf(m, ": %s", krb5->name); 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); struct rxgk_context *gk, *dead = NULL;
return conn->rxgk.keys[0]; 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; struct rxgk_context *gk;
int ret; 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_ix = token->security_index;
conn->security_level = token->rxgk->level; 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); 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)) if (IS_ERR(gk))
return PTR_ERR(gk); return PTR_ERR(gk);
conn->rxgk.enctype = gk->krb5->etype; conn->rxgk.enctype = gk->krb5->etype;
conn->rxgk.keys[0] = gk; conn->rxgk.keys[gk->key_number & 3] = gk;
switch (conn->security_level) { switch (conn->security_level) {
case RXRPC_SECURITY_PLAIN: case RXRPC_SECURITY_PLAIN: