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))
|
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_
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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 */
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
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);
|
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:
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue