[PATCH 07/15] ksmbd: query security.capability on POSIX EA handles
Ze Tan <[email protected]> Fri, 24 Jul 2026 18:40:01 +0800
| Newsgroups | org.kernel.vger.linux-cifs |
|---|---|
| Message-ID | <e24e14bf49f1749d8caa863980263b7307441f85.1784888897.git.tanze@kylinos.cn> |
Treat security.capability as a native xattr only for a file handle opened with an SMB3 POSIX create context. Non-POSIX handles retain the existing user namespace mapping. Allow native queries when the mapped fsuid is root or the tree connection has KSMBD_TREE_CONN_FLAG_ADMIN_ACCOUNT. In native mode, return security.capability without a prefix and hide a colliding user.security.capability backing xattr. Reject embedded NUL bytes before classifying a requested SMB EA name. Signed-off-by: Ze Tan <[email protected]> --- fs/smb/server/smb2pdu.c | 80 ++++++++++++++++++++++++++++++++++------- 1 file changed, 68 insertions(+), 12 deletions(-) diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index 1f0872bf11ae..7fdc6df6310a 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -2643,6 +2643,43 @@ static noinline int create_smb2_pipe(struct ksmbd_work *work) return err; } +static const char * const ksmbd_native_xattr_names[] = { + XATTR_NAME_CAPS, +}; + +static bool ksmbd_native_xattrs_allowed(struct ksmbd_work *work) +{ + return uid_eq(current_fsuid(), GLOBAL_ROOT_UID) || + test_tree_conn_flag(work->tcon, + KSMBD_TREE_CONN_FLAG_ADMIN_ACCOUNT); +} + +static int ksmbd_check_native_xattr(struct ksmbd_work *work, + const char *name, size_t name_len) +{ + bool native = false; + size_t i; + + if (memchr(name, '\0', name_len)) + return -EINVAL; + + for (i = 0; i < ARRAY_SIZE(ksmbd_native_xattr_names); i++) { + size_t xattr_len = strlen(ksmbd_native_xattr_names[i]); + + if (name_len == xattr_len && + !memcmp(name, ksmbd_native_xattr_names[i], name_len)) { + native = true; + break; + } + } + + if (!native) + return 0; + if (!ksmbd_native_xattrs_allowed(work)) + return -EACCES; + return 1; +} + static int ksmbd_map_ea_name_to_xattr(const char *ea_name, size_t ea_name_len, char *attr_name) { @@ -2655,24 +2692,36 @@ 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(struct ksmbd_work *work, + bool native_xattrs, 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 (!strncmp(*ea_name, STREAM_PREFIX, STREAM_PREFIX_LEN)) + return false; - if (!strncmp(*ea_name, STREAM_PREFIX, STREAM_PREFIX_LEN)) - return false; + if (!strncmp(*ea_name, DOS_ATTRIBUTE_PREFIX, + DOS_ATTRIBUTE_PREFIX_LEN)) + return false; - if (!strncmp(*ea_name, DOS_ATTRIBUTE_PREFIX, - DOS_ATTRIBUTE_PREFIX_LEN)) + if (native_xattrs && + ksmbd_check_native_xattr(work, *ea_name, *ea_name_len)) + return false; + + return true; + } + + if (!native_xattrs || + ksmbd_check_native_xattr(work, name, name_len) <= 0) return false; + *ea_name = name; + *ea_name_len = name_len; return true; } @@ -5181,7 +5230,6 @@ 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 */ if (req->InputBufferLength) { if (le32_to_cpu(req->InputBufferLength) <= sizeof(struct smb2_ea_info_req)) @@ -5194,6 +5242,13 @@ static int smb2_get_ea(struct ksmbd_work *work, struct ksmbd_file *fp, offsetof(struct smb2_ea_info_req, name) + ea_req->EaNameLength) return -EINVAL; + + if (fp->is_posix_ctxt) { + rc = ksmbd_check_native_xattr(work, ea_req->name, + ea_req->EaNameLength); + if (rc < 0) + return rc; + } } else { /* need to send all EAs, if no specific EA is requested*/ if (le32_to_cpu(req->Flags) & SL_RETURN_SINGLE_ENTRY) @@ -5234,7 +5289,8 @@ 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, fp->is_posix_ctxt, name, + &ea_name, &visible_name_len)) continue; -- 2.43.0