Re: [PATCH] ksmbd: check if a mount point is crossed during path lookup

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



In the future if we want to make this more visible to the client
(although presumably query fs info/statfs can still return the correct
information depending on the path given by the user) we could always
make this a fake DFS referral after we cross a mount point on the
server

On Fri, Jul 14, 2023 at 8:25 AM Namjae Jeon <linkinjeon@xxxxxxxxxx> wrote:
>
> 2023-07-14 21:44 GMT+09:00, Tom Talpey <tom@xxxxxxxxxx>:
> > On 7/12/2023 7:40 PM, Namjae Jeon wrote:
> >> 2023-07-13 2:23 GMT+09:00, Tom Talpey <tom@xxxxxxxxxx>:
> >>> On 7/11/2023 8:30 AM, Namjae Jeon wrote:
> >>>> Since commit 74d7970febf7 ("ksmbd: fix racy issue from using ->d_parent
> >>>> and ->d_name"),
> >>>> ksmbd can not lookup cross mount points. If last component is a cross
> >>>> mount point during path lookup, check if it is crossed to follow it
> >>>> down.
> >>>
> >>> I actually thought not crossing mount points was intended, since
> >>> semantics can shift if the crossing changes filesystems or fs types.
> >>>
> >>> Does this change somehow prevent walking out of a mount via embedded
> >>> "/../" in the path? It's not obvious to me whether
> >>> vfs_path_parent_lookup
> >>> denies this.
> >> Yes, LOOKUP_BENEATH flags protect to lookup out of share.
> >>>
> >>> Tom.
> >>>
> >>> I'm not familiar enough with the VFS protection, but does this
> >> I have checked it before and now again. There is no problem.
> >
> > I'm still concerned about this. Crossing mount points in the server is
> > completely invisible to the client, yet the semantics change radically.
> Have you checked it with samba server ? This is exactly the same behavior
> as samba server.
>
> And users are also wanting exactly the same behavior as before.
> https://github.com/namjaejeon/ksmbd/issues/436
> >
> > What if someone shares "/mnt", and proceeds to mount an ext4, a USB
> > FAT32, a CD-Rom, and an NFS filesystem in subdirectories? The client
> > has no clue, and will walk into them all, finding a very different
> > world in each.
> >
> > How will Posix extensions work across this? Case sensitivity/preserving?
> > Caching and locks/leases?
> I didn't understand exactly your question.
> And NFS also supports crossmnt, and we will be able to add 'crossmnt' parameter
> in smb.conf and work crossmnt by default.
>
> Thanks.
> >
> > Tom.
> >> Thanks!
> >>>>
> >>>> Fixes: 74d7970febf7 ("ksmbd: fix racy issue from using ->d_parent and
> >>>> ->d_name")
> >>>> Signed-off-by: Namjae Jeon <linkinjeon@xxxxxxxxxx>
> >>>> ---
> >>>>    fs/smb/server/smb2pdu.c | 27 ++++++++++++--------
> >>>>    fs/smb/server/vfs.c     | 56
> >>>> +++++++++++++++++++++++------------------
> >>>>    fs/smb/server/vfs.h     |  4 +--
> >>>>    3 files changed, 49 insertions(+), 38 deletions(-)
> >>>>
> >>>> diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c
> >>>> index cf8822103f50..ca276634fd58 100644
> >>>> --- a/fs/smb/server/smb2pdu.c
> >>>> +++ b/fs/smb/server/smb2pdu.c
> >>>> @@ -2467,8 +2467,9 @@ static void smb2_update_xattrs(struct
> >>>> ksmbd_tree_connect *tcon,
> >>>>            }
> >>>>    }
> >>>>
> >>>> -static int smb2_creat(struct ksmbd_work *work, struct path *path, char
> >>>> *name,
> >>>> -                int open_flags, umode_t posix_mode, bool is_dir)
> >>>> +static int smb2_creat(struct ksmbd_work *work, struct path
> >>>> *parent_path,
> >>>> +                struct path *path, char *name, int open_flags,
> >>>> +                umode_t posix_mode, bool is_dir)
> >>>>    {
> >>>>            struct ksmbd_tree_connect *tcon = work->tcon;
> >>>>            struct ksmbd_share_config *share = tcon->share_conf;
> >>>> @@ -2495,7 +2496,7 @@ static int smb2_creat(struct ksmbd_work *work,
> >>>> struct path *path, char *name,
> >>>>                            return rc;
> >>>>            }
> >>>>
> >>>> -  rc = ksmbd_vfs_kern_path_locked(work, name, 0, path, 0);
> >>>> +  rc = ksmbd_vfs_kern_path_locked(work, name, 0, parent_path, path, 0);
> >>>>            if (rc) {
> >>>>                    pr_err("cannot get linux path (%s), err = %d\n",
> >>>>                           name, rc);
> >>>> @@ -2565,7 +2566,7 @@ int smb2_open(struct ksmbd_work *work)
> >>>>            struct ksmbd_tree_connect *tcon = work->tcon;
> >>>>            struct smb2_create_req *req;
> >>>>            struct smb2_create_rsp *rsp;
> >>>> -  struct path path;
> >>>> +  struct path path, parent_path;
> >>>>            struct ksmbd_share_config *share = tcon->share_conf;
> >>>>            struct ksmbd_file *fp = NULL;
> >>>>            struct file *filp = NULL;
> >>>> @@ -2786,7 +2787,8 @@ int smb2_open(struct ksmbd_work *work)
> >>>>                    goto err_out1;
> >>>>            }
> >>>>
> >>>> -  rc = ksmbd_vfs_kern_path_locked(work, name, LOOKUP_NO_SYMLINKS,
> >>>> &path,
> >>>> 1);
> >>>> +  rc = ksmbd_vfs_kern_path_locked(work, name, LOOKUP_NO_SYMLINKS,
> >>>> +                                  &parent_path, &path, 1);
> >>>>            if (!rc) {
> >>>>                    file_present = true;
> >>>>
> >>>> @@ -2906,7 +2908,8 @@ int smb2_open(struct ksmbd_work *work)
> >>>>
> >>>>            /*create file if not present */
> >>>>            if (!file_present) {
> >>>> -          rc = smb2_creat(work, &path, name, open_flags, posix_mode,
> >>>> +          rc = smb2_creat(work, &parent_path, &path, name, open_flags,
> >>>> +                          posix_mode,
> >>>>                                    req->CreateOptions & FILE_DIRECTORY_FILE_LE);
> >>>>                    if (rc) {
> >>>>                            if (rc == -ENOENT) {
> >>>> @@ -3321,8 +3324,9 @@ int smb2_open(struct ksmbd_work *work)
> >>>>
> >>>>    err_out:
> >>>>            if (file_present || created) {
> >>>> -          inode_unlock(d_inode(path.dentry->d_parent));
> >>>> -          dput(path.dentry);
> >>>> +          inode_unlock(d_inode(parent_path.dentry));
> >>>> +          path_put(&path);
> >>>> +          path_put(&parent_path);
> >>>>            }
> >>>>            ksmbd_revert_fsids(work);
> >>>>    err_out1:
> >>>> @@ -5545,7 +5549,7 @@ static int smb2_create_link(struct ksmbd_work
> >>>> *work,
> >>>>                                struct nls_table *local_nls)
> >>>>    {
> >>>>            char *link_name = NULL, *target_name = NULL, *pathname = NULL;
> >>>> -  struct path path;
> >>>> +  struct path path, parent_path;
> >>>>            bool file_present = false;
> >>>>            int rc;
> >>>>
> >>>> @@ -5575,7 +5579,7 @@ static int smb2_create_link(struct ksmbd_work
> >>>> *work,
> >>>>
> >>>>            ksmbd_debug(SMB, "target name is %s\n", target_name);
> >>>>            rc = ksmbd_vfs_kern_path_locked(work, link_name,
> >>>> LOOKUP_NO_SYMLINKS,
> >>>> -                                  &path, 0);
> >>>> +                                  &parent_path, &path, 0);
> >>>>            if (rc) {
> >>>>                    if (rc != -ENOENT)
> >>>>                            goto out;
> >>>> @@ -5605,8 +5609,9 @@ static int smb2_create_link(struct ksmbd_work
> >>>> *work,
> >>>>                    rc = -EINVAL;
> >>>>    out:
> >>>>            if (file_present) {
> >>>> -          inode_unlock(d_inode(path.dentry->d_parent));
> >>>> +          inode_unlock(d_inode(parent_path.dentry));
> >>>>                    path_put(&path);
> >>>> +          path_put(&parent_path);
> >>>>            }
> >>>>            if (!IS_ERR(link_name))
> >>>>                    kfree(link_name);
> >>>> diff --git a/fs/smb/server/vfs.c b/fs/smb/server/vfs.c
> >>>> index e35914457350..fd06d267de61 100644
> >>>> --- a/fs/smb/server/vfs.c
> >>>> +++ b/fs/smb/server/vfs.c
> >>>> @@ -63,13 +63,13 @@ int ksmbd_vfs_lock_parent(struct dentry *parent,
> >>>> struct dentry *child)
> >>>>
> >>>>    static int ksmbd_vfs_path_lookup_locked(struct ksmbd_share_config
> >>>> *share_conf,
> >>>>                                            char *pathname, unsigned int flags,
> >>>> +                                  struct path *parent_path,
> >>>>                                            struct path *path)
> >>>>    {
> >>>>            struct qstr last;
> >>>>            struct filename *filename;
> >>>>            struct path *root_share_path = &share_conf->vfs_path;
> >>>>            int err, type;
> >>>> -  struct path parent_path;
> >>>>            struct dentry *d;
> >>>>
> >>>>            if (pathname[0] == '\0') {
> >>>> @@ -84,7 +84,7 @@ static int ksmbd_vfs_path_lookup_locked(struct
> >>>> ksmbd_share_config *share_conf,
> >>>>                    return PTR_ERR(filename);
> >>>>
> >>>>            err = vfs_path_parent_lookup(filename, flags,
> >>>> -                               &parent_path, &last, &type,
> >>>> +                               parent_path, &last, &type,
> >>>>                                         root_share_path);
> >>>>            if (err) {
> >>>>                    putname(filename);
> >>>> @@ -92,13 +92,13 @@ static int ksmbd_vfs_path_lookup_locked(struct
> >>>> ksmbd_share_config *share_conf,
> >>>>            }
> >>>>
> >>>>            if (unlikely(type != LAST_NORM)) {
> >>>> -          path_put(&parent_path);
> >>>> +          path_put(parent_path);
> >>>>                    putname(filename);
> >>>>                    return -ENOENT;
> >>>>            }
> >>>>
> >>>> -  inode_lock_nested(parent_path.dentry->d_inode, I_MUTEX_PARENT);
> >>>> -  d = lookup_one_qstr_excl(&last, parent_path.dentry, 0);
> >>>> +  inode_lock_nested(parent_path->dentry->d_inode, I_MUTEX_PARENT);
> >>>> +  d = lookup_one_qstr_excl(&last, parent_path->dentry, 0);
> >>>>            if (IS_ERR(d))
> >>>>                    goto err_out;
> >>>>
> >>>> @@ -108,15 +108,20 @@ static int ksmbd_vfs_path_lookup_locked(struct
> >>>> ksmbd_share_config *share_conf,
> >>>>            }
> >>>>
> >>>>            path->dentry = d;
> >>>> -  path->mnt = share_conf->vfs_path.mnt;
> >>>> -  path_put(&parent_path);
> >>>> -  putname(filename);
> >>>> +  path->mnt = mntget(parent_path->mnt);
> >>>> +
> >>>> +  err = follow_down(path, 0);
> >>>> +  if (err < 0) {
> >>>> +          path_put(path);
> >>>> +          goto err_out;
> >>>> +  }
> >>>>
> >>>> +  putname(filename);
> >>>>            return 0;
> >>>>
> >>>>    err_out:
> >>>> -  inode_unlock(parent_path.dentry->d_inode);
> >>>> -  path_put(&parent_path);
> >>>> +  inode_unlock(d_inode(parent_path->dentry));
> >>>> +  path_put(parent_path);
> >>>>            putname(filename);
> >>>>            return -ENOENT;
> >>>>    }
> >>>> @@ -1194,14 +1199,14 @@ static int ksmbd_vfs_lookup_in_dir(const struct
> >>>> path *dir, char *name,
> >>>>     * Return:      0 on success, otherwise error
> >>>>     */
> >>>>    int ksmbd_vfs_kern_path_locked(struct ksmbd_work *work, char *name,
> >>>> -                         unsigned int flags, struct path *path,
> >>>> -                         bool caseless)
> >>>> +                         unsigned int flags, struct path *parent_path,
> >>>> +                         struct path *path, bool caseless)
> >>>>    {
> >>>>            struct ksmbd_share_config *share_conf = work->tcon->share_conf;
> >>>>            int err;
> >>>> -  struct path parent_path;
> >>>>
> >>>> -  err = ksmbd_vfs_path_lookup_locked(share_conf, name, flags, path);
> >>>> +  err = ksmbd_vfs_path_lookup_locked(share_conf, name, flags,
> >>>> parent_path,
> >>>> +                                     path);
> >>>>            if (!err)
> >>>>                    return 0;
> >>>>
> >>>> @@ -1216,10 +1221,10 @@ int ksmbd_vfs_kern_path_locked(struct
> >>>> ksmbd_work
> >>>> *work, char *name,
> >>>>                    path_len = strlen(filepath);
> >>>>                    remain_len = path_len;
> >>>>
> >>>> -          parent_path = share_conf->vfs_path;
> >>>> -          path_get(&parent_path);
> >>>> +          *parent_path = share_conf->vfs_path;
> >>>> +          path_get(parent_path);
> >>>>
> >>>> -          while (d_can_lookup(parent_path.dentry)) {
> >>>> +          while (d_can_lookup(parent_path->dentry)) {
> >>>>                            char *filename = filepath + path_len - remain_len;
> >>>>                            char *next = strchrnul(filename, '/');
> >>>>                            size_t filename_len = next - filename;
> >>>> @@ -1228,7 +1233,7 @@ int ksmbd_vfs_kern_path_locked(struct ksmbd_work
> >>>> *work, char *name,
> >>>>                            if (filename_len == 0)
> >>>>                                    break;
> >>>>
> >>>> -                  err = ksmbd_vfs_lookup_in_dir(&parent_path, filename,
> >>>> +                  err = ksmbd_vfs_lookup_in_dir(parent_path, filename,
> >>>>                                                          filename_len,
> >>>>                                                          work->conn->um);
> >>>>                            if (err)
> >>>> @@ -1245,8 +1250,8 @@ int ksmbd_vfs_kern_path_locked(struct ksmbd_work
> >>>> *work, char *name,
> >>>>                                    goto out2;
> >>>>                            else if (is_last)
> >>>>                                    goto out1;
> >>>> -                  path_put(&parent_path);
> >>>> -                  parent_path = *path;
> >>>> +                  path_put(parent_path);
> >>>> +                  *parent_path = *path;
> >>>>
> >>>>                            next[0] = '/';
> >>>>                            remain_len -= filename_len + 1;
> >>>> @@ -1254,16 +1259,17 @@ int ksmbd_vfs_kern_path_locked(struct
> >>>> ksmbd_work
> >>>> *work, char *name,
> >>>>
> >>>>                    err = -EINVAL;
> >>>>    out2:
> >>>> -          path_put(&parent_path);
> >>>> +          path_put(parent_path);
> >>>>    out1:
> >>>>                    kfree(filepath);
> >>>>            }
> >>>>
> >>>>            if (!err) {
> >>>> -          err = ksmbd_vfs_lock_parent(parent_path.dentry, path->dentry);
> >>>> -          if (err)
> >>>> -                  dput(path->dentry);
> >>>> -          path_put(&parent_path);
> >>>> +          err = ksmbd_vfs_lock_parent(parent_path->dentry, path->dentry);
> >>>> +          if (err) {
> >>>> +                  path_put(path);
> >>>> +                  path_put(parent_path);
> >>>> +          }
> >>>>            }
> >>>>            return err;
> >>>>    }
> >>>> diff --git a/fs/smb/server/vfs.h b/fs/smb/server/vfs.h
> >>>> index 80039312c255..72f9fb4b48d1 100644
> >>>> --- a/fs/smb/server/vfs.h
> >>>> +++ b/fs/smb/server/vfs.h
> >>>> @@ -115,8 +115,8 @@ int ksmbd_vfs_xattr_stream_name(char *stream_name,
> >>>> char **xattr_stream_name,
> >>>>    int ksmbd_vfs_remove_xattr(struct mnt_idmap *idmap,
> >>>>                               const struct path *path, char *attr_name);
> >>>>    int ksmbd_vfs_kern_path_locked(struct ksmbd_work *work, char *name,
> >>>> -                         unsigned int flags, struct path *path,
> >>>> -                         bool caseless);
> >>>> +                         unsigned int flags, struct path *parent_path,
> >>>> +                         struct path *path, bool caseless);
> >>>>    struct dentry *ksmbd_vfs_kern_path_create(struct ksmbd_work *work,
> >>>>                                              const char *name,
> >>>>                                              unsigned int flags,
> >>>
> >>
> >



-- 
Thanks,

Steve




[Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux