Skip to content

Commit f40125c

Browse files
committed
Merge tag '6.5-rc3-ksmbd-server-fixes' of git://git.samba.org/ksmbd
Pull ksmbd server fixes from Steve French: - fixes for two possible out of bounds access (in negotiate, and in decrypt msg) - fix unsigned compared to zero warning - fix path lookup crossing a mountpoint - fix case when first compound request is a tree connect - fix memory leak if reads are compounded * tag '6.5-rc3-ksmbd-server-fixes' of git://git.samba.org/ksmbd: ksmbd: fix out of bounds in init_smb2_rsp_hdr() ksmbd: no response from compound read ksmbd: validate session id and tree id in compound request ksmbd: fix out of bounds in smb3_decrypt_req() ksmbd: check if a mount point is crossed during path lookup ksmbd: Fix unsigned expression compared with zero
2 parents 5f0bc0b + 536bb49 commit f40125c

File tree

7 files changed

+88
-59
lines changed

7 files changed

+88
-59
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/server.c

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,7 @@ static void handle_ksmbd_work(struct work_struct *wk)
286286
static int queue_ksmbd_work(struct ksmbd_conn *conn)
287287
{
288288
struct ksmbd_work *work;
289+
int err;
289290

290291
work = ksmbd_alloc_work_struct();
291292
if (!work) {
@@ -297,7 +298,11 @@ static int queue_ksmbd_work(struct ksmbd_conn *conn)
297298
work->request_buf = conn->request_buf;
298299
conn->request_buf = NULL;
299300

300-
ksmbd_init_smb_server(work);
301+
err = ksmbd_init_smb_server(work);
302+
if (err) {
303+
ksmbd_free_work_struct(work);
304+
return 0;
305+
}
301306

302307
ksmbd_conn_enqueue_request(work);
303308
atomic_inc(&conn->r_count);

fs/smb/server/smb2pdu.c

Lines changed: 29 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -87,9 +87,9 @@ struct channel *lookup_chann_list(struct ksmbd_session *sess, struct ksmbd_conn
8787
*/
8888
int smb2_get_ksmbd_tcon(struct ksmbd_work *work)
8989
{
90-
struct smb2_hdr *req_hdr = smb2_get_msg(work->request_buf);
90+
struct smb2_hdr *req_hdr = ksmbd_req_buf_next(work);
9191
unsigned int cmd = le16_to_cpu(req_hdr->Command);
92-
int tree_id;
92+
unsigned int tree_id;
9393

9494
if (cmd == SMB2_TREE_CONNECT_HE ||
9595
cmd == SMB2_CANCEL_HE ||
@@ -114,7 +114,7 @@ int smb2_get_ksmbd_tcon(struct ksmbd_work *work)
114114
pr_err("The first operation in the compound does not have tcon\n");
115115
return -EINVAL;
116116
}
117-
if (work->tcon->id != tree_id) {
117+
if (tree_id != UINT_MAX && work->tcon->id != tree_id) {
118118
pr_err("tree id(%u) is different with id(%u) in first operation\n",
119119
tree_id, work->tcon->id);
120120
return -EINVAL;
@@ -559,9 +559,9 @@ int smb2_allocate_rsp_buf(struct ksmbd_work *work)
559559
*/
560560
int smb2_check_user_session(struct ksmbd_work *work)
561561
{
562-
struct smb2_hdr *req_hdr = smb2_get_msg(work->request_buf);
562+
struct smb2_hdr *req_hdr = ksmbd_req_buf_next(work);
563563
struct ksmbd_conn *conn = work->conn;
564-
unsigned int cmd = conn->ops->get_cmd_val(work);
564+
unsigned int cmd = le16_to_cpu(req_hdr->Command);
565565
unsigned long long sess_id;
566566

567567
/*
@@ -587,7 +587,7 @@ int smb2_check_user_session(struct ksmbd_work *work)
587587
pr_err("The first operation in the compound does not have sess\n");
588588
return -EINVAL;
589589
}
590-
if (work->sess->id != sess_id) {
590+
if (sess_id != ULLONG_MAX && work->sess->id != sess_id) {
591591
pr_err("session id(%llu) is different with the first operation(%lld)\n",
592592
sess_id, work->sess->id);
593593
return -EINVAL;
@@ -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);
@@ -6209,6 +6214,11 @@ int smb2_read(struct ksmbd_work *work)
62096214
unsigned int max_read_size = conn->vals->max_read_size;
62106215

62116216
WORK_BUFFERS(work, req, rsp);
6217+
if (work->next_smb2_rcv_hdr_off) {
6218+
work->send_no_response = 1;
6219+
err = -EOPNOTSUPP;
6220+
goto out;
6221+
}
62126222

62136223
if (test_share_config_flag(work->tcon->share_conf,
62146224
KSMBD_SHARE_FLAG_PIPE)) {
@@ -8609,7 +8619,8 @@ int smb3_decrypt_req(struct ksmbd_work *work)
86098619
struct smb2_transform_hdr *tr_hdr = smb2_get_msg(buf);
86108620
int rc = 0;
86118621

8612-
if (buf_data_size < sizeof(struct smb2_hdr)) {
8622+
if (pdu_length < sizeof(struct smb2_transform_hdr) ||
8623+
buf_data_size < sizeof(struct smb2_hdr)) {
86138624
pr_err("Transform message is too small (%u)\n",
86148625
pdu_length);
86158626
return -ECONNABORTED;

fs/smb/server/smb_common.c

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -388,26 +388,29 @@ static struct smb_version_cmds smb1_server_cmds[1] = {
388388
[SMB_COM_NEGOTIATE_EX] = { .proc = smb1_negotiate, },
389389
};
390390

391-
static void init_smb1_server(struct ksmbd_conn *conn)
391+
static int init_smb1_server(struct ksmbd_conn *conn)
392392
{
393393
conn->ops = &smb1_server_ops;
394394
conn->cmds = smb1_server_cmds;
395395
conn->max_cmds = ARRAY_SIZE(smb1_server_cmds);
396+
return 0;
396397
}
397398

398-
void ksmbd_init_smb_server(struct ksmbd_work *work)
399+
int ksmbd_init_smb_server(struct ksmbd_work *work)
399400
{
400401
struct ksmbd_conn *conn = work->conn;
401402
__le32 proto;
402403

403-
if (conn->need_neg == false)
404-
return;
405-
406404
proto = *(__le32 *)((struct smb_hdr *)work->request_buf)->Protocol;
405+
if (conn->need_neg == false) {
406+
if (proto == SMB1_PROTO_NUMBER)
407+
return -EINVAL;
408+
return 0;
409+
}
410+
407411
if (proto == SMB1_PROTO_NUMBER)
408-
init_smb1_server(conn);
409-
else
410-
init_smb3_11_server(conn);
412+
return init_smb1_server(conn);
413+
return init_smb3_11_server(conn);
411414
}
412415

413416
int ksmbd_populate_dot_dotdot_entries(struct ksmbd_work *work, int info_level,

fs/smb/server/smb_common.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -427,7 +427,7 @@ bool ksmbd_smb_request(struct ksmbd_conn *conn);
427427

428428
int ksmbd_lookup_dialect_by_id(__le16 *cli_dialects, __le16 dialects_count);
429429

430-
void ksmbd_init_smb_server(struct ksmbd_work *work);
430+
int ksmbd_init_smb_server(struct ksmbd_work *work);
431431

432432
struct ksmbd_kstat;
433433
int ksmbd_populate_dot_dotdot_entries(struct ksmbd_work *work,

fs/smb/server/vfs.c

Lines changed: 37 additions & 28 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);
114112

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+
}
120+
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
}
@@ -412,7 +419,8 @@ static int ksmbd_vfs_stream_write(struct ksmbd_file *fp, char *buf, loff_t *pos,
412419
{
413420
char *stream_buf = NULL, *wbuf;
414421
struct mnt_idmap *idmap = file_mnt_idmap(fp->filp);
415-
size_t size, v_len;
422+
size_t size;
423+
ssize_t v_len;
416424
int err = 0;
417425

418426
ksmbd_debug(VFS, "write stream data pos : %llu, count : %zd\n",
@@ -429,9 +437,9 @@ static int ksmbd_vfs_stream_write(struct ksmbd_file *fp, char *buf, loff_t *pos,
429437
fp->stream.name,
430438
fp->stream.size,
431439
&stream_buf);
432-
if ((int)v_len < 0) {
440+
if (v_len < 0) {
433441
pr_err("not found stream in xattr : %zd\n", v_len);
434-
err = (int)v_len;
442+
err = v_len;
435443
goto out;
436444
}
437445

@@ -1194,14 +1202,14 @@ static int ksmbd_vfs_lookup_in_dir(const struct path *dir, char *name,
11941202
* Return: 0 on success, otherwise error
11951203
*/
11961204
int ksmbd_vfs_kern_path_locked(struct ksmbd_work *work, char *name,
1197-
unsigned int flags, struct path *path,
1198-
bool caseless)
1205+
unsigned int flags, struct path *parent_path,
1206+
struct path *path, bool caseless)
11991207
{
12001208
struct ksmbd_share_config *share_conf = work->tcon->share_conf;
12011209
int err;
1202-
struct path parent_path;
12031210

1204-
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);
12051213
if (!err)
12061214
return 0;
12071215

@@ -1216,10 +1224,10 @@ int ksmbd_vfs_kern_path_locked(struct ksmbd_work *work, char *name,
12161224
path_len = strlen(filepath);
12171225
remain_len = path_len;
12181226

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

1222-
while (d_can_lookup(parent_path.dentry)) {
1230+
while (d_can_lookup(parent_path->dentry)) {
12231231
char *filename = filepath + path_len - remain_len;
12241232
char *next = strchrnul(filename, '/');
12251233
size_t filename_len = next - filename;
@@ -1228,7 +1236,7 @@ int ksmbd_vfs_kern_path_locked(struct ksmbd_work *work, char *name,
12281236
if (filename_len == 0)
12291237
break;
12301238

1231-
err = ksmbd_vfs_lookup_in_dir(&parent_path, filename,
1239+
err = ksmbd_vfs_lookup_in_dir(parent_path, filename,
12321240
filename_len,
12331241
work->conn->um);
12341242
if (err)
@@ -1245,25 +1253,26 @@ int ksmbd_vfs_kern_path_locked(struct ksmbd_work *work, char *name,
12451253
goto out2;
12461254
else if (is_last)
12471255
goto out1;
1248-
path_put(&parent_path);
1249-
parent_path = *path;
1256+
path_put(parent_path);
1257+
*parent_path = *path;
12501258

12511259
next[0] = '/';
12521260
remain_len -= filename_len + 1;
12531261
}
12541262

12551263
err = -EINVAL;
12561264
out2:
1257-
path_put(&parent_path);
1265+
path_put(parent_path);
12581266
out1:
12591267
kfree(filepath);
12601268
}
12611269

12621270
if (!err) {
1263-
err = ksmbd_vfs_lock_parent(parent_path.dentry, path->dentry);
1264-
if (err)
1265-
dput(path->dentry);
1266-
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+
}
12671276
}
12681277
return err;
12691278
}

0 commit comments

Comments
 (0)