[SRU][F][PATCH 1/1] CIFS: New mount option for cifs.upcall namespace resolution

Vinicius Peixoto vinicius.peixoto at canonical.com
Wed Apr 30 16:54:42 UTC 2025


From: Ritvik Budhiraja <rbudhiraja at microsoft.com>

BugLink: https://bugs.launchpad.net/bugs/2099914

In the current implementation, the SMB filesystem on a mount point can
trigger upcalls from the kernel to the userspace to enable certain
functionalities like spnego, dns_resolution, amongst others. These upcalls
usually either happen in the context of the mount or in the context of an
application/user. The upcall handler for cifs, cifs.upcall already has
existing code which switches the namespaces to the caller's namespace
before handling the upcall. This behaviour is expected for scenarios like
multiuser mounts, but might not cover all single user scenario with
services such as Kubernetes, where the mount can happen from different
locations such as on the host, from an app container, or a driver pod
which does the mount on behalf of a different pod.

This patch introduces a new mount option called upcall_target, to
customise the upcall behaviour. upcall_target can take 'mount' and 'app'
as possible values. This aids use cases like Kubernetes where the mount
happens on behalf of the application in another container altogether.
Having this new mount option allows the mount command to specify where the
upcall should happen: 'mount' for resolving the upcall to the host
namespace, and 'app' for resolving the upcall to the ns of the calling
thread. This will enable both the scenarios where the Kerberos credentials
can be found on the application namespace or the host namespace to which
just the mount operation is "delegated".

Reviewed-by: Shyam Prasad <shyam.prasad at microsoft.com>
Reviewed-by: Bharath S M <bharathsm at microsoft.com>
Reviewed-by: Ronnie Sahlberg <ronniesahlberg at gmail.com>
Signed-off-by: Ritvik Budhiraja <rbudhiraja at microsoft.com>
Signed-off-by: Steve French <stfrench at microsoft.com>
(backported from commit db363b0a1d9e6b9dc556296f1b1007aeb496a8cf)
[vpeixoto: this required several adjustments due to the diff between 5.4
and 6.13:
- fixed context conflicts related to missing eb90e8ecb2b54 ("smb: client:
  introduce reparse mount option")
- we're missing a6a9cffad0a2 ("cifs: add files to host new mount api"),
  which split a bunch of functionality into fs/cifs/fs_context.{c,h}, so
  adjust the function changes to their old pre-split locations
- the usage of the fs_context API was introduced only later, so adjust
  all uses of smb3_fs_context to smb_vol and use the older
  match_table_t-based mount option parsing scheme
- adjust the usage of cifs_sb_info to cb_ses
- other small adjustments to the Opt_* enums
]
CVE-2025-2312
Signed-off-by: Matthew Ruffell <matthew.ruffell at canonical.com>
Signed-off-by: Vinicius Peixoto <vinicius.peixoto at canonical.com>
---
 fs/cifs/cifs_spnego.c | 16 ++++++++++
 fs/cifs/cifsfs.c      | 25 ++++++++++++++++
 fs/cifs/cifsglob.h    |  8 +++++
 fs/cifs/connect.c     | 70 +++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 119 insertions(+)

diff --git a/fs/cifs/cifs_spnego.c b/fs/cifs/cifs_spnego.c
index 6eb65988321f..ca54705be5a7 100644
--- a/fs/cifs/cifs_spnego.c
+++ b/fs/cifs/cifs_spnego.c
@@ -94,6 +94,9 @@ struct key_type cifs_spnego_key_type = {
 /* strlen of ";pid=0x" */
 #define PID_KEY_LEN		7
 
+/* strlen of ";upcall_target=" */
+#define UPCALL_TARGET_KEY_LEN	15
+
 /* get a key struct with a SPNEGO security blob, suitable for session setup */
 struct key *
 cifs_get_spnego_key(struct cifs_ses *sesInfo)
@@ -120,6 +123,11 @@ cifs_get_spnego_key(struct cifs_ses *sesInfo)
 	if (sesInfo->user_name)
 		desc_len += USER_KEY_LEN + strlen(sesInfo->user_name);
 
+	if (sesInfo->upcall_target == UPTARGET_MOUNT)
+		desc_len += UPCALL_TARGET_KEY_LEN + 5; // strlen("mount")
+	else
+		desc_len += UPCALL_TARGET_KEY_LEN + 3; // strlen("app")
+
 	spnego_key = ERR_PTR(-ENOMEM);
 	description = kzalloc(desc_len, GFP_KERNEL);
 	if (description == NULL)
@@ -168,6 +176,14 @@ cifs_get_spnego_key(struct cifs_ses *sesInfo)
 	dp = description + strlen(description);
 	sprintf(dp, ";pid=0x%x", current->pid);
 
+	if (sesInfo->upcall_target == UPTARGET_MOUNT) {
+		dp = description + strlen(description);
+		sprintf(dp, ";upcall_target=mount");
+	} else {
+		dp = description + strlen(description);
+		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, "");
diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c
index 0cef941e6ea7..e4ca60af7a22 100644
--- a/fs/cifs/cifsfs.c
+++ b/fs/cifs/cifsfs.c
@@ -470,6 +470,30 @@ cifs_show_nls(struct seq_file *s, struct nls_table *cur)
 	unload_nls(def);
 }
 
+static void
+cifs_show_upcall_target(struct seq_file *s, struct cifs_ses *ses)
+{
+	if (ses->upcall_target == UPTARGET_UNSPECIFIED) {
+		seq_puts(s, ",upcall_target=app");
+		return;
+	}
+
+	seq_puts(s, ",upcall_target=");
+
+	switch (ses->upcall_target) {
+	case UPTARGET_APP:
+		seq_puts(s, "app");
+		break;
+	case UPTARGET_MOUNT:
+		seq_puts(s, "mount");
+		break;
+	default:
+		/* shouldn't ever happen */
+		seq_puts(s, "unknown");
+		break;
+	}
+}
+
 /*
  * cifs_show_options() is for displaying mount options in /proc/mounts.
  * Not all settable options are displayed but most of the important
@@ -486,6 +510,7 @@ cifs_show_options(struct seq_file *s, struct dentry *root)
 	seq_show_option(s, "vers", tcon->ses->server->vals->version_string);
 	cifs_show_security(s, tcon->ses);
 	cifs_show_cache_flavor(s, cifs_sb);
+	cifs_show_upcall_target(s, tcon->ses);
 
 	if (tcon->no_lease)
 		seq_puts(s, ",nolease");
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
index 0fbd2e74a78c..b0e9344d1853 100644
--- a/fs/cifs/cifsglob.h
+++ b/fs/cifs/cifsglob.h
@@ -129,6 +129,12 @@ enum securityEnum {
 	Kerberos,		/* Kerberos via SPNEGO */
 };
 
