[RFC PATCH 1/9] smb: client: support security.capability over EAs

Ze Tan <[email protected]>
Newsgroups org.kernel.vger.linux-cifs,org.kernel.vger.linux-kernel
Message-ID <[email protected]>
generic/093 uses setcap and getcap to check that a write clears the file
capability. The CIFS client does not have a security xattr handler, so
these commands cannot set or read security.capability through SMB EAs.

Register a security xattr handler and build the complete EA name for
SET_INFO and QUERY_INFO. Keep security.capability unchanged in EA lists.
Reject other security xattrs.

Signed-off-by: Ze Tan <[email protected]>
---
 fs/smb/client/smb2ops.c | 26 ++++++++++++---
 fs/smb/client/xattr.c   | 70 ++++++++++++++++++++++++++++++++++++++---
 2 files changed, 87 insertions(+), 9 deletions(-)

diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c
index 06e9322a762a..d58185f676a4 100644
--- a/fs/smb/client/smb2ops.c
+++ b/fs/smb/client/smb2ops.c
@@ -14,6 +14,7 @@
 #include <crypto/aead.h>
 #include <linux/fiemap.h>
 #include <linux/folio_queue.h>
+#include <linux/xattr.h>
 #include <uapi/linux/magic.h>
 #include "cifsfs.h"
 #include "cifsglob.h"
@@ -1043,6 +1044,15 @@ static int smb2_query_file_info(const unsigned int xid, struct cifs_tcon *tcon,
 }
 
 #ifdef CONFIG_CIFS_XATTR
