Skip to content

Commit 9496e26

Browse files
namjaejeonsmfrench
authored andcommitted
ksmbd: add request buffer validation in smb2_set_info
Add buffer validation in smb2_set_info, and remove unused variable in set_file_basic_info. and smb2_set_info infolevel functions take structure pointer argument. Cc: Tom Talpey <[email protected]> Cc: Ronnie Sahlberg <[email protected]> Cc: Ralph Böhme <[email protected]> Cc: Sergey Senozhatsky <[email protected]> Acked-by: Hyunchul Lee <[email protected]> Reviewed-by: Ralph Boehme <[email protected]> Signed-off-by: Namjae Jeon <[email protected]> Signed-off-by: Steve French <[email protected]>
1 parent 88d3005 commit 9496e26

File tree

1 file changed

+107
-42
lines changed

1 file changed

+107
-42
lines changed

fs/ksmbd/smb2pdu.c

Lines changed: 107 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -2102,16 +2102,22 @@ static noinline int create_smb2_pipe(struct ksmbd_work *work)
21022102
* smb2_set_ea() - handler for setting extended attributes using set
21032103
* info command
21042104
* @eabuf: set info command buffer
2105+
* @buf_len: set info command buffer length
21052106
* @path: dentry path for get ea
21062107
*
21072108
* Return: 0 on success, otherwise error
21082109
*/
2109-
static int smb2_set_ea(struct smb2_ea_info *eabuf, struct path *path)
2110+
static int smb2_set_ea(struct smb2_ea_info *eabuf, unsigned int buf_len,
2111+
struct path *path)
21102112
{
21112113
struct user_namespace *user_ns = mnt_user_ns(path->mnt);
21122114
char *attr_name = NULL, *value;
21132115
int rc = 0;
2114-
int next = 0;
2116+
unsigned int next = 0;
2117+
2118+
if (buf_len < sizeof(struct smb2_ea_info) + eabuf->EaNameLength +
2119+
le16_to_cpu(eabuf->EaValueLength))
2120+
return -EINVAL;
21152121

21162122
attr_name = kmalloc(XATTR_NAME_MAX + 1, GFP_KERNEL);
21172123
if (!attr_name)
@@ -2176,7 +2182,13 @@ static int smb2_set_ea(struct smb2_ea_info *eabuf, struct path *path)
21762182

21772183
next:
21782184
next = le32_to_cpu(eabuf->NextEntryOffset);
2185+
if (next == 0 || buf_len < next)
2186+
break;
2187+
buf_len -= next;
21792188
eabuf = (struct smb2_ea_info *)((char *)eabuf + next);
2189+
if (next < (u32)eabuf->EaNameLength + le16_to_cpu(eabuf->EaValueLength))
2190+
break;
2191+
21802192
} while (next != 0);
21812193

