Skip to content

Commit 265fd19

Browse files
hcleesmfrench
authored andcommitted
ksmbd: use LOOKUP_BENEATH to prevent the out of share access
instead of removing '..' in a given path, call kern_path with LOOKUP_BENEATH flag to prevent the out of share access. ran various test on this: smb2-cat-async smb://127.0.0.1/homes/../out_of_share smb2-cat-async smb://127.0.0.1/homes/foo/../../out_of_share smbclient //127.0.0.1/homes -c "mkdir ../foo2" smbclient //127.0.0.1/homes -c "rename bar ../bar" Cc: Ronnie Sahlberg <[email protected]> Cc: Ralph Boehme <[email protected]> Tested-by: Steve French <[email protected]> Tested-by: Namjae Jeon <[email protected]> Acked-by: Namjae Jeon <[email protected]> Signed-off-by: Hyunchul Lee <[email protected]> Signed-off-by: Steve French <[email protected]>
1 parent 4ea4779 commit 265fd19

File tree

5 files changed

+140
-206
lines changed

5 files changed

+140
-206
lines changed

fs/ksmbd/misc.c

Lines changed: 19 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -158,25 +158,21 @@ int parse_stream_name(char *filename, char **stream_name, int *s_type)
158158
* Return : windows path string or error
159159
*/
160160

161-
char *convert_to_nt_pathname(char *filename, char *sharepath)
161+
char *convert_to_nt_pathname(char *filename)
162162
{
163163
char *ab_pathname;
164-
int len, name_len;
165164

166-
name_len = strlen(filename);
167-
ab_pathname = kmalloc(name_len, GFP_KERNEL);
168-
if (!ab_pathname)
169-
return NULL;
170-
171-
ab_pathname[0] = '\\';
172-
ab_pathname[1] = '\0';
165+
if (strlen(filename) == 0) {
166+
ab_pathname = kmalloc(2, GFP_KERNEL);
167+
ab_pathname[0] = '\\';
168+
ab_pathname[1] = '\0';
169+
} else {
170+
ab_pathname = kstrdup(filename, GFP_KERNEL);
171+
if (!ab_pathname)
172+
return NULL;
173173

174-
len = strlen(sharepath);
175-
if (!strncmp(filename, sharepath, len) && name_len != len) {
176-
strscpy(ab_pathname, &filename[len], name_len);
177174
ksmbd_conv_path_to_windows(ab_pathname);
178175
}
179-
180176
return ab_pathname;
181177
}
182178

@@ -191,77 +187,19 @@ int get_nlink(struct kstat *st)
191187
return nlink;
192188
}
193189

194-
char *ksmbd_conv_path_to_unix(char *path)
190+
void ksmbd_conv_path_to_unix(char *path)
195191
{
196-
size_t path_len, remain_path_len, out_path_len;
197-
char *out_path, *out_next;
198-
int i, pre_dotdot_cnt = 0, slash_cnt = 0;
199-
bool is_last;
200-
201192
strreplace(path, '\\', '/');
202-
path_len = strlen(path);
203-
remain_path_len = path_len;
204-
if (path_len == 0)
205-
return ERR_PTR(-EINVAL);
206-
207-
out_path = kzalloc(path_len + 2, GFP_KERNEL);
208-
if (!out_path)
209-
return ERR_PTR(-ENOMEM);
210-
out_path_len = 0;
211-
out_next = out_path;
212-
213-
do {
214-
char *name = path + path_len - remain_path_len;
215-
char *next = strchrnul(name, '/');
216-
size_t name_len = next - name;
217-
218-
is_last = !next[0];
219-
if (name_len == 2 && name[0] == '.' && name[1] == '.') {
220-
pre_dotdot_cnt++;
221-
/* handle the case that path ends with "/.." */
222-
if (is_last)
223-
goto follow_dotdot;
224-
} else {
225-
if (pre_dotdot_cnt) {
226-
follow_dotdot:
227-
slash_cnt = 0;
228-
for (i = out_path_len - 1; i >= 0; i--) {
229-
if (out_path[i] == '/' &&
230-
++slash_cnt == pre_dotdot_cnt + 1)
231-
break;
232-
}
233-
234-
if (i < 0 &&
235-
slash_cnt != pre_dotdot_cnt) {
236-
kfree(out_path);
237-
return ERR_PTR(-EINVAL);
238-
}
239-
240-
out_next = &out_path[i+1];
241-
*out_next = '\0';
242-
out_path_len = i + 1;
243-
244-
}
245-
246-
if (name_len != 0 &&
247-
!(name_len == 1 && name[0] == '.') &&
248-
!(name_len == 2 && name[0] == '.' && name[1] == '.')) {
249-
next[0] = '\0';
250-
sprintf(out_next, "%s/", name);
251-
out_next += name_len + 1;
252-
out_path_len += name_len + 1;
253-
next[0] = '/';
254-
}
255-
pre_dotdot_cnt = 0;
256-
}
193+
}
257194

258-
remain_path_len -= name_len + 1;
259-
} while (!is_last);
195+
void ksmbd_strip_last_slash(char *path)
196+
{
197+
int len = strlen(path);
260198

261-
if (out_path_len > 0)
262-
out_path[out_path_len-1] = '\0';
263-
path[path_len] = '\0';
264-
return out_path;
199+
while (len && path[len - 1] == '/') {
200+
path[len - 1] = '\0';
201+
len--;
202+
}
265203
}
266204

