RFC: Switch namespaces before upcall

Shyam Prasad N posted 1 patch 3 months, 2 weeks ago
fs/smb/client/cifs_spnego.c | 84 +++++++++++++++++++++----------------
fs/smb/client/cifs_spnego.h |  2 +
fs/smb/client/cifsglob.h    |  5 +++
fs/smb/client/connect.c     | 29 +++++++++++++
4 files changed, 84 insertions(+), 36 deletions(-)
RFC: Switch namespaces before upcall
Posted by Shyam Prasad N 3 months, 2 weeks ago
Hi David / Christian,

I've been trying to make changes in the kernel to allow keys to switch
namespaces before making the upcall using the UMH code in the kernel.
The kernel keys subsystem lacks this ability today, forcing the upcall
handlers to first switch namespaces using setns call, and then do the
handling. But for many container based environments, this is a serious
inconvenience.

In an attempt to get this to work, I've needed to export some
functions in nsproxy.c, which were earlier static. Now, it looks to me
like it's tripping in some part of namespace installation, which I'm
unable to decode. Could you please help me with this?

-----------------------------------------
[Sun Oct 26 17:01:03 2025] Key type cifs.spnego registered
[Sun Oct 26 17:01:03 2025] Key type cifs.idmap registered
[Sun Oct 26 17:01:03 2025] CIFS: No dialect specified on mount.
Default has changed to a more secure dialect, SMB2.1 or later (e.g.
SMB3.1.1), from CIFS (SMB1). To use the less secure SMB1 dialect to
access ol
d servers which do not support SMB3.1.1 (or even SMB3 or SMB2.1)
specify vers=1.0 on mount.
[Sun Oct 26 17:01:03 2025] CIFS: Attempting to mount //192.168.122.1/testshare
[Sun Oct 26 17:01:04 2025] CIFS: VFS: unknown or missing server auth
type, use krb5
[Sun Oct 26 17:01:04 2025]
==================================================================
[Sun Oct 26 17:01:04 2025] BUG: KASAN: null-ptr-deref in
current_is_single_threaded+0xea/0x570
[Sun Oct 26 17:01:04 2025] Read of size 4 at addr 00000000000000cc by
task kworker/u91:0/1570

[Sun Oct 26 17:01:04 2025] CPU: 18 UID: 0 PID: 1570 Comm:
kworker/u91:0 Not tainted 6.17.0-rc6-nsupcall #26 PREEMPT(voluntary)
[Sun Oct 26 17:01:04 2025] Hardware name: QEMU Standard PC (Q35 +
ICH9, 2009), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
[Sun Oct 26 17:01:04 2025] Call Trace:
[Sun Oct 26 17:01:04 2025]  <TASK>
[Sun Oct 26 17:01:04 2025]  dump_stack_lvl+0x91/0xf0
[Sun Oct 26 17:01:04 2025]  print_report+0x442/0x660
[Sun Oct 26 17:01:04 2025]  ? lock_acquired+0x196/0x3a0
[Sun Oct 26 17:01:04 2025]  ? kasan_addr_to_slab+0xd/0xb0
[Sun Oct 26 17:01:04 2025]  kasan_report+0xf3/0x130
[Sun Oct 26 17:01:04 2025]  ? current_is_single_threaded+0xea/0x570
[Sun Oct 26 17:01:04 2025]  ? current_is_single_threaded+0xea/0x570
[Sun Oct 26 17:01:04 2025]  kasan_check_range+0x11c/0x200
[Sun Oct 26 17:01:04 2025]  __kasan_check_read+0x11/0x20
[Sun Oct 26 17:01:04 2025]  current_is_single_threaded+0xea/0x570
[Sun Oct 26 17:01:04 2025]  ? security_capable+0x77/0x1c0
[Sun Oct 26 17:01:04 2025]  timens_install+0x48/0x340
[Sun Oct 26 17:01:04 2025]  ? netns_install+0x141/0x210
[Sun Oct 26 17:01:04 2025]  validate_nsset+0x4b8/0xc60
[Sun Oct 26 17:01:04 2025]  umh_keys_init+0x23e/0x340
[Sun Oct 26 17:01:04 2025]  ? kvm_sched_clock_read+0x11/0x20
[Sun Oct 26 17:01:04 2025]  ? __pfx_umh_keys_init+0x10/0x10
[Sun Oct 26 17:01:04 2025]  ? local_clock_noinstr+0xe/0xd0
[Sun Oct 26 17:01:04 2025]  ? do_raw_spin_unlock+0x14b/0x200
[Sun Oct 26 17:01:04 2025]  call_usermodehelper_exec_async+0x1c3/0x470
[Sun Oct 26 17:01:04 2025]  ? __pfx_call_usermodehelper_exec_async+0x10/0x10
[Sun Oct 26 17:01:04 2025]  ret_from_fork+0x3ed/0x540
[Sun Oct 26 17:01:04 2025]  ? __pfx_call_usermodehelper_exec_async+0x10/0x10
[Sun Oct 26 17:01:04 2025]  ret_from_fork_asm+0x1a/0x30
[Sun Oct 26 17:01:04 2025]  </TASK>
[Sun Oct 26 17:01:04 2025]
==================================================================
-----------------------------------------

