[RFC PATCH 6/9] ksmbd: support security.capability EAs
Ze Tan <[email protected]> Wed, 15 Jul 2026 15:48:47 +0800
| Newsgroups | gmane.linux.kernel,gmane.linux.kernel.cifs,gmane.network.samba.internals |
|---|---|
| Message-ID | <[email protected]> |
generic/093 uses setcap and getcap to check that a write clears the file capability. ksmbd adds the user prefix to every SMB EA name, so setcap stores security.capability as user.security.capability and getcap cannot read it as a file capability. When SMB3 POSIX extensions are available, map security.capability to the same backing xattr name. Use this mapping for set, get, and list operations. Signed-off-by: Ze Tan <[email protected]> --- fs/smb/server/smb2pdu.c | 71 +++++++++++++++++++++++++++++++---------- 1 file changed, 55 insertions(+), 16 deletions(-) diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index 757dee9afaa4..255300501af3 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -2734,9 +2734,35 @@ static bool smb2_is_private_ea(const char *name, size_t name_len) return false; } -static int ksmbd_map_ea_name_to_xattr(const char *ea_name, - size_t ea_name_len, char *attr_name) +static bool ksmbd_is_security_capability_ea_name(const char *name, + size_t name_len) { + return name_len == sizeof(XATTR_NAME_CAPS) - 1 && + !strncmp(name, XATTR_NAME_CAPS, name_len); +} + +static bool ksmbd_is_posix_ea_name(const char *name, size_t name_len) +{ + if (ksmbd_is_security_capability_ea_name(name, name_len)) + return true; + + return false; +} + +static int ksmbd_map_ea_name_to_xattr(bool posix_extensions, + const char *ea_name, size_t ea_name_len, + char *attr_name) +{ + if (posix_extensions && + ksmbd_is_posix_ea_name(ea_name, ea_name_len)) { + if (ea_name_len > XATTR_NAME_MAX) + return -EINVAL; + + memcpy(attr_name, ea_name, ea_name_len); + attr_name[ea_name_len] = '\0'; + return ea_name_len; + } + if (ea_name_len > XATTR_NAME_MAX - XATTR_USER_PREFIX_LEN) return -EINVAL; @@ -2746,20 +2772,26 @@ static int ksmbd_map_ea_name_to_xattr(const char *ea_name, return XATTR_USER_PREFIX_LEN + ea_name_len; } -static bool ksmbd_is_visible_ea_name(const char *name, const char **ea_name, - size_t *ea_name_len) +static bool ksmbd_is_visible_ea_name(bool posix_extensions, const char *name, + const char **ea_name, size_t *ea_name_len) { size_t name_len = strlen(name); - if (strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN)) - return false; + if (!strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN)) { + *ea_name = name + XATTR_USER_PREFIX_LEN; + *ea_name_len = name_len - XATTR_USER_PREFIX_LEN; - *ea_name = name + XATTR_USER_PREFIX_LEN; - *ea_name_len = name_len - XATTR_USER_PREFIX_LEN; + if (smb2_is_private_ea(*ea_name, *ea_name_len)) + return false; + + return true; + } - if (smb2_is_private_ea(*ea_name, *ea_name_len)) + if (!posix_extensions || !ksmbd_is_posix_ea_name(name, name_len)) return false; + *ea_name = name; + *ea_name_len = name_len; return true; } @@ -2770,11 +2802,13 @@ static bool ksmbd_is_visible_ea_name(const char *name, const char **ea_name, * @buf_len: set info command buffer length * @path: dentry path for get ea * @get_write: get write access to a mount + * @posix_extensions: client negotiated SMB3 POSIX extensions * * Return: 0 on success, otherwise error */ static int smb2_set_ea(struct smb2_ea_info *eabuf, unsigned int buf_len, - const struct path *path, bool get_write) + const struct path *path, bool get_write, + bool posix_extensions) { struct mnt_idmap *idmap = mnt_idmap(path->mnt); char *attr_name = NULL, *value; @@ -2799,7 +2833,8 @@ static int smb2_set_ea(struct smb2_ea_info *eabuf, unsigned int buf_len, le16_to_cpu(eabuf->EaValueLength), le32_to_cpu(eabuf->NextEntryOffset)); - attr_name_len = ksmbd_map_ea_name_to_xattr(eabuf->name, + attr_name_len = ksmbd_map_ea_name_to_xattr(posix_extensions, + eabuf->name, eabuf->EaNameLength, attr_name); if (attr_name_len < 0) { @@ -3886,7 +3921,8 @@ int smb2_open(struct ksmbd_work *work) rc = smb2_set_ea(&ea_buf->ea, le32_to_cpu(ea_buf->ccontext.DataLength), - &path, false); + &path, false, + work->tcon->posix_extensions); if (rc == -EOPNOTSUPP) rc = 0; else if (rc) @@ -5580,7 +5616,7 @@ static int smb2_get_ea(struct ksmbd_work *work, struct ksmbd_file *fp, } path = &fp->filp->f_path; - /* single EA entry is requested with given user.* name */ + /* single EA entry is requested with a client-visible EA name */ if (req->InputBufferLength) { if (le32_to_cpu(req->InputBufferLength) <= sizeof(struct smb2_ea_info_req)) @@ -5633,14 +5669,16 @@ static int smb2_get_ea(struct ksmbd_work *work, struct ksmbd_file *fp, ksmbd_debug(SMB, "%s, len %d\n", name, name_len); idx += name_len + 1; - if (!ksmbd_is_visible_ea_name(name, &ea_name, + if (!ksmbd_is_visible_ea_name(work->tcon->posix_extensions, name, + &ea_name, &visible_name_len)) continue; name_len = visible_name_len; if (req->InputBufferLength && - strncmp(ea_name, ea_req->name, ea_req->EaNameLength)) + (name_len != ea_req->EaNameLength || + strncmp(ea_name, ea_req->name, ea_req->EaNameLength))) continue; ptr = eainfo->name + name_len + 1; @@ -7618,7 +7656,8 @@ static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp, return -EMSGSIZE; return smb2_set_ea((struct smb2_ea_info *)buffer, - buf_len, &fp->filp->f_path, true); + buf_len, &fp->filp->f_path, true, + work->tcon->posix_extensions); } case FILE_POSITION_INFORMATION: { -- 2.43.0