267205
void ksmbd_conv_path_to_windows(char *path)
@@ -298,7 +236,7 @@ char *ksmbd_extract_sharename(char *treename)
298236
*
299237
* Return: converted name on success, otherwise NULL
300238
*/
301-
char *convert_to_unix_name(struct ksmbd_share_config *share, char *name)
239+
char *convert_to_unix_name(struct ksmbd_share_config *share, const char *name)
302240
{
303241
int no_slash = 0, name_len, path_len;
304242
char *new_name;

fs/ksmbd/misc.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,13 @@ struct ksmbd_file;
1414
int match_pattern(const char *str, size_t len, const char *pattern);
1515
int ksmbd_validate_filename(char *filename);
1616
int parse_stream_name(char *filename, char **stream_name, int *s_type);
17-
char *convert_to_nt_pathname(char *filename, char *sharepath);
17+
char *convert_to_nt_pathname(char *filename);
1818
int get_nlink(struct kstat *st);
19-
char *ksmbd_conv_path_to_unix(char *path);
19+
void ksmbd_conv_path_to_unix(char *path);
20+
void ksmbd_strip_last_slash(char *path);
2021
void ksmbd_conv_path_to_windows(char *path);
2122
char *ksmbd_extract_sharename(char *treename);
22-
char *convert_to_unix_name(struct ksmbd_share_config *share, char *name);
23+
char *convert_to_unix_name(struct ksmbd_share_config *share, const char *name);
2324

2425
#define KSMBD_DIR_INFO_ALIGNMENT 8
2526
struct ksmbd_dir_info;

fs/ksmbd/smb2pdu.c

Lines changed: 28 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -634,31 +634,17 @@ static char *
634634
smb2_get_name(struct ksmbd_share_config *share, const char *src,
635635
const int maxlen, struct nls_table *local_nls)
636636
{
637-
char *name, *norm_name, *unixname;
637+
char *name;
638638

639639
name = smb_strndup_from_utf16(src, maxlen, 1, local_nls);
640640
if (IS_ERR(name)) {
641641
pr_err("failed to get name %ld\n", PTR_ERR(name));
642642
return name;
643643
}
644644

645-
/* change it to absolute unix name */
646-
norm_name = ksmbd_conv_path_to_unix(name);
647-
if (IS_ERR(norm_name)) {
648-
kfree(name);
649-
return norm_name;
650-
}
651-
kfree(name);
652-
653-
unixname = convert_to_unix_name(share, norm_name);
654-
kfree(norm_name);
655-
if (!unixname) {
656-
pr_err("can not convert absolute name\n");
657-
return ERR_PTR(-ENOMEM);
658-
}
659-
660-
ksmbd_debug(SMB, "absolute name = %s\n", unixname);
661-
return unixname;
645+
ksmbd_conv_path_to_unix(name);
646+
ksmbd_strip_last_slash(name);
647+
return name;
662648
}
663649

664650
int setup_async_work(struct ksmbd_work *work, void (*fn)(void **), void **arg)
@@ -2352,7 +2338,7 @@ static int smb2_creat(struct ksmbd_work *work, struct path *path, char *name,
23522338
return rc;
23532339
}
23542340

2355-
rc = ksmbd_vfs_kern_path(name, 0, path, 0);
2341+
rc = ksmbd_vfs_kern_path(work, name, 0, path, 0);
23562342
if (rc) {
23572343
pr_err("cannot get linux path (%s), err = %d\n",
23582344
name, rc);
@@ -2427,7 +2413,7 @@ int smb2_open(struct ksmbd_work *work)
24272413
struct oplock_info *opinfo;
24282414
__le32 *next_ptr = NULL;
24292415
int req_op_level = 0, open_flags = 0, may_flags = 0, file_info = 0;
2430-
int rc = 0, len = 0;
2416+
int rc = 0;
24312417
int contxt_cnt = 0, query_disk_id = 0;
24322418
int maximal_access_ctxt = 0, posix_ctxt = 0;
24332419
int s_type = 0;
@@ -2499,17 +2485,11 @@ int smb2_open(struct ksmbd_work *work)
24992485
goto err_out1;
25002486
}
25012487
} else {
2502-
len = strlen(share->path);
2503-
ksmbd_debug(SMB, "share path len %d\n", len);
2504-
name = kmalloc(len + 1, GFP_KERNEL);
2488+
name = kstrdup("", GFP_KERNEL);
25052489
if (!name) {
2506-
rsp->hdr.Status = STATUS_NO_MEMORY;
25072490
rc = -ENOMEM;
25082491
goto err_out1;
25092492
}
2510-
2511-
memcpy(name, share->path, len);
2512-
*(name + len) = '\0';
25132493
}
25142494

