ksmbd: validate OutputBufferLength of QUERY_DIR, QUERY_INFO, IOCTL requests

Validate OutputBufferLength of QUERY_DIR, QUERY_INFO, IOCTL requests and
check the free size of response buffer for these requests.

Acked-by: Namjae Jeon <linkinjeon@kernel.org>
Signed-off-by: Hyunchul Lee <hyc.lee@gmail.com>
Signed-off-by: Steve French <stfrench@microsoft.com>

authored by Hyunchul Lee and committed by Steve French 34061d6b 7a334887

+52 -16
+52 -16
fs/ksmbd/smb2pdu.c
··· 3762 return 0; 3763 } 3764 3765 int smb2_query_dir(struct ksmbd_work *work) 3766 { 3767 struct ksmbd_conn *conn = work->conn; ··· 3856 memset(&d_info, 0, sizeof(struct ksmbd_dir_info)); 3857 d_info.wptr = (char *)rsp->Buffer; 3858 d_info.rptr = (char *)rsp->Buffer; 3859 - d_info.out_buf_len = (work->response_sz - (get_rfc1002_len(rsp_org) + 4)); 3860 - d_info.out_buf_len = min_t(int, d_info.out_buf_len, le32_to_cpu(req->OutputBufferLength)) - 3861 - sizeof(struct smb2_query_directory_rsp); 3862 d_info.flags = srch_flag; 3863 3864 /* ··· 4096 le32_to_cpu(req->Flags)); 4097 } 4098 4099 - buf_free_len = work->response_sz - 4100 - (get_rfc1002_len(rsp_org) + 4) - 4101 - sizeof(struct smb2_query_info_rsp); 4102 - 4103 - if (le32_to_cpu(req->OutputBufferLength) < buf_free_len) 4104 - buf_free_len = le32_to_cpu(req->OutputBufferLength); 4105 4106 rc = ksmbd_vfs_listxattr(path->dentry, &xattr_list); 4107 if (rc < 0) { ··· 4411 struct path *path = &fp->filp->f_path; 4412 ssize_t xattr_list_len; 4413 int nbytes = 0, streamlen, stream_name_len, next, idx = 0; 4414 4415 generic_fillattr(file_mnt_user_ns(fp->filp), file_inode(fp->filp), 4416 &stat); ··· 4425 ksmbd_debug(SMB, "empty xattr in the file\n"); 4426 goto out; 4427 } 4428 4429 while (idx < xattr_list_len) { 4430 stream_name = xattr_list + idx; ··· 4456 streamlen = snprintf(stream_buf, streamlen + 1, 4457 ":%s", &stream_name[XATTR_NAME_STREAM_LEN]); 4458 4459 file_info = (struct smb2_file_stream_info *)&rsp->Buffer[nbytes]; 4460 streamlen = smbConvertToUTF16((__le16 *)file_info->StreamName, 4461 stream_buf, streamlen, ··· 4470 file_info->StreamSize = cpu_to_le64(stream_name_len); 4471 file_info->StreamAllocationSize = cpu_to_le64(stream_name_len); 4472 4473 - next = sizeof(struct smb2_file_stream_info) + streamlen; 4474 nbytes += next; 4475 file_info->NextEntryOffset = cpu_to_le32(next); 4476 } 4477 4478 - if (!S_ISDIR(stat.mode)) { 4479 file_info = (struct smb2_file_stream_info *) 4480 &rsp->Buffer[nbytes]; 4481 streamlen = smbConvertToUTF16((__le16 *)file_info->StreamName, ··· 7487 } 7488 7489 cnt_code = le32_to_cpu(req->CntCode); 7490 - out_buf_len = le32_to_cpu(req->MaxOutputResponse); 7491 - out_buf_len = 7492 - min_t(u32, work->response_sz - work->next_smb2_rsp_hdr_off - 7493 - (offsetof(struct smb2_ioctl_rsp, Buffer) - 4), 7494 - out_buf_len); 7495 in_buf_len = le32_to_cpu(req->InputCount); 7496 7497 switch (cnt_code) {
··· 3762 return 0; 3763 } 3764 3765 + static int smb2_calc_max_out_buf_len(struct ksmbd_work *work, 3766 + unsigned short hdr2_len, 3767 + unsigned int out_buf_len) 3768 + { 3769 + int free_len; 3770 + 3771 + if (out_buf_len > work->conn->vals->max_trans_size) 3772 + return -EINVAL; 3773 + 3774 + free_len = (int)(work->response_sz - 3775 + (get_rfc1002_len(work->response_buf) + 4)) - 3776 + hdr2_len; 3777 + if (free_len < 0) 3778 + return -EINVAL; 3779 + 3780 + return min_t(int, out_buf_len, free_len); 3781 + } 3782 + 3783 int smb2_query_dir(struct ksmbd_work *work) 3784 { 3785 struct ksmbd_conn *conn = work->conn; ··· 3838 memset(&d_info, 0, sizeof(struct ksmbd_dir_info)); 3839 d_info.wptr = (char *)rsp->Buffer; 3840 d_info.rptr = (char *)rsp->Buffer; 3841 + d_info.out_buf_len = 3842 + smb2_calc_max_out_buf_len(work, 8, 3843 + le32_to_cpu(req->OutputBufferLength)); 3844 + if (d_info.out_buf_len < 0) { 3845 + rc = -EINVAL; 3846 + goto err_out; 3847 + } 3848 d_info.flags = srch_flag; 3849 3850 /* ··· 4074 le32_to_cpu(req->Flags)); 4075 } 4076 4077 + buf_free_len = 4078 + smb2_calc_max_out_buf_len(work, 8, 4079 + le32_to_cpu(req->OutputBufferLength)); 4080 + if (buf_free_len < 0) 4081 + return -EINVAL; 4082 4083 rc = ksmbd_vfs_listxattr(path->dentry, &xattr_list); 4084 if (rc < 0) { ··· 4390 struct path *path = &fp->filp->f_path; 4391 ssize_t xattr_list_len; 4392 int nbytes = 0, streamlen, stream_name_len, next, idx = 0; 4393 + int buf_free_len; 4394 + struct smb2_query_info_req *req = ksmbd_req_buf_next(work); 4395 4396 generic_fillattr(file_mnt_user_ns(fp->filp), file_inode(fp->filp), 4397 &stat); ··· 4402 ksmbd_debug(SMB, "empty xattr in the file\n"); 4403 goto out; 4404 } 4405 + 4406 + buf_free_len = 4407 + smb2_calc_max_out_buf_len(work, 8, 4408 + le32_to_cpu(req->OutputBufferLength)); 4409 + if (buf_free_len < 0) 4410 + goto out; 4411 4412 while (idx < xattr_list_len) { 4413 stream_name = xattr_list + idx; ··· 4427 streamlen = snprintf(stream_buf, streamlen + 1, 4428 ":%s", &stream_name[XATTR_NAME_STREAM_LEN]); 4429 4430 + next = sizeof(struct smb2_file_stream_info) + streamlen * 2; 4431 + if (next > buf_free_len) 4432 + break; 4433 + 4434 file_info = (struct smb2_file_stream_info *)&rsp->Buffer[nbytes]; 4435 streamlen = smbConvertToUTF16((__le16 *)file_info->StreamName, 4436 stream_buf, streamlen, ··· 4437 file_info->StreamSize = cpu_to_le64(stream_name_len); 4438 file_info->StreamAllocationSize = cpu_to_le64(stream_name_len); 4439 4440 nbytes += next; 4441 + buf_free_len -= next; 4442 file_info->NextEntryOffset = cpu_to_le32(next); 4443 } 4444 4445 + if (!S_ISDIR(stat.mode) && 4446 + buf_free_len >= sizeof(struct smb2_file_stream_info) + 7 * 2) { 4447 file_info = (struct smb2_file_stream_info *) 4448 &rsp->Buffer[nbytes]; 4449 streamlen = smbConvertToUTF16((__le16 *)file_info->StreamName, ··· 7453 } 7454 7455 cnt_code = le32_to_cpu(req->CntCode); 7456 + ret = smb2_calc_max_out_buf_len(work, 48, 7457 + le32_to_cpu(req->MaxOutputResponse)); 7458 + if (ret < 0) { 7459 + rsp->hdr.Status = STATUS_INVALID_PARAMETER; 7460 + goto out; 7461 + } 7462 + out_buf_len = (unsigned int)ret; 7463 in_buf_len = le32_to_cpu(req->InputCount); 7464 7465 switch (cnt_code) {