Could it be because the process that is attempting to do
validate_nsset is a kernel tsk and not a userspace process? If so, is
there any easy way to work around this to get the solution working?

Thanks in advance.

-- 
Regards,
Shyam
From c05f8636e3f00f93ede37e79cdb4b7bb308a1bb2 Mon Sep 17 00:00:00 2001
From: Shyam Prasad N <sprasad@microsoft.com>
Date: Fri, 19 Sep 2025 10:06:56 +0530
Subject: [PATCH] cifs: leverage namespace switching capability of request_key
 to upcall

request_key had a limitation of not being able to upcall to the
right set of namespaces as the calling process. As a result, there's
a need to pass the pid of the calling process in the key description
and later allow the upcall handler to first switch to the right
namespaces before proceeding to work.

With my prev change:
keys: allow usermode helper to switch namespaces before upcall
...request_key now switches to the right set of namespaces *before*
upcalling. This way, cifs upcalls do not need to pass the pid of the
calling process, and hence the upcall handler in that namespace will
directly get called.

Signed-off-by: Shyam Prasad N <sprasad@microsoft.com>
---
 fs/smb/client/cifs_spnego.c | 84 +++++++++++++++++++++----------------
 fs/smb/client/cifs_spnego.h |  2 +
 fs/smb/client/cifsglob.h    |  5 +++
 fs/smb/client/connect.c     | 29 +++++++++++++
 4 files changed, 84 insertions(+), 36 deletions(-)

diff --git a/fs/smb/client/cifs_spnego.c b/fs/smb/client/cifs_spnego.c
index 43b86fa4d695..cf14db7a0a4a 100644
--- a/fs/smb/client/cifs_spnego.c
+++ b/fs/smb/client/cifs_spnego.c
@@ -18,7 +18,6 @@
 #include "cifs_spnego.h"
 #include "cifs_debug.h"
 #include "cifsproto.h"
-static const struct cred *spnego_cred;
 
 /* create a new cifs key */
 static int
@@ -96,7 +95,8 @@ cifs_get_spnego_key(struct cifs_ses *sesInfo,
 	size_t desc_len;
 	struct key *spnego_key;
 	const char *hostname = server->hostname;
-	const struct cred *saved_cred;
+	const struct cred *saved_cred = NULL;
+	struct user_namespace *user_ns;
 
 	/* length of fields (with semicolons): ver=0xyz ip4=ipaddress
 	   host=hostname sec=mechanism uid=0xFF user=username */
@@ -147,26 +147,31 @@ cifs_get_spnego_key(struct cifs_ses *sesInfo,
 		dp += sprintf(dp, ";sec=krb5");
 	}
 
+	if (sesInfo->upcall_target == UPTARGET_MOUNT) {
+		dp += sprintf(dp, ";upcall_target=mount");
+		dp += sprintf(dp, ";pid=0x%x", 0);
+		user_ns = sesInfo->spnego_cred->user_ns;
+		saved_cred = override_creds(sesInfo->spnego_cred);
+	} else {
+		dp += sprintf(dp, ";upcall_target=app");
+		dp += sprintf(dp, ";pid=0x%x", current->pid);
+		user_ns = &init_user_ns;
+	}
+
 	dp += sprintf(dp, ";uid=0x%x",
-		      from_kuid_munged(&init_user_ns, sesInfo->linux_uid));
+		      from_kuid_munged(user_ns, sesInfo->linux_uid));
 
 	dp += sprintf(dp, ";creduid=0x%x",
-		from_kuid_munged(&init_user_ns, sesInfo->cred_uid));
+		from_kuid_munged(user_ns, sesInfo->cred_uid));
 
 	if (sesInfo->user_name)
 		dp += sprintf(dp, ";user=%s", sesInfo->user_name);
-
-	dp += sprintf(dp, ";pid=0x%x", current->pid);
-
-	if (sesInfo->upcall_target == UPTARGET_MOUNT)
-		dp += sprintf(dp, ";upcall_target=mount");
-	else
-		dp += sprintf(dp, ";upcall_target=app");
-
+	
 	cifs_dbg(FYI, "key description = %s\n", description);
-	saved_cred = override_creds(spnego_cred);
 	spnego_key = request_key(&cifs_spnego_key_type, description, "");
-	revert_creds(saved_cred);
+
+	if (saved_cred)
+		revert_creds(saved_cred);
 
 #ifdef CONFIG_CIFS_DEBUG2
 	if (cifsFYI && !IS_ERR(spnego_key)) {
@@ -181,39 +186,35 @@ cifs_get_spnego_key(struct cifs_ses *sesInfo,
 	return spnego_key;
 }
 
-int
-init_cifs_spnego(void)
+/* Create per-session SPNEGO credentials and keyring */
+int cifs_setup_session_spnego(struct cifs_ses *ses)
 {
 	struct cred *cred;
 	struct key *keyring;
 	int ret;
 
-	cifs_dbg(FYI, "Registering the %s key type\n",
-		 cifs_spnego_key_type.name);
+	BUG_ON(ses->spnego_cred);
 
 	/*
 	 * Create an override credential set with special thread keyring for
-	 * spnego upcalls.
+	 * spnego upcalls, scoped to the session's user namespace.
 	 */
-
-	cred = prepare_kernel_cred(&init_task);
+	cred = prepare_kernel_cred(current);
 	if (!cred)
 		return -ENOMEM;
 
 	keyring = keyring_alloc(".cifs_spnego",
-				GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, cred,
+				ses->linux_uid,
+				ses->linux_gid, cred,
 				(KEY_POS_ALL & ~KEY_POS_SETATTR) |
 				KEY_USR_VIEW | KEY_USR_READ,
 				KEY_ALLOC_NOT_IN_QUOTA, NULL, NULL);
 	if (IS_ERR(keyring)) {
 		ret = PTR_ERR(keyring);
-		goto failed_put_cred;
+		put_cred(cred);
+		return ret;
 	}
 
-	ret = register_key_type(&cifs_spnego_key_type);
-	if (ret < 0)
-		goto failed_put_key;
-
 	/*
 	 * instruct request_key() to use this special keyring as a cache for
 	 * the results it looks up
@@ -221,23 +222,34 @@ init_cifs_spnego(void)
 	set_bit(KEY_FLAG_ROOT_CAN_CLEAR, &keyring->flags);
 	cred->thread_keyring = keyring;
 	cred->jit_keyring = KEY_REQKEY_DEFL_THREAD_KEYRING;
-	spnego_cred = cred;
 
-	cifs_dbg(FYI, "cifs spnego keyring: %d\n", key_serial(keyring));
+	ses->spnego_cred = cred;
+	cifs_dbg(FYI, "cifs session spnego keyring: %d\n", key_serial(keyring));
 	return 0;
+}
 
-failed_put_key:
-	key_put(keyring);
-failed_put_cred:
-	put_cred(cred);
-	return ret;
+/* Clean up per-session SPNEGO credentials */
+void cifs_cleanup_session_spnego(struct cifs_ses *ses)
+{
+	if (ses->spnego_cred) {
+		key_revoke(ses->spnego_cred->thread_keyring);
+		put_cred(ses->spnego_cred);
+		ses->spnego_cred = NULL;
+	}
+}
+
+int
+init_cifs_spnego(void)
+{
+	cifs_dbg(FYI, "Registering the %s key type\n",
+		 cifs_spnego_key_type.name);
+
+	return register_key_type(&cifs_spnego_key_type);
 }
 
 void
 exit_cifs_spnego(void)
 {
-	key_revoke(spnego_cred->thread_keyring);
 	unregister_key_type(&cifs_spnego_key_type);
-	put_cred(spnego_cred);
 	cifs_dbg(FYI, "Unregistered %s key type\n", cifs_spnego_key_type.name);
 }
diff --git a/fs/smb/client/cifs_spnego.h b/fs/smb/client/cifs_spnego.h
index e4d751b0c812..5ca2e29a9825 100644
--- a/fs/smb/client/cifs_spnego.h
+++ b/fs/smb/client/cifs_spnego.h
@@ -31,6 +31,8 @@ struct cifs_spnego_msg {
 extern struct key_type cifs_spnego_key_type;
 extern struct key *cifs_get_spnego_key(struct cifs_ses *sesInfo,
 				       struct TCP_Server_Info *server);
+extern int cifs_setup_session_spnego(struct cifs_ses *ses);
+extern void cifs_cleanup_session_spnego(struct cifs_ses *ses);
 #endif /* KERNEL */
 
 #endif /* _CIFS_SPNEGO_H */
diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h
index 0fae95cf81c4..90148866f02f 100644
--- a/fs/smb/client/cifsglob.h
+++ b/fs/smb/client/cifsglob.h
@@ -1112,6 +1112,7 @@ struct cifs_ses {
 	char *serverDomain;	/* security realm of server */
 	__u64 Suid;		/* remote smb uid  */
 	kuid_t linux_uid;	/* overriding owner of files on the mount */
+	kgid_t linux_gid;	/* overriding owner group of files on the mount */
 	kuid_t cred_uid;	/* owner of credentials */
 	unsigned int capabilities;
 	char ip_addr[INET6_ADDRSTRLEN + 1]; /* Max ipv6 (or v4) addr string len */
@@ -1136,6 +1137,10 @@ struct cifs_ses {
 	__u8 smb3decryptionkey[SMB3_ENC_DEC_KEY_SIZE];
 	__u8 preauth_sha_hash[SMB2_PREAUTH_HASH_SIZE];
 
+	/* Per-session SPNEGO credentials and namespace context */
+	struct cred *spnego_cred;	/* Per-session SPNEGO credential with keyring */
+	struct nsproxy *nsproxy;	/* Namespace proxy of the calling process */
+
 	/*
 	 * Network interfaces available on the server this session is
 	 * connected to.
diff --git a/fs/smb/client/connect.c b/fs/smb/client/connect.c
index dd12f3eb61dc..4aceeaeafe57 100644
--- a/fs/smb/client/connect.c
+++ b/fs/smb/client/connect.c
@@ -51,6 +51,7 @@
 #endif
 #include "fs_context.h"
 #include "cifs_swn.h"
+#include "cifs_spnego.h"
 
 /* FIXME: should these be tunable? */
 #define TLINK_ERROR_EXPIRE	(1 * HZ)
@@ -1940,6 +1941,10 @@ static int match_session(struct cifs_ses *ses,
 	if (!match_super && ctx->dfs_root_ses != ses->dfs_root_ses)
 		return 0;
 
+	/* Compare namespace proxy - sessions must be from the same namespace */
+	if (ses->nsproxy != current->nsproxy)
+		return 0;
+	
 	/*
 	 * If an existing session is limited to less channels than
 	 * requested, it should not be reused
@@ -2174,6 +2179,12 @@ void __cifs_put_smb_ses(struct cifs_ses *ses)
 		ses->chans[0].server = NULL;
 	}
 
+	cifs_cleanup_session_spnego(ses);
+	if (ses->nsproxy) {
+		put_nsproxy(ses->nsproxy);
+		ses->nsproxy = NULL;
+	}
+
 	sesInfoFree(ses);
 	cifs_put_tcp_session(server, 0);
 }
@@ -2457,6 +2468,7 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx)
 		ses->domainAuto = ctx->domainauto;
 	ses->cred_uid = ctx->cred_uid;
 	ses->linux_uid = ctx->linux_uid;
+	ses->linux_gid = ctx->linux_gid;
 
 	ses->unicode = ctx->unicode;
 	ses->sectype = ctx->sectype;
@@ -2491,6 +2503,17 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx)
 	ses->chans_need_reconnect = 1;
 	spin_unlock(&ses->chan_lock);
 
+	/* Capture the namespace proxy of the calling process */
+	get_nsproxy(current->nsproxy);
+	ses->nsproxy = current->nsproxy;
+
+	/* Set up per-session SPNEGO credentials */
+	rc = cifs_setup_session_spnego(ses);
+	if (rc) {
+		cifs_dbg(VFS, "Failed to setup session SPNEGO credentials: %d\n", rc);
+		goto get_ses_fail;
+	}
+
 retry_new_session:
 	mutex_lock(&ses->session_mutex);
 	rc = cifs_negotiate_protocol(xid, ses, server);
@@ -2532,6 +2555,12 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx)
 	return ses;
 
 get_ses_fail:
+    cifs_cleanup_session_spnego(ses);
+    if (ses && ses->nsproxy) {
+        put_nsproxy(ses->nsproxy);
+        ses->nsproxy = NULL;
+    }
+
 	sesInfoFree(ses);
 	free_xid(xid);
 	return ERR_PTR(rc);
From 75bd1ccaaaf9a42840df293e45e49436797b1bca Mon Sep 17 00:00:00 2001
From: Shyam Prasad N <sprasad@microsoft.com>
Date: Fri, 4 Jul 2025 22:38:53 +0530
Subject: [PATCH] keys: allow usermode helper to switch namespaces before
 upcall

When request_key needs to upcall to userspace today, namespaces of
a target container cannot be specified. As a result, the upcall handler
needs to be configured in the host namespace. This is not convenient for
workloads where the mount happens inside a container, as some tools will
need to be installed on the host.

For example, cifs.upcall handler (in cifs-utils) handles this today
by first switching namespaces (key description contains the pid, which
can be used to get namespace details).

This change allows the key subsystem to switch to the namespaces of the
calling process. This way, there should not be a dependency on the userspace
handler to do this.

This change needed exporting some functions in nsproxy code which were
earlier static as it was only the setns syscall that used them.

Signed-off-by: Shyam Prasad N <sprasad@microsoft.com>
---
 include/linux/nsproxy.h     | 16 +++----
 kernel/nsproxy.c            | 21 +++++++--
 security/keys/request_key.c | 92 ++++++++++++++++++++++++++++++++-----
 3 files changed, 103 insertions(+), 26 deletions(-)

diff --git a/include/linux/nsproxy.h b/include/linux/nsproxy.h
index dab6a1734a22..5adaf59b98cd 100644
--- a/include/linux/nsproxy.h
+++ b/include/linux/nsproxy.h
@@ -112,17 +112,13 @@ int unshare_nsproxy_namespaces(unsigned long, struct nsproxy **,
 	struct cred *, struct fs_struct *);
 int __init nsproxy_cache_init(void);
 
-static inline void put_nsproxy(struct nsproxy *ns)
-{
-	if (refcount_dec_and_test(&ns->count))
-		free_nsproxy(ns);
-}
-
-static inline void get_nsproxy(struct nsproxy *ns)
-{
-	refcount_inc(&ns->count);
-}
+void put_nsset(struct nsset *nsset);
+int prepare_nsset(unsigned flags, struct nsset *nsset);
+int validate_nsset(struct nsset *nsset, struct pid *pid);
+void commit_nsset(struct nsset *nsset);
 
+void get_nsproxy(struct nsproxy *ns);
+void put_nsproxy(struct nsproxy *ns);
 DEFINE_FREE(put_nsproxy, struct nsproxy *, if (_T) put_nsproxy(_T))
 
 #endif
diff --git a/kernel/nsproxy.c b/kernel/nsproxy.c
index 5f31fdff8a38..85afdda77dc5 100644
--- a/kernel/nsproxy.c
+++ b/kernel/nsproxy.c
@@ -196,6 +196,19 @@ void free_nsproxy(struct nsproxy *ns)
 	kmem_cache_free(nsproxy_cachep, ns);
 }
 
+void put_nsproxy(struct nsproxy *ns)
+{
+	if (refcount_dec_and_test(&ns->count))
+		free_nsproxy(ns);
+}
+EXPORT_SYMBOL(put_nsproxy);
+
+void get_nsproxy(struct nsproxy *ns)
+{
+	refcount_inc(&ns->count);
+}
+EXPORT_SYMBOL(get_nsproxy);
+
 /*
  * Called from unshare. Unshare all the namespaces part of nsproxy.
  * On success, returns the new nsproxy.
@@ -302,7 +315,7 @@ static int check_setns_flags(unsigned long flags)
 	return 0;
 }
 
-static void put_nsset(struct nsset *nsset)
+void put_nsset(struct nsset *nsset)
 {
 	unsigned flags = nsset->flags;
 
@@ -318,7 +331,7 @@ static void put_nsset(struct nsset *nsset)
 		free_nsproxy(nsset->nsproxy);
 }
 
-static int prepare_nsset(unsigned flags, struct nsset *nsset)
+int prepare_nsset(unsigned flags, struct nsset *nsset)
 {
 	struct task_struct *me = current;
 
@@ -362,7 +375,7 @@ static inline int validate_ns(struct nsset *nsset, struct ns_common *ns)
  * namespaces occurs at the point of no return after installation of
  * all requested namespaces was successful in commit_nsset().
  */
-static int validate_nsset(struct nsset *nsset, struct pid *pid)
+int validate_nsset(struct nsset *nsset, struct pid *pid)
 {
 	int ret = 0;
 	unsigned flags = nsset->flags;
@@ -499,7 +512,7 @@ static int validate_nsset(struct nsset *nsset, struct pid *pid)
  * exported anymore a simple commit handler for each namespace
  * should be added to ns_common.
  */
-static void commit_nsset(struct nsset *nsset)
+void commit_nsset(struct nsset *nsset)
 {
 	unsigned flags = nsset->flags;
 	struct task_struct *me = current;
diff --git a/security/keys/request_key.c b/security/keys/request_key.c
index a7673ad86d18..8d21305adf41 100644
--- a/security/keys/request_key.c
+++ b/security/keys/request_key.c
@@ -13,6 +13,8 @@
 #include <linux/err.h>
 #include <linux/keyctl.h>
 #include <linux/slab.h>
+#include <linux/user_namespace.h>
+#include <linux/nsproxy.h>
 #include <net/net_namespace.h>
 #include "internal.h"
 #include <keys/request_key_auth-type.h>
@@ -70,18 +72,66 @@ void complete_request_key(struct key *authkey, int error)
 }
 EXPORT_SYMBOL(complete_request_key);
 
+struct umh_info {
+	struct key *keyring;
+	const struct cred *orig_cred;
+	const struct cred *saved_cred;
+	struct nsproxy *orig_nsproxy;
+};
+
 /*
  * Initialise a usermode helper that is going to have a specific session
- * keyring.
+ * keyring and run in the correct namespace context.
  *
  * This is called in context of freshly forked kthread before kernel_execve(),
- * so we can simply install the desired session_keyring at this point.
+ * so we can install the desired session_keyring and set up the namespace context.
  */
 static int umh_keys_init(struct subprocess_info *info, struct cred *cred)
 {
-	struct key *keyring = info->data;
+	struct umh_info *umh_info = info->data;
+	struct key *keyring = umh_info->keyring;
+	const struct cred *orig_cred = umh_info->orig_cred;
+	struct nsproxy *orig_nsproxy = umh_info->orig_nsproxy;
+	struct nsproxy *temp_nsproxy;
+	struct nsset nsset = {};
+	unsigned long setns_flags = (CLONE_NEWNS | CLONE_NEWUTS | CLONE_NEWIPC |
+			       CLONE_NEWNET | CLONE_NEWPID | CLONE_NEWCGROUP |
+			       CLONE_NEWTIME);
+	int ret = 0;
+
+	ret = install_session_keyring_to_cred(cred, keyring);
+	if (ret < 0)
+		return ret;
+
+	/* Copy the user namespace from the original calling context */
+	if (orig_cred->user_ns != cred->user_ns)
+		override_creds(orig_cred);
+
+	/* 
+	 * Switch to the original caller's namespace context using the proper
+	 * setns-like sequence: prepare_nsset, validate_nsset, commit_nsset
+	 */
+	if (orig_nsproxy != current->nsproxy) {
+		/* Prepare namespace set for switching */
+		ret = prepare_nsset(setns_flags, &nsset);
+		if (ret < 0)
+			return ret;
+
+		/* Validate the namespace set */
+		ret = validate_nsset(&nsset, task_pid(current));
+		if (ret < 0) {
+			put_nsset(&nsset);
+			return ret;
+		}
 
-	return install_session_keyring_to_cred(cred, keyring);
+		/* Commit the namespace switch */
+		commit_nsset(&nsset);
+
+		/* Clean up the nsset structure */
+		put_nsset(&nsset);
+	}
+
+	return 0;
 }
 
 /*
@@ -89,23 +139,41 @@ static int umh_keys_init(struct subprocess_info *info, struct cred *cred)
  */
 static void umh_keys_cleanup(struct subprocess_info *info)
 {
-	struct key *keyring = info->data;
-	key_put(keyring);
+	struct umh_info *umh_info = info->data;
+	
+	key_put(umh_info->keyring);
+	put_cred(umh_info->orig_cred);
+	put_nsproxy(umh_info->orig_nsproxy);
+	kfree(umh_info);
 }
 
 /*
- * Call a usermode helper with a specific session keyring.
+ * Call a usermode helper with a specific session keyring and namespace context.
  */
 static int call_usermodehelper_keys(const char *path, char **argv, char **envp,
 					struct key *session_keyring, int wait)
 {
 	struct subprocess_info *info;
+	struct umh_info *umh_info;
+
+	umh_info = kmalloc(sizeof(*umh_info), GFP_KERNEL);
+	if (!umh_info)
+		return -ENOMEM;
+
+	umh_info->keyring = session_keyring;
+	umh_info->orig_cred = get_current_cred();
+	umh_info->orig_nsproxy = current->nsproxy;
+	get_nsproxy(umh_info->orig_nsproxy);
 
 	info = call_usermodehelper_setup(path, argv, envp, GFP_KERNEL,
 					  umh_keys_init, umh_keys_cleanup,
-					  session_keyring);
-	if (!info)
+					  umh_info);
+	if (!info) {
+		put_cred(umh_info->orig_cred);
+		put_nsproxy(umh_info->orig_nsproxy);
+		kfree(umh_info);
 		return -ENOMEM;
+	}
 
 	key_get(session_keyring);
 	return call_usermodehelper_exec(info, wait);
@@ -151,9 +219,9 @@ static int call_sbin_request_key(struct key *authkey, void *aux)
 	if (ret < 0)
 		goto error_link;
 
-	/* record the UID and GID */
-	sprintf(uid_str, "%d", from_kuid(&init_user_ns, cred->fsuid));
-	sprintf(gid_str, "%d", from_kgid(&init_user_ns, cred->fsgid));
+	/* record the UID and GID - use original caller's user_ns for mapping */
+	sprintf(uid_str, "%d", from_kuid(cred->user_ns, cred->fsuid));
+	sprintf(gid_str, "%d", from_kgid(cred->user_ns, cred->fsgid));
 
 	/* we say which key is under construction */
 	sprintf(key_str, "%d", key->serial);