Commit 9496e268 authored by Namjae Jeon's avatar Namjae Jeon Committed by Steve French

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 <tom@talpey.com>
Cc: Ronnie Sahlberg <ronniesahlberg@gmail.com>
Cc: Ralph Böhme <slow@samba.org>
Cc: Sergey Senozhatsky <senozhatsky@chromium.org>
Acked-by: default avatarHyunchul Lee <hyc.lee@gmail.com>
Reviewed-by: default avatarRalph Boehme <slow@samba.org>
Signed-off-by: default avatarNamjae Jeon <linkinjeon@kernel.org>
Signed-off-by: default avatarSteve French <stfrench@microsoft.com>
parent 88d30052
...@@ -2102,16 +2102,22 @@ static noinline int create_smb2_pipe(struct ksmbd_work *work) ...@@ -2102,16 +2102,22 @@ static noinline int create_smb2_pipe(struct ksmbd_work *work)
* smb2_set_ea() - handler for setting extended attributes using set * smb2_set_ea() - handler for setting extended attributes using set
* info command * info command
* @eabuf: set info command buffer * @eabuf: set info command buffer
* @buf_len: set info command buffer length
* @path: dentry path for get ea * @path: dentry path for get ea
* *
* Return: 0 on success, otherwise error * Return: 0 on success, otherwise error
*/ */
static int smb2_set_ea(struct smb2_ea_info *eabuf, struct path *path) static int smb2_set_ea(struct smb2_ea_info *eabuf, unsigned int buf_len,
struct path *path)
{ {
struct user_namespace *user_ns = mnt_user_ns(path->mnt); struct user_namespace *user_ns = mnt_user_ns(path->mnt);
char *attr_name = NULL, *value; char *attr_name = NULL, *value;
int rc = 0; int rc = 0;
int next = 0; unsigned int next = 0;
if (buf_len < sizeof(struct smb2_ea_info) + eabuf->EaNameLength +
le16_to_cpu(eabuf->EaValueLength))
return -EINVAL;
attr_name = kmalloc(XATTR_NAME_MAX + 1, GFP_KERNEL); attr_name = kmalloc(XATTR_NAME_MAX + 1, GFP_KERNEL);
if (!attr_name) if (!attr_name)
...@@ -2176,7 +2182,13 @@ static int smb2_set_ea(struct smb2_ea_info *eabuf, struct path *path) ...@@ -2176,7 +2182,13 @@ static int smb2_set_ea(struct smb2_ea_info *eabuf, struct path *path)
next: next:
next = le32_to_cpu(eabuf->NextEntryOffset); next = le32_to_cpu(eabuf->NextEntryOffset);
if (next == 0 || buf_len < next)
break;
buf_len -= next;
eabuf = (struct smb2_ea_info *)((char *)eabuf + next); eabuf = (struct smb2_ea_info *)((char *)eabuf + next);
if (next < (u32)eabuf->EaNameLength + le16_to_cpu(eabuf->EaValueLength))
break;
} while (next != 0); } while (next != 0);
kfree(attr_name); kfree(attr_name);
...@@ -2757,7 +2769,15 @@ int smb2_open(struct ksmbd_work *work) ...@@ -2757,7 +2769,15 @@ int smb2_open(struct ksmbd_work *work)
created = true; created = true;
user_ns = mnt_user_ns(path.mnt); user_ns = mnt_user_ns(path.mnt);
if (ea_buf) { if (ea_buf) {
rc = smb2_set_ea(&ea_buf->ea, &path); if (le32_to_cpu(ea_buf->ccontext.DataLength) <
sizeof(struct smb2_ea_info)) {
rc = -EINVAL;
goto err_out;
}
rc = smb2_set_ea(&ea_buf->ea,
le32_to_cpu(ea_buf->ccontext.DataLength),
&path);
if (rc == -EOPNOTSUPP) if (rc == -EOPNOTSUPP)
rc = 0; rc = 0;
else if (rc) else if (rc)
...@@ -5341,7 +5361,7 @@ static int smb2_rename(struct ksmbd_work *work, ...@@ -5341,7 +5361,7 @@ static int smb2_rename(struct ksmbd_work *work,
static int smb2_create_link(struct ksmbd_work *work, static int smb2_create_link(struct ksmbd_work *work,
struct ksmbd_share_config *share, struct ksmbd_share_config *share,
struct smb2_file_link_info *file_info, struct smb2_file_link_info *file_info,
struct file *filp, unsigned int buf_len, struct file *filp,
struct nls_table *local_nls) struct nls_table *local_nls)
{ {
char *link_name = NULL, *target_name = NULL, *pathname = NULL; char *link_name = NULL, *target_name = NULL, *pathname = NULL;
...@@ -5349,6 +5369,10 @@ static int smb2_create_link(struct ksmbd_work *work, ...@@ -5349,6 +5369,10 @@ static int smb2_create_link(struct ksmbd_work *work,
bool file_present = true; bool file_present = true;
int rc; int rc;
if (buf_len < (u64)sizeof(struct smb2_file_link_info) +
le32_to_cpu(file_info->FileNameLength))
return -EINVAL;
ksmbd_debug(SMB, "setting FILE_LINK_INFORMATION\n"); ksmbd_debug(SMB, "setting FILE_LINK_INFORMATION\n");
pathname = kmalloc(PATH_MAX, GFP_KERNEL); pathname = kmalloc(PATH_MAX, GFP_KERNEL);
if (!pathname) if (!pathname)
...@@ -5408,10 +5432,10 @@ static int smb2_create_link(struct ksmbd_work *work, ...@@ -5408,10 +5432,10 @@ static int smb2_create_link(struct ksmbd_work *work,
return rc; return rc;
} }
static int set_file_basic_info(struct ksmbd_file *fp, char *buf, static int set_file_basic_info(struct ksmbd_file *fp,
struct smb2_file_basic_info *file_info,
struct ksmbd_share_config *share) struct ksmbd_share_config *share)
{ {
struct smb2_file_basic_info *file_info;
struct iattr attrs; struct iattr attrs;
struct timespec64 ctime; struct timespec64 ctime;
struct file *filp; struct file *filp;
...@@ -5422,7 +5446,6 @@ static int set_file_basic_info(struct ksmbd_file *fp, char *buf, ...@@ -5422,7 +5446,6 @@ static int set_file_basic_info(struct ksmbd_file *fp, char *buf,
if (!(fp->daccess & FILE_WRITE_ATTRIBUTES_LE)) if (!(fp->daccess & FILE_WRITE_ATTRIBUTES_LE))
return -EACCES; return -EACCES;
file_info = (struct smb2_file_basic_info *)buf;
attrs.ia_valid = 0; attrs.ia_valid = 0;
filp = fp->filp; filp = fp->filp;
inode = file_inode(filp); inode = file_inode(filp);
...@@ -5499,7 +5522,8 @@ static int set_file_basic_info(struct ksmbd_file *fp, char *buf, ...@@ -5499,7 +5522,8 @@ static int set_file_basic_info(struct ksmbd_file *fp, char *buf,
} }
static int set_file_allocation_info(struct ksmbd_work *work, static int set_file_allocation_info(struct ksmbd_work *work,
struct ksmbd_file *fp, char *buf) struct ksmbd_file *fp,
struct smb2_file_alloc_info *file_alloc_info)
{ {
/* /*
* TODO : It's working fine only when store dos attributes * TODO : It's working fine only when store dos attributes
...@@ -5507,7 +5531,6 @@ static int set_file_allocation_info(struct ksmbd_work *work, ...@@ -5507,7 +5531,6 @@ static int set_file_allocation_info(struct ksmbd_work *work,
* properly with any smb.conf option * properly with any smb.conf option
*/ */
struct smb2_file_alloc_info *file_alloc_info;
loff_t alloc_blks; loff_t alloc_blks;
struct inode *inode; struct inode *inode;
int rc; int rc;
...@@ -5515,7 +5538,6 @@ static int set_file_allocation_info(struct ksmbd_work *work, ...@@ -5515,7 +5538,6 @@ static int set_file_allocation_info(struct ksmbd_work *work,
if (!(fp->daccess & FILE_WRITE_DATA_LE)) if (!(fp->daccess & FILE_WRITE_DATA_LE))
return -EACCES; return -EACCES;
file_alloc_info = (struct smb2_file_alloc_info *)buf;
alloc_blks = (le64_to_cpu(file_alloc_info->AllocationSize) + 511) >> 9; alloc_blks = (le64_to_cpu(file_alloc_info->AllocationSize) + 511) >> 9;
inode = file_inode(fp->filp); inode = file_inode(fp->filp);
...@@ -5551,9 +5573,8 @@ static int set_file_allocation_info(struct ksmbd_work *work, ...@@ -5551,9 +5573,8 @@ static int set_file_allocation_info(struct ksmbd_work *work,
} }
static int set_end_of_file_info(struct ksmbd_work *work, struct ksmbd_file *fp, static int set_end_of_file_info(struct ksmbd_work *work, struct ksmbd_file *fp,
char *buf) struct smb2_file_eof_info *file_eof_info)
{ {
struct smb2_file_eof_info *file_eof_info;
loff_t newsize; loff_t newsize;
struct inode *inode; struct inode *inode;
int rc; int rc;
...@@ -5561,7 +5582,6 @@ static int set_end_of_file_info(struct ksmbd_work *work, struct ksmbd_file *fp, ...@@ -5561,7 +5582,6 @@ static int set_end_of_file_info(struct ksmbd_work *work, struct ksmbd_file *fp,
if (!(fp->daccess & FILE_WRITE_DATA_LE)) if (!(fp->daccess & FILE_WRITE_DATA_LE))
return -EACCES; return -EACCES;
file_eof_info = (struct smb2_file_eof_info *)buf;
newsize = le64_to_cpu(file_eof_info->EndOfFile); newsize = le64_to_cpu(file_eof_info->EndOfFile);
inode = file_inode(fp->filp); inode = file_inode(fp->filp);
...@@ -5588,7 +5608,8 @@ static int set_end_of_file_info(struct ksmbd_work *work, struct ksmbd_file *fp, ...@@ -5588,7 +5608,8 @@ static int set_end_of_file_info(struct ksmbd_work *work, struct ksmbd_file *fp,
} }
static int set_rename_info(struct ksmbd_work *work, struct ksmbd_file *fp, static int set_rename_info(struct ksmbd_work *work, struct ksmbd_file *fp,
char *buf) struct smb2_file_rename_info *rename_info,
unsigned int buf_len)
{ {
struct user_namespace *user_ns; struct user_namespace *user_ns;
struct ksmbd_file *parent_fp; struct ksmbd_file *parent_fp;
...@@ -5601,6 +5622,10 @@ static int set_rename_info(struct ksmbd_work *work, struct ksmbd_file *fp, ...@@ -5601,6 +5622,10 @@ static int set_rename_info(struct ksmbd_work *work, struct ksmbd_file *fp,
return -EACCES; return -EACCES;
} }
if (buf_len < (u64)sizeof(struct smb2_file_rename_info) +
le32_to_cpu(rename_info->FileNameLength))
return -EINVAL;
user_ns = file_mnt_user_ns(fp->filp); user_ns = file_mnt_user_ns(fp->filp);
if (ksmbd_stream_fd(fp)) if (ksmbd_stream_fd(fp))
goto next; goto next;
...@@ -5623,14 +5648,13 @@ static int set_rename_info(struct ksmbd_work *work, struct ksmbd_file *fp, ...@@ -5623,14 +5648,13 @@ static int set_rename_info(struct ksmbd_work *work, struct ksmbd_file *fp,
} }
} }
next: next:
return smb2_rename(work, fp, user_ns, return smb2_rename(work, fp, user_ns, rename_info,
(struct smb2_file_rename_info *)buf,
work->sess->conn->local_nls); work->sess->conn->local_nls);
} }
static int set_file_disposition_info(struct ksmbd_file *fp, char *buf) static int set_file_disposition_info(struct ksmbd_file *fp,
struct smb2_file_disposition_info *file_info)
{ {
struct smb2_file_disposition_info *file_info;
struct inode *inode; struct inode *inode;
if (!(fp->daccess & FILE_DELETE_LE)) { if (!(fp->daccess & FILE_DELETE_LE)) {
...@@ -5639,7 +5663,6 @@ static int set_file_disposition_info(struct ksmbd_file *fp, char *buf) ...@@ -5639,7 +5663,6 @@ static int set_file_disposition_info(struct ksmbd_file *fp, char *buf)
} }
inode = file_inode(fp->filp); inode = file_inode(fp->filp);
file_info = (struct smb2_file_disposition_info *)buf;
if (file_info->DeletePending) { if (file_info->DeletePending) {
if (S_ISDIR(inode->i_mode) && if (S_ISDIR(inode->i_mode) &&
ksmbd_vfs_empty_dir(fp) == -ENOTEMPTY) ksmbd_vfs_empty_dir(fp) == -ENOTEMPTY)
...@@ -5651,15 +5674,14 @@ static int set_file_disposition_info(struct ksmbd_file *fp, char *buf) ...@@ -5651,15 +5674,14 @@ static int set_file_disposition_info(struct ksmbd_file *fp, char *buf)
return 0; return 0;
} }
static int set_file_position_info(struct ksmbd_file *fp, char *buf) static int set_file_position_info(struct ksmbd_file *fp,
struct smb2_file_pos_info *file_info)
{ {
struct smb2_file_pos_info *file_info;
loff_t current_byte_offset; loff_t current_byte_offset;
unsigned long sector_size; unsigned long sector_size;
struct inode *inode; struct inode *inode;
inode = file_inode(fp->filp); inode = file_inode(fp->filp);
file_info = (struct smb2_file_pos_info *)buf;
current_byte_offset = le64_to_cpu(file_info->CurrentByteOffset); current_byte_offset = le64_to_cpu(file_info->CurrentByteOffset);
sector_size = inode->i_sb->s_blocksize; sector_size = inode->i_sb->s_blocksize;
...@@ -5675,12 +5697,11 @@ static int set_file_position_info(struct ksmbd_file *fp, char *buf) ...@@ -5675,12 +5697,11 @@ static int set_file_position_info(struct ksmbd_file *fp, char *buf)
return 0; return 0;
} }
static int set_file_mode_info(struct ksmbd_file *fp, char *buf) static int set_file_mode_info(struct ksmbd_file *fp,
struct smb2_file_mode_info *file_info)
{ {
struct smb2_file_mode_info *file_info;
__le32 mode; __le32 mode;
file_info = (struct smb2_file_mode_info *)buf;
mode = file_info->Mode; mode = file_info->Mode;
if ((mode & ~FILE_MODE_INFO_MASK) || if ((mode & ~FILE_MODE_INFO_MASK) ||
...@@ -5710,40 +5731,74 @@ static int set_file_mode_info(struct ksmbd_file *fp, char *buf) ...@@ -5710,40 +5731,74 @@ static int set_file_mode_info(struct ksmbd_file *fp, char *buf)
* TODO: need to implement an error handling for STATUS_INFO_LENGTH_MISMATCH * TODO: need to implement an error handling for STATUS_INFO_LENGTH_MISMATCH
*/ */
static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp, static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp,
int info_class, char *buf, struct smb2_set_info_req *req,
struct ksmbd_share_config *share) struct ksmbd_share_config *share)
{ {
switch (info_class) { unsigned int buf_len = le32_to_cpu(req->BufferLength);
switch (req->FileInfoClass) {
case FILE_BASIC_INFORMATION: case FILE_BASIC_INFORMATION:
return set_file_basic_info(fp, buf, share); {
if (buf_len < sizeof(struct smb2_file_basic_info))
return -EINVAL;
return set_file_basic_info(fp, (struct smb2_file_basic_info *)req->Buffer, share);
}
case FILE_ALLOCATION_INFORMATION: case FILE_ALLOCATION_INFORMATION:
return set_file_allocation_info(work, fp, buf); {
if (buf_len < sizeof(struct smb2_file_alloc_info))
return -EINVAL;
return set_file_allocation_info(work, fp,
(struct smb2_file_alloc_info *)req->Buffer);
}
case FILE_END_OF_FILE_INFORMATION: case FILE_END_OF_FILE_INFORMATION:
return set_end_of_file_info(work, fp, buf); {
if (buf_len < sizeof(struct smb2_file_eof_info))
return -EINVAL;
return set_end_of_file_info(work, fp,
(struct smb2_file_eof_info *)req->Buffer);
}
case FILE_RENAME_INFORMATION: case FILE_RENAME_INFORMATION:
{
if (!test_tree_conn_flag(work->tcon, KSMBD_TREE_CONN_FLAG_WRITABLE)) { if (!test_tree_conn_flag(work->tcon, KSMBD_TREE_CONN_FLAG_WRITABLE)) {
ksmbd_debug(SMB, ksmbd_debug(SMB,
"User does not have write permission\n"); "User does not have write permission\n");
return -EACCES; return -EACCES;
} }
return set_rename_info(work, fp, buf);
if (buf_len < sizeof(struct smb2_file_rename_info))
return -EINVAL;
return set_rename_info(work, fp,
(struct smb2_file_rename_info *)req->Buffer,
buf_len);
}
case FILE_LINK_INFORMATION: case FILE_LINK_INFORMATION:
{
if (buf_len < sizeof(struct smb2_file_link_info))
return -EINVAL;
return smb2_create_link(work, work->tcon->share_conf, return smb2_create_link(work, work->tcon->share_conf,
(struct smb2_file_link_info *)buf, fp->filp, (struct smb2_file_link_info *)req->Buffer,
buf_len, fp->filp,
work->sess->conn->local_nls); work->sess->conn->local_nls);
}
case FILE_DISPOSITION_INFORMATION: case FILE_DISPOSITION_INFORMATION:
{
if (!test_tree_conn_flag(work->tcon, KSMBD_TREE_CONN_FLAG_WRITABLE)) { if (!test_tree_conn_flag(work->tcon, KSMBD_TREE_CONN_FLAG_WRITABLE)) {
ksmbd_debug(SMB, ksmbd_debug(SMB,
"User does not have write permission\n"); "User does not have write permission\n");
return -EACCES; return -EACCES;
} }
return set_file_disposition_info(fp, buf);
if (buf_len < sizeof(struct smb2_file_disposition_info))
return -EINVAL;
return set_file_disposition_info(fp,
(struct smb2_file_disposition_info *)req->Buffer);
}
case FILE_FULL_EA_INFORMATION: case FILE_FULL_EA_INFORMATION:
{ {
if (!(fp->daccess & FILE_WRITE_EA_LE)) { 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, ...@@ -5752,18 +5807,29 @@ static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp,
return -EACCES; return -EACCES;
} }
return smb2_set_ea((struct smb2_ea_info *)buf, if (buf_len < sizeof(struct smb2_ea_info))
&fp->filp->f_path); return -EINVAL;
}
return smb2_set_ea((struct smb2_ea_info *)req->Buffer,
buf_len, &fp->filp->f_path);
}
case FILE_POSITION_INFORMATION: case FILE_POSITION_INFORMATION:
return set_file_position_info(fp, buf); {
if (buf_len < sizeof(struct smb2_file_pos_info))
return -EINVAL;
return set_file_position_info(fp, (struct smb2_file_pos_info *)req->Buffer);
}
case FILE_MODE_INFORMATION: case FILE_MODE_INFORMATION:
return set_file_mode_info(fp, buf); {
if (buf_len < sizeof(struct smb2_file_mode_info))
return -EINVAL;
return set_file_mode_info(fp, (struct smb2_file_mode_info *)req->Buffer);
}
} }
pr_err("Unimplemented Fileinfoclass :%d\n", info_class); pr_err("Unimplemented Fileinfoclass :%d\n", req->FileInfoClass);
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }
...@@ -5824,8 +5890,7 @@ int smb2_set_info(struct ksmbd_work *work) ...@@ -5824,8 +5890,7 @@ int smb2_set_info(struct ksmbd_work *work)
switch (req->InfoType) { switch (req->InfoType) {
case SMB2_O_INFO_FILE: case SMB2_O_INFO_FILE:
ksmbd_debug(SMB, "GOT SMB2_O_INFO_FILE\n"); ksmbd_debug(SMB, "GOT SMB2_O_INFO_FILE\n");
rc = smb2_set_info_file(work, fp, req->FileInfoClass, rc = smb2_set_info_file(work, fp, req, work->tcon->share_conf);
req->Buffer, work->tcon->share_conf);
break; break;
case SMB2_O_INFO_SECURITY: case SMB2_O_INFO_SECURITY:
ksmbd_debug(SMB, "GOT SMB2_O_INFO_SECURITY\n"); ksmbd_debug(SMB, "GOT SMB2_O_INFO_SECURITY\n");
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment