1/*
2   Unix SMB/CIFS implementation.
3   Core SMB2 server
4
5   Copyright (C) Stefan Metzmacher 2009
6
7   This program is free software; you can redistribute it and/or modify
8   it under the terms of the GNU General Public License as published by
9   the Free Software Foundation; either version 3 of the License, or
10   (at your option) any later version.
11
12   This program is distributed in the hope that it will be useful,
13   but WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15   GNU General Public License for more details.
16
17   You should have received a copy of the GNU General Public License
18   along with this program.  If not, see <http://www.gnu.org/licenses/>.
19*/
20
21#include "includes.h"
22#include "smbd/globals.h"
23#include "../libcli/smb/smb_common.h"
24
25static struct tevent_req *smbd_smb2_oplock_break_send(TALLOC_CTX *mem_ctx,
26						      struct tevent_context *ev,
27						      struct smbd_smb2_request *smb2req,
28						      uint8_t in_oplock_level,
29						      uint64_t in_file_id_volatile);
30static NTSTATUS smbd_smb2_oplock_break_recv(struct tevent_req *req,
31					    uint8_t *out_oplock_level);
32
33static void smbd_smb2_request_oplock_break_done(struct tevent_req *subreq);
34NTSTATUS smbd_smb2_request_process_break(struct smbd_smb2_request *req)
35{
36	const uint8_t *inhdr;
37	const uint8_t *inbody;
38	int i = req->current_idx;
39	size_t expected_body_size = 0x18;
40	size_t body_size;
41	uint8_t in_oplock_level;
42	uint64_t in_file_id_persistent;
43	uint64_t in_file_id_volatile;
44	struct tevent_req *subreq;
45
46	inhdr = (const uint8_t *)req->in.vector[i+0].iov_base;
47	if (req->in.vector[i+1].iov_len != (expected_body_size & 0xFFFFFFFE)) {
48		return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
49	}
50
51	inbody = (const uint8_t *)req->in.vector[i+1].iov_base;
52
53	body_size = SVAL(inbody, 0x00);
54	if (body_size != expected_body_size) {
55		return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
56	}
57
58	in_oplock_level		= CVAL(inbody, 0x02);
59	/* 0x03 1 bytes reserved */
60	/* 0x04 4 bytes reserved */
61	in_file_id_persistent		= BVAL(inbody, 0x08);
62	in_file_id_volatile		= BVAL(inbody, 0x10);
63
64	if (req->compat_chain_fsp) {
65		/* skip check */
66	} else if (in_file_id_persistent != 0) {
67		return smbd_smb2_request_error(req, NT_STATUS_FILE_CLOSED);
68	}
69
70	subreq = smbd_smb2_oplock_break_send(req,
71					     req->sconn->smb2.event_ctx,
72					     req,
73					     in_oplock_level,
74					     in_file_id_volatile);
75	if (subreq == NULL) {
76		return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
77	}
78	tevent_req_set_callback(subreq, smbd_smb2_request_oplock_break_done, req);
79
80	return smbd_smb2_request_pending_queue(req, subreq);
81}
82
83static void smbd_smb2_request_oplock_break_done(struct tevent_req *subreq)
84{
85	struct smbd_smb2_request *req = tevent_req_callback_data(subreq,
86					struct smbd_smb2_request);
87	const uint8_t *inbody;
88	int i = req->current_idx;
89	uint64_t in_file_id_persistent;
90	uint64_t in_file_id_volatile;
91	uint8_t out_oplock_level = 0;
92	DATA_BLOB outbody;
93	NTSTATUS status;
94	NTSTATUS error; /* transport error */
95
96	status = smbd_smb2_oplock_break_recv(subreq, &out_oplock_level);
97	TALLOC_FREE(subreq);
98	if (!NT_STATUS_IS_OK(status)) {
99		error = smbd_smb2_request_error(req, status);
100		if (!NT_STATUS_IS_OK(error)) {
101			smbd_server_connection_terminate(req->sconn,
102							 nt_errstr(error));
103			return;
104		}
105		return;
106	}
107
108	inbody = (const uint8_t *)req->in.vector[i+1].iov_base;
109
110	in_file_id_persistent	= BVAL(inbody, 0x08);
111	in_file_id_volatile	= BVAL(inbody, 0x10);
112
113	outbody = data_blob_talloc(req->out.vector, NULL, 0x18);
114	if (outbody.data == NULL) {
115		error = smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
116		if (!NT_STATUS_IS_OK(error)) {
117			smbd_server_connection_terminate(req->sconn,
118							 nt_errstr(error));
119			return;
120		}
121		return;
122	}
123
124	SSVAL(outbody.data, 0x00, 0x18);	/* struct size */
125	SCVAL(outbody.data, 0x02,
126	      out_oplock_level);		/* oplock level */
127	SCVAL(outbody.data, 0x03, 0);		/* reserved */
128	SIVAL(outbody.data, 0x04, 0);		/* reserved */
129	SBVAL(outbody.data, 0x08,
130	      in_file_id_persistent);		/* file id (persistent) */
131	SBVAL(outbody.data, 0x10,
132	      in_file_id_volatile);		/* file id (volatile) */
133
134	error = smbd_smb2_request_done(req, outbody, NULL);
135	if (!NT_STATUS_IS_OK(error)) {
136		smbd_server_connection_terminate(req->sconn,
137						 nt_errstr(error));
138		return;
139	}
140}
141
142struct smbd_smb2_oplock_break_state {
143	struct smbd_smb2_request *smb2req;
144	uint8_t out_oplock_level;
145};
146
147static struct tevent_req *smbd_smb2_oplock_break_send(TALLOC_CTX *mem_ctx,
148						      struct tevent_context *ev,
149						      struct smbd_smb2_request *smb2req,
150						      uint8_t in_oplock_level,
151						      uint64_t in_file_id_volatile)
152{
153	struct tevent_req *req;
154	struct smbd_smb2_oplock_break_state *state;
155	struct smb_request *smbreq;
156	connection_struct *conn = smb2req->tcon->compat_conn;
157	files_struct *fsp;
158
159	req = tevent_req_create(mem_ctx, &state,
160				struct smbd_smb2_oplock_break_state);
161	if (req == NULL) {
162		return NULL;
163	}
164	state->smb2req = smb2req;
165	state->out_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
166
167	DEBUG(10,("smbd_smb2_oplock_break_send: file_id[0x%016llX]\n",
168		  (unsigned long long)in_file_id_volatile));
169
170	smbreq = smbd_smb2_fake_smb_request(smb2req);
171	if (tevent_req_nomem(smbreq, req)) {
172		return tevent_req_post(req, ev);
173	}
174
175	fsp = file_fsp(smbreq, (uint16_t)in_file_id_volatile);
176	if (fsp == NULL) {
177		tevent_req_nterror(req, NT_STATUS_FILE_CLOSED);
178		return tevent_req_post(req, ev);
179	}
180	if (conn != fsp->conn) {
181		tevent_req_nterror(req, NT_STATUS_FILE_CLOSED);
182		return tevent_req_post(req, ev);
183	}
184	if (smb2req->session->vuid != fsp->vuid) {
185		tevent_req_nterror(req, NT_STATUS_FILE_CLOSED);
186		return tevent_req_post(req, ev);
187	}
188
189	tevent_req_nterror(req, NT_STATUS_NOT_IMPLEMENTED);
190	return tevent_req_post(req, ev);
191}
192
193static NTSTATUS smbd_smb2_oplock_break_recv(struct tevent_req *req,
194					    uint8_t *out_oplock_level)
195{
196	NTSTATUS status;
197	struct smbd_smb2_oplock_break_state *state =
198		tevent_req_data(req,
199		struct smbd_smb2_oplock_break_state);
200
201	if (tevent_req_is_nterror(req, &status)) {
202		tevent_req_received(req);
203		return status;
204	}
205
206	*out_oplock_level = state->out_oplock_level;
207
208	tevent_req_received(req);
209	return NT_STATUS_OK;
210}
211