25152495
req_op_level = req->RequestedOplockLevel;
@@ -2632,7 +2612,7 @@ int smb2_open(struct ksmbd_work *work)
26322612
goto err_out1;
26332613
}
26342614

2635-
rc = ksmbd_vfs_kern_path(name, LOOKUP_NO_SYMLINKS, &path, 1);
2615+
rc = ksmbd_vfs_kern_path(work, name, LOOKUP_NO_SYMLINKS, &path, 1);
26362616
if (!rc) {
26372617
if (req->CreateOptions & FILE_DELETE_ON_CLOSE_LE) {
26382618
/*
@@ -2661,11 +2641,8 @@ int smb2_open(struct ksmbd_work *work)
26612641
}
26622642

26632643
if (rc) {
2664-
if (rc == -EACCES) {
2665-
ksmbd_debug(SMB,
2666-
"User does not have right permission\n");
2644+
if (rc != -ENOENT)
26672645
goto err_out;
2668-
}
26692646
ksmbd_debug(SMB, "can not get linux path for %s, rc = %d\n",
26702647
name, rc);
26712648
rc = 0;
@@ -3161,7 +3138,7 @@ int smb2_open(struct ksmbd_work *work)
31613138
rsp->hdr.Status = STATUS_INVALID_PARAMETER;
31623139
else if (rc == -EOPNOTSUPP)
31633140
rsp->hdr.Status = STATUS_NOT_SUPPORTED;
3164-
else if (rc == -EACCES || rc == -ESTALE)
3141+
else if (rc == -EACCES || rc == -ESTALE || rc == -EXDEV)
31653142
rsp->hdr.Status = STATUS_ACCESS_DENIED;
31663143
else if (rc == -ENOENT)
31673144
rsp->hdr.Status = STATUS_OBJECT_NAME_INVALID;
@@ -4277,8 +4254,7 @@ static int get_file_all_info(struct ksmbd_work *work,
42774254
return -EACCES;
42784255
}
42794256

4280-
filename = convert_to_nt_pathname(fp->filename,
4281-
work->tcon->share_conf->path);
4257+
filename = convert_to_nt_pathname(fp->filename);
42824258
if (!filename)
42834259
return -ENOMEM;
42844260

@@ -4733,7 +4709,7 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work,
47334709
int rc = 0, len;
47344710
int fs_infoclass_size = 0;
47354711

4736-
rc = ksmbd_vfs_kern_path(share->path, LOOKUP_NO_SYMLINKS, &path, 0);
4712+
rc = kern_path(share->path, LOOKUP_NO_SYMLINKS, &path);
47374713
if (rc) {
47384714
pr_err("cannot create vfs path\n");
47394715
return -EIO;
@@ -5282,7 +5258,7 @@ static int smb2_rename(struct ksmbd_work *work,
52825258
goto out;
52835259

52845260
len = strlen(new_name);
5285-
if (new_name[len - 1] != '/') {
5261+
if (len > 0 && new_name[len - 1] != '/') {
52865262
pr_err("not allow base filename in rename\n");
52875263
rc = -ESHARE;
52885264
goto out;
@@ -5310,11 +5286,14 @@ static int smb2_rename(struct ksmbd_work *work,
53105286
}
53115287

53125288
ksmbd_debug(SMB, "new name %s\n", new_name);
5313-
rc = ksmbd_vfs_kern_path(new_name, LOOKUP_NO_SYMLINKS, &path, 1);
5314-
if (rc)
5289+
rc = ksmbd_vfs_kern_path(work, new_name, LOOKUP_NO_SYMLINKS, &path, 1);
5290+
if (rc) {
5291+
if (rc != -ENOENT)
5292+
goto out;
53155293
file_present = false;
5316-
else
5294+
} else {
53175295
path_put(&path);
5296+
}
53185297

53195298
if (ksmbd_share_veto_filename(share, new_name)) {
53205299
rc = -ENOENT;
@@ -5384,11 +5363,14 @@ static int smb2_create_link(struct ksmbd_work *work,
53845363
}
53855364

53865365
ksmbd_debug(SMB, "target name is %s\n", target_name);
5387-
rc = ksmbd_vfs_kern_path(link_name, LOOKUP_NO_SYMLINKS, &path, 0);
5388-
if (rc)
5366+
rc = ksmbd_vfs_kern_path(work, link_name, LOOKUP_NO_SYMLINKS, &path, 0);
5367+
if (rc) {
5368+
if (rc != -ENOENT)
5369+
goto out;
53895370
file_present = false;
5390-
else
5371+
} else {
53915372
path_put(&path);
5373+
}
53925374

53935375
if (file_info->ReplaceIfExists) {
53945376
if (file_present) {
@@ -5548,7 +5530,7 @@ static int set_file_allocation_info(struct ksmbd_work *work,
55485530
* inode size is retained by backup inode size.
55495531
*/
55505532
size = i_size_read(inode);
5551-
rc = ksmbd_vfs_truncate(work, NULL, fp, alloc_blks * 512);
5533+
rc = ksmbd_vfs_truncate(work, fp, alloc_blks * 512);
55525534
if (rc) {
55535535
pr_err("truncate failed! filename : %s, err %d\n",
55545536
fp->filename, rc);
@@ -5585,7 +5567,7 @@ static int set_end_of_file_info(struct ksmbd_work *work, struct ksmbd_file *fp,
55855567
if (inode->i_sb->s_magic != MSDOS_SUPER_MAGIC) {
55865568
ksmbd_debug(SMB, "filename : %s truncated to newsize %lld\n",
55875569
fp->filename, newsize);
5588-
rc = ksmbd_vfs_truncate(work, NULL, fp, newsize);
5570+
rc = ksmbd_vfs_truncate(work, fp, newsize);
55895571
if (rc) {
55905572
ksmbd_debug(SMB, "truncate failed! filename : %s err %d\n",
55915573
fp->filename, rc);
@@ -5862,7 +5844,7 @@ int smb2_set_info(struct ksmbd_work *work)
58625844
return 0;
58635845

58645846
err_out:
5865-
if (rc == -EACCES || rc == -EPERM)
5847+
if (rc == -EACCES || rc == -EPERM || rc == -EXDEV)
58665848
rsp->hdr.Status = STATUS_ACCESS_DENIED;
58675849
else if (rc == -EINVAL)
58685850
rsp->hdr.Status = STATUS_INVALID_PARAMETER;

0 commit comments

Comments
 (0)