cifsd: add the check if parent is stable by unexpected rename

This patch add the check if parent is stable by unexpected rename.

Signed-off-by: Hyunchul Lee <hyc.lee@gmail.com>
Signed-off-by: Namjae Jeon <namjae.jeon@samsung.com>
Signed-off-by: Steve French <stfrench@microsoft.com>
This commit is contained in:
Namjae Jeon 2021-04-13 13:18:10 +09:00 committed by Steve French
parent d40012a83f
commit ff1d572725
2 changed files with 80 additions and 30 deletions

View File

@ -2844,12 +2844,10 @@ int smb2_open(struct ksmbd_work *work)
* is already granted.
*/
if (daccess & ~(FILE_READ_ATTRIBUTES_LE | FILE_READ_CONTROL_LE)) {
if (ksmbd_vfs_inode_permission(path.dentry,
open_flags & O_ACCMODE,
may_delete)) {
rc = -EACCES;
rc = ksmbd_vfs_inode_permission(path.dentry,
open_flags & O_ACCMODE, may_delete);
if (rc)
goto err_out;
}
}
}
@ -3260,7 +3258,7 @@ err_out1:
rsp->hdr.Status = STATUS_INVALID_PARAMETER;
else if (rc == -EOPNOTSUPP)
rsp->hdr.Status = STATUS_NOT_SUPPORTED;
else if (rc == -EACCES)
else if (rc == -EACCES || rc == -ESTALE)
rsp->hdr.Status = STATUS_ACCESS_DENIED;
else if (rc == -ENOENT)
rsp->hdr.Status = STATUS_OBJECT_NAME_INVALID;
@ -5938,7 +5936,7 @@ err_out:
rsp->hdr.Status = STATUS_DIRECTORY_NOT_EMPTY;
else if (rc == -EAGAIN)
rsp->hdr.Status = STATUS_FILE_LOCK_CONFLICT;
else if (rc == -EBADF)
else if (rc == -EBADF || rc == -ESTALE)
rsp->hdr.Status = STATUS_INVALID_HANDLE;
else if (rc == -EEXIST)
rsp->hdr.Status = STATUS_OBJECT_NAME_COLLISION;

View File

@ -70,7 +70,7 @@ static void ksmbd_vfs_inherit_owner(struct ksmbd_work *work,
int ksmbd_vfs_inode_permission(struct dentry *dentry, int acc_mode, bool delete)
{
int mask;
int mask, ret = 0;
mask = 0;
acc_mode &= O_ACCMODE;
@ -86,24 +86,39 @@ int ksmbd_vfs_inode_permission(struct dentry *dentry, int acc_mode, bool delete)
return -EACCES;
if (delete) {
struct dentry *parent;
struct dentry *child, *parent;
parent = dget_parent(dentry);
if (!parent)
return -EINVAL;
inode_lock_nested(d_inode(parent), I_MUTEX_PARENT);
child = lookup_one_len(dentry->d_name.name, parent,
dentry->d_name.len);
if (IS_ERR(child)) {
ret = PTR_ERR(child);
goto out_lock;
}
if (child != dentry) {
ret = -ESTALE;
dput(child);
goto out_lock;
}
dput(child);
if (inode_permission(&init_user_ns, d_inode(parent), MAY_EXEC | MAY_WRITE)) {
dput(parent);
return -EACCES;
ret = -EACCES;
goto out_lock;
}
out_lock:
inode_unlock(d_inode(parent));
dput(parent);
}
return 0;
return ret;
}
int ksmbd_vfs_query_maximal_access(struct dentry *dentry, __le32 *daccess)
{
struct dentry *parent;
struct dentry *parent, *child;
int ret = 0;
*daccess = cpu_to_le32(FILE_READ_ATTRIBUTES | READ_CONTROL);
@ -120,13 +135,28 @@ int ksmbd_vfs_query_maximal_access(struct dentry *dentry, __le32 *daccess)
*daccess |= FILE_EXECUTE_LE;
parent = dget_parent(dentry);
if (!parent)
return 0;
inode_lock_nested(d_inode(parent), I_MUTEX_PARENT);
child = lookup_one_len(dentry->d_name.name, parent,
dentry->d_name.len);
if (IS_ERR(child)) {
ret = PTR_ERR(child);
goto out_lock;
}
if (child != dentry) {
ret = -ESTALE;
dput(child);
goto out_lock;
}
dput(child);
if (!inode_permission(&init_user_ns, d_inode(parent), MAY_EXEC | MAY_WRITE))
*daccess |= FILE_DELETE_LE;
out_lock:
inode_unlock(d_inode(parent));
dput(parent);
return 0;
return ret;
}
/**
@ -726,7 +756,7 @@ int ksmbd_vfs_fp_rename(struct ksmbd_work *work, struct ksmbd_file *fp,
{
struct path dst_path;
struct dentry *src_dent_parent, *dst_dent_parent;
struct dentry *src_dent, *trap_dent;
struct dentry *src_dent, *trap_dent, *src_child;
char *dst_name;
int err;
@ -735,11 +765,7 @@ int ksmbd_vfs_fp_rename(struct ksmbd_work *work, struct ksmbd_file *fp,
return -EINVAL;
src_dent_parent = dget_parent(fp->filp->f_path.dentry);
if (!src_dent_parent)
return -EINVAL;
src_dent = fp->filp->f_path.dentry;
dget(src_dent);
err = kern_path(newname, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &dst_path);
if (err) {
@ -747,20 +773,36 @@ int ksmbd_vfs_fp_rename(struct ksmbd_work *work, struct ksmbd_file *fp,
goto out;
}
dst_dent_parent = dst_path.dentry;
dget(dst_dent_parent);
trap_dent = lock_rename(src_dent_parent, dst_dent_parent);
dget(src_dent);
dget(dst_dent_parent);
src_child = lookup_one_len(src_dent->d_name.name, src_dent_parent,
src_dent->d_name.len);
if (IS_ERR(src_child)) {
err = PTR_ERR(src_child);
goto out_lock;
}
if (src_child != src_dent) {
err = -ESTALE;
dput(src_child);
goto out_lock;
}
dput(src_child);
err = __ksmbd_vfs_rename(work,
src_dent_parent,
src_dent,
dst_dent_parent,
trap_dent,
dst_name);
unlock_rename(src_dent_parent, dst_dent_parent);
out_lock:
dput(src_dent);
dput(dst_dent_parent);
unlock_rename(src_dent_parent, dst_dent_parent);
path_put(&dst_path);
out:
dput(src_dent);
dput(src_dent_parent);
return err;
}
@ -1050,23 +1092,33 @@ int ksmbd_vfs_remove_xattr(struct dentry *dentry, char *attr_name)
int ksmbd_vfs_unlink(struct dentry *dir, struct dentry *dentry)
{
struct dentry *child;
int err = 0;
dget(dentry);
inode_lock_nested(d_inode(dir), I_MUTEX_PARENT);
if (!d_inode(dentry) || !d_inode(dentry)->i_nlink) {
err = -ENOENT;
dget(dentry);
child = lookup_one_len(dentry->d_name.name, dir,
dentry->d_name.len);
if (IS_ERR(child)) {
err = PTR_ERR(child);
goto out;
}
if (child != dentry) {
err = -ESTALE;
dput(child);
goto out;
}
dput(child);
if (S_ISDIR(d_inode(dentry)->i_mode))
err = vfs_rmdir(&init_user_ns, d_inode(dir), dentry);
else
err = vfs_unlink(&init_user_ns, d_inode(dir), dentry, NULL);
out:
inode_unlock(d_inode(dir));
dput(dentry);
inode_unlock(d_inode(dir));
if (err)
ksmbd_debug(VFS, "failed to delete, err %d\n", err);