mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-08-27 10:39:02 -04:00
Merge tag 'cifs-fixes-7.3-rc1' of https://git.manguebit.org/linux
Pull smb client updates from Paulo Alcantara: - clear sensitive data after use (stack and heap cryptographic keys/hashes) - file size and cache synchronization fixes (fscache cookie serialization and truncation handling) - protocol validation and buffer safety fixes (prevent OOB access and loff_t underflow) - metadata and POSIX attribute fixes (proper hard-link counts and setuid/setgid stripping) - DFS cache and unmount fixes (prevent target-hint UAF and unmount hangs) - general client improvements (fix read request leaks, stats loops, handle servers that don't support O_TMPFILE) * tag 'cifs-fixes-7.3-rc1' of https://git.manguebit.org/linux: (33 commits) cifs: fix loff_t underflow in cifs_remap_file_range() when len == 0 smb: client: reject a tree connect response whose byte count is too small cifs: call pagecache_isize_extended() in cifs_setsize() when extending smb: client: fix copy-paste error in WSL EA length accounting for $LXDEV smb: client: remove redundant NULL check before kfree() smb: client: restore the data_offset bound in is_valid_oplock_break() cifs: clear tcon after cifsFileInfo_put() in cifs_file_set_size() smb: client: Avoid leaking sensitive data to the heap in connect.c smb: client: Clear sensitive stack data in smb1encrypt.c smb: client: Clear sensitive stack data in cifsencrypt.c smb: client: Clear sensitive stack and heap data in smb2ops.c smb: client: Clear sensitive stack data in smb2transport.c Revert "cifs: remove all cifs files before kill super" smb: client: fix use-before-check of ReparseDataLength in reparse_buf_ptr() smb: client: fix ALIGN() overflow in symlink_data() error context loop smb: client: simplify __build_path_from_dentry_optional_prefix() smb: client: fix UAF and buffer leak in cifs_check_trans2() for malformed secondary T2 smb: client: fix OOB read/write from unvalidated DataOffset in coalesce_t2() smb/client: decode reparse metadata using its payload type smb/client: preserve open info type across compound queries ...
This commit is contained in:
@@ -754,7 +754,7 @@ static ssize_t cifs_stats_proc_write(struct file *file,
|
||||
atomic_set(&server->smb2slowcmd[i], 0);
|
||||
server->time_per_cmd[i] = 0;
|
||||
server->slowest_cmd[i] = 0;
|
||||
server->fastest_cmd[0] = 0;
|
||||
server->fastest_cmd[i] = 0;
|
||||
}
|
||||
#endif /* CONFIG_CIFS_STATS2 */
|
||||
list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) {
|
||||
|
||||
@@ -249,12 +249,13 @@ static int calc_ntlmv2_hash(struct cifs_ses *ses, char *ntlmv2_hash,
|
||||
E_md4hash(ses->password, nt_hash, nls_cp);
|
||||
|
||||
hmac_md5_init_usingrawkey(&hmac_ctx, nt_hash, CIFS_NTHASH_SIZE);
|
||||
memzero_explicit(nt_hash, sizeof(nt_hash));
|
||||
|
||||
/* convert ses->user_name to unicode */
|
||||
len = ses->user_name ? strlen(ses->user_name) : 0;
|
||||
user = kmalloc(2 + (len * 2), GFP_KERNEL);
|
||||
if (user == NULL)
|
||||
return -ENOMEM;
|
||||
goto out_nomem;
|
||||
|
||||
if (len) {
|
||||
len = cifs_strtoUTF16(user, ses->user_name, len, nls_cp);
|
||||
@@ -272,7 +273,7 @@ static int calc_ntlmv2_hash(struct cifs_ses *ses, char *ntlmv2_hash,
|
||||
|
||||
domain = kmalloc(2 + (len * 2), GFP_KERNEL);
|
||||
if (domain == NULL)
|
||||
return -ENOMEM;
|
||||
goto out_nomem;
|
||||
|
||||
len = cifs_strtoUTF16((__le16 *)domain, ses->domainName, len,
|
||||
nls_cp);
|
||||
@@ -284,7 +285,7 @@ static int calc_ntlmv2_hash(struct cifs_ses *ses, char *ntlmv2_hash,
|
||||
|
||||
server = kmalloc(2 + (len * 2), GFP_KERNEL);
|
||||
if (server == NULL)
|
||||
return -ENOMEM;
|
||||
goto out_nomem;
|
||||
|
||||
len = cifs_strtoUTF16((__le16 *)server, ses->ip_addr, len, nls_cp);
|
||||
hmac_md5_update(&hmac_ctx, (const u8 *)server, 2 * len);
|
||||
@@ -293,6 +294,10 @@ static int calc_ntlmv2_hash(struct cifs_ses *ses, char *ntlmv2_hash,
|
||||
|
||||
hmac_md5_final(&hmac_ctx, ntlmv2_hash);
|
||||
return 0;
|
||||
|
||||
out_nomem:
|
||||
memzero_explicit(&hmac_ctx, sizeof(hmac_ctx));
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
static void CalcNTLMv2_response(const struct cifs_ses *ses, char *ntlmv2_hash)
|
||||
@@ -463,6 +468,7 @@ setup_ntlmv2_rsp(struct cifs_ses *ses, const struct nls_table *nls_cp)
|
||||
rc = 0;
|
||||
unlock:
|
||||
cifs_server_unlock(ses->server);
|
||||
memzero_explicit(ntlmv2_hash, sizeof(ntlmv2_hash));
|
||||
setup_ntlmv2_rsp_ret:
|
||||
kfree_sensitive(tiblob);
|
||||
|
||||
|
||||
@@ -1413,8 +1413,19 @@ static loff_t cifs_remap_file_range(struct file *src_file, loff_t off,
|
||||
*/
|
||||
lock_two_nondirectories(target_inode, src_inode);
|
||||
|
||||
if (len == 0)
|
||||
len = src_inode->i_size - off;
|
||||
if (len == 0) {
|
||||
loff_t src_size = i_size_read(src_inode);
|
||||
|
||||
if (off > src_size) {
|
||||
rc = -EINVAL;
|
||||
goto unlock;
|
||||
}
|
||||
len = src_size - off;
|
||||
if (!len) {
|
||||
rc = 0;
|
||||
goto unlock;
|
||||
}
|
||||
}
|
||||
|
||||
cifs_dbg(FYI, "clone range\n");
|
||||
|
||||
@@ -1466,11 +1477,7 @@ static loff_t cifs_remap_file_range(struct file *src_file, loff_t off,
|
||||
if (target_tcon->ses->server->ops->duplicate_extents) {
|
||||
rc = target_tcon->ses->server->ops->duplicate_extents(xid,
|
||||
smb_file_src, smb_file_target, off, len, destoff);
|
||||
if (rc == 0 && new_size > i_size) {
|
||||
truncate_setsize(target_inode, new_size);
|
||||
fscache_resize_cookie(cifs_inode_cookie(target_inode),
|
||||
new_size);
|
||||
} else if (rc == -EOPNOTSUPP) {
|
||||
if (rc == -EOPNOTSUPP) {
|
||||
/*
|
||||
* copy_file_range syscall man page indicates EINVAL
|
||||
* is returned e.g when "fd_in and fd_out refer to the
|
||||
|
||||
@@ -147,6 +147,7 @@ ssize_t cifs_file_copychunk_range(unsigned int xid, struct file *src_file,
|
||||
|
||||
long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg);
|
||||
void cifs_setsize(struct inode *inode, loff_t offset);
|
||||
void cifs_resize_file_locked(struct inode *inode, loff_t offset);
|
||||
|
||||
struct fs_context;
|
||||
struct smb3_fs_context;
|
||||
|
||||
@@ -615,6 +615,11 @@ CIFSTCon(const unsigned int xid, struct cifs_ses *ses,
|
||||
tcon->tid = smb_buffer_response->Tid;
|
||||
bcc_ptr = pByteArea(smb_buffer_response);
|
||||
bytes_left = get_bcc(smb_buffer_response);
|
||||
if (bytes_left < 2) {
|
||||
rc = smb_EIO2(smb_eio_trace_tcon_bcc_too_small,
|
||||
bytes_left, 2);
|
||||
goto out;
|
||||
}
|
||||
length = strnlen(bcc_ptr, bytes_left - 2);
|
||||
if (smb_buffer->Flags2 & SMBFLG2_UNICODE)
|
||||
is_unicode = true;
|
||||
@@ -670,6 +675,7 @@ CIFSTCon(const unsigned int xid, struct cifs_ses *ses,
|
||||
reset_cifs_unix_caps(xid, tcon, NULL, NULL);
|
||||
}
|
||||
}
|
||||
out:
|
||||
cifs_buf_release(smb_buffer);
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -1143,7 +1143,7 @@ clean_demultiplex_info(struct TCP_Server_Info *server)
|
||||
put_net(cifs_net_ns(server));
|
||||
kfree(server->leaf_fullpath);
|
||||
kfree(server->hostname);
|
||||
kfree(server);
|
||||
kfree_sensitive(server);
|
||||
|
||||
length = atomic_dec_return(&tcpSesAllocCount);
|
||||
if (length > 0)
|
||||
@@ -4003,9 +4003,6 @@ cifs_umount(struct cifs_sb_info *cifs_sb)
|
||||
}
|
||||
spin_unlock(&cifs_sb->tlink_tree_lock);
|
||||
|
||||
flush_workqueue(serverclose_wq);
|
||||
flush_workqueue(fileinfo_put_wq);
|
||||
|
||||
kfree(cifs_sb->prepath);
|
||||
call_rcu(&cifs_sb->rcu, delayed_free);
|
||||
}
|
||||
|
||||
@@ -122,6 +122,8 @@ static inline void free_tgts(struct cache_entry *ce)
|
||||
kfree(t->name);
|
||||
kfree(t);
|
||||
}
|
||||
|
||||
WRITE_ONCE(ce->tgthint, NULL);
|
||||
}
|
||||
|
||||
static inline void flush_cache_ent(struct cache_entry *ce)
|
||||
@@ -869,13 +871,22 @@ int dfs_cache_find(const unsigned int xid, struct cifs_ses *ses, const struct nl
|
||||
goto out_free_path;
|
||||
}
|
||||
|
||||
if (ref)
|
||||
rc = setup_referral(path, ce, ref, get_tgt_name(ce));
|
||||
else
|
||||
if (ref) {
|
||||
char *target = get_tgt_name(ce);
|
||||
|
||||
if (IS_ERR(target)) {
|
||||
rc = PTR_ERR(target);
|
||||
goto out_unlock;
|
||||
}
|
||||
rc = setup_referral(path, ce, ref, target);
|
||||
} else {
|
||||
rc = 0;
|
||||
}
|
||||
|
||||
if (!rc && tgt_list)
|
||||
rc = get_targets(ce, tgt_list);
|
||||
|
||||
out_unlock:
|
||||
up_read(&htable_rw_lock);
|
||||
|
||||
out_free_path:
|
||||
@@ -915,10 +926,17 @@ int dfs_cache_noreq_find(const char *path, struct dfs_info3_param *ref,
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
if (ref)
|
||||
rc = setup_referral(path, ce, ref, get_tgt_name(ce));
|
||||
else
|
||||
if (ref) {
|
||||
char *target = get_tgt_name(ce);
|
||||
|
||||
if (IS_ERR(target)) {
|
||||
rc = PTR_ERR(target);
|
||||
goto out_unlock;
|
||||
}
|
||||
rc = setup_referral(path, ce, ref, target);
|
||||
} else {
|
||||
rc = 0;
|
||||
}
|
||||
if (!rc && tgt_list)
|
||||
rc = get_targets(ce, tgt_list);
|
||||
|
||||
@@ -959,7 +977,8 @@ void dfs_cache_noreq_update_tgthint(const char *path, const struct dfs_cache_tgt
|
||||
|
||||
t = READ_ONCE(ce->tgthint);
|
||||
|
||||
if (unlikely(!strcasecmp(it->it_name, t->name)))
|
||||
/* Check 't' in case ce->tgthint was cleared by free_tgts() */
|
||||
if (t && unlikely(!strcasecmp(it->it_name, t->name)))
|
||||
goto out_unlock;
|
||||
|
||||
list_for_each_entry(t, &ce->tlist, list) {
|
||||
|
||||
@@ -115,11 +115,7 @@ char *__build_path_from_dentry_optional_prefix(struct dentry *direntry, void *pa
|
||||
}
|
||||
if (dirsep != '/') {
|
||||
/* BB test paths to Windows with '/' in the midst of prepath */
|
||||
char *p;
|
||||
|
||||
for (p = s; *p; p++)
|
||||
if (*p == '/')
|
||||
*p = dirsep;
|
||||
strreplace(s, '/', dirsep);
|
||||
}
|
||||
if (dfsplen) {
|
||||
s -= dfsplen;
|
||||
@@ -1138,6 +1134,8 @@ int cifs_tmpfile(struct mnt_idmap *idmap, struct inode *dir,
|
||||
} while (unlikely(rc == -EEXIST) && ++retries < max_retries);
|
||||
|
||||
if (rc) {
|
||||
if (rc == -ENOENT)
|
||||
rc = -EOPNOTSUPP;
|
||||
cifs_del_pending_open(&open);
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -1016,6 +1016,7 @@ static int cifs_do_truncate(const unsigned int xid, struct dentry *dentry)
|
||||
if (!rc) {
|
||||
netfs_resize_file(&cinode->netfs, 0, true);
|
||||
cifs_setsize(inode, 0);
|
||||
cifs_invalidate_cache(inode, 0);
|
||||
}
|
||||
}
|
||||
if (cfile)
|
||||
|
||||
@@ -1215,7 +1215,7 @@ static int reparse_info_to_fattr(struct cifs_open_info_data *data,
|
||||
break;
|
||||
case IO_REPARSE_TAG_INTERNAL:
|
||||
rc = 0;
|
||||
if (le32_to_cpu(data->fi.Attributes) & ATTR_DIRECTORY) {
|
||||
if (cifs_open_data_attrs(data) & ATTR_DIRECTORY) {
|
||||
cifs_create_junction_fattr(fattr, sb);
|
||||
goto out;
|
||||
}
|
||||
@@ -1239,7 +1239,7 @@ static int reparse_info_to_fattr(struct cifs_open_info_data *data,
|
||||
*/
|
||||
if (rc == -EOPNOTSUPP &&
|
||||
IS_REPARSE_TAG_NAME_SURROGATE(data->reparse.tag) &&
|
||||
(le32_to_cpu(data->fi.Attributes) & ATTR_DIRECTORY)) {
|
||||
(cifs_open_data_attrs(data) & ATTR_DIRECTORY)) {
|
||||
rc = 0;
|
||||
cifs_create_junction_fattr(fattr, sb);
|
||||
goto out;
|
||||
@@ -1257,13 +1257,14 @@ static int reparse_info_to_fattr(struct cifs_open_info_data *data,
|
||||
}
|
||||
|
||||
if (data->reparse.tag == IO_REPARSE_TAG_SYMLINK && !rc) {
|
||||
bool directory = le32_to_cpu(data->fi.Attributes) & ATTR_DIRECTORY;
|
||||
bool directory = cifs_open_data_attrs(data) & ATTR_DIRECTORY;
|
||||
|
||||
rc = smb2_fix_symlink_target_type(&data->symlink_target, directory, cifs_sb);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (tcon->posix_extensions)
|
||||
if (data->contains_posix_file_info)
|
||||
smb311_posix_info_to_fattr(fattr, data, sb);
|
||||
else
|
||||
cifs_open_info_to_fattr(fattr, data, sb);
|
||||
@@ -2655,11 +2656,8 @@ cifs_rename2(struct mnt_idmap *idmap, struct inode *source_dir,
|
||||
if (d_really_is_positive(target_dentry)) {
|
||||
if (!rc) {
|
||||
struct inode *inode = d_inode(target_dentry);
|
||||
/*
|
||||
* Samba and ksmbd servers allow renaming a target
|
||||
* directory that is open, so make sure to update
|
||||
* ->i_nlink and then mark it as delete pending.
|
||||
*/
|
||||
|
||||
/* Update the target link count after rename. */
|
||||
if (S_ISDIR(inode->i_mode)) {
|
||||
drop_cached_dir_by_name(xid, tcon, to_name, cifs_sb);
|
||||
spin_lock(&inode->i_lock);
|
||||
@@ -2670,6 +2668,10 @@ cifs_rename2(struct mnt_idmap *idmap, struct inode *source_dir,
|
||||
CIFS_I(inode)->time = 0; /* force reval */
|
||||
inode_set_ctime_current(inode);
|
||||
inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode));
|
||||
} else {
|
||||
cifs_mark_open_handles_for_deleted_file(inode, to_name);
|
||||
cifs_drop_nlink(inode);
|
||||
inode_set_ctime_current(inode);
|
||||
}
|
||||
} else if (rc == -EACCES || rc == -EEXIST) {
|
||||
/*
|
||||
@@ -3064,9 +3066,27 @@ void cifs_setsize(struct inode *inode, loff_t offset)
|
||||
inode->i_blocks = blocks;
|
||||
spin_unlock(&inode->i_lock);
|
||||
inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode));
|
||||
if (offset > old_size)
|
||||
pagecache_isize_extended(inode, old_size, offset);
|
||||
truncate_pagecache(inode, offset);
|
||||
netfs_wait_for_outstanding_io(inode);
|
||||
fscache_resize_cookie(cifs_inode_cookie(inode), offset);
|
||||
}
|
||||
|
||||
void cifs_resize_file_locked(struct inode *inode, loff_t offset)
|
||||
{
|
||||
struct fscache_cookie *cookie = cifs_inode_cookie(inode);
|
||||
|
||||
lockdep_assert_held_write(&inode->i_rwsem);
|
||||
|
||||
netfs_resize_file(netfs_inode(inode), offset, true);
|
||||
cifs_setsize(inode, offset);
|
||||
|
||||
if (!cookie)
|
||||
return;
|
||||
|
||||
fscache_use_cookie(cookie, true);
|
||||
fscache_resize_cookie(cookie, offset);
|
||||
cifs_fscache_unuse_inode_cookie(inode, true);
|
||||
}
|
||||
|
||||
int cifs_file_set_size(const unsigned int xid, struct dentry *dentry,
|
||||
@@ -3107,6 +3127,7 @@ int cifs_file_set_size(const unsigned int xid, struct dentry *dentry,
|
||||
size, false);
|
||||
cifs_dbg(FYI, "%s: set_file_size: rc = %d\n", __func__, rc);
|
||||
cifsFileInfo_put(open_file);
|
||||
tcon = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3132,10 +3153,8 @@ int cifs_file_set_size(const unsigned int xid, struct dentry *dentry,
|
||||
cifs_put_tlink(tlink);
|
||||
|
||||
set_size_out:
|
||||
if (rc == 0) {
|
||||
netfs_resize_file(&cifsInode->netfs, size, true);
|
||||
cifs_setsize(inode, size);
|
||||
}
|
||||
if (rc == 0)
|
||||
cifs_resize_file_locked(inode, size);
|
||||
|
||||
return rc;
|
||||
}
|
||||
@@ -3221,9 +3240,13 @@ cifs_setattr_unix(struct dentry *direntry, struct iattr *attrs)
|
||||
attrs->ia_valid &= ~(ATTR_CTIME | ATTR_MTIME);
|
||||
}
|
||||
|
||||
/* skip mode change if it's just for clearing setuid/setgid */
|
||||
if (attrs->ia_valid & (ATTR_KILL_SUID|ATTR_KILL_SGID))
|
||||
attrs->ia_valid &= ~ATTR_MODE;
|
||||
/*
|
||||
* This function is only called when Unix extensions are in effect,
|
||||
* so the mode is always sent to and stored on the server. Do not
|
||||
* skip the mode change when clearing setuid/setgid bits: dropping
|
||||
* ATTR_MODE here would leave those bits set on the server after a
|
||||
* write, which is a security issue.
|
||||
*/
|
||||
|
||||
args = kmalloc_obj(*args);
|
||||
if (args == NULL) {
|
||||
@@ -3296,13 +3319,6 @@ cifs_setattr_unix(struct dentry *direntry, struct iattr *attrs)
|
||||
if (rc)
|
||||
goto out;
|
||||
|
||||
if ((attrs->ia_valid & ATTR_SIZE) &&
|
||||
attrs->ia_size != i_size_read(inode)) {
|
||||
truncate_setsize(inode, attrs->ia_size);
|
||||
netfs_resize_file(&cifsInode->netfs, attrs->ia_size, true);
|
||||
fscache_resize_cookie(cifs_inode_cookie(inode), attrs->ia_size);
|
||||
}
|
||||
|
||||
setattr_copy(&nop_mnt_idmap, inode, attrs);
|
||||
mark_inode_dirty(inode);
|
||||
|
||||
@@ -3432,8 +3448,23 @@ cifs_setattr_nounix(struct dentry *direntry, struct iattr *attrs)
|
||||
attrs->ia_valid &= ~(ATTR_UID | ATTR_GID);
|
||||
}
|
||||
|
||||
/* skip mode change if it's just for clearing setuid/setgid */
|
||||
if (attrs->ia_valid & (ATTR_KILL_SUID|ATTR_KILL_SGID))
|
||||
/*
|
||||
* Skip the mode change if it is only being done to clear the
|
||||
* setuid/setgid bits *and* the mode is emulated via the DOS
|
||||
* read-only attribute (the default, non-ACL case), which cannot
|
||||
* represent the setuid/setgid bits anyway.
|
||||
*
|
||||
* When the mode is instead stored on the server - i.e. with the
|
||||
* cifsacl or modefromsid mount options (via an ACL) or with the
|
||||
* SMB3.1.1 POSIX extensions - the cleared mode must be pushed to
|
||||
* the server. Dropping ATTR_MODE here would leave the setuid/
|
||||
* setgid bit set on the server after a write, which is a security
|
||||
* issue (the bits are not stripped as they are on local
|
||||
* filesystems).
|
||||
*/
|
||||
if ((attrs->ia_valid & (ATTR_KILL_SUID|ATTR_KILL_SGID)) &&
|
||||
!((sbflags & (CIFS_MOUNT_CIFS_ACL | CIFS_MOUNT_MODE_FROM_SID)) ||
|
||||
cifs_sb_master_tcon(cifs_sb)->posix_extensions))
|
||||
attrs->ia_valid &= ~ATTR_MODE;
|
||||
|
||||
if (attrs->ia_valid & ATTR_MODE) {
|
||||
@@ -3508,13 +3539,6 @@ cifs_setattr_nounix(struct dentry *direntry, struct iattr *attrs)
|
||||
if (rc)
|
||||
goto cifs_setattr_exit;
|
||||
|
||||
if ((attrs->ia_valid & ATTR_SIZE) &&
|
||||
attrs->ia_size != i_size_read(inode)) {
|
||||
truncate_setsize(inode, attrs->ia_size);
|
||||
netfs_resize_file(&cifsInode->netfs, attrs->ia_size, true);
|
||||
fscache_resize_cookie(cifs_inode_cookie(inode), attrs->ia_size);
|
||||
}
|
||||
|
||||
setattr_copy(&nop_mnt_idmap, inode, attrs);
|
||||
mark_inode_dirty(inode);
|
||||
|
||||
|
||||
@@ -133,8 +133,7 @@ static int cifs_set_compression_by_path(unsigned int xid, struct file *filep,
|
||||
|
||||
close:
|
||||
server->ops->close(xid, tcon, &fid);
|
||||
if (tmp_cfile)
|
||||
kfree(tmp_cfile);
|
||||
kfree(tmp_cfile);
|
||||
cifs_free_open_info(&data);
|
||||
out:
|
||||
free_dentry_path(page);
|
||||
|
||||
@@ -98,15 +98,21 @@ static inline bool reparse_inode_match(struct inode *inode,
|
||||
timespec64_equal(&ctime, &fattr->cf_ctime);
|
||||
}
|
||||
|
||||
static inline u32 cifs_open_data_attrs(const struct cifs_open_info_data *data)
|
||||
{
|
||||
if (data->contains_posix_file_info)
|
||||
return le32_to_cpu(data->posix_fi.DosAttributes);
|
||||
|
||||
return le32_to_cpu(data->fi.Attributes);
|
||||
}
|
||||
|
||||
static inline bool cifs_open_data_reparse(struct cifs_open_info_data *data)
|
||||
{
|
||||
u32 attrs;
|
||||
bool ret;
|
||||
u32 attrs = cifs_open_data_attrs(data);
|
||||
|
||||
if (data->contains_posix_file_info) {
|
||||
struct smb311_posix_qinfo *fi = &data->posix_fi;
|
||||
|
||||
attrs = le32_to_cpu(fi->DosAttributes);
|
||||
if (data->reparse_point) {
|
||||
attrs |= ATTR_REPARSE_POINT;
|
||||
fi->DosAttributes = cpu_to_le32(attrs);
|
||||
@@ -115,16 +121,13 @@ static inline bool cifs_open_data_reparse(struct cifs_open_info_data *data)
|
||||
} else {
|
||||
struct smb2_file_all_info *fi = &data->fi;
|
||||
|
||||
attrs = le32_to_cpu(fi->Attributes);
|
||||
if (data->reparse_point) {
|
||||
attrs |= ATTR_REPARSE_POINT;
|
||||
fi->Attributes = cpu_to_le32(attrs);
|
||||
}
|
||||
}
|
||||
|
||||
ret = attrs & ATTR_REPARSE_POINT;
|
||||
|
||||
return ret;
|
||||
return attrs & ATTR_REPARSE_POINT;
|
||||
}
|
||||
|
||||
bool cifs_reparse_point_to_fattr(struct cifs_sb_info *cifs_sb,
|
||||
|
||||
@@ -81,6 +81,7 @@ int cifs_sign_rqst(struct smb_rqst *rqst, struct TCP_Server_Info *server,
|
||||
else
|
||||
memcpy(cifs_pdu->Signature.SecuritySignature, smb_signature, 8);
|
||||
|
||||
memzero_explicit(smb_signature, sizeof(smb_signature));
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -126,15 +127,13 @@ int cifs_verify_signature(struct smb_rqst *rqst,
|
||||
rc = cifs_calc_signature(rqst, server, what_we_think_sig_should_be);
|
||||
cifs_server_unlock(server);
|
||||
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* cifs_dump_mem("what we think it should be: ",
|
||||
what_we_think_sig_should_be, 16); */
|
||||
|
||||
if (crypto_memneq(server_response_sig, what_we_think_sig_should_be, 8))
|
||||
return -EACCES;
|
||||
else
|
||||
return 0;
|
||||
if (!rc) {
|
||||
if (crypto_memneq(server_response_sig,
|
||||
what_we_think_sig_should_be, 8))
|
||||
rc = -EACCES;
|
||||
}
|
||||
|
||||
memzero_explicit(what_we_think_sig_should_be,
|
||||
sizeof(what_we_think_sig_should_be));
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -80,7 +80,8 @@ is_valid_oplock_break(char *buffer, struct TCP_Server_Info *srv)
|
||||
(struct smb_com_transaction_change_notify_rsp *)buf;
|
||||
struct file_notify_information *pnotify;
|
||||
__u32 data_offset = 0;
|
||||
size_t len = srv->total_read - srv->pdu_size;
|
||||
/* total_read excludes the RFC1002 preamble */
|
||||
size_t len = srv->total_read;
|
||||
|
||||
if (get_bcc(buf) > sizeof(struct file_notify_information)) {
|
||||
data_offset = le32_to_cpu(pSMBr->DataOffset);
|
||||
|
||||
@@ -542,6 +542,7 @@ static int cifs_query_path_info(const unsigned int xid,
|
||||
|
||||
data->reparse_point = false;
|
||||
data->adjust_tz = false;
|
||||
data->unknown_nlink = false;
|
||||
|
||||
/*
|
||||
* First try CIFSSMBQPathInfo() function which returns more info
|
||||
@@ -608,6 +609,7 @@ static int cifs_query_path_info(const unsigned int xid,
|
||||
fi.EASize = di->EaSize;
|
||||
}
|
||||
fi.NumberOfLinks = cpu_to_le32(1);
|
||||
data->unknown_nlink = true;
|
||||
fi.DeletePending = 0;
|
||||
fi.Directory = !!(le32_to_cpu(fi.Attributes) & ATTR_DIRECTORY);
|
||||
cifs_buf_release(search_info.ntwrk_buf_start);
|
||||
@@ -630,6 +632,8 @@ static int cifs_query_path_info(const unsigned int xid,
|
||||
rc = SMBQueryInformation(xid, tcon, full_path, &fi, cifs_sb->local_nls,
|
||||
cifs_remap(cifs_sb));
|
||||
data->adjust_tz = true;
|
||||
if (!rc)
|
||||
data->unknown_nlink = true;
|
||||
} else if ((rc == -EOPNOTSUPP || rc == -EINVAL) && non_unicode_wildcard) {
|
||||
/* Path with non-UNICODE wildcard character cannot exist. */
|
||||
rc = -ENOENT;
|
||||
@@ -717,7 +721,7 @@ static int cifs_query_path_info(const unsigned int xid,
|
||||
ea->ea_value_length = cpu_to_le16(SMB2_WSL_XATTR_DEV_SIZE);
|
||||
memcpy(&ea->ea_data[0], SMB2_WSL_XATTR_DEV, SMB2_WSL_XATTR_NAME_LEN + 1);
|
||||
data->wsl.eas_len += ALIGN(sizeof(*ea) + SMB2_WSL_XATTR_NAME_LEN + 1 +
|
||||
SMB2_WSL_XATTR_MODE_SIZE, 4);
|
||||
SMB2_WSL_XATTR_DEV_SIZE, 4);
|
||||
rc = 0;
|
||||
} else if (rc >= 0) {
|
||||
/* It is an error if EA $LXDEV has wrong size. */
|
||||
@@ -893,8 +897,10 @@ static int cifs_open_file(const unsigned int xid, struct cifs_open_parms *oparms
|
||||
else
|
||||
rc = CIFS_open(xid, oparms, oplock, &fi);
|
||||
|
||||
if (!rc && data)
|
||||
if (!rc && data) {
|
||||
move_cifs_info_to_smb2(&data->fi, &fi);
|
||||
data->unknown_nlink = true;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -375,12 +375,31 @@ coalesce_t2(char *second_buf, struct smb_hdr *target_hdr, unsigned int *pdu_len)
|
||||
data_area_of_tgt = (char *)&pSMBt->hdr.Protocol +
|
||||
get_unaligned_le16(&pSMBt->t2_rsp.DataOffset);
|
||||
|
||||
/* validate target area */
|
||||
data_area_of_src = (char *)&pSMBs->hdr.Protocol +
|
||||
get_unaligned_le16(&pSMBs->t2_rsp.DataOffset);
|
||||
|
||||
data_area_of_tgt += total_in_tgt;
|
||||
|
||||
/*
|
||||
* DataOffset fields are server-supplied and not validated against
|
||||
* buffer bounds; check both data pointers before mutating the
|
||||
* target header.
|
||||
*/
|
||||
if (data_area_of_tgt < (char *)target_hdr +
|
||||
sizeof(struct smb_t2_rsp) + sizeof(__le16) ||
|
||||
data_area_of_tgt + total_in_src >
|
||||
(char *)target_hdr + CIFSMaxBufSize + MAX_CIFS_HDR_SIZE) {
|
||||
cifs_dbg(VFS, "%s: target data area out of bounds\n", __func__);
|
||||
return -EPROTO;
|
||||
}
|
||||
if (data_area_of_src < second_buf +
|
||||
sizeof(struct smb_t2_rsp) + sizeof(__le16) ||
|
||||
data_area_of_src + total_in_src >
|
||||
second_buf + smbCalcSize((struct smb_hdr *)second_buf)) {
|
||||
cifs_dbg(VFS, "%s: secondary data area out of bounds\n", __func__);
|
||||
return -EPROTO;
|
||||
}
|
||||
|
||||
total_in_tgt += total_in_src;
|
||||
/* is the result too big for the field? */
|
||||
if (total_in_tgt > USHRT_MAX) {
|
||||
@@ -430,10 +449,18 @@ bool
|
||||
cifs_check_trans2(struct mid_q_entry *mid, struct TCP_Server_Info *server,
|
||||
char *buf, int malformed)
|
||||
{
|
||||
if (malformed)
|
||||
return false;
|
||||
if (check2ndT2(buf) <= 0)
|
||||
if (malformed || check2ndT2(buf) <= 0) {
|
||||
/* mid->multiRsp blocks the server buf detach in handle_mid();
|
||||
* returning false here would leak resp_buf and leave a dangling
|
||||
* server->smallbuf/bigbuf after the user thread frees resp_buf.
|
||||
*/
|
||||
if (mid->multiRsp) {
|
||||
mid->multiEnd = true;
|
||||
dequeue_mid(server, mid, true);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
mid->multiRsp = true;
|
||||
if (mid->resp_buf) {
|
||||
/* merge response - fix up 1st*/
|
||||
|
||||
@@ -61,7 +61,10 @@ static struct smb2_symlink_err_rsp *symlink_data(const struct kvec *iov)
|
||||
cifs_dbg(FYI, "%s: skipping unhandled error context: 0x%x\n",
|
||||
__func__, le32_to_cpu(p->ErrorId));
|
||||
|
||||
len = ALIGN(le32_to_cpu(p->ErrorDataLength), 8);
|
||||
len = le32_to_cpu(p->ErrorDataLength);
|
||||
if (len > end - ((u8 *)p + sizeof(*p)))
|
||||
return ERR_PTR(-EINVAL);
|
||||
len = ALIGN(len, 8);
|
||||
if (len > end - ((u8 *)p + sizeof(*p)))
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "smb2glob.h"
|
||||
#include "smb2proto.h"
|
||||
#include "cached_dir.h"
|
||||
#include "reparse.h"
|
||||
#include "../common/smb2status.h"
|
||||
#include "../common/smbfsctl.h"
|
||||
|
||||
@@ -40,9 +41,11 @@ static struct reparse_data_buffer *reparse_buf_ptr(struct kvec *iov)
|
||||
|
||||
buf = (struct reparse_data_buffer *)((u8 *)io + off);
|
||||
len = sizeof(*buf);
|
||||
rdlen = le16_to_cpu(buf->ReparseDataLength);
|
||||
if (count < len)
|
||||
return ERR_PTR(smb_EIO2(smb_eio_trace_reparse_rdlen, count, 0));
|
||||
|
||||
if (count < len || count < rdlen + len)
|
||||
rdlen = le16_to_cpu(buf->ReparseDataLength);
|
||||
if (count < rdlen + len)
|
||||
return ERR_PTR(smb_EIO2(smb_eio_trace_reparse_rdlen, count, rdlen));
|
||||
return buf;
|
||||
}
|
||||
@@ -574,8 +577,10 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
idata->fi.Attributes = create_rsp->FileAttributes;
|
||||
idata->fi.AllocationSize = create_rsp->AllocationSize;
|
||||
idata->fi.EndOfFile = create_rsp->EndofFile;
|
||||
idata->contains_posix_file_info = false;
|
||||
if (le32_to_cpu(idata->fi.NumberOfLinks) == 0)
|
||||
idata->fi.NumberOfLinks = cpu_to_le32(1); /* dummy value */
|
||||
idata->unknown_nlink = true;
|
||||
idata->fi.DeletePending = 0; /* successful open = not delete pending */
|
||||
idata->fi.Directory = !!(le32_to_cpu(create_rsp->FileAttributes) & ATTR_DIRECTORY);
|
||||
|
||||
@@ -596,7 +601,6 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
switch (cmds[i]) {
|
||||
case SMB2_OP_QUERY_INFO:
|
||||
idata = in_iov[i].iov_base;
|
||||
idata->contains_posix_file_info = false;
|
||||
if (rc == 0 && cfile && cfile->symlink_target) {
|
||||
idata->symlink_target = kstrdup(cfile->symlink_target, GFP_KERNEL);
|
||||
if (!idata->symlink_target)
|
||||
@@ -609,6 +613,8 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
le16_to_cpu(qi_rsp->OutputBufferOffset),
|
||||
le32_to_cpu(qi_rsp->OutputBufferLength),
|
||||
&rsp_iov[i + 1], sizeof(idata->fi), (char *)&idata->fi);
|
||||
if (!rc)
|
||||
idata->contains_posix_file_info = false;
|
||||
}
|
||||
SMB2_query_info_free(&rqst[num_rqst++]);
|
||||
if (rc)
|
||||
@@ -620,7 +626,6 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
break;
|
||||
case SMB2_OP_POSIX_QUERY_INFO:
|
||||
idata = in_iov[i].iov_base;
|
||||
idata->contains_posix_file_info = true;
|
||||
if (rc == 0 && cfile && cfile->symlink_target) {
|
||||
idata->symlink_target = kstrdup(cfile->symlink_target, GFP_KERNEL);
|
||||
if (!idata->symlink_target)
|
||||
@@ -634,6 +639,8 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
le32_to_cpu(qi_rsp->OutputBufferLength),
|
||||
&rsp_iov[i + 1], sizeof(idata->posix_fi) /* add SIDs */,
|
||||
(char *)&idata->posix_fi);
|
||||
if (!rc)
|
||||
idata->contains_posix_file_info = true;
|
||||
}
|
||||
if (rc == 0)
|
||||
rc = parse_posix_sids(idata, &rsp_iov[i + 1]);
|
||||
@@ -705,7 +712,6 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
idata = in_iov[i].iov_base;
|
||||
idata->reparse.io.iov = *iov;
|
||||
idata->reparse.io.buftype = resp_buftype[i + 1];
|
||||
idata->contains_posix_file_info = false; /* BB VERIFY */
|
||||
rbuf = reparse_buf_ptr(iov);
|
||||
if (IS_ERR(rbuf)) {
|
||||
rc = PTR_ERR(rbuf);
|
||||
@@ -727,7 +733,6 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
case SMB2_OP_QUERY_WSL_EA:
|
||||
if (!rc) {
|
||||
idata = in_iov[i].iov_base;
|
||||
idata->contains_posix_file_info = false;
|
||||
qi_rsp = rsp_iov[i + 1].iov_base;
|
||||
data[0] = (u8 *)qi_rsp + le16_to_cpu(qi_rsp->OutputBufferOffset);
|
||||
size[0] = le32_to_cpu(qi_rsp->OutputBufferLength);
|
||||
@@ -1000,12 +1005,13 @@ int smb2_query_path_info(const unsigned int xid,
|
||||
/*
|
||||
* If the symlink was already parsed in create response then it is needed to fix
|
||||
* its type now (after the second call with OPEN_REPARSE_POINT which filled the
|
||||
* data->fi.Attributes). If the symlink was not parsed in create response then
|
||||
* metadata attributes). If the symlink was not parsed in create response then
|
||||
* the data->symlink_target was not filled yet and then the type will be fixed
|
||||
* later after data->symlink_target is filled.
|
||||
*/
|
||||
if (data->reparse.tag == IO_REPARSE_TAG_SYMLINK && !rc && data->symlink_target) {
|
||||
bool directory = le32_to_cpu(data->fi.Attributes) & ATTR_DIRECTORY;
|
||||
bool directory = cifs_open_data_attrs(data) & ATTR_DIRECTORY;
|
||||
|
||||
rc = smb2_fix_symlink_target_type(&data->symlink_target, directory, cifs_sb);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -1569,7 +1569,7 @@ SMB2_request_res_key(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
memcpy(pcchunk->SourceKey, res_key->ResumeKey, COPY_CHUNK_RES_KEY_SIZE);
|
||||
|
||||
req_res_key_exit:
|
||||
kfree(res_key);
|
||||
kfree_sensitive(res_key);
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -2222,8 +2222,7 @@ smb2_duplicate_extents(const unsigned int xid,
|
||||
rc = smb2_set_file_size(xid, tcon, trgtfile, dest_off + len, false);
|
||||
if (rc)
|
||||
goto duplicate_extents_out;
|
||||
netfs_resize_file(netfs_inode(inode), dest_off + len, true);
|
||||
cifs_setsize(inode, dest_off + len);
|
||||
cifs_resize_file_locked(inode, dest_off + len);
|
||||
}
|
||||
rc = SMB2_ioctl(xid, tcon, trgtfile->fid.persistent_fid,
|
||||
trgtfile->fid.volatile_fid,
|
||||
@@ -3776,8 +3775,7 @@ static long smb3_simple_falloc(struct file *file, struct cifs_tcon *tcon,
|
||||
}
|
||||
|
||||
new_eof = off + len;
|
||||
netfs_resize_file(&cifsi->netfs, new_eof, true);
|
||||
cifs_setsize(inode, new_eof);
|
||||
cifs_resize_file_locked(inode, new_eof);
|
||||
|
||||
qrc = SMB2_query_info(xid, tcon,
|
||||
cfile->fid.persistent_fid,
|
||||
@@ -3825,8 +3823,7 @@ static long smb3_simple_falloc(struct file *file, struct cifs_tcon *tcon,
|
||||
if (rc)
|
||||
goto out;
|
||||
|
||||
netfs_resize_file(&cifsi->netfs, new_eof, true);
|
||||
cifs_setsize(inode, new_eof);
|
||||
cifs_resize_file_locked(inode, new_eof);
|
||||
|
||||
qrc = SMB2_query_info(xid, tcon,
|
||||
cfile->fid.persistent_fid,
|
||||
@@ -4636,7 +4633,7 @@ crypt_message(struct TCP_Server_Info *server, int num_rqst,
|
||||
rc = crypto_aead_setkey(tfm, key, SMB3_GCM256_CRYPTKEY_SIZE);
|
||||
else
|
||||
rc = crypto_aead_setkey(tfm, key, SMB3_GCM128_CRYPTKEY_SIZE);
|
||||
|
||||
memzero_explicit(key, sizeof(key));
|
||||
if (rc) {
|
||||
cifs_server_dbg(VFS, "%s: Failed to set aead key %d\n", __func__, rc);
|
||||
return rc;
|
||||
|
||||
@@ -3372,6 +3372,7 @@ SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path,
|
||||
#endif /* CIFS_DEBUG2 */
|
||||
|
||||
if (file_info) {
|
||||
buf->contains_posix_file_info = false;
|
||||
file_info->CreationTime = rsp->CreationTime;
|
||||
file_info->LastAccessTime = rsp->LastAccessTime;
|
||||
file_info->LastWriteTime = rsp->LastWriteTime;
|
||||
@@ -4564,8 +4565,10 @@ smb2_new_read_req(void **buf, unsigned int *total_len,
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
if (server == NULL)
|
||||
return -ECONNABORTED;
|
||||
if (!server) {
|
||||
rc = -ECONNABORTED;
|
||||
goto free_req;
|
||||
}
|
||||
|
||||
shdr = &req->hdr;
|
||||
shdr->Id.SyncId.ProcessId = cpu_to_le32(io_parms->pid);
|
||||
@@ -4596,8 +4599,10 @@ smb2_new_read_req(void **buf, unsigned int *total_len,
|
||||
|
||||
rdata->mr = smbd_register_mr(server->smbd_conn, &rdata->subreq.io_iter,
|
||||
true, need_invalidate);
|
||||
if (!rdata->mr)
|
||||
return -EAGAIN;
|
||||
if (!rdata->mr) {
|
||||
rc = -EAGAIN;
|
||||
goto free_req;
|
||||
}
|
||||
|
||||
req->Channel = SMB2_CHANNEL_RDMA_V1_INVALIDATE;
|
||||
if (need_invalidate)
|
||||
@@ -4638,6 +4643,10 @@ smb2_new_read_req(void **buf, unsigned int *total_len,
|
||||
|
||||
*buf = req;
|
||||
return rc;
|
||||
|
||||
free_req:
|
||||
cifs_small_buf_release(req);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -4885,6 +4894,7 @@ smb2_async_readv(struct cifs_io_subrequest *rdata)
|
||||
smb2_should_replay(tcon,
|
||||
&rdata->retries,
|
||||
&rdata->cur_sleep)) {
|
||||
rdata->replay = true;
|
||||
trace_netfs_sreq(&rdata->subreq, netfs_sreq_trace_io_retry_needed);
|
||||
__set_bit(NETFS_SREQ_NEED_RETRY, &rdata->subreq.flags);
|
||||
}
|
||||
|
||||
@@ -249,6 +249,8 @@ smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)
|
||||
if (!rc)
|
||||
memcpy(shdr->Signature, smb2_signature, SMB2_SIGNATURE_SIZE);
|
||||
|
||||
memzero_explicit(key, sizeof(key));
|
||||
memzero_explicit(&hmac_ctx, sizeof(hmac_ctx));
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -283,6 +285,7 @@ static void generate_key(struct cifs_ses *ses, struct kvec label,
|
||||
hmac_sha256_final(&hmac_ctx, prfhash);
|
||||
|
||||
memcpy(key, prfhash, key_size);
|
||||
memzero_explicit(prfhash, sizeof(prfhash));
|
||||
}
|
||||
|
||||
struct derivation {
|
||||
@@ -482,6 +485,7 @@ smb3_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)
|
||||
memset(shdr->Signature, 0x0, SMB2_SIGNATURE_SIZE);
|
||||
|
||||
rc = aes_cmac_preparekey(&cmac_key, key, SMB2_CMACAES_SIZE);
|
||||
memzero_explicit(key, sizeof(key));
|
||||
if (rc) {
|
||||
cifs_server_dbg(VFS, "%s: Could not set key for cmac aes\n", __func__);
|
||||
return rc;
|
||||
|
||||
@@ -133,6 +133,7 @@
|
||||
EM(smb_eio_trace_sym_slash, "sym_slash") \
|
||||
EM(smb_eio_trace_sym_target_len, "sym_target_len") \
|
||||
EM(smb_eio_trace_symlink_file_size, "symlink_file_size") \
|
||||
EM(smb_eio_trace_tcon_bcc_too_small, "tcon_bcc_too_small") \
|
||||
EM(smb_eio_trace_tdis_in_reconnect, "tdis_in_reconnect") \
|
||||
EM(smb_eio_trace_tx_chained_async, "tx_chained_async") \
|
||||
EM(smb_eio_trace_tx_compress_failed, "tx_compress_failed") \
|
||||
@@ -213,7 +214,7 @@
|
||||
#define EM(a, b) a,
|
||||
#define E_(a, b) a
|
||||
|
||||
enum smb_eio_trace { smb_eio_traces } __mode(byte);
|
||||
enum smb_eio_trace { smb_eio_traces };
|
||||
enum smb3_rw_credits_trace { smb3_rw_credits_traces } __mode(byte);
|
||||
enum smb3_tcon_ref_trace { smb3_tcon_ref_traces } __mode(byte);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user