NTSTATUS smbd_smb2_request_process_ioctl(struct smbd_smb2_request *req) { const uint8_t *inhdr; const uint8_t *inbody; int i = req->current_idx; size_t expected_body_size = 0x39; size_t body_size; uint32_t in_ctl_code; uint64_t in_file_id_persistent; uint64_t in_file_id_volatile; uint32_t in_input_offset; uint32_t in_input_length; DATA_BLOB in_input_buffer; uint32_t in_max_output_length; uint32_t in_flags; struct tevent_req *subreq; inhdr = (const uint8_t *)req->in.vector[i+0].iov_base; if (req->in.vector[i+1].iov_len != (expected_body_size & 0xFFFFFFFE)) { return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); } inbody = (const uint8_t *)req->in.vector[i+1].iov_base; body_size = SVAL(inbody, 0x00); if (body_size != expected_body_size) { return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); } in_ctl_code = IVAL(inbody, 0x04); in_file_id_persistent = BVAL(inbody, 0x08); in_file_id_volatile = BVAL(inbody, 0x10); in_input_offset = IVAL(inbody, 0x18); in_input_length = IVAL(inbody, 0x1C); in_max_output_length = IVAL(inbody, 0x2C); in_flags = IVAL(inbody, 0x30); if (in_input_offset != (SMB2_HDR_BODY + (body_size & 0xFFFFFFFE))) { return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); } if (in_input_length > req->in.vector[i+2].iov_len) { return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); } in_input_buffer.data = (uint8_t *)req->in.vector[i+2].iov_base; in_input_buffer.length = in_input_length; if (req->compat_chain_fsp) { /* skip check */ } else if (in_file_id_persistent == UINT64_MAX && in_file_id_volatile == UINT64_MAX) { /* without a handle */ } else if (in_file_id_persistent != in_file_id_volatile) { return smbd_smb2_request_error(req, NT_STATUS_FILE_CLOSED); } subreq = smbd_smb2_ioctl_send(req, req->sconn->smb2.event_ctx, req, in_ctl_code, in_file_id_volatile, in_input_buffer, in_max_output_length, in_flags); if (subreq == NULL) { return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY); } tevent_req_set_callback(subreq, smbd_smb2_request_ioctl_done, req); return smbd_smb2_request_pending_queue(req, subreq); }
NTSTATUS smbd_smb2_request_process_ioctl(struct smbd_smb2_request *req) { NTSTATUS status; const uint8_t *inbody; uint32_t min_buffer_offset; uint32_t max_buffer_offset; uint32_t min_output_offset; uint32_t allowed_length_in; uint32_t allowed_length_out; uint32_t in_ctl_code; uint64_t in_file_id_persistent; uint64_t in_file_id_volatile; struct files_struct *in_fsp = NULL; uint32_t in_input_offset; uint32_t in_input_length; DATA_BLOB in_input_buffer = data_blob_null; uint32_t in_max_input_length; uint32_t in_output_offset; uint32_t in_output_length; DATA_BLOB in_output_buffer = data_blob_null; uint32_t in_max_output_length; uint32_t in_flags; uint32_t data_length_in; uint32_t data_length_out; uint32_t data_length_tmp; uint32_t data_length_max; struct tevent_req *subreq; status = smbd_smb2_request_verify_sizes(req, 0x39); if (!NT_STATUS_IS_OK(status)) { return smbd_smb2_request_error(req, status); } inbody = SMBD_SMB2_IN_BODY_PTR(req); in_ctl_code = IVAL(inbody, 0x04); in_file_id_persistent = BVAL(inbody, 0x08); in_file_id_volatile = BVAL(inbody, 0x10); in_input_offset = IVAL(inbody, 0x18); in_input_length = IVAL(inbody, 0x1C); in_max_input_length = IVAL(inbody, 0x20); in_output_offset = IVAL(inbody, 0x24); in_output_length = IVAL(inbody, 0x28); in_max_output_length = IVAL(inbody, 0x2C); in_flags = IVAL(inbody, 0x30); min_buffer_offset = SMB2_HDR_BODY + SMBD_SMB2_IN_BODY_LEN(req); max_buffer_offset = min_buffer_offset + SMBD_SMB2_IN_DYN_LEN(req); min_output_offset = min_buffer_offset; /* * InputOffset (4 bytes): The offset, in bytes, from the beginning of * the SMB2 header to the input data buffer. If no input data is * required for the FSCTL/IOCTL command being issued, the client SHOULD * set this value to 0.<49> * <49> If no input data is required for the FSCTL/IOCTL command being * issued, Windows-based clients set this field to any value. */ allowed_length_in = 0; if ((in_input_offset > 0) && (in_input_length > 0)) { uint32_t tmp_ofs; if (in_input_offset < min_buffer_offset) { return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); } if (in_input_offset > max_buffer_offset) { return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); } allowed_length_in = max_buffer_offset - in_input_offset; tmp_ofs = in_input_offset - min_buffer_offset; in_input_buffer.data = SMBD_SMB2_IN_DYN_PTR(req); in_input_buffer.data += tmp_ofs; in_input_buffer.length = in_input_length; min_output_offset += tmp_ofs; min_output_offset += in_input_length; } if (in_input_length > allowed_length_in) { return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); } allowed_length_out = 0; if (in_output_offset > 0) { if (in_output_offset < min_buffer_offset) { return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); } if (in_output_offset > max_buffer_offset) { return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); } allowed_length_out = max_buffer_offset - in_output_offset; } if (in_output_length > allowed_length_out) { return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); } if (in_output_length > 0) { uint32_t tmp_ofs; if (in_output_offset < min_output_offset) { return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); } tmp_ofs = in_output_offset - min_buffer_offset; in_output_buffer.data = SMBD_SMB2_IN_DYN_PTR(req); in_output_buffer.data += tmp_ofs; in_output_buffer.length = in_output_length; } /* * verify the credits and avoid overflows * in_input_buffer.length and in_output_buffer.length * are already verified. */ data_length_in = in_input_buffer.length + in_output_buffer.length; data_length_out = in_max_input_length; data_length_tmp = UINT32_MAX - data_length_out; if (data_length_tmp < in_max_output_length) { return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); } data_length_out += in_max_output_length; data_length_max = MAX(data_length_in, data_length_out); status = smbd_smb2_request_verify_creditcharge(req, data_length_max); if (!NT_STATUS_IS_OK(status)) { return smbd_smb2_request_error(req, status); } /* * If the Flags field of the request is not SMB2_0_IOCTL_IS_FSCTL the * server MUST fail the request with STATUS_NOT_SUPPORTED. */ if (in_flags != SMB2_IOCTL_FLAG_IS_FSCTL) { return smbd_smb2_request_error(req, NT_STATUS_NOT_SUPPORTED); } switch (in_ctl_code) { case FSCTL_DFS_GET_REFERRALS: case FSCTL_DFS_GET_REFERRALS_EX: case FSCTL_PIPE_WAIT: case FSCTL_VALIDATE_NEGOTIATE_INFO_224: case FSCTL_VALIDATE_NEGOTIATE_INFO: case FSCTL_QUERY_NETWORK_INTERFACE_INFO: /* * Some SMB2 specific CtlCodes like FSCTL_DFS_GET_REFERRALS or * FSCTL_PIPE_WAIT does not take a file handle. * * If FileId in the SMB2 Header of the request is not * 0xFFFFFFFFFFFFFFFF, then the server MUST fail the request * with STATUS_INVALID_PARAMETER. */ if (in_file_id_persistent != UINT64_MAX || in_file_id_volatile != UINT64_MAX) { return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); } break; default: in_fsp = file_fsp_smb2(req, in_file_id_persistent, in_file_id_volatile); if (in_fsp == NULL) { return smbd_smb2_request_error(req, NT_STATUS_FILE_CLOSED); } break; } subreq = smbd_smb2_ioctl_send(req, req->sconn->ev_ctx, req, in_fsp, in_ctl_code, in_input_buffer, in_max_output_length, in_flags); if (subreq == NULL) { return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY); } tevent_req_set_callback(subreq, smbd_smb2_request_ioctl_done, req); return smbd_smb2_request_pending_queue(req, subreq, 1000); }