From: David Howells <dhowells@redhat.com>
To: netdev@vger.kernel.org
Cc: David Howells <dhowells@redhat.com>,
Marc Dionne <marc.dionne@auristor.com>,
Jakub Kicinski <kuba@kernel.org>,
"David S. Miller" <davem@davemloft.net>,
Eric Dumazet <edumazet@google.com>,
Paolo Abeni <pabeni@redhat.com>, Simon Horman <horms@kernel.org>,
linux-afs@lists.infradead.org, linux-kernel@vger.kernel.org,
Jeffrey Altman <jaltman@auristor.com>,
Jarkko Sakkinen <jarkko@kernel.org>,
keyrings@vger.kernel.org, stable@kernel.org
Subject: [PATCH net v2 13/16] afs: Create a server appdata key
Date: Fri, 10 Jul 2026 20:22:15 +0100 [thread overview]
Message-ID: <20260710192220.1922433-14-dhowells@redhat.com> (raw)
In-Reply-To: <20260710192220.1922433-1-dhowells@redhat.com>
Currently, when a CHALLENGE packet comes in, it's queued in an OOB queue on
the AF_RXRPC socket that generated one of the calls on that connection for
the application (which might be in userspace) to service. The application
then picks up the CHALLENGE and requests a RESPONSE packet be generated,
allowing the app to include app-specific data in it if appropriate. There
is, however, no actual limit on the capacity of the CHALLENGE queue, and
this could be abused remotely - and also getting the OOB mechanism right
has proven tricky.
Further, by analogy with other AFS codebases, it's not actually necessary
to generate the application data in response to the CHALLENGE. The reason
I did this was to set the encryption on the app-data to be the same as that
specified in the CHALLENGE as the server must be able to handle that.
However, it's sufficient to use the encoding type set in the token that is
going to be sent to the server; presumably the kerberos server knows that
the fileserver can handle that type - otherwise why tell the client to use
it?
This is a part of the fix. With this, the AFS filesystem creates an
appdata key for each fileserver it talks to with RxGK and attaches it to
the afs_server record.
Fixes: 5800b1cf3fd8 ("rxrpc: Allow CHALLENGEs to the passed to the app for a RESPONSE")
Link: https://sashiko.dev/#/patchset/20260624163819.3017002-1-dhowells%40redhat.com
Signed-off-by: David Howells <dhowells@redhat.com>
cc: Marc Dionne <marc.dionne@auristor.com>
cc: Jeffrey Altman <jaltman@auristor.com>
cc: Eric Dumazet <edumazet@google.com>
cc: "David S. Miller" <davem@davemloft.net>
cc: Jakub Kicinski <kuba@kernel.org>
cc: Paolo Abeni <pabeni@redhat.com>
cc: Simon Horman <horms@kernel.org>
cc: Jarkko Sakkinen <jarkko@kernel.org>
cc: linux-afs@lists.infradead.org
cc: keyrings@vger.kernel.org
cc: stable@kernel.org
---
fs/afs/cm_security.c | 232 +++++++++++++++++++++++++++++++++++++++++
fs/afs/fs_probe.c | 5 +
fs/afs/internal.h | 2 +
fs/afs/server.c | 1 +
include/net/af_rxrpc.h | 2 +
net/rxrpc/key.c | 37 +++++++
6 files changed, 279 insertions(+)
diff --git a/fs/afs/cm_security.c b/fs/afs/cm_security.c
index 103168c70dd4..36907a04efd0 100644
--- a/fs/afs/cm_security.c
+++ b/fs/afs/cm_security.c
@@ -6,7 +6,9 @@
*/
#include <linux/slab.h>
+#include <linux/key-type.h>
#include <crypto/krb5.h>
+#include <keys/user-type.h>
#include "internal.h"
#include "afs_cm.h"
#include "afs_fs.h"
@@ -23,6 +25,236 @@ static int afs_create_yfs_cm_token(struct sk_buff *challenge,
struct afs_server *server);
#endif
+#ifdef CONFIG_RXGK
+/*
+ * As the YFS RxGK appdata to be passed in the YFS.FS-service RESPONSE packet,
+ * create a GSS token to use as a ticket to the specified fileserver.
+ */
+static int afs_create_yfs_rxgk_cm_appdata(struct afs_server *server, u32 enctype)
+{
+ const struct krb5_enctype *conn_krb5, *token_krb5;
+ const struct krb5_buffer *token_key;
+ struct crypto_aead *aead;
+ struct scatterlist sg;
+ struct afs_net *net = server->cell->net;
+ const struct key *cm_key = net->fs_cm_token_key;
+ struct key *appdata_key = NULL;
+ size_t keysize, uuidsize, authsize, toksize, encsize, contsize;
+ size_t adatasize, offset;
+ __be32 caps[1] = {
+ [0] = htonl(AFS_CAP_ERROR_TRANSLATION),
+ };
+ __be32 *xdr;
+ void *appdata, *K0, *encbase;
+ int ret;
+
+ if (!cm_key)
+ return -ENOKEY;
+
+ /* Assume that the fileserver is happy to use the same encoding type as
+ * we were told to use by the token obtained by the user.
+ */
+ conn_krb5 = crypto_krb5_find_enctype(enctype);
+ if (!conn_krb5)
+ return -ENOPKG;
+ token_krb5 = cm_key->payload.data[0];
+ token_key = (const struct krb5_buffer *)&cm_key->payload.data[2];
+
+ /* struct rxgk_key {
+ * afs_uint32 enctype;
+ * opaque key<>;
+ * };
+ */
+ keysize = 4 + xdr_len_object(conn_krb5->key_len);
+
+ /* struct RXGK_AuthName {
+ * afs_int32 kind;
+ * opaque data<AUTHDATAMAX>;
+ * opaque display<AUTHPRINTABLEMAX>;
+ * };
+ */
+ uuidsize = sizeof(server->uuid);
+ authsize = 4 + xdr_len_object(uuidsize) + xdr_len_object(0);
+
+ /* struct RXGK_Token {
+ * rxgk_key K0;
+ * RXGK_Level level;
+ * rxgkTime starttime;
+ * afs_int32 lifetime;
+ * afs_int32 bytelife;
+ * rxgkTime expirationtime;
+ * struct RXGK_AuthName identities<>;
+ * };
+ */
+ toksize = keysize + 8 + 4 + 4 + 8 + xdr_len_object(authsize);
+
+ offset = 0;
+ encsize = crypto_krb5_how_much_buffer(token_krb5, KRB5_ENCRYPT_MODE, toksize, &offset);
+
+ /* struct RXGK_TokenContainer {
+ * afs_int32 kvno;
+ * afs_int32 enctype;
+ * opaque encrypted_token<>;
+ * };
+ */
+ contsize = 4 + 4 + xdr_len_object(encsize);
+
+ /* struct YFSAppData {
+ * opr_uuid initiatorUuid;
+ * opr_uuid acceptorUuid;
+ * Capabilities caps;
+ * afs_int32 enctype;
+ * opaque callbackKey<>;
+ * opaque callbackToken<>;
+ * };
+ */
+ adatasize = 16 + 16 +
+ xdr_len_object(sizeof(caps)) +
+ 4 +
+ xdr_len_object(conn_krb5->key_len) +
+ xdr_len_object(contsize);
+
+ ret = -ENOMEM;
+ appdata = kzalloc(adatasize, GFP_KERNEL);
+ if (!appdata)
+ goto out;
+ xdr = appdata;
+
+ memcpy(xdr, &net->uuid, 16); /* appdata.initiatorUuid */
+ xdr += 16 / 4;
+ memcpy(xdr, &server->uuid, 16); /* appdata.acceptorUuid */
+ xdr += 16 / 4;
+ *xdr++ = htonl(ARRAY_SIZE(caps)); /* appdata.caps.len */
+ memcpy(xdr, &caps, sizeof(caps)); /* appdata.caps */
+ xdr += ARRAY_SIZE(caps);
+ *xdr++ = htonl(conn_krb5->etype); /* appdata.enctype */
+
+ *xdr++ = htonl(conn_krb5->key_len); /* appdata.callbackKey.len */
+ K0 = xdr;
+ get_random_bytes(K0, conn_krb5->key_len); /* appdata.callbackKey.data */
+ xdr += xdr_round_up(conn_krb5->key_len) / 4;
+
+ *xdr++ = htonl(contsize); /* appdata.callbackToken.len */
+ *xdr++ = htonl(1); /* cont.kvno */
+ *xdr++ = htonl(token_krb5->etype); /* cont.enctype */
+ *xdr++ = htonl(encsize); /* cont.encrypted_token.len */
+
+ encbase = xdr;
+ xdr += offset / 4;
+ *xdr++ = htonl(conn_krb5->etype); /* token.K0.enctype */
+ *xdr++ = htonl(conn_krb5->key_len); /* token.K0.key.len */
+ memcpy(xdr, K0, conn_krb5->key_len); /* token.K0.key.data */
+ xdr += xdr_round_up(conn_krb5->key_len) / 4;
+
+ *xdr++ = htonl(RXRPC_SECURITY_ENCRYPT); /* token.level */
+ *xdr++ = htonl(0); /* token.starttime */
+ *xdr++ = htonl(0); /* " */
+ *xdr++ = htonl(0); /* token.lifetime */
+ *xdr++ = htonl(0); /* token.bytelife */
+ *xdr++ = htonl(0); /* token.expirationtime */
+ *xdr++ = htonl(0); /* " */
+ *xdr++ = htonl(1); /* token.identities.count */
+ *xdr++ = htonl(0); /* token.identities[0].kind */
+ *xdr++ = htonl(uuidsize); /* token.identities[0].data.len */
+ memcpy(xdr, &server->uuid, uuidsize);
+ xdr += xdr_round_up(uuidsize) / 4;
+ *xdr++ = htonl(0); /* token.identities[0].display.len */
+
+ xdr = encbase + xdr_round_up(encsize);
+
+ if ((unsigned long)xdr - (unsigned long)appdata != adatasize)
+ pr_err("Appdata size incorrect %lx != %zx\n",
+ (unsigned long)xdr - (unsigned long)appdata, adatasize);
+
+ aead = crypto_krb5_prepare_encryption(token_krb5, token_key, RXGK_SERVER_ENC_TOKEN,
+ GFP_KERNEL);
+ if (IS_ERR(aead)) {
+ ret = PTR_ERR(aead);
+ goto out_token;
+ }
+
+ sg_init_one(&sg, encbase, encsize);
+ ret = crypto_krb5_encrypt(token_krb5, aead, &sg, 1, encsize, offset, toksize, false);
+ if (ret < 0)
+ goto out_aead;
+
+ appdata_key = key_alloc(&key_type_user, "rxrpc: afs rxgk appdata",
+ GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, current_cred(),
+ KEY_POS_VIEW | KEY_POS_SEARCH | KEY_USR_VIEW,
+ KEY_ALLOC_NOT_IN_QUOTA, NULL);
+ if (IS_ERR(appdata_key)) {
+ ret = PTR_ERR(appdata_key);
+ goto out_aead;
+ }
+
+ ret = key_instantiate_and_link(appdata_key, appdata, adatasize, NULL, NULL);
+ if (ret < 0) {
+ key_put(appdata_key);
+ goto out_aead;
+ }
+
+ /* Store the appdata before the key pointer */
+ smp_store_release(&server->yfs_rxgk_appdata, appdata_key);
+
+out_aead:
+ crypto_free_aead(aead);
+out_token:
+ kfree(appdata);
+out:
+ return ret;
+}
+#endif /* CONFIG_RXGK */
+
+/*
+ * Create the application data to go in a RESPONSE packet a server's CHALLENGE
+ * from the parameters contained in a key. The key specifies the security
+ * index and other appropriate parameters such as the encoding type for RxGK.
+ */
+int afs_create_server_appdata(struct afs_server *server, struct key *key)
+{
+ u32 krb5_enctype;
+ int ret;
+ u8 security_index;
+
+ if (!key)
+ return 0;
+
+ rxrpc_kernel_query_key(key, &security_index, &krb5_enctype);
+
+ _enter("%u,%u", security_index, krb5_enctype);
+
+ switch (security_index) {
+#ifdef CONFIG_RXGK
+ case RXRPC_SECURITY_YFS_RXGK:
+ /* Read the key pointer before the appdata */
+ if (smp_load_acquire(&server->yfs_rxgk_appdata))
+ return 0;
+ break;
+#endif
+ default:
+ return 0;
+ }
+
+ ret = 0;
+ mutex_lock(&server->cm_token_lock);
+
+ switch (security_index) {
+#ifdef CONFIG_RXGK
+ case RXRPC_SECURITY_YFS_RXGK:
+ /* Read the key pointer before the appdata */
+ if (smp_load_acquire(&server->yfs_rxgk_appdata))
+ break;
+ ret = afs_create_yfs_rxgk_cm_appdata(server, krb5_enctype);
+ break;
+#endif
+ default:
+ break;
+ }
+
+ mutex_unlock(&server->cm_token_lock);
+ return ret;
+}
+
/*
* Respond to an RxGK challenge, adding appdata.
*/
diff --git a/fs/afs/fs_probe.c b/fs/afs/fs_probe.c
index a91ad1938d07..e26f7f1e30a2 100644
--- a/fs/afs/fs_probe.c
+++ b/fs/afs/fs_probe.c
@@ -241,9 +241,14 @@ int afs_fs_probe_fileserver(struct afs_net *net, struct afs_server *server,
struct afs_endpoint_state *estate, *old;
struct afs_addr_list *old_alist = NULL, *alist;
unsigned long unprobed;
+ int ret;
_enter("%pU", &server->uuid);
+ ret = afs_create_server_appdata(server, key);
+ if (ret < 0)
+ return ret;
+
estate = kzalloc_obj(*estate);
if (!estate)
return -ENOMEM;
diff --git a/fs/afs/internal.h b/fs/afs/internal.h
index 47e2cae979b6..738d529ec97b 100644
--- a/fs/afs/internal.h
+++ b/fs/afs/internal.h
@@ -547,6 +547,7 @@ struct afs_server {
struct timer_list timer; /* Management timer */
struct mutex cm_token_lock; /* Lock governing creation of appdata */
struct krb5_buffer cm_rxgk_appdata; /* Appdata to be included in RESPONSE packet */
+ struct key *yfs_rxgk_appdata; /* Appdata to be included in RESPONSE packet */
time64_t unuse_time; /* Time at which last unused */
unsigned long flags;
#define AFS_SERVER_FL_RESPONDING 0 /* The server is responding */
@@ -1089,6 +1090,7 @@ extern bool afs_cm_incoming_call(struct afs_call *);
/*
* cm_security.c
*/
+int afs_create_server_appdata(struct afs_server *server, struct key *key);
void afs_process_oob_queue(struct work_struct *work);
#ifdef CONFIG_RXGK
int afs_create_token_key(struct afs_net *net, struct socket *socket);
diff --git a/fs/afs/server.c b/fs/afs/server.c
index 0fe162ea2a36..b08d9080b0c5 100644
--- a/fs/afs/server.c
+++ b/fs/afs/server.c
@@ -399,6 +399,7 @@ static void afs_server_rcu(struct rcu_head *rcu)
afs_estate_trace_put_server);
afs_put_cell(server->cell, afs_cell_trace_put_server);
kfree(server->cm_rxgk_appdata.data);
+ key_put(server->yfs_rxgk_appdata);
kfree(server);
}
diff --git a/include/net/af_rxrpc.h b/include/net/af_rxrpc.h
index 0fb4c41c9bbf..c4b68049c06f 100644
--- a/include/net/af_rxrpc.h
+++ b/include/net/af_rxrpc.h
@@ -109,6 +109,8 @@ int rxkad_kernel_respond_to_challenge(struct sk_buff *challenge);
u32 rxgk_kernel_query_challenge(struct sk_buff *challenge);
int rxgk_kernel_respond_to_challenge(struct sk_buff *challenge,
struct krb5_buffer *appdata);
+void rxrpc_kernel_query_key(const struct key *key, u8 *_security_index,
+ u32 *_krb5_enctype);
u8 rxrpc_kernel_query_call_security(struct rxrpc_call *call,
u16 *_service_id, u32 *_enctype);
diff --git a/net/rxrpc/key.c b/net/rxrpc/key.c
index a0aa78d89289..16bab72e6f07 100644
--- a/net/rxrpc/key.c
+++ b/net/rxrpc/key.c
@@ -893,3 +893,40 @@ static long rxrpc_read(const struct key *key,
_leave(" = %zu", size);
return size;
}
+
+/**
+ * rxrpc_kernel_query_key - Query parameters from an rxrpc key
+ * @key: The key to query
+ * @_security_index: Where to return the security index
+ * @_krb5_enctype: Where to return the krb5 encryption type if applicable
+ *
+ * Query an rxrpc authentication key, extracting the security index from the
+ * first token therein.
+ */
+void rxrpc_kernel_query_key(const struct key *key, u8 *_security_index,
+ u32 *_krb5_enctype)
+{
+ const struct rxrpc_key_token *token;
+
+ token = key->payload.data[0];
+ if (!token) {
+ *_security_index = 0;
+ *_krb5_enctype = 0;
+ return;
+ }
+
+ *_security_index = token->security_index;
+ switch (token->security_index) {
+ case RXRPC_SECURITY_RXKAD:
+ *_krb5_enctype = 0;
+ break;
+ case RXRPC_SECURITY_YFS_RXGK:
+ *_krb5_enctype = token->rxgk->enctype;
+ break;
+ default:
+ WARN_ON_ONCE(1);
+ *_krb5_enctype = 0;
+ break;
+ }
+}
+EXPORT_SYMBOL(rxrpc_kernel_query_key);
next prev parent reply other threads:[~2026-07-10 19:23 UTC|newest]
Thread overview: 17+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-10 19:22 [PATCH net v2 00/16] rxrpc: Fix CHALLENGE packet handling David Howells
2026-07-10 19:22 ` [PATCH net v2 01/16] afs: Fix NULL deref in afs_deliver_cb_init_call_back_state3() David Howells
2026-07-10 19:22 ` [PATCH net v2 02/16] rxrpc: Fix sendmsg to not return an error if last packet queued David Howells
2026-07-10 19:22 ` [PATCH net v2 03/16] afs: Fix UAF when sending a message David Howells
2026-07-10 19:22 ` [PATCH net v2 04/16] afs: Fix two delivery functions to pass back -EAGAIN David Howells
2026-07-10 19:22 ` [PATCH net v2 05/16] afs: Fix afs_fs_fetch_data() to set call->async David Howells
2026-07-10 19:22 ` [PATCH net v2 06/16] rxrpc: Fix packet encryption error handling David Howells
2026-07-10 19:22 ` [PATCH net v2 07/16] rxrpc: Fix update of call->tx_pending without holding lock David Howells
2026-07-10 19:22 ` [PATCH net v2 08/16] rxrpc: Fix generation of notifications after call completion David Howells
2026-07-10 19:22 ` [PATCH net v2 09/16] afs: Simplify call refcounting David Howells
2026-07-10 19:22 ` [PATCH net v2 10/16] afs: Make afs_put_call() take trace argument David Howells
2026-07-10 19:22 ` [PATCH net v2 11/16] afs: Fix UAF in afs_make_call() David Howells
2026-07-10 19:22 ` [PATCH net v2 12/16] keys: Add refcounting to user-defined key type payload David Howells
2026-07-10 19:22 ` David Howells [this message]
2026-07-10 19:22 ` [PATCH net v2 14/16] rxrpc: Pass appdata key to rxrpc_call and thence to rxrpc_bundle David Howells
2026-07-10 19:22 ` [PATCH net v2 15/16] rxrpc: Fix CHALLENGE packet overqueuing and simplify RESPONSE generation David Howells
2026-07-10 19:22 ` [PATCH net v2 16/16] rxrpc: Remove OOB challenge/response code David Howells
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260710192220.1922433-14-dhowells@redhat.com \
--to=dhowells@redhat.com \
--cc=davem@davemloft.net \
--cc=edumazet@google.com \
--cc=horms@kernel.org \
--cc=jaltman@auristor.com \
--cc=jarkko@kernel.org \
--cc=keyrings@vger.kernel.org \
--cc=kuba@kernel.org \
--cc=linux-afs@lists.infradead.org \
--cc=linux-kernel@vger.kernel.org \
--cc=marc.dionne@auristor.com \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
--cc=stable@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox