]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
ksmbd: fix path resolution in ksmbd_vfs_kern_path_create
authorDavide Ornaghi <d.ornaghi97@gmail.com>
Mon, 15 Jun 2026 11:35:01 +0000 (20:35 +0900)
committerSteve French <stfrench@microsoft.com>
Tue, 16 Jun 2026 23:57:22 +0000 (18:57 -0500)
The SMB2 open lookup is rooted at the share with LOOKUP_BENEATH, but the
create/mkdir/hardlink sink is not: ksmbd_vfs_kern_path_create() builds an
absolute path with convert_to_unix_name() and resolves it from AT_FDCWD
via start_creating_path(), so a ".." component is walked from the real
filesystem root and escapes the export.

An authenticated client races a missing path component so the rooted open
lookup returns -ENOENT (taking the create branch) while the same component
is present (a directory) when the create walk runs; the create then
resolves ".." out of the share.

Root the create walk at the share like the lookup and rename paths already
are: resolve the parent with vfs_path_parent_lookup(..., LOOKUP_BENEATH,
&share_conf->vfs_path) and create the final component with
start_creating_noperm(). convert_to_unix_name() then has no callers and is
removed.

Fixes: 265fd1991c1d ("ksmbd: use LOOKUP_BENEATH to prevent the out of share access")
Cc: stable@vger.kernel.org
Signed-off-by: Davide Ornaghi <d.ornaghi97@gmail.com>
Acked-by: Namjae Jeon <linkinjeon@kernel.org>
Signed-off-by: Steve French <stfrench@microsoft.com>
fs/smb/server/misc.c
fs/smb/server/misc.h
fs/smb/server/vfs.c

index a543ec9d3581778b952bcd89178a36656d5d430f..966004c414a8c8b7b55d8762aa739507b107b777 100644 (file)
@@ -283,39 +283,6 @@ char *ksmbd_extract_sharename(struct unicode_map *um, const char *treename)
        return ksmbd_casefold_sharename(um, name);
 }
 
-/**
- * convert_to_unix_name() - convert windows name to unix format
- * @share:     ksmbd_share_config pointer
- * @name:      file name that is relative to share
- *
- * Return:     converted name on success, otherwise NULL
- */
-char *convert_to_unix_name(struct ksmbd_share_config *share, const char *name)
-{
-       int no_slash = 0, name_len, path_len;
-       char *new_name;
-
-       if (name[0] == '/')
-               name++;
-
-       path_len = share->path_sz;
-       name_len = strlen(name);
-       new_name = kmalloc(path_len + name_len + 2, KSMBD_DEFAULT_GFP);
-       if (!new_name)
-               return new_name;
-
-       memcpy(new_name, share->path, path_len);
-       if (new_name[path_len - 1] != '/') {
-               new_name[path_len] = '/';
-               no_slash = 1;
-       }
-
-       memcpy(new_name + path_len + no_slash, name, name_len);
-       path_len += name_len + no_slash;
-       new_name[path_len] = 0x00;
-       return new_name;
-}
-
 char *ksmbd_convert_dir_info_name(struct ksmbd_dir_info *d_info,
                                  const struct nls_table *local_nls,
                                  int *conv_len)
index 13423696ae8c62f81f19e59607d378143347b673..3909104e18ad4858c8d37934dd3b78807bb7e451 100644 (file)
@@ -25,7 +25,6 @@ void ksmbd_strip_last_slash(char *path);
 void ksmbd_conv_path_to_windows(char *path);
 char *ksmbd_casefold_sharename(struct unicode_map *um, const char *name);
 char *ksmbd_extract_sharename(struct unicode_map *um, const char *treename);
-char *convert_to_unix_name(struct ksmbd_share_config *share, const char *name);
 
 #define KSMBD_DIR_INFO_ALIGNMENT       8
 struct ksmbd_dir_info;
index fe376453a51986ab69e1b5fbe80ff403bba56bdf..74b0307cb1009595af5a48a5ff60555535334301 100644 (file)
@@ -1259,15 +1259,30 @@ struct dentry *ksmbd_vfs_kern_path_create(struct ksmbd_work *work,
                                          unsigned int flags,
                                          struct path *path)
 {
-       char *abs_name;
+       struct ksmbd_share_config *share_conf = work->tcon->share_conf;
+       struct qstr last;
        struct dentry *dent;
+       int err;
 
-       abs_name = convert_to_unix_name(work->tcon->share_conf, name);
-       if (!abs_name)
-               return ERR_PTR(-ENOMEM);
+       /* resolve the name beneath the share root so ".." cannot escape */
+       CLASS(filename_kernel, filename)(name);
 
-       dent = start_creating_path(AT_FDCWD, abs_name, path, flags);
-       kfree(abs_name);
+       err = vfs_path_parent_lookup(filename, flags | LOOKUP_BENEATH,
+                                    path, &last, &share_conf->vfs_path);
+       if (err)
+               return ERR_PTR(err);
+
+       err = mnt_want_write(path->mnt);
+       if (err) {
+               path_put(path);
+               return ERR_PTR(err);
+       }
+
+       dent = start_creating_noperm(path->dentry, &last);
+       if (IS_ERR(dent)) {
+               mnt_drop_write(path->mnt);
+               path_put(path);
+       }
        return dent;
 }