Commit 7a7513a3 authored by David Howells's avatar David Howells Committed by Jakub Kicinski
Browse files

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: default avatarDavid 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: default avatarJakub Kicinski <kuba@kernel.org>
parent 9d1d2b59
Loading
Loading
Loading
Loading
+24 −0
Original line number Diff line number Diff line
@@ -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_

+4 −1
Original line number Diff line number Diff line
@@ -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 */
+1 −0
Original line number Diff line number Diff line
@@ -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;
+152 −6
Original line number Diff line number Diff line
@@ -76,11 +76,155 @@ static void rxgk_describe_server_key(const struct key *key, struct seq_file *m)
		seq_printf(m, ": %s", krb5->name);
}

/*
 * 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)
{
	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,
					 u16 *specific_key_number)
					 const u16 *specific_key_number)
{
	refcount_inc(&conn->rxgk.keys[0]->usage);
	return conn->rxgk.keys[0];
	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: