Commit f7db8fd0 authored by Namjae Jeon's avatar Namjae Jeon Committed by Steve French

ksmbd: add validation in smb2_ioctl

Add validation for request/response buffer size check in smb2_ioctl and
fsctl_copychunk() take copychunk_ioctl_req pointer and the other arguments
instead of smb2_ioctl_req structure and remove an unused smb2_ioctl_req
argument of fsctl_validate_negotiate_info.

Cc: Tom Talpey <tom@talpey.com>
Cc: Ronnie Sahlberg <ronniesahlberg@gmail.com>
Cc: Ralph Böhme <slow@samba.org>
Cc: Steve French <smfrench@gmail.com>
Cc: Sergey Senozhatsky <senozhatsky@chromium.org>
Acked-by: default avatarHyunchul Lee <hyc.lee@gmail.com>
Signed-off-by: default avatarColin Ian King <colin.king@canonical.com>
Signed-off-by: default avatarNamjae Jeon <linkinjeon@kernel.org>
Signed-off-by: default avatarSteve French <stfrench@microsoft.com>
parent 64570fbc
...@@ -7023,24 +7023,26 @@ int smb2_lock(struct ksmbd_work *work) ...@@ -7023,24 +7023,26 @@ int smb2_lock(struct ksmbd_work *work)
return err; return err;
} }
static int fsctl_copychunk(struct ksmbd_work *work, struct smb2_ioctl_req *req, static int fsctl_copychunk(struct ksmbd_work *work,
struct copychunk_ioctl_req *ci_req,
unsigned int cnt_code,
unsigned int input_count,
unsigned long long volatile_id,
unsigned long long persistent_id,
struct smb2_ioctl_rsp *rsp) struct smb2_ioctl_rsp *rsp)
{ {
struct copychunk_ioctl_req *ci_req;
struct copychunk_ioctl_rsp *ci_rsp; struct copychunk_ioctl_rsp *ci_rsp;
struct ksmbd_file *src_fp = NULL, *dst_fp = NULL; struct ksmbd_file *src_fp = NULL, *dst_fp = NULL;
struct srv_copychunk *chunks; struct srv_copychunk *chunks;
unsigned int i, chunk_count, chunk_count_written = 0; unsigned int i, chunk_count, chunk_count_written = 0;
unsigned int chunk_size_written = 0; unsigned int chunk_size_written = 0;
loff_t total_size_written = 0; loff_t total_size_written = 0;
int ret, cnt_code; int ret = 0;
cnt_code = le32_to_cpu(req->CntCode);
ci_req = (struct copychunk_ioctl_req *)&req->Buffer[0];
ci_rsp = (struct copychunk_ioctl_rsp *)&rsp->Buffer[0]; ci_rsp = (struct copychunk_ioctl_rsp *)&rsp->Buffer[0];
rsp->VolatileFileId = req->VolatileFileId; rsp->VolatileFileId = cpu_to_le64(volatile_id);
rsp->PersistentFileId = req->PersistentFileId; rsp->PersistentFileId = cpu_to_le64(persistent_id);
ci_rsp->ChunksWritten = ci_rsp->ChunksWritten =
cpu_to_le32(ksmbd_server_side_copy_max_chunk_count()); cpu_to_le32(ksmbd_server_side_copy_max_chunk_count());
ci_rsp->ChunkBytesWritten = ci_rsp->ChunkBytesWritten =
...@@ -7050,12 +7052,13 @@ static int fsctl_copychunk(struct ksmbd_work *work, struct smb2_ioctl_req *req, ...@@ -7050,12 +7052,13 @@ static int fsctl_copychunk(struct ksmbd_work *work, struct smb2_ioctl_req *req,
chunks = (struct srv_copychunk *)&ci_req->Chunks[0]; chunks = (struct srv_copychunk *)&ci_req->Chunks[0];
chunk_count = le32_to_cpu(ci_req->ChunkCount); chunk_count = le32_to_cpu(ci_req->ChunkCount);
if (chunk_count == 0)
goto out;
total_size_written = 0; total_size_written = 0;
/* verify the SRV_COPYCHUNK_COPY packet */ /* verify the SRV_COPYCHUNK_COPY packet */
if (chunk_count > ksmbd_server_side_copy_max_chunk_count() || if (chunk_count > ksmbd_server_side_copy_max_chunk_count() ||
le32_to_cpu(req->InputCount) < input_count < offsetof(struct copychunk_ioctl_req, Chunks) +
offsetof(struct copychunk_ioctl_req, Chunks) +
chunk_count * sizeof(struct srv_copychunk)) { chunk_count * sizeof(struct srv_copychunk)) {
rsp->hdr.Status = STATUS_INVALID_PARAMETER; rsp->hdr.Status = STATUS_INVALID_PARAMETER;
return -EINVAL; return -EINVAL;
...@@ -7076,9 +7079,7 @@ static int fsctl_copychunk(struct ksmbd_work *work, struct smb2_ioctl_req *req, ...@@ -7076,9 +7079,7 @@ static int fsctl_copychunk(struct ksmbd_work *work, struct smb2_ioctl_req *req,
src_fp = ksmbd_lookup_foreign_fd(work, src_fp = ksmbd_lookup_foreign_fd(work,
le64_to_cpu(ci_req->ResumeKey[0])); le64_to_cpu(ci_req->ResumeKey[0]));
dst_fp = ksmbd_lookup_fd_slow(work, dst_fp = ksmbd_lookup_fd_slow(work, volatile_id, persistent_id);
le64_to_cpu(req->VolatileFileId),
le64_to_cpu(req->PersistentFileId));
ret = -EINVAL; ret = -EINVAL;
if (!src_fp || if (!src_fp ||
src_fp->persistent_id != le64_to_cpu(ci_req->ResumeKey[1])) { src_fp->persistent_id != le64_to_cpu(ci_req->ResumeKey[1])) {
...@@ -7153,8 +7154,8 @@ static __be32 idev_ipv4_address(struct in_device *idev) ...@@ -7153,8 +7154,8 @@ static __be32 idev_ipv4_address(struct in_device *idev)
} }
static int fsctl_query_iface_info_ioctl(struct ksmbd_conn *conn, static int fsctl_query_iface_info_ioctl(struct ksmbd_conn *conn,
struct smb2_ioctl_req *req, struct smb2_ioctl_rsp *rsp,
struct smb2_ioctl_rsp *rsp) unsigned int out_buf_len)
{ {
struct network_interface_info_ioctl_rsp *nii_rsp = NULL; struct network_interface_info_ioctl_rsp *nii_rsp = NULL;
int nbytes = 0; int nbytes = 0;
...@@ -7166,6 +7167,12 @@ static int fsctl_query_iface_info_ioctl(struct ksmbd_conn *conn, ...@@ -7166,6 +7167,12 @@ static int fsctl_query_iface_info_ioctl(struct ksmbd_conn *conn,
rtnl_lock(); rtnl_lock();
for_each_netdev(&init_net, netdev) { for_each_netdev(&init_net, netdev) {
if (out_buf_len <
nbytes + sizeof(struct network_interface_info_ioctl_rsp)) {
rtnl_unlock();
return -ENOSPC;
}
if (netdev->type == ARPHRD_LOOPBACK) if (netdev->type == ARPHRD_LOOPBACK)
continue; continue;
...@@ -7245,11 +7252,6 @@ static int fsctl_query_iface_info_ioctl(struct ksmbd_conn *conn, ...@@ -7245,11 +7252,6 @@ static int fsctl_query_iface_info_ioctl(struct ksmbd_conn *conn,
if (nii_rsp) if (nii_rsp)
nii_rsp->Next = 0; nii_rsp->Next = 0;
if (!nbytes) {
rsp->hdr.Status = STATUS_BUFFER_TOO_SMALL;
return -EINVAL;
}
rsp->PersistentFileId = cpu_to_le64(SMB2_NO_FID); rsp->PersistentFileId = cpu_to_le64(SMB2_NO_FID);
rsp->VolatileFileId = cpu_to_le64(SMB2_NO_FID); rsp->VolatileFileId = cpu_to_le64(SMB2_NO_FID);
return nbytes; return nbytes;
...@@ -7257,11 +7259,16 @@ static int fsctl_query_iface_info_ioctl(struct ksmbd_conn *conn, ...@@ -7257,11 +7259,16 @@ static int fsctl_query_iface_info_ioctl(struct ksmbd_conn *conn,
static int fsctl_validate_negotiate_info(struct ksmbd_conn *conn, static int fsctl_validate_negotiate_info(struct ksmbd_conn *conn,
struct validate_negotiate_info_req *neg_req, struct validate_negotiate_info_req *neg_req,
struct validate_negotiate_info_rsp *neg_rsp) struct validate_negotiate_info_rsp *neg_rsp,
unsigned int in_buf_len)
{ {
int ret = 0; int ret = 0;
int dialect; int dialect;
if (in_buf_len < sizeof(struct validate_negotiate_info_req) +
le16_to_cpu(neg_req->DialectCount) * sizeof(__le16))
return -EINVAL;
dialect = ksmbd_lookup_dialect_by_id(neg_req->Dialects, dialect = ksmbd_lookup_dialect_by_id(neg_req->Dialects,
neg_req->DialectCount); neg_req->DialectCount);
if (dialect == BAD_PROT_ID || dialect != conn->dialect) { if (dialect == BAD_PROT_ID || dialect != conn->dialect) {
...@@ -7295,7 +7302,7 @@ static int fsctl_validate_negotiate_info(struct ksmbd_conn *conn, ...@@ -7295,7 +7302,7 @@ static int fsctl_validate_negotiate_info(struct ksmbd_conn *conn,
static int fsctl_query_allocated_ranges(struct ksmbd_work *work, u64 id, static int fsctl_query_allocated_ranges(struct ksmbd_work *work, u64 id,
struct file_allocated_range_buffer *qar_req, struct file_allocated_range_buffer *qar_req,
struct file_allocated_range_buffer *qar_rsp, struct file_allocated_range_buffer *qar_rsp,
int in_count, int *out_count) unsigned int in_count, unsigned int *out_count)
{ {
struct ksmbd_file *fp; struct ksmbd_file *fp;
loff_t start, length; loff_t start, length;
...@@ -7322,7 +7329,8 @@ static int fsctl_query_allocated_ranges(struct ksmbd_work *work, u64 id, ...@@ -7322,7 +7329,8 @@ static int fsctl_query_allocated_ranges(struct ksmbd_work *work, u64 id,
} }
static int fsctl_pipe_transceive(struct ksmbd_work *work, u64 id, static int fsctl_pipe_transceive(struct ksmbd_work *work, u64 id,
int out_buf_len, struct smb2_ioctl_req *req, unsigned int out_buf_len,
struct smb2_ioctl_req *req,
struct smb2_ioctl_rsp *rsp) struct smb2_ioctl_rsp *rsp)
{ {
struct ksmbd_rpc_command *rpc_resp; struct ksmbd_rpc_command *rpc_resp;
...@@ -7436,8 +7444,7 @@ int smb2_ioctl(struct ksmbd_work *work) ...@@ -7436,8 +7444,7 @@ int smb2_ioctl(struct ksmbd_work *work)
{ {
struct smb2_ioctl_req *req; struct smb2_ioctl_req *req;
struct smb2_ioctl_rsp *rsp, *rsp_org; struct smb2_ioctl_rsp *rsp, *rsp_org;
int cnt_code, nbytes = 0; unsigned int cnt_code, nbytes = 0, out_buf_len, in_buf_len;
int out_buf_len;
u64 id = KSMBD_NO_FID; u64 id = KSMBD_NO_FID;
struct ksmbd_conn *conn = work->conn; struct ksmbd_conn *conn = work->conn;
int ret = 0; int ret = 0;
...@@ -7466,7 +7473,11 @@ int smb2_ioctl(struct ksmbd_work *work) ...@@ -7466,7 +7473,11 @@ int smb2_ioctl(struct ksmbd_work *work)
cnt_code = le32_to_cpu(req->CntCode); cnt_code = le32_to_cpu(req->CntCode);
out_buf_len = le32_to_cpu(req->MaxOutputResponse); out_buf_len = le32_to_cpu(req->MaxOutputResponse);
out_buf_len = min(KSMBD_IPC_MAX_PAYLOAD, out_buf_len); out_buf_len =
min_t(u32, work->response_sz - work->next_smb2_rsp_hdr_off -
(offsetof(struct smb2_ioctl_rsp, Buffer) - 4),
out_buf_len);
in_buf_len = le32_to_cpu(req->InputCount);
switch (cnt_code) { switch (cnt_code) {
case FSCTL_DFS_GET_REFERRALS: case FSCTL_DFS_GET_REFERRALS:
...@@ -7494,6 +7505,7 @@ int smb2_ioctl(struct ksmbd_work *work) ...@@ -7494,6 +7505,7 @@ int smb2_ioctl(struct ksmbd_work *work)
break; break;
} }
case FSCTL_PIPE_TRANSCEIVE: case FSCTL_PIPE_TRANSCEIVE:
out_buf_len = min_t(u32, KSMBD_IPC_MAX_PAYLOAD, out_buf_len);
nbytes = fsctl_pipe_transceive(work, id, out_buf_len, req, rsp); nbytes = fsctl_pipe_transceive(work, id, out_buf_len, req, rsp);
break; break;
case FSCTL_VALIDATE_NEGOTIATE_INFO: case FSCTL_VALIDATE_NEGOTIATE_INFO:
...@@ -7502,9 +7514,16 @@ int smb2_ioctl(struct ksmbd_work *work) ...@@ -7502,9 +7514,16 @@ int smb2_ioctl(struct ksmbd_work *work)
goto out; goto out;
} }
if (in_buf_len < sizeof(struct validate_negotiate_info_req))
return -EINVAL;
if (out_buf_len < sizeof(struct validate_negotiate_info_rsp))
return -EINVAL;
ret = fsctl_validate_negotiate_info(conn, ret = fsctl_validate_negotiate_info(conn,
(struct validate_negotiate_info_req *)&req->Buffer[0], (struct validate_negotiate_info_req *)&req->Buffer[0],
(struct validate_negotiate_info_rsp *)&rsp->Buffer[0]); (struct validate_negotiate_info_rsp *)&rsp->Buffer[0],
in_buf_len);
if (ret < 0) if (ret < 0)
goto out; goto out;
...@@ -7513,9 +7532,10 @@ int smb2_ioctl(struct ksmbd_work *work) ...@@ -7513,9 +7532,10 @@ int smb2_ioctl(struct ksmbd_work *work)
rsp->VolatileFileId = cpu_to_le64(SMB2_NO_FID); rsp->VolatileFileId = cpu_to_le64(SMB2_NO_FID);
break; break;
case FSCTL_QUERY_NETWORK_INTERFACE_INFO: case FSCTL_QUERY_NETWORK_INTERFACE_INFO:
nbytes = fsctl_query_iface_info_ioctl(conn, req, rsp); ret = fsctl_query_iface_info_ioctl(conn, rsp, out_buf_len);
if (nbytes < 0) if (ret < 0)
goto out; goto out;
nbytes = ret;
break; break;
case FSCTL_REQUEST_RESUME_KEY: case FSCTL_REQUEST_RESUME_KEY:
if (out_buf_len < sizeof(struct resume_key_ioctl_rsp)) { if (out_buf_len < sizeof(struct resume_key_ioctl_rsp)) {
...@@ -7540,15 +7560,33 @@ int smb2_ioctl(struct ksmbd_work *work) ...@@ -7540,15 +7560,33 @@ int smb2_ioctl(struct ksmbd_work *work)
goto out; goto out;
} }
if (in_buf_len < sizeof(struct copychunk_ioctl_req)) {
ret = -EINVAL;
goto out;
}
if (out_buf_len < sizeof(struct copychunk_ioctl_rsp)) { if (out_buf_len < sizeof(struct copychunk_ioctl_rsp)) {
ret = -EINVAL; ret = -EINVAL;
goto out; goto out;
} }
nbytes = sizeof(struct copychunk_ioctl_rsp); nbytes = sizeof(struct copychunk_ioctl_rsp);
fsctl_copychunk(work, req, rsp); rsp->VolatileFileId = req->VolatileFileId;
rsp->PersistentFileId = req->PersistentFileId;
fsctl_copychunk(work,
(struct copychunk_ioctl_req *)&req->Buffer[0],
le32_to_cpu(req->CntCode),
le32_to_cpu(req->InputCount),
le64_to_cpu(req->VolatileFileId),
le64_to_cpu(req->PersistentFileId),
rsp);
break; break;
case FSCTL_SET_SPARSE: case FSCTL_SET_SPARSE:
if (in_buf_len < sizeof(struct file_sparse)) {
ret = -EINVAL;
goto out;
}
ret = fsctl_set_sparse(work, id, ret = fsctl_set_sparse(work, id,
(struct file_sparse *)&req->Buffer[0]); (struct file_sparse *)&req->Buffer[0]);
if (ret < 0) if (ret < 0)
...@@ -7567,6 +7605,11 @@ int smb2_ioctl(struct ksmbd_work *work) ...@@ -7567,6 +7605,11 @@ int smb2_ioctl(struct ksmbd_work *work)
goto out; goto out;
} }
if (in_buf_len < sizeof(struct file_zero_data_information)) {
ret = -EINVAL;
goto out;
}
zero_data = zero_data =
(struct file_zero_data_information *)&req->Buffer[0]; (struct file_zero_data_information *)&req->Buffer[0];
...@@ -7586,6 +7629,11 @@ int smb2_ioctl(struct ksmbd_work *work) ...@@ -7586,6 +7629,11 @@ int smb2_ioctl(struct ksmbd_work *work)
break; break;
} }
case FSCTL_QUERY_ALLOCATED_RANGES: case FSCTL_QUERY_ALLOCATED_RANGES:
if (in_buf_len < sizeof(struct file_allocated_range_buffer)) {
ret = -EINVAL;
goto out;
}
ret = fsctl_query_allocated_ranges(work, id, ret = fsctl_query_allocated_ranges(work, id,
(struct file_allocated_range_buffer *)&req->Buffer[0], (struct file_allocated_range_buffer *)&req->Buffer[0],
(struct file_allocated_range_buffer *)&rsp->Buffer[0], (struct file_allocated_range_buffer *)&rsp->Buffer[0],
...@@ -7626,6 +7674,11 @@ int smb2_ioctl(struct ksmbd_work *work) ...@@ -7626,6 +7674,11 @@ int smb2_ioctl(struct ksmbd_work *work)
struct duplicate_extents_to_file *dup_ext; struct duplicate_extents_to_file *dup_ext;
loff_t src_off, dst_off, length, cloned; loff_t src_off, dst_off, length, cloned;
if (in_buf_len < sizeof(struct duplicate_extents_to_file)) {
ret = -EINVAL;
goto out;
}
dup_ext = (struct duplicate_extents_to_file *)&req->Buffer[0]; dup_ext = (struct duplicate_extents_to_file *)&req->Buffer[0];
fp_in = ksmbd_lookup_fd_slow(work, dup_ext->VolatileFileHandle, fp_in = ksmbd_lookup_fd_slow(work, dup_ext->VolatileFileHandle,
...@@ -7696,6 +7749,8 @@ int smb2_ioctl(struct ksmbd_work *work) ...@@ -7696,6 +7749,8 @@ int smb2_ioctl(struct ksmbd_work *work)
rsp->hdr.Status = STATUS_OBJECT_NAME_NOT_FOUND; rsp->hdr.Status = STATUS_OBJECT_NAME_NOT_FOUND;
else if (ret == -EOPNOTSUPP) else if (ret == -EOPNOTSUPP)
rsp->hdr.Status = STATUS_NOT_SUPPORTED; rsp->hdr.Status = STATUS_NOT_SUPPORTED;
else if (ret == -ENOSPC)
rsp->hdr.Status = STATUS_BUFFER_TOO_SMALL;
else if (ret < 0 || rsp->hdr.Status == 0) else if (ret < 0 || rsp->hdr.Status == 0)
rsp->hdr.Status = STATUS_INVALID_PARAMETER; rsp->hdr.Status = STATUS_INVALID_PARAMETER;
smb2_set_err_rsp(work); smb2_set_err_rsp(work);
......
...@@ -1023,7 +1023,7 @@ int ksmbd_vfs_zero_data(struct ksmbd_work *work, struct ksmbd_file *fp, ...@@ -1023,7 +1023,7 @@ int ksmbd_vfs_zero_data(struct ksmbd_work *work, struct ksmbd_file *fp,
int ksmbd_vfs_fqar_lseek(struct ksmbd_file *fp, loff_t start, loff_t length, int ksmbd_vfs_fqar_lseek(struct ksmbd_file *fp, loff_t start, loff_t length,
struct file_allocated_range_buffer *ranges, struct file_allocated_range_buffer *ranges,
int in_count, int *out_count) unsigned int in_count, unsigned int *out_count)
{ {
struct file *f = fp->filp; struct file *f = fp->filp;
struct inode *inode = file_inode(fp->filp); struct inode *inode = file_inode(fp->filp);
......
...@@ -166,7 +166,7 @@ int ksmbd_vfs_zero_data(struct ksmbd_work *work, struct ksmbd_file *fp, ...@@ -166,7 +166,7 @@ int ksmbd_vfs_zero_data(struct ksmbd_work *work, struct ksmbd_file *fp,
struct file_allocated_range_buffer; struct file_allocated_range_buffer;
int ksmbd_vfs_fqar_lseek(struct ksmbd_file *fp, loff_t start, loff_t length, int ksmbd_vfs_fqar_lseek(struct ksmbd_file *fp, loff_t start, loff_t length,
struct file_allocated_range_buffer *ranges, struct file_allocated_range_buffer *ranges,
int in_count, int *out_count); unsigned int in_count, unsigned int *out_count);
int ksmbd_vfs_unlink(struct user_namespace *user_ns, int ksmbd_vfs_unlink(struct user_namespace *user_ns,
struct dentry *dir, struct dentry *dentry); struct dentry *dir, struct dentry *dentry);
void *ksmbd_vfs_init_kstat(char **p, struct ksmbd_kstat *ksmbd_kstat); void *ksmbd_vfs_init_kstat(char **p, struct ksmbd_kstat *ksmbd_kstat);
......
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