diff options
author | Thiago Rafael Becker <trbecker@gmail.com> | 2021-12-17 15:20:22 -0300 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2021-12-22 09:32:48 +0100 |
commit | c63433a09d6ae4c226fcbc66da4c58fc189fd746 (patch) | |
tree | 70523e7fb9917eaf37012f2c3626c8a346eacfce /fs/cifs | |
parent | c5664d508674c77a52d311af8b1e11d08ac0cf4b (diff) | |
download | linux-stable-c63433a09d6ae4c226fcbc66da4c58fc189fd746.tar.gz linux-stable-c63433a09d6ae4c226fcbc66da4c58fc189fd746.tar.bz2 linux-stable-c63433a09d6ae4c226fcbc66da4c58fc189fd746.zip |
cifs: sanitize multiple delimiters in prepath
commit a31080899d5fdafcccf7f39dd214a814a2c82626 upstream.
mount.cifs can pass a device with multiple delimiters in it. This will
cause rename(2) to fail with ENOENT.
V2:
- Make sanitize_path more readable.
- Fix multiple delimiters between UNC and prepath.
- Avoid a memory leak if a bad user starts putting a lot of delimiters
in the path on purpose.
BugLink: https://bugzilla.redhat.com/show_bug.cgi?id=2031200
Fixes: 24e0a1eff9e2 ("cifs: switch to new mount api")
Cc: stable@vger.kernel.org # 5.11+
Acked-by: Ronnie Sahlberg <lsahlber@redhat.com>
Signed-off-by: Thiago Rafael Becker <trbecker@gmail.com>
Signed-off-by: Steve French <stfrench@microsoft.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'fs/cifs')
-rw-r--r-- | fs/cifs/fs_context.c | 38 |
1 files changed, 37 insertions, 1 deletions
diff --git a/fs/cifs/fs_context.c b/fs/cifs/fs_context.c index 0e660d6ade20..0a2542286552 100644 --- a/fs/cifs/fs_context.c +++ b/fs/cifs/fs_context.c @@ -432,6 +432,42 @@ out: } /* + * Remove duplicate path delimiters. Windows is supposed to do that + * but there are some bugs that prevent rename from working if there are + * multiple delimiters. + * + * Returns a sanitized duplicate of @path. The caller is responsible for + * cleaning up the original. + */ +#define IS_DELIM(c) ((c) == '/' || (c) == '\\') +static char *sanitize_path(char *path) +{ + char *cursor1 = path, *cursor2 = path; + + /* skip all prepended delimiters */ + while (IS_DELIM(*cursor1)) + cursor1++; + + /* copy the first letter */ + *cursor2 = *cursor1; + + /* copy the remainder... */ + while (*(cursor1++)) { + /* ... skipping all duplicated delimiters */ + if (IS_DELIM(*cursor1) && IS_DELIM(*cursor2)) + continue; + *(++cursor2) = *cursor1; + } + + /* if the last character is a delimiter, skip it */ + if (IS_DELIM(*(cursor2 - 1))) + cursor2--; + + *(cursor2) = '\0'; + return kstrdup(path, GFP_KERNEL); +} + +/* * Parse a devname into substrings and populate the ctx->UNC and ctx->prepath * fields with the result. Returns 0 on success and an error otherwise * (e.g. ENOMEM or EINVAL) @@ -490,7 +526,7 @@ smb3_parse_devname(const char *devname, struct smb3_fs_context *ctx) if (!*pos) return 0; - ctx->prepath = kstrdup(pos, GFP_KERNEL); + ctx->prepath = sanitize_path(pos); if (!ctx->prepath) return -ENOMEM; |