21822194
kfree(attr_name);
@@ -2757,7 +2769,15 @@ int smb2_open(struct ksmbd_work *work)
27572769
created = true;
27582770
user_ns = mnt_user_ns(path.mnt);
27592771
if (ea_buf) {
2760-
rc = smb2_set_ea(&ea_buf->ea, &path);
2772+
if (le32_to_cpu(ea_buf->ccontext.DataLength) <
2773+
sizeof(struct smb2_ea_info)) {
2774+
rc = -EINVAL;
2775+
goto err_out;
2776+
}
2777+
2778+
rc = smb2_set_ea(&ea_buf->ea,
2779+
le32_to_cpu(ea_buf->ccontext.DataLength),
2780+
&path);
27612781
if (rc == -EOPNOTSUPP)
27622782
rc = 0;
27632783
else if (rc)
@@ -5341,14 +5361,18 @@ static int smb2_rename(struct ksmbd_work *work,
53415361
static int smb2_create_link(struct ksmbd_work *work,
53425362
struct ksmbd_share_config *share,
53435363
struct smb2_file_link_info *file_info,
5344-
struct file *filp,
5364+
unsigned int buf_len, struct file *filp,
53455365
struct nls_table *local_nls)
53465366
{
53475367
char *link_name = NULL, *target_name = NULL, *pathname = NULL;
53485368
struct path path;
53495369
bool file_present = true;
53505370
int rc;
53515371

5372+
if (buf_len < (u64)sizeof(struct smb2_file_link_info) +
5373+
le32_to_cpu(file_info->FileNameLength))
5374+
return -EINVAL;
5375+
53525376
ksmbd_debug(SMB, "setting FILE_LINK_INFORMATION\n");
53535377
pathname = kmalloc(PATH_MAX, GFP_KERNEL);
53545378
if (!pathname)
@@ -5408,10 +5432,10 @@ static int smb2_create_link(struct ksmbd_work *work,
54085432
return rc;
54095433
}
54105434

5411-
static int set_file_basic_info(struct ksmbd_file *fp, char *buf,
5435+
static int set_file_basic_info(struct ksmbd_file *fp,
5436+
struct smb2_file_basic_info *file_info,
54125437
struct ksmbd_share_config *share)
54135438
{
5414-
struct smb2_file_basic_info *file_info;
54155439
struct iattr attrs;
54165440
struct timespec64 ctime;
54175441
struct file *filp;
@@ -5422,7 +5446,6 @@ static int set_file_basic_info(struct ksmbd_file *fp, char *buf,
54225446
if (!(fp->daccess & FILE_WRITE_ATTRIBUTES_LE))
54235447
return -EACCES;
54245448

5425-
file_info = (struct smb2_file_basic_info *)buf;
54265449
attrs.ia_valid = 0;
54275450
filp = fp->filp;
54285451
inode = file_inode(filp);
@@ -5499,23 +5522,22 @@ static int set_file_basic_info(struct ksmbd_file *fp, char *buf,
54995522
}
55005523

55015524
static int set_file_allocation_info(struct ksmbd_work *work,
5502-
struct ksmbd_file *fp, char *buf)
5525+
struct ksmbd_file *fp,
5526+
struct smb2_file_alloc_info *file_alloc_info)
55035527
{
55045528
/*
55055529
* TODO : It's working fine only when store dos attributes
55065530
* is not yes. need to implement a logic which works
55075531
* properly with any smb.conf option
55085532
*/
55095533

5510-
struct smb2_file_alloc_info *file_alloc_info;
55115534
loff_t alloc_blks;
55125535
struct inode *inode;
55135536
int rc;
55145537

55155538
if (!(fp->daccess & FILE_WRITE_DATA_LE))
55165539
return -EACCES;
55175540

5518-
file_alloc_info = (struct smb2_file_alloc_info *)buf;
55195541
alloc_blks = (le64_to_cpu(file_alloc_info->AllocationSize) + 511) >> 9;
55205542
inode = file_inode(fp->filp);
55215543

@@ -5551,17 +5573,15 @@ static int set_file_allocation_info(struct ksmbd_work *work,
55515573
}
55525574

55535575
static int set_end_of_file_info(struct ksmbd_work *work, struct ksmbd_file *fp,
5554-
char *buf)
5576+
struct smb2_file_eof_info *file_eof_info)
55555577
{
5556-
struct smb2_file_eof_info *file_eof_info;
55575578
loff_t newsize;
55585579
struct inode *inode;
55595580
int rc;
55605581

55615582
if (!(fp->daccess & FILE_WRITE_DATA_LE))
55625583
return -EACCES;
55635584

5564-
file_eof_info = (struct smb2_file_eof_info *)buf;
55655585
newsize = le64_to_cpu(file_eof_info->EndOfFile);
55665586
inode = file_inode(fp->filp);
55675587

@@ -5588,7 +5608,8 @@ static int set_end_of_file_info(struct ksmbd_work *work, struct ksmbd_file *fp,
55885608
}
55895609

55905610
static int set_rename_info(struct ksmbd_work *work, struct ksmbd_file *fp,
5591-
char *buf)
5611+
struct smb2_file_rename_info *rename_info,
5612+
unsigned int buf_len)
55925613
{
55935614
struct user_namespace *user_ns;
55945615
struct ksmbd_file *parent_fp;
@@ -5601,6 +5622,10 @@ static int set_rename_info(struct ksmbd_work *work, struct ksmbd_file *fp,
56015622
return -EACCES;
56025623
}
56035624

5625+
if (buf_len < (u64)sizeof(struct smb2_file_rename_info) +
5626+
le32_to_cpu(rename_info->FileNameLength))
5627+
return -EINVAL;
5628+
56045629
user_ns = file_mnt_user_ns(fp->filp);
56055630
if (ksmbd_stream_fd(fp))
56065631
goto next;
@@ -5623,14 +5648,13 @@ static int set_rename_info(struct ksmbd_work *work, struct ksmbd_file *fp,
56235648
}
56245649
}
56255650
next:
5626-
return smb2_rename(work, fp, user_ns,
5627-
(struct smb2_file_rename_info *)buf,
5651+
return smb2_rename(work, fp, user_ns, rename_info,
56285652
work->sess->conn->local_nls);
56295653
}
56305654

5631-
static int set_file_disposition_info(struct ksmbd_file *fp, char *buf)
5655+
static int set_file_disposition_info(struct ksmbd_file *fp,
5656+
struct smb2_file_disposition_info *file_info)
56325657
{
5633-
struct smb2_file_disposition_info *file_info;
56345658
struct inode *inode;
56355659

56365660
if (!(fp->daccess & FILE_DELETE_LE)) {
@@ -5639,7 +5663,6 @@ static int set_file_disposition_info(struct ksmbd_file *fp, char *buf)
56395663
}
56405664

56415665
inode = file_inode(fp->filp);
5642-
file_info = (struct smb2_file_disposition_info *)buf;
56435666
if (file_info->DeletePending) {
56445667
if (S_ISDIR(inode->i_mode) &&
56455668
ksmbd_vfs_empty_dir(fp) == -ENOTEMPTY)
@@ -5651,15 +5674,14 @@ static int set_file_disposition_info(struct ksmbd_file *fp, char *buf)
56515674
return 0;
56525675
}
56535676

5654-
static int set_file_position_info(struct ksmbd_file *fp, char *buf)
5677+
static int set_file_position_info(struct ksmbd_file *fp,
5678+
struct smb2_file_pos_info *file_info)
56555679
{
5656-
struct smb2_file_pos_info *file_info;
56575680
loff_t current_byte_offset;
56585681
unsigned long sector_size;
56595682
struct inode *inode;
56605683

56615684
inode = file_inode(fp->filp);
5662-
file_info = (struct smb2_file_pos_info *)buf;
56635685
current_byte_offset = le64_to_cpu(file_info->CurrentByteOffset);
56645686
sector_size = inode->i_sb->s_blocksize;
56655687

@@ -5675,12 +5697,11 @@ static int set_file_position_info(struct ksmbd_file *fp, char *buf)
56755697
return 0;
56765698
}
56775699

5678-
static int set_file_mode_info(struct ksmbd_file *fp, char *buf)
5700+
static int set_file_mode_info(struct ksmbd_file *fp,
5701+
struct smb2_file_mode_info *file_info)
56795702
{
5680-
struct smb2_file_mode_info *file_info;
56815703
__le32 mode;
56825704

5683-
file_info = (struct smb2_file_mode_info *)buf;
56845705
mode = file_info->Mode;
56855706

56865707
if ((mode & ~FILE_MODE_INFO_MASK) ||
@@ -5710,40 +5731,74 @@ static int set_file_mode_info(struct ksmbd_file *fp, char *buf)
57105731
* TODO: need to implement an error handling for STATUS_INFO_LENGTH_MISMATCH
57115732
*/
57125733
static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp,
5713-
int info_class, char *buf,
5734+
struct smb2_set_info_req *req,
57145735
struct ksmbd_share_config *share)
57155736
{
5716-
switch (info_class) {
5737+
unsigned int buf_len = le32_to_cpu(req->BufferLength);
5738+
5739+
switch (req->FileInfoClass) {
57175740
case FILE_BASIC_INFORMATION:
5718-
return set_file_basic_info(fp, buf, share);
5741+
{
5742+
if (buf_len < sizeof(struct smb2_file_basic_info))
5743+
return -EINVAL;
57195744

5745+
return set_file_basic_info(fp, (struct smb2_file_basic_info *)req->Buffer, share);
5746+
}
57205747
case FILE_ALLOCATION_INFORMATION:
5721-
return set_file_allocation_info(work, fp, buf);
5748+
{
5749+
if (buf_len < sizeof(struct smb2_file_alloc_info))
5750+
return -EINVAL;
57225751

5752+
return set_file_allocation_info(work, fp,
5753+
(struct smb2_file_alloc_info *)req->Buffer);
5754+
}
57235755
case FILE_END_OF_FILE_INFORMATION:
5724-
return set_end_of_file_info(work, fp, buf);
5756+
{
5757+
if (buf_len < sizeof(struct smb2_file_eof_info))
5758+
return -EINVAL;
57255759

5760+
return set_end_of_file_info(work, fp,
5761+
(struct smb2_file_eof_info *)req->Buffer);
5762+
}
57265763
case FILE_RENAME_INFORMATION:
5764+
{
57275765
if (!test_tree_conn_flag(work->tcon, KSMBD_TREE_CONN_FLAG_WRITABLE)) {
57285766
ksmbd_debug(SMB,
57295767
"User does not have write permission\n");
57305768
return -EACCES;
57315769
}
5732-
return set_rename_info(work, fp, buf);
57335770

5771+
if (buf_len < sizeof(struct smb2_file_rename_info))
5772+
return -EINVAL;
5773+
5774+
return set_rename_info(work, fp,
5775+
(struct smb2_file_rename_info *)req->Buffer,
5776+
buf_len);
5777+
}
57345778
case FILE_LINK_INFORMATION:
5779+
{
5780+
if (buf_len < sizeof(struct smb2_file_link_info))
5781+
return -EINVAL;
5782+
57355783
return smb2_create_link(work, work->tcon->share_conf,
5736-
(struct smb2_file_link_info *)buf, fp->filp,
5784+
(struct smb2_file_link_info *)req->Buffer,
5785+
buf_len, fp->filp,
57375786
work->sess->conn->local_nls);
5738-
5787+
}
57395788
case FILE_DISPOSITION_INFORMATION:
5789+
{
57405790
if (!test_tree_conn_flag(work->tcon, KSMBD_TREE_CONN_FLAG_WRITABLE)) {
57415791
ksmbd_debug(SMB,
57425792
"User does not have write permission\n");
57435793
return -EACCES;
57445794
}
5745-
return set_file_disposition_info(fp, buf);
57465795

5796+
if (buf_len < sizeof(struct smb2_file_disposition_info))
5797+
return -EINVAL;
5798+
5799+
return set_file_disposition_info(fp,
5800+
(struct smb2_file_disposition_info *)req->Buffer);
5801+
}
57475802
case FILE_FULL_EA_INFORMATION:
57485803
{
57495804
if (!(fp->daccess & FILE_WRITE_EA_LE)) {
@@ -5752,18 +5807,29 @@ static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp,
57525807
return -EACCES;
57535808
}
57545809

5755-
return smb2_set_ea((struct smb2_ea_info *)buf,
5756-
&fp->filp->f_path);
5757-
}
5810+
if (buf_len < sizeof(struct smb2_ea_info))
5811+
return -EINVAL;
57585812

5813+
return smb2_set_ea((struct smb2_ea_info *)req->Buffer,
5814+
buf_len, &fp->filp->f_path);
5815+
}
57595816
case FILE_POSITION_INFORMATION:
5760-
return set_file_position_info(fp, buf);
5817+
{
5818+
if (buf_len < sizeof(struct smb2_file_pos_info))
5819+
return -EINVAL;
57615820

5821+
return set_file_position_info(fp, (struct smb2_file_pos_info *)req->Buffer);
5822+
}
57625823
case FILE_MODE_INFORMATION:
5763-
return set_file_mode_info(fp, buf);
5824+
{
5825+
if (buf_len < sizeof(struct smb2_file_mode_info))
5826+
return -EINVAL;
5827+
5828+
return set_file_mode_info(fp, (struct smb2_file_mode_info *)req->Buffer);
5829+
}
57645830
}
57655831

5766-
pr_err("Unimplemented Fileinfoclass :%d\n", info_class);
5832+
pr_err("Unimplemented Fileinfoclass :%d\n", req->FileInfoClass);
57675833
return -EOPNOTSUPP;
57685834
}
57695835

@@ -5824,8 +5890,7 @@ int smb2_set_info(struct ksmbd_work *work)
58245890
switch (req->InfoType) {
58255891
case SMB2_O_INFO_FILE:
58265892
ksmbd_debug(SMB, "GOT SMB2_O_INFO_FILE\n");
5827-
rc = smb2_set_info_file(work, fp, req->FileInfoClass,
5828-
req->Buffer, work->tcon->share_conf);
5893+
rc = smb2_set_info_file(work, fp, req, work->tcon->share_conf);
58295894
break;
58305895
case SMB2_O_INFO_SECURITY:
58315896
ksmbd_debug(SMB, "GOT SMB2_O_INFO_SECURITY\n");

0 commit comments

Comments
 (0)