Skip to content

Commit 2b57a43

Browse files
namjaejeonsmfrench
authored andcommitted
ksmbd: check if a mount point is crossed during path lookup
Since commit 74d7970 ("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. And allow path lookup to cross a mount point when a crossmnt parameter is set to 'yes' in smb.conf. Cc: [email protected] Fixes: 74d7970 ("ksmbd: fix racy issue from using ->d_parent and ->d_name") Signed-off-by: Namjae Jeon <[email protected]> Signed-off-by: Steve French <[email protected]>
1 parent 0266a2f commit 2b57a43

File tree

4 files changed

+53
-39
lines changed

4 files changed

+53
-39
lines changed

fs/smb/server/ksmbd_netlink.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -352,7 +352,8 @@ enum KSMBD_TREE_CONN_STATUS {
352352
#define KSMBD_SHARE_FLAG_STREAMS BIT(11)
353353
#define KSMBD_SHARE_FLAG_FOLLOW_SYMLINKS BIT(12)
354354
#define KSMBD_SHARE_FLAG_ACL_XATTR BIT(13)
355-
#define KSMBD_SHARE_FLAG_UPDATE BIT(14)
355+
#define KSMBD_SHARE_FLAG_UPDATE BIT(14)
356+
#define KSMBD_SHARE_FLAG_CROSSMNT BIT(15)
356357

357358
/*
358359
* Tree connect request flags.

fs/smb/server/smb2pdu.c

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2467,8 +2467,9 @@ static void smb2_update_xattrs(struct ksmbd_tree_connect *tcon,
24672467
}
24682468
}
24692469

2470-
static int smb2_creat(struct ksmbd_work *work, struct path *path, char *name,
2471-
int open_flags, umode_t posix_mode, bool is_dir)
2470+
static int smb2_creat(struct ksmbd_work *work, struct path *parent_path,
2471+
struct path *path, char *name, int open_flags,
2472+
umode_t posix_mode, bool is_dir)
24722473
{
24732474
struct ksmbd_tree_connect *tcon = work->tcon;
24742475
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,
24952496
return rc;
24962497
}
24972498

2498-
rc = ksmbd_vfs_kern_path_locked(work, name, 0, path, 0);
2499+
rc = ksmbd_vfs_kern_path_locked(work, name, 0, parent_path, path, 0);
24992500
if (rc) {
25002501
pr_err("cannot get linux path (%s), err = %d\n",
25012502
name, rc);
@@ -2565,7 +2566,7 @@ int smb2_open(struct ksmbd_work *work)
25652566
struct ksmbd_tree_connect *tcon = work->tcon;
25662567
struct smb2_create_req *req;
25672568
struct smb2_create_rsp *rsp;
2568-
struct path path;
2569+
struct path path, parent_path;
25692570
struct ksmbd_share_config *share = tcon->share_conf;
25702571
struct ksmbd_file *fp = NULL;
25712572
struct file *filp = NULL;
@@ -2786,7 +2787,8 @@ int smb2_open(struct ksmbd_work *work)
27862787
goto err_out1;
27872788
}
27882789

2789-
rc = ksmbd_vfs_kern_path_locked(work, name, LOOKUP_NO_SYMLINKS, &path, 1);
2790+
rc = ksmbd_vfs_kern_path_locked(work, name, LOOKUP_NO_SYMLINKS,
2791+
&parent_path, &path, 1);
27902792
if (!rc) {
27912793
file_present = true;
27922794

@@ -2906,7 +2908,8 @@ int smb2_open(struct ksmbd_work *work)
29062908

29072909
/*create file if not present */
29082910
if (!file_present) {
2909-
rc = smb2_creat(work, &path, name, open_flags, posix_mode,
2911+
rc = smb2_creat(work, &parent_path, &path, name, open_flags,
2912+
posix_mode,
29102913
req->CreateOptions & FILE_DIRECTORY_FILE_LE);
29112914
if (rc) {
29122915
if (rc == -ENOENT) {
@@ -3321,8 +3324,9 @@ int smb2_open(struct ksmbd_work *work)
33213324

33223325
err_out:
33233326
if (file_present || created) {
3324-
inode_unlock(d_inode(path.dentry->d_parent));
3325-
dput(path.dentry);
3327+
inode_unlock(d_inode(parent_path.dentry));
3328+
path_put(&path);
3329+
path_put(&parent_path);
33263330
}
33273331
ksmbd_revert_fsids(work);
33283332
err_out1:
@@ -5545,7 +5549,7 @@ static int smb2_create_link(struct ksmbd_work *work,
55455549
struct nls_table *local_nls)
55465550
{
55475551
char *link_name = NULL, *target_name = NULL, *pathname = NULL;
5548-
struct path path;
5552+
struct path path, parent_path;
55495553
bool file_present = false;
55505554
int rc;
55515555

@@ -5575,7 +5579,7 @@ static int smb2_create_link(struct ksmbd_work *work,
55755579

55765580
ksmbd_debug(SMB, "target name is %s\n", target_name);
55775581
rc = ksmbd_vfs_kern_path_locked(work, link_name, LOOKUP_NO_SYMLINKS,
5578-
&path, 0);
5582+
&parent_path, &path, 0);
55795583
if (rc) {
55805584
if (rc != -ENOENT)
55815585
goto out;
@@ -5605,8 +5609,9 @@ static int smb2_create_link(struct ksmbd_work *work,
56055609
rc = -EINVAL;
56065610
out:
56075611
if (file_present) {
5608-
inode_unlock(d_inode(path.dentry->d_parent));
5612+
inode_unlock(d_inode(parent_path.dentry));
56095613
path_put(&path);
5614+
path_put(&parent_path);
56105615
}
56115616
if (!IS_ERR(link_name))
56125617
kfree(link_name);

fs/smb/server/vfs.c

Lines changed: 33 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -63,13 +63,13 @@ int ksmbd_vfs_lock_parent(struct dentry *parent, struct dentry *child)
6363

6464
static int ksmbd_vfs_path_lookup_locked(struct ksmbd_share_config *share_conf,
6565
char *pathname, unsigned int flags,
66+
struct path *parent_path,
6667
struct path *path)
6768
{
6869
struct qstr last;
6970
struct filename *filename;
7071
struct path *root_share_path = &share_conf->vfs_path;
7172
int err, type;
72-
struct path parent_path;
7373
struct dentry *d;
7474

7575
if (pathname[0] == '\0') {
@@ -84,21 +84,21 @@ static int ksmbd_vfs_path_lookup_locked(struct ksmbd_share_config *share_conf,
8484
return PTR_ERR(filename);
8585

8686
err = vfs_path_parent_lookup(filename, flags,
87-
&parent_path, &last, &type,
87+
parent_path, &last, &type,
8888
root_share_path);
8989
if (err) {
9090
putname(filename);
9191
return err;
9292
}
9393

9494
if (unlikely(type != LAST_NORM)) {
95-
path_put(&parent_path);
95+
path_put(parent_path);
9696
putname(filename);
9797
return -ENOENT;
9898
}
9999

100-
inode_lock_nested(parent_path.dentry->d_inode, I_MUTEX_PARENT);
101-
d = lookup_one_qstr_excl(&last, parent_path.dentry, 0);
100+
inode_lock_nested(parent_path->dentry->d_inode, I_MUTEX_PARENT);
101+
d = lookup_one_qstr_excl(&last, parent_path->dentry, 0);
102102
if (IS_ERR(d))
103103
goto err_out;
104104

@@ -108,15 +108,22 @@ static int ksmbd_vfs_path_lookup_locked(struct ksmbd_share_config *share_conf,
108108
}
109109

110110
path->dentry = d;
111-
path->mnt = share_conf->vfs_path.mnt;
112-
path_put(&parent_path);
113-
putname(filename);
111+
path->mnt = mntget(parent_path->mnt);
112+
113+
if (test_share_config_flag(share_conf, KSMBD_SHARE_FLAG_CROSSMNT)) {
114+
err = follow_down(path, 0);
115+
if (err < 0) {
116+
path_put(path);
117+
goto err_out;
118+
}
119+
}
114120

121+
putname(filename);
115122
return 0;
116123

117124
err_out:
118-
inode_unlock(parent_path.dentry->d_inode);
119-
path_put(&parent_path);
125+
inode_unlock(d_inode(parent_path->dentry));
126+
path_put(parent_path);
120127
putname(filename);
121128
return -ENOENT;
122129
}
@@ -1195,14 +1202,14 @@ static int ksmbd_vfs_lookup_in_dir(const struct path *dir, char *name,
11951202
* Return: 0 on success, otherwise error
11961203
*/
11971204
int ksmbd_vfs_kern_path_locked(struct ksmbd_work *work, char *name,
1198-
unsigned int flags, struct path *path,
1199-
bool caseless)
1205+
unsigned int flags, struct path *parent_path,
1206+
struct path *path, bool caseless)
12001207
{
12011208
struct ksmbd_share_config *share_conf = work->tcon->share_conf;
12021209
int err;
1203-
struct path parent_path;
12041210

1205-
err = ksmbd_vfs_path_lookup_locked(share_conf, name, flags, path);
1211+
err = ksmbd_vfs_path_lookup_locked(share_conf, name, flags, parent_path,
1212+
path);
12061213
if (!err)
12071214
return 0;
12081215

@@ -1217,10 +1224,10 @@ int ksmbd_vfs_kern_path_locked(struct ksmbd_work *work, char *name,
12171224
path_len = strlen(filepath);
12181225
remain_len = path_len;
12191226

1220-
parent_path = share_conf->vfs_path;
1221-
path_get(&parent_path);
1227+
*parent_path = share_conf->vfs_path;
1228+
path_get(parent_path);
12221229

1223-
while (d_can_lookup(parent_path.dentry)) {
1230+
while (d_can_lookup(parent_path->dentry)) {
12241231
char *filename = filepath + path_len - remain_len;
12251232
char *next = strchrnul(filename, '/');
12261233
size_t filename_len = next - filename;
@@ -1229,7 +1236,7 @@ int ksmbd_vfs_kern_path_locked(struct ksmbd_work *work, char *name,
12291236
if (filename_len == 0)
12301237
break;
12311238

1232-
err = ksmbd_vfs_lookup_in_dir(&parent_path, filename,
1239+
err = ksmbd_vfs_lookup_in_dir(parent_path, filename,
12331240
filename_len,
12341241
work->conn->um);
12351242
if (err)
@@ -1246,25 +1253,26 @@ int ksmbd_vfs_kern_path_locked(struct ksmbd_work *work, char *name,
12461253
goto out2;
12471254
else if (is_last)
12481255
goto out1;
1249-
path_put(&parent_path);
1250-
parent_path = *path;
1256+
path_put(parent_path);
1257+
*parent_path = *path;
12511258

12521259
next[0] = '/';
12531260
remain_len -= filename_len + 1;
12541261
}
12551262

12561263
err = -EINVAL;
12571264
out2:
1258-
path_put(&parent_path);
1265+
path_put(parent_path);
12591266
out1:
12601267
kfree(filepath);
12611268
}
12621269

12631270
if (!err) {
1264-
err = ksmbd_vfs_lock_parent(parent_path.dentry, path->dentry);
1265-
if (err)
1266-
dput(path->dentry);
1267-
path_put(&parent_path);
1271+
err = ksmbd_vfs_lock_parent(parent_path->dentry, path->dentry);
1272+
if (err) {
1273+
path_put(path);
1274+
path_put(parent_path);
1275+
}
12681276
}
12691277
return err;
12701278
}

fs/smb/server/vfs.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,8 +115,8 @@ int ksmbd_vfs_xattr_stream_name(char *stream_name, char **xattr_stream_name,
115115
int ksmbd_vfs_remove_xattr(struct mnt_idmap *idmap,
116116
const struct path *path, char *attr_name);
117117
int ksmbd_vfs_kern_path_locked(struct ksmbd_work *work, char *name,
118-
unsigned int flags, struct path *path,
119-
bool caseless);
118+
unsigned int flags, struct path *parent_path,
119+
struct path *path, bool caseless);
120120
struct dentry *ksmbd_vfs_kern_path_create(struct ksmbd_work *work,
121121
const char *name,
122122
unsigned int flags,

0 commit comments

Comments
 (0)