From nobody Sun Feb 8 21:47:04 2026 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 118E420E6EC for ; Mon, 7 Apr 2025 16:12:38 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1744042361; cv=none; b=Lg5keiWsBOD5c0oX1mJ/n/EqvrJ4k3Imzx/kpYoHPqMxHO17yIRL7nGPApam0NNz4wkxIqK6FiJnftwvv5WLUWbdiLoM/w7uCe985EAl9dPk3iUG+Xcju72QCL6cQVmKcZN+nw4tnRgOros7t9bjvTE0/+P6C3TNV1Qhv5vK5/s= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1744042361; c=relaxed/simple; bh=IEoJA7+Nk6wydSQS+fZHLkbVSf0pKCEQSfbBy2Ooh68=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=qS1A2I3Gp1VLU3S0/aNaneDDYQKUi5IPkaIm2xKDbKWQQZK2PsOxtOqM+tuuf7nbkR9j77o1799N060bNPRXPIYyvMf/NPF5dD0wLUS/R0O3Qixj0BsvNANcNWqID/VrLjDJ6zyMFBv3GZBck7kmqO/Hk76jLciQhKm9/vUnGX8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=i2p4briD; arc=none smtp.client-ip=170.10.133.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=redhat.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="i2p4briD" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1744042358; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=BtKYezJeXrnGnpMDLH9YTY5/0Gj1aLpLss8snpJtjgo=; b=i2p4briDliS4JTntS36B3GEQRbt87zY45Qv+R6G0uVH3nHV8/aTFS1aB5Tach/TGvSQ7G5 KTKtSwyBdA6YYUP2Uk3vXg6pmhE8n092TJXXYRWdgDfWF9nGrEORb6mf8G407+DKPqcXIU 9N7hHFCzcnjI4FOhB82uoZNGFi7KL+M= Received: from mx-prod-mc-01.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-466-P4eHg-gfNTeFZ_9I5AAJjA-1; Mon, 07 Apr 2025 12:12:35 -0400 X-MC-Unique: P4eHg-gfNTeFZ_9I5AAJjA-1 X-Mimecast-MFC-AGG-ID: P4eHg-gfNTeFZ_9I5AAJjA_1744042353 Received: from mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.4]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 33E5619560BB; Mon, 7 Apr 2025 16:12:33 +0000 (UTC) Received: from warthog.procyon.org.com (unknown [10.42.28.40]) by mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 2DE723001D0E; Mon, 7 Apr 2025 16:12:29 +0000 (UTC) From: David Howells To: netdev@vger.kernel.org Cc: David Howells , Marc Dionne , Jakub Kicinski , "David S. Miller" , Eric Dumazet , Paolo Abeni , Simon Horman , Christian Brauner , Chuck Lever , linux-afs@lists.infradead.org, linux-kernel@vger.kernel.org Subject: [PATCH net-next v2 11/13] afs: Use rxgk RESPONSE to pass token for callback channel Date: Mon, 7 Apr 2025 17:11:24 +0100 Message-ID: <20250407161130.1349147-12-dhowells@redhat.com> In-Reply-To: <20250407161130.1349147-1-dhowells@redhat.com> References: <20250407161130.1349147-1-dhowells@redhat.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.4 Content-Type: text/plain; charset="utf-8" Implement in kafs the hook for adding appdata into a RESPONSE packet generated in response to an RxGK CHALLENGE packet, and include the key for securing the callback channel so that notifications from the fileserver get encrypted. This will be necessary when more complex notifications are used that convey changed data around. Signed-off-by: David Howells cc: Marc Dionne cc: Jakub Kicinski cc: "David S. Miller" cc: Eric Dumazet cc: Paolo Abeni cc: Simon Horman cc: linux-afs@lists.infradead.org cc: netdev@vger.kernel.org --- fs/afs/Kconfig | 1 + fs/afs/cm_security.c | 255 +++++++++++++++++++++++++++++++++++++++++++ fs/afs/internal.h | 12 ++ fs/afs/rxrpc.c | 7 +- fs/afs/server.c | 2 + 5 files changed, 276 insertions(+), 1 deletion(-) diff --git a/fs/afs/Kconfig b/fs/afs/Kconfig index fc8ba9142f2f..682bd8ec2c10 100644 --- a/fs/afs/Kconfig +++ b/fs/afs/Kconfig @@ -5,6 +5,7 @@ config AFS_FS select AF_RXRPC select DNS_RESOLVER select NETFS_SUPPORT + select CRYPTO_KRB5 help If you say Y here, you will get an experimental Andrew File System driver. It currently only supports unsecured read-only AFS access. diff --git a/fs/afs/cm_security.c b/fs/afs/cm_security.c index e8eb63e4d124..edcbd249d202 100644 --- a/fs/afs/cm_security.c +++ b/fs/afs/cm_security.c @@ -8,11 +8,21 @@ #include #include #include "internal.h" +#include "afs_cm.h" #include "afs_fs.h" #include "protocol_yfs.h" #define RXRPC_TRACE_ONLY_DEFINE_ENUMS #include =20 +#define RXGK_SERVER_ENC_TOKEN 1036U // 0x40c +#define xdr_round_up(x) (round_up((x), sizeof(__be32))) +#define xdr_len_object(x) (4 + round_up((x), sizeof(__be32))) + +#ifdef CONFIG_RXGK +static int afs_create_yfs_cm_token(struct sk_buff *challenge, + struct afs_server *server); +#endif + /* * Respond to an RxGK challenge, adding appdata. */ @@ -20,6 +30,7 @@ static int afs_respond_to_challenge(struct sk_buff *chall= enge) { #ifdef CONFIG_RXGK struct krb5_buffer appdata =3D {}; + struct afs_server *server; #endif struct rxrpc_peer *peer; unsigned long peer_data; @@ -55,7 +66,23 @@ static int afs_respond_to_challenge(struct sk_buff *chal= lenge) =20 #ifdef CONFIG_RXGK case RXRPC_SECURITY_RXGK: + return rxgk_kernel_respond_to_challenge(challenge, &appdata); + case RXRPC_SECURITY_YFS_RXGK: + switch (service_id) { + case FS_SERVICE: + case YFS_FS_SERVICE: + server =3D (struct afs_server *)peer_data; + if (!server->cm_rxgk_appdata.data) { + mutex_lock(&server->cm_token_lock); + if (!server->cm_rxgk_appdata.data) + afs_create_yfs_cm_token(challenge, server); + mutex_unlock(&server->cm_token_lock); + } + if (server->cm_rxgk_appdata.data) + appdata =3D server->cm_rxgk_appdata; + break; + } return rxgk_kernel_respond_to_challenge(challenge, &appdata); #endif =20 @@ -83,3 +110,231 @@ void afs_process_oob_queue(struct work_struct *work) rxrpc_kernel_free_oob(oob); } } + +#ifdef CONFIG_RXGK +/* + * Create a securities keyring for the cache manager and attach a key to i= t for + * the RxGK tokens we want to use to secure the callback connection back f= rom + * the fileserver. + */ +int afs_create_token_key(struct afs_net *net, struct socket *socket) +{ + const struct krb5_enctype *krb5; + struct key *ring; + key_ref_t key; + char K0[32], *desc; + int ret; + + ring =3D keyring_alloc("kafs", + GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, current_cred(), + KEY_POS_SEARCH | KEY_POS_WRITE | + KEY_USR_VIEW | KEY_USR_READ | KEY_USR_SEARCH, + KEY_ALLOC_NOT_IN_QUOTA, + NULL, NULL); + if (IS_ERR(ring)) + return PTR_ERR(ring); + + ret =3D rxrpc_sock_set_security_keyring(socket->sk, ring); + if (ret < 0) + goto out; + + ret =3D -ENOPKG; + krb5 =3D crypto_krb5_find_enctype(KRB5_ENCTYPE_AES128_CTS_HMAC_SHA1_96); + if (!krb5) + goto out; + + if (WARN_ON_ONCE(krb5->key_len > sizeof(K0))) + goto out; + + ret =3D -ENOMEM; + desc =3D kasprintf(GFP_KERNEL, "%u:%u:%u:%u", + YFS_CM_SERVICE, RXRPC_SECURITY_YFS_RXGK, 1, krb5->etype); + if (!desc) + goto out; + + wait_for_random_bytes(); + get_random_bytes(K0, krb5->key_len); + + key =3D key_create(make_key_ref(ring, true), + "rxrpc_s", desc, + K0, krb5->key_len, + KEY_POS_VIEW | KEY_POS_READ | KEY_POS_SEARCH | KEY_USR_VIEW, + KEY_ALLOC_NOT_IN_QUOTA); + kfree(desc); + if (IS_ERR(key)) { + ret =3D PTR_ERR(key); + goto out; + } + + net->fs_cm_token_key =3D key_ref_to_ptr(key); + ret =3D 0; +out: + key_put(ring); + return ret; +} + +/* + * Create an YFS RxGK GSS token to use as a ticket to the specified filese= rver. + */ +static int afs_create_yfs_cm_token(struct sk_buff *challenge, + struct afs_server *server) +{ + 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 =3D server->cell->net; + const struct key *key =3D net->fs_cm_token_key; + size_t keysize, uuidsize, authsize, toksize, encsize, contsize, adatasize= , offset; + __be32 caps[1] =3D { + [0] =3D htonl(AFS_CAP_ERROR_TRANSLATION), + }; + __be32 *xdr; + void *appdata, *K0, *encbase; + u32 enctype; + int ret; + + if (!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. + */ + enctype =3D rxgk_kernel_query_challenge(challenge); + + conn_krb5 =3D crypto_krb5_find_enctype(enctype); + if (!conn_krb5) + return -ENOPKG; + token_krb5 =3D key->payload.data[0]; + token_key =3D (const struct krb5_buffer *)&key->payload.data[2]; + + /* struct rxgk_key { + * afs_uint32 enctype; + * opaque key<>; + * }; + */ + keysize =3D 4 + xdr_len_object(conn_krb5->key_len); + + /* struct RXGK_AuthName { + * afs_int32 kind; + * opaque data; + * opaque display; + * }; + */ + uuidsize =3D sizeof(server->uuid); + authsize =3D 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 =3D keysize + 8 + 4 + 4 + 8 + xdr_len_object(authsize); + + offset =3D 0; + encsize =3D crypto_krb5_how_much_buffer(token_krb5, KRB5_ENCRYPT_MODE, to= ksize, &offset); + + /* struct RXGK_TokenContainer { + * afs_int32 kvno; + * afs_int32 enctype; + * opaque encrypted_token<>; + * }; + */ + contsize =3D 4 + 4 + xdr_len_object(encsize); + + /* struct YFSAppData { + * opr_uuid initiatorUuid; + * opr_uuid acceptorUuid; + * Capabilities caps; + * afs_int32 enctype; + * opaque callbackKey<>; + * opaque callbackToken<>; + * }; + */ + adatasize =3D 16 + 16 + + xdr_len_object(sizeof(caps)) + + 4 + + xdr_len_object(conn_krb5->key_len) + + xdr_len_object(contsize); + + ret =3D -ENOMEM; + appdata =3D kzalloc(adatasize, GFP_KERNEL); + if (!appdata) + goto out; + xdr =3D appdata; + + memcpy(xdr, &net->uuid, 16); /* appdata.initiatorUuid */ + xdr +=3D 16 / 4; + memcpy(xdr, &server->uuid, 16); /* appdata.acceptorUuid */ + xdr +=3D 16 / 4; + *xdr++ =3D htonl(ARRAY_SIZE(caps)); /* appdata.caps.len */ + memcpy(xdr, &caps, sizeof(caps)); /* appdata.caps */ + xdr +=3D ARRAY_SIZE(caps); + *xdr++ =3D htonl(conn_krb5->etype); /* appdata.enctype */ + + *xdr++ =3D htonl(conn_krb5->key_len); /* appdata.callbackKey.len */ + K0 =3D xdr; + get_random_bytes(K0, conn_krb5->key_len); /* appdata.callbackKey.data */ + xdr +=3D xdr_round_up(conn_krb5->key_len) / 4; + + *xdr++ =3D htonl(contsize); /* appdata.callbackToken.len */ + *xdr++ =3D htonl(1); /* cont.kvno */ + *xdr++ =3D htonl(token_krb5->etype); /* cont.enctype */ + *xdr++ =3D htonl(encsize); /* cont.encrypted_token.len */ + + encbase =3D xdr; + xdr +=3D offset / 4; + *xdr++ =3D htonl(conn_krb5->etype); /* token.K0.enctype */ + *xdr++ =3D htonl(conn_krb5->key_len); /* token.K0.key.len */ + memcpy(xdr, K0, conn_krb5->key_len); /* token.K0.key.data */ + xdr +=3D xdr_round_up(conn_krb5->key_len) / 4; + + *xdr++ =3D htonl(RXRPC_SECURITY_ENCRYPT); /* token.level */ + *xdr++ =3D htonl(0); /* token.starttime */ + *xdr++ =3D htonl(0); /* " */ + *xdr++ =3D htonl(0); /* token.lifetime */ + *xdr++ =3D htonl(0); /* token.bytelife */ + *xdr++ =3D htonl(0); /* token.expirationtime */ + *xdr++ =3D htonl(0); /* " */ + *xdr++ =3D htonl(1); /* token.identities.count */ + *xdr++ =3D htonl(0); /* token.identities[0].kind */ + *xdr++ =3D htonl(uuidsize); /* token.identities[0].data.len */ + memcpy(xdr, &server->uuid, uuidsize); + xdr +=3D xdr_round_up(uuidsize) / 4; + *xdr++ =3D htonl(0); /* token.identities[0].display.len */ + + xdr =3D encbase + xdr_round_up(encsize); + + if ((unsigned long)xdr - (unsigned long)appdata !=3D adatasize) + pr_err("Appdata size incorrect %lx !=3D %zx\n", + (unsigned long)xdr - (unsigned long)appdata, adatasize); + + aead =3D crypto_krb5_prepare_encryption(token_krb5, token_key, RXGK_SERVE= R_ENC_TOKEN, + GFP_KERNEL); + if (IS_ERR(aead)) { + ret =3D PTR_ERR(aead); + goto out_token; + } + + sg_init_one(&sg, encbase, encsize); + ret =3D crypto_krb5_encrypt(token_krb5, aead, &sg, 1, encsize, offset, to= ksize, false); + if (ret < 0) + goto out_aead; + + server->cm_rxgk_appdata.len =3D adatasize; + server->cm_rxgk_appdata.data =3D appdata; + appdata =3D NULL; + +out_aead: + crypto_free_aead(aead); +out_token: + kfree(appdata); +out: + return ret; +} +#endif /* CONFIG_RXGK */ diff --git a/fs/afs/internal.h b/fs/afs/internal.h index 178804817efb..1124ea4000cb 100644 --- a/fs/afs/internal.h +++ b/fs/afs/internal.h @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -308,6 +309,7 @@ struct afs_net { struct list_head fs_probe_slow; /* List of afs_server to probe at 5m inte= rvals */ struct hlist_head fs_proc; /* procfs servers list */ =20 + struct key *fs_cm_token_key; /* Key for creating CM tokens */ struct work_struct fs_prober; struct timer_list fs_probe_timer; atomic_t servers_outstanding; @@ -543,6 +545,8 @@ struct afs_server { struct list_head volumes; /* RCU list of afs_server_entry objects */ struct work_struct destroyer; /* Work item to try and destroy a 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 */ time64_t unuse_time; /* Time at which last unused */ unsigned long flags; #define AFS_SERVER_FL_RESPONDING 0 /* The server is responding */ @@ -1065,6 +1069,14 @@ extern bool afs_cm_incoming_call(struct afs_call *); * cm_security.c */ void afs_process_oob_queue(struct work_struct *work); +#ifdef CONFIG_RXGK +int afs_create_token_key(struct afs_net *net, struct socket *socket); +#else +static inline int afs_create_token_key(struct afs_net *net, struct socket = *socket) +{ + return 0; +} +#endif =20 /* * dir.c diff --git a/fs/afs/rxrpc.c b/fs/afs/rxrpc.c index 00b3bc087f61..c1cadf8fb346 100644 --- a/fs/afs/rxrpc.c +++ b/fs/afs/rxrpc.c @@ -78,6 +78,10 @@ int afs_open_socket(struct afs_net *net) if (ret < 0) goto error_2; =20 + ret =3D afs_create_token_key(net, socket); + if (ret < 0) + pr_err("Couldn't create RxGK CM key: %d\n", ret); + ret =3D kernel_bind(socket, (struct sockaddr *) &srx, sizeof(srx)); if (ret =3D=3D -EADDRINUSE) { srx.transport.sin6.sin6_port =3D 0; @@ -140,6 +144,7 @@ void afs_close_socket(struct afs_net *net) flush_workqueue(afs_async_calls); net->socket->sk->sk_user_data =3D NULL; sock_release(net->socket); + key_put(net->fs_cm_token_key); =20 _debug("dework"); _leave(""); @@ -820,7 +825,7 @@ static int afs_deliver_cm_op_id(struct afs_call *call) trace_afs_cb_call(call); call->work.func =3D call->type->work; =20 - /* pass responsibility for the remainer of this message off to the + /* pass responsibility for the remainder of this message off to the * cache manager op */ return call->type->deliver(call); } diff --git a/fs/afs/server.c b/fs/afs/server.c index c530d1ca15df..72f7aa649544 100644 --- a/fs/afs/server.c +++ b/fs/afs/server.c @@ -131,6 +131,7 @@ static struct afs_server *afs_alloc_server(struct afs_c= ell *cell, const uuid_t * timer_setup(&server->timer, afs_server_timer, 0); INIT_LIST_HEAD(&server->volumes); init_waitqueue_head(&server->probe_wq); + mutex_init(&server->cm_token_lock); INIT_LIST_HEAD(&server->probe_link); INIT_HLIST_NODE(&server->proc_link); spin_lock_init(&server->probe_lock); @@ -396,6 +397,7 @@ static void afs_server_rcu(struct rcu_head *rcu) afs_put_endpoint_state(rcu_access_pointer(server->endpoint_state), afs_estate_trace_put_server); afs_put_cell(server->cell, afs_cell_trace_put_server); + kfree(server->cm_rxgk_appdata.data); kfree(server); }