+static bool cifs_passthrough(const char *name, size_t name_len)
+{
+	if (name_len == sizeof(XATTR_NAME_CAPS) - 1 &&
+	    !memcmp(name, XATTR_NAME_CAPS, name_len))
+		return true;
+
+	return false;
+}
+
 static ssize_t
 move_smb2_ea_to_cifs(char *dst, size_t dst_size,
 		     struct smb2_file_full_ea_info *src, size_t src_size,
@@ -1085,16 +1095,24 @@ move_smb2_ea_to_cifs(char *dst, size_t dst_size,
 				goto out;
 			}
 		} else {
-			/* 'user.' plus a terminating null */
-			user_name_len = 5 + 1 + name_len;
+			bool passthrough_name;
+
+			passthrough_name = cifs_passthrough(name, name_len);
+			if (passthrough_name)
+				user_name_len = name_len + 1;
+			else
+				/* 'user.' plus a terminating null */
+				user_name_len = 5 + 1 + name_len;
 
 			if (buf_size == 0) {
 				/* skip copy - calc size only */
 				rc += user_name_len;
 			} else if (dst_size >= user_name_len) {
 				dst_size -= user_name_len;
-				memcpy(dst, "user.", 5);
-				dst += 5;
+				if (!passthrough_name) {
+					memcpy(dst, "user.", 5);
+					dst += 5;
+				}
 				memcpy(dst, src->ea_data, name_len);
 				dst += name_len;
 				*dst = 0;
diff --git a/fs/smb/client/xattr.c b/fs/smb/client/xattr.c
index 5091f6c0d7fe..8fa40593bc93 100644
--- a/fs/smb/client/xattr.c
+++ b/fs/smb/client/xattr.c
@@ -36,12 +36,38 @@
 #define SMB3_XATTR_CIFS_NTSD_FULL "system.smb3_ntsd_full" /* owner/DACL/SACL */
 #define SMB3_XATTR_ATTRIB "smb3.dosattrib"  /* full name: user.smb3.dosattrib */
 #define SMB3_XATTR_CREATETIME "smb3.creationtime"  /* user.smb3.creationtime */
-/* BB need to add server (Samba e.g) support for security and trusted prefix */
-
-enum { XATTR_USER, XATTR_CIFS_ACL, XATTR_ACL_ACCESS, XATTR_ACL_DEFAULT,
+enum { XATTR_USER, XATTR_SECURITY,
+	XATTR_CIFS_ACL, XATTR_ACL_ACCESS, XATTR_ACL_DEFAULT,
 	XATTR_CIFS_NTSD_SACL, XATTR_CIFS_NTSD_OWNER,
 	XATTR_CIFS_NTSD, XATTR_CIFS_NTSD_FULL };
 
+static int cifs_build_ea_name(int xattr_flag, const char *name, char *ea_name,
+			      size_t ea_name_size)
+{
+	size_t name_len;
+
+	switch (xattr_flag) {
+	case XATTR_SECURITY:
+		/*
+		 * Only security.capability has native Linux security xattr
+		 * semantics on POSIX-capable servers.
+		 */
+		if (strcmp(name, XATTR_CAPS_SUFFIX))
+			return -EOPNOTSUPP;
+		name_len = strlen(name);
+		if (ea_name_size <= XATTR_SECURITY_PREFIX_LEN ||
+		    name_len > ea_name_size - XATTR_SECURITY_PREFIX_LEN - 1)
+			return -ERANGE;
+		memcpy(ea_name, XATTR_SECURITY_PREFIX,
+		       XATTR_SECURITY_PREFIX_LEN);
+		memcpy(ea_name + XATTR_SECURITY_PREFIX_LEN, name,
+		       name_len + 1);
+		return XATTR_SECURITY_PREFIX_LEN + name_len;
+	default:
+		return -EOPNOTSUPP;
+	}
+}
+
 static int cifs_attrib_set(unsigned int xid, struct cifs_tcon *pTcon,
 			   struct inode *inode, const char *full_path,
 			   const void *value, size_t size)
@@ -104,6 +130,8 @@ static int cifs_xattr_set(const struct xattr_handler *handler,
 	struct cifs_tcon *pTcon;
 	const char *full_path;
 	void *page;
+	char ea_name[XATTR_NAME_MAX + 1];
+	const char *server_ea_name = name;
 
 	tlink = cifs_sb_tlink(cifs_sb);
 	if (IS_ERR(tlink))
@@ -131,6 +159,16 @@ static int cifs_xattr_set(const struct xattr_handler *handler,
 	}
 
 	switch (handler->flags) {
+	case XATTR_SECURITY:
+		rc = cifs_build_ea_name(handler->flags, name, ea_name,
+					sizeof(ea_name));
+		if (rc < 0)
+			goto out;
+		server_ea_name = ea_name;
+		cifs_dbg(FYI, "%s: setting security xattr %s\n",
+			 __func__, name);
+		goto set_ea;
+
 	case XATTR_USER:
 		cifs_dbg(FYI, "%s:setting user xattr %s\n", __func__, name);
 		if ((strcmp(name, CIFS_XATTR_ATTRIB) == 0) ||
@@ -149,12 +187,13 @@ static int cifs_xattr_set(const struct xattr_handler *handler,
 			break;
 		}
 
+set_ea:
 		if (cifs_sb_flags(cifs_sb) & CIFS_MOUNT_NO_XATTR)
 			goto out;
 
 		if (pTcon->ses->server->ops->set_EA) {
 			rc = pTcon->ses->server->ops->set_EA(xid, pTcon,
-				full_path, name, value, (__u16)size,
+				full_path, server_ea_name, value, (__u16)size,
 				cifs_sb->local_nls, cifs_sb);
 			if (rc == 0)
 				inode_set_ctime_current(inode);
@@ -280,6 +319,8 @@ static int cifs_xattr_get(const struct xattr_handler *handler,
 	struct cifs_tcon *pTcon;
 	const char *full_path;
 	void *page;
+	char ea_name[XATTR_NAME_MAX + 1];
+	const char *server_ea_name = name;
 
 	tlink = cifs_sb_tlink(cifs_sb);
 	if (IS_ERR(tlink))
@@ -297,6 +338,16 @@ static int cifs_xattr_get(const struct xattr_handler *handler,
 
 	/* return alt name if available as pseudo attr */
 	switch (handler->flags) {
+	case XATTR_SECURITY:
+		rc = cifs_build_ea_name(handler->flags, name, ea_name,
+					sizeof(ea_name));
+		if (rc < 0)
+			goto out;
+		server_ea_name = ea_name;
+		cifs_dbg(FYI, "%s: querying security xattr %s\n",
+			 __func__, name);
+		goto query_ea;
+
 	case XATTR_USER:
 		cifs_dbg(FYI, "%s:querying user xattr %s\n", __func__, name);
 		if ((strcmp(name, CIFS_XATTR_ATTRIB) == 0) ||
@@ -309,12 +360,13 @@ static int cifs_xattr_get(const struct xattr_handler *handler,
 			break;
 		}
 
+query_ea:
 		if (cifs_sb_flags(cifs_sb) & CIFS_MOUNT_NO_XATTR)
 			goto out;
 
 		if (pTcon->ses->server->ops->query_all_EAs)
 			rc = pTcon->ses->server->ops->query_all_EAs(xid, pTcon,
-				full_path, name, value, size, cifs_sb);
+				full_path, server_ea_name, value, size, cifs_sb);
 		break;
 
 	case XATTR_CIFS_ACL:
@@ -438,6 +490,13 @@ static const struct xattr_handler cifs_user_xattr_handler = {
 	.set = cifs_xattr_set,
 };
 
+static const struct xattr_handler cifs_security_xattr_handler = {
+	.prefix = XATTR_SECURITY_PREFIX,
+	.flags = XATTR_SECURITY,
+	.get = cifs_xattr_get,
+	.set = cifs_xattr_set,
+};
+
 /* os2.* attributes are treated like user.* attributes */
 static const struct xattr_handler cifs_os2_xattr_handler = {
 	.prefix = XATTR_OS2_PREFIX,
@@ -522,6 +581,7 @@ static const struct xattr_handler smb3_ntsd_full_xattr_handler = {
 
 const struct xattr_handler * const cifs_xattr_handlers[] = {
 	&cifs_user_xattr_handler,
+	&cifs_security_xattr_handler,
 	&cifs_os2_xattr_handler,
 	&cifs_cifs_acl_xattr_handler,
 	&smb3_acl_xattr_handler, /* alias for above since avoiding "cifs" */
-- 
2.43.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.