+enum upcall_target_enum {
+	UPTARGET_UNSPECIFIED, /* not specified, defaults to app */
+	UPTARGET_MOUNT, /* upcall to the mount namespace */
+	UPTARGET_APP, /* upcall to the application namespace which did the mount */
+};
+
 struct session_key {
 	unsigned int len;
 	char *response;
@@ -551,6 +557,7 @@ struct smb_vol {
 	umode_t file_mode;
 	umode_t dir_mode;
 	enum securityEnum sectype; /* sectype requested via mnt opts */
+	enum upcall_target_enum upcall_target;
 	bool sign; /* was signing requested via mnt opts? */
 	bool ignore_signature:1;
 	bool retry:1;
@@ -989,6 +996,7 @@ struct cifs_ses {
 	struct session_key auth_key;
 	struct ntlmssp_auth *ntlmssp; /* ciphertext, flags, server challenge */
 	enum securityEnum sectype; /* what security flavor was specified? */
+	enum upcall_target_enum upcall_target; /* what upcall target was specified? */
 	bool sign;		/* is signing required? */
 	bool need_reconnect:1; /* connection reset, uid now invalid */
 	bool domainAuto:1;
diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
index aec67a79cd1f..66e735bfeb8e 100644
--- a/fs/cifs/connect.c
+++ b/fs/cifs/connect.c
@@ -113,6 +113,7 @@ enum {
 	Opt_domain, Opt_srcaddr, Opt_iocharset,
 	Opt_netbiosname, Opt_servern,
 	Opt_ver, Opt_vers, Opt_sec, Opt_cache,
+	Opt_upcall_target,
 
 	/* Mount options to be ignored */
 	Opt_ignore,
@@ -247,6 +248,7 @@ static const match_table_t cifs_mount_option_tokens = {
 	{ Opt_vers, "vers=%s" },
 	{ Opt_sec, "sec=%s" },
 	{ Opt_cache, "cache=%s" },
+	{ Opt_upcall_target, "upcall_target=%s" },
 
 	{ Opt_ignore, "cred" },
 	{ Opt_ignore, "credentials" },
@@ -298,6 +300,18 @@ static const match_table_t cifs_secflavor_tokens = {
 	{ Opt_sec_err, NULL }
 };
 
+enum {
+	Opt_upcall_target_mount,
+	Opt_upcall_target_application,
+	Opt_upcall_target_err
+};
+
+static const match_table_t cifs_upcall_target_tokens = {
+	{ Opt_upcall_target_mount, "mount" },
+	{ Opt_upcall_target_application, "app" },
+	{ Opt_upcall_target_err, NULL }
+};
+
 /* cache flavors */
 enum {
 	Opt_cache_loose,
@@ -1470,6 +1484,29 @@ static int cifs_parse_security_flavors(char *value,
 	return 0;
 }
 
+static int
+cifs_parse_upcall_target(char *value, struct smb_vol *volume_info)
+{
+	substring_t args[MAX_OPT_ARGS];
+
+	volume_info->upcall_target = UPTARGET_UNSPECIFIED;
+
+	switch (match_token(value, cifs_upcall_target_tokens, args)) {
+	case Opt_upcall_target_mount:
+		volume_info->upcall_target = UPTARGET_MOUNT;
+		break;
+	case Opt_upcall_target_application:
+		volume_info->upcall_target = UPTARGET_APP;
+		break;
+
+	default:
+		cifs_dbg(VFS, "bad upcall target: %s\n", value);
+		return 1;
+	}
+
+	return 0;
+}
+
 static int
 cifs_parse_cache_flavor(char *value, struct smb_vol *vol)
 {
@@ -2427,6 +2464,14 @@ cifs_parse_mount_options(const char *mountdata, const char *devname,
 			if (cifs_parse_security_flavors(string, vol) != 0)
 				goto cifs_parse_mount_err;
 			break;
+		case Opt_upcall_target:
+			string = match_strdup(args);
+			if (string == NULL)
+				goto out_nomem;
+
+			if (cifs_parse_upcall_target(string, vol) != 0)
+				goto cifs_parse_mount_err;
+			break;
 		case Opt_cache:
 			string = match_strdup(args);
 			if (string == NULL)
@@ -2491,6 +2536,11 @@ cifs_parse_mount_options(const char *mountdata, const char *devname,
 		}
 	}
 
+	if (vol->multiuser && vol->upcall_target == UPTARGET_MOUNT) {
+		cifs_dbg(VFS, "multiuser mount option not supported with upcalltarget set as 'mount'\n");
+		goto cifs_parse_mount_err;
+	}
+
 	/* set the port that we got earlier */
 	cifs_set_port(dstaddr, port);
 
@@ -3325,6 +3375,26 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb_vol *volume_info)
 
 	ses->sectype = volume_info->sectype;
 	ses->sign = volume_info->sign;
+
+	/*
+	 *Explicitly marking upcall_target mount option for easier handling
+	 * by cifs_spnego.c and eventually cifs.upcall.c
+	 */
+
+	switch (volume_info->upcall_target) {
+	case UPTARGET_UNSPECIFIED: /* default to app */
+	case UPTARGET_APP:
+	        ses->upcall_target = UPTARGET_APP;
+	        break;
+	case UPTARGET_MOUNT:
+	        ses->upcall_target = UPTARGET_MOUNT;
+	        break;
+	default:
+	        // should never happen
+	        ses->upcall_target = UPTARGET_APP;
+	        break;
+	}
+
 	mutex_lock(&ses->session_mutex);
 	rc = cifs_negotiate_protocol(xid, ses);
 	if (!rc)
-- 
2.45.2




More information about the kernel-team mailing list