summaryrefslogtreecommitdiff
path: root/fs/ksmbd/misc.c
diff options
context:
space:
mode:
authorHyunchul Lee <hyc.lee@gmail.com>2021-09-17 22:14:08 +0900
committerSteve French <stfrench@microsoft.com>2021-09-17 17:18:48 -0500
commitf58eae6c5fa882d6d0a6b7587a099602a59d57b5 (patch)
tree348ec6f925414c7a37b5f5c2f94a7b0c9b5a0392 /fs/ksmbd/misc.c
parenta9b3043de47b7f8cbe38c36aee572526665b6315 (diff)
ksmbd: prevent out of share access
Because of .., files outside the share directory could be accessed. To prevent this, normalize the given path and remove all . and .. components. In addition to the usual large set of regression tests (smbtorture and xfstests), ran various tests on this to specifically check path name validation including libsmb2 tests to verify path normalization: ./examples/smb2-ls-async smb://172.30.1.15/homes2/../ ./examples/smb2-ls-async smb://172.30.1.15/homes2/foo/../ ./examples/smb2-ls-async smb://172.30.1.15/homes2/foo/../../ ./examples/smb2-ls-async smb://172.30.1.15/homes2/foo/../ ./examples/smb2-ls-async smb://172.30.1.15/homes2/foo/..bar/ ./examples/smb2-ls-async smb://172.30.1.15/homes2/foo/bar../ ./examples/smb2-ls-async smb://172.30.1.15/homes2/foo/bar.. ./examples/smb2-ls-async smb://172.30.1.15/homes2/foo/bar../../../../ Signed-off-by: Hyunchul Lee <hyc.lee@gmail.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org> Signed-off-by: Steve French <stfrench@microsoft.com>
Diffstat (limited to 'fs/ksmbd/misc.c')
-rw-r--r--fs/ksmbd/misc.c76
1 files changed, 67 insertions, 9 deletions
diff --git a/fs/ksmbd/misc.c b/fs/ksmbd/misc.c
index 0b307ca28a19..3eac3c01749f 100644
--- a/fs/ksmbd/misc.c
+++ b/fs/ksmbd/misc.c
@@ -191,19 +191,77 @@ int get_nlink(struct kstat *st)
return nlink;
}
-void ksmbd_conv_path_to_unix(char *path)
+char *ksmbd_conv_path_to_unix(char *path)
{
+ size_t path_len, remain_path_len, out_path_len;
+ char *out_path, *out_next;
+ int i, pre_dotdot_cnt = 0, slash_cnt = 0;
+ bool is_last;
+
strreplace(path, '\\', '/');
-}
+ path_len = strlen(path);
+ remain_path_len = path_len;
+ if (path_len == 0)
+ return ERR_PTR(-EINVAL);
-void ksmbd_strip_last_slash(char *path)
-{
- int len = strlen(path);
+ out_path = kzalloc(path_len + 2, GFP_KERNEL);
+ if (!out_path)
+ return ERR_PTR(-ENOMEM);
+ out_path_len = 0;
+ out_next = out_path;
+
+ do {
+ char *name = path + path_len - remain_path_len;
+ char *next = strchrnul(name, '/');
+ size_t name_len = next - name;
+
+ is_last = !next[0];
+ if (name_len == 2 && name[0] == '.' && name[1] == '.') {
+ pre_dotdot_cnt++;
+ /* handle the case that path ends with "/.." */
+ if (is_last)
+ goto follow_dotdot;
+ } else {
+ if (pre_dotdot_cnt) {
+follow_dotdot:
+ slash_cnt = 0;
+ for (i = out_path_len - 1; i >= 0; i--) {
+ if (out_path[i] == '/' &&
+ ++slash_cnt == pre_dotdot_cnt + 1)
+ break;
+ }
+
+ if (i < 0 &&
+ slash_cnt != pre_dotdot_cnt) {
+ kfree(out_path);
+ return ERR_PTR(-EINVAL);
+ }
+
+ out_next = &out_path[i+1];
+ *out_next = '\0';
+ out_path_len = i + 1;
- while (len && path[len - 1] == '/') {
- path[len - 1] = '\0';
- len--;
- }
+ }
+
+ if (name_len != 0 &&
+ !(name_len == 1 && name[0] == '.') &&
+ !(name_len == 2 && name[0] == '.' && name[1] == '.')) {
+ next[0] = '\0';
+ sprintf(out_next, "%s/", name);
+ out_next += name_len + 1;
+ out_path_len += name_len + 1;
+ next[0] = '/';
+ }
+ pre_dotdot_cnt = 0;
+ }
+
+ remain_path_len -= name_len + 1;
+ } while (!is_last);
+
+ if (out_path_len > 0)
+ out_path[out_path_len-1] = '\0';
+ path[path_len] = '\0';
+ return out_path;
}
void ksmbd_conv_path_to_windows(char *path)