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_notify_send(TALLOC_CTX *mem_ctx,
26						struct tevent_context *ev,
27						struct smbd_smb2_request *smb2req,
28						uint16_t in_flags,
29						uint32_t in_output_buffer_length,
30						uint64_t in_file_id_volatile,
31						uint64_t in_completion_filter);
32static NTSTATUS smbd_smb2_notify_recv(struct tevent_req *req,
33				      TALLOC_CTX *mem_ctx,
34				      DATA_BLOB *out_output_buffer);
35
36static void smbd_smb2_request_notify_done(struct tevent_req *subreq);
37NTSTATUS smbd_smb2_request_process_notify(struct smbd_smb2_request *req)
38{
39	const uint8_t *inhdr;
40	const uint8_t *inbody;
41	int i = req->current_idx;
42	size_t expected_body_size = 0x20;
43	size_t body_size;
44	uint16_t in_flags;
45	uint32_t in_output_buffer_length;
46	uint64_t in_file_id_persistent;
47	uint64_t in_file_id_volatile;
48	uint64_t in_completion_filter;
49	struct tevent_req *subreq;
50
51	inhdr = (const uint8_t *)req->in.vector[i+0].iov_base;
52	if (req->in.vector[i+1].iov_len != (expected_body_size & 0xFFFFFFFE)) {
53		return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
54	}
55
56	inbody = (const uint8_t *)req->in.vector[i+1].iov_base;
57
58	body_size = SVAL(inbody, 0x00);
59	if (body_size != expected_body_size) {
60		return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
61	}
62
63	in_flags		= SVAL(inbody, 0x02);
64	in_output_buffer_length	= IVAL(inbody, 0x04);
65	in_file_id_persistent	= BVAL(inbody, 0x08);
66	in_file_id_volatile	= BVAL(inbody, 0x10);
67	in_completion_filter	= IVAL(inbody, 0x18);
68
69	/*
70	 * 0x00010000 is what Windows 7 uses,
71	 * Windows 2008 uses 0x00080000
72	 */
73	if (in_output_buffer_length > 0x00010000) {
74		return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
75	}
76
77	if (req->compat_chain_fsp) {
78		/* skip check */
79	} else if (in_file_id_persistent != 0) {
80		return smbd_smb2_request_error(req, NT_STATUS_FILE_CLOSED);
81	}
82
83	subreq = smbd_smb2_notify_send(req,
84				       req->sconn->smb2.event_ctx,
85				       req,
86				       in_flags,
87				       in_output_buffer_length,
88				       in_file_id_volatile,
89				       in_completion_filter);
90	if (subreq == NULL) {
91		return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
92	}
93	tevent_req_set_callback(subreq, smbd_smb2_request_notify_done, req);
94
95	return smbd_smb2_request_pending_queue(req, subreq);
96}
97
98static void smbd_smb2_request_notify_done(struct tevent_req *subreq)
99{
100	struct smbd_smb2_request *req = tevent_req_callback_data(subreq,
101					struct smbd_smb2_request);
102	int i = req->current_idx;
103	uint8_t *outhdr;
104	DATA_BLOB outbody;
105	DATA_BLOB outdyn;
106	uint16_t out_output_buffer_offset;
107	DATA_BLOB out_output_buffer = data_blob_null;
108	NTSTATUS status;
109	NTSTATUS error; /* transport error */
110
111	status = smbd_smb2_notify_recv(subreq,
112				       req,
113				       &out_output_buffer);
114	TALLOC_FREE(subreq);
115	if (!NT_STATUS_IS_OK(status)) {
116		error = smbd_smb2_request_error(req, status);
117		if (!NT_STATUS_IS_OK(error)) {
118			smbd_server_connection_terminate(req->sconn,
119							 nt_errstr(error));
120			return;
121		}
122		return;
123	}
124
125	out_output_buffer_offset = SMB2_HDR_BODY + 0x08;
126
127	outhdr = (uint8_t *)req->out.vector[i].iov_base;
128
129	outbody = data_blob_talloc(req->out.vector, NULL, 0x08);
130	if (outbody.data == NULL) {
131		error = smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
132		if (!NT_STATUS_IS_OK(error)) {
133			smbd_server_connection_terminate(req->sconn,
134							 nt_errstr(error));
135			return;
136		}
137		return;
138	}
139
140	SSVAL(outbody.data, 0x00, 0x08 + 1);	/* struct size */
141	SSVAL(outbody.data, 0x02,
142	      out_output_buffer_offset);	/* output buffer offset */
143	SIVAL(outbody.data, 0x04,
144	      out_output_buffer.length);	/* output buffer length */
145
146	outdyn = out_output_buffer;
147
148	error = smbd_smb2_request_done(req, outbody, &outdyn);
149	if (!NT_STATUS_IS_OK(error)) {
150		smbd_server_connection_terminate(req->sconn,
151						 nt_errstr(error));
152		return;
153	}
154}
155
156struct smbd_smb2_notify_state {
157	struct smbd_smb2_request *smb2req;
158	struct smb_request *smbreq;
159	struct tevent_immediate *im;
160	NTSTATUS status;
161	DATA_BLOB out_output_buffer;
162};
163
164static void smbd_smb2_notify_reply(struct smb_request *smbreq,
165				   NTSTATUS error_code,
166				   uint8_t *buf, size_t len);
167static void smbd_smb2_notify_reply_trigger(struct tevent_context *ctx,
168					   struct tevent_immediate *im,
169					   void *private_data);
170static bool smbd_smb2_notify_cancel(struct tevent_req *req);
171
172static struct tevent_req *smbd_smb2_notify_send(TALLOC_CTX *mem_ctx,
173						struct tevent_context *ev,
174						struct smbd_smb2_request *smb2req,
175						uint16_t in_flags,
176						uint32_t in_output_buffer_length,
177						uint64_t in_file_id_volatile,
178						uint64_t in_completion_filter)
179{
180	struct tevent_req *req;
181	struct smbd_smb2_notify_state *state;
182	struct smb_request *smbreq;
183	connection_struct *conn = smb2req->tcon->compat_conn;
184	files_struct *fsp;
185	bool recursive = (in_flags & 0x0001) ? true : false;
186	NTSTATUS status;
187
188	req = tevent_req_create(mem_ctx, &state,
189				struct smbd_smb2_notify_state);
190	if (req == NULL) {
191		return NULL;
192	}
193	state->smb2req = smb2req;
194	state->status = NT_STATUS_INTERNAL_ERROR;
195	state->out_output_buffer = data_blob_null;
196	state->im = NULL;
197
198	DEBUG(10,("smbd_smb2_notify_send: file_id[0x%016llX]\n",
199		  (unsigned long long)in_file_id_volatile));
200
201	smbreq = smbd_smb2_fake_smb_request(smb2req);
202	if (tevent_req_nomem(smbreq, req)) {
203		return tevent_req_post(req, ev);
204	}
205
206	state->smbreq = smbreq;
207	smbreq->async_priv = (void *)req;
208
209	fsp = file_fsp(smbreq, (uint16_t)in_file_id_volatile);
210	if (fsp == NULL) {
211		tevent_req_nterror(req, NT_STATUS_FILE_CLOSED);
212		return tevent_req_post(req, ev);
213	}
214	if (conn != fsp->conn) {
215		tevent_req_nterror(req, NT_STATUS_FILE_CLOSED);
216		return tevent_req_post(req, ev);
217	}
218	if (smb2req->session->vuid != fsp->vuid) {
219		tevent_req_nterror(req, NT_STATUS_FILE_CLOSED);
220		return tevent_req_post(req, ev);
221	}
222
223	{
224		char *filter_string;
225
226		filter_string = notify_filter_string(NULL, in_completion_filter);
227		if (tevent_req_nomem(filter_string, req)) {
228			return tevent_req_post(req, ev);
229		}
230
231		DEBUG(3,("smbd_smb2_notify_send: notify change "
232			 "called on %s, filter = %s, recursive = %d\n",
233			 fsp_str_dbg(fsp), filter_string, recursive));
234
235		TALLOC_FREE(filter_string);
236	}
237
238	if ((!fsp->is_directory) || (conn != fsp->conn)) {
239		tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
240		return tevent_req_post(req, ev);
241	}
242
243	if (fsp->notify == NULL) {
244
245		status = change_notify_create(fsp,
246					      in_completion_filter,
247					      recursive);
248		if (!NT_STATUS_IS_OK(status)) {
249			DEBUG(10, ("change_notify_create returned %s\n",
250				   nt_errstr(status)));
251			tevent_req_nterror(req, status);
252			return tevent_req_post(req, ev);
253		}
254	}
255
256	if (fsp->notify->num_changes != 0) {
257
258		/*
259		 * We've got changes pending, respond immediately
260		 */
261
262		/*
263		 * TODO: write a torture test to check the filtering behaviour
264		 * here.
265		 */
266
267		change_notify_reply(fsp->conn, smbreq,
268				    NT_STATUS_OK,
269				    in_output_buffer_length,
270				    fsp->notify,
271				    smbd_smb2_notify_reply);
272
273		/*
274		 * change_notify_reply() above has independently
275		 * called tevent_req_done().
276		 */
277		return tevent_req_post(req, ev);
278	}
279
280	state->im = tevent_create_immediate(state);
281	if (tevent_req_nomem(state->im, req)) {
282		return tevent_req_post(req, ev);
283	}
284
285	/*
286	 * No changes pending, queue the request
287	 */
288
289	status = change_notify_add_request(smbreq,
290			in_output_buffer_length,
291			in_completion_filter,
292			recursive, fsp,
293			smbd_smb2_notify_reply);
294	if (!NT_STATUS_IS_OK(status)) {
295		tevent_req_nterror(req, status);
296		return tevent_req_post(req, ev);
297	}
298
299	/* allow this request to be canceled */
300	tevent_req_set_cancel_fn(req, smbd_smb2_notify_cancel);
301
302	return req;
303}
304
305static void smbd_smb2_notify_reply(struct smb_request *smbreq,
306				   NTSTATUS error_code,
307				   uint8_t *buf, size_t len)
308{
309	struct tevent_req *req = talloc_get_type_abort(smbreq->async_priv,
310						       struct tevent_req);
311	struct smbd_smb2_notify_state *state = tevent_req_data(req,
312					       struct smbd_smb2_notify_state);
313
314	state->status = error_code;
315	if (!NT_STATUS_IS_OK(error_code)) {
316		/* nothing */
317	} else if (len == 0) {
318		state->status = STATUS_NOTIFY_ENUM_DIR;
319	} else {
320		state->out_output_buffer = data_blob_talloc(state, buf, len);
321		if (state->out_output_buffer.data == NULL) {
322			state->status = NT_STATUS_NO_MEMORY;
323		}
324	}
325
326	if (state->im == NULL) {
327		smbd_smb2_notify_reply_trigger(NULL, NULL, req);
328		return;
329	}
330
331	/*
332	 * if this is called async, we need to go via an immediate event
333	 * because the caller replies on the smb_request (a child of req
334	 * being arround after calling this function
335	 */
336	tevent_schedule_immediate(state->im,
337				  state->smb2req->sconn->smb2.event_ctx,
338				  smbd_smb2_notify_reply_trigger,
339				  req);
340}
341
342static void smbd_smb2_notify_reply_trigger(struct tevent_context *ctx,
343					   struct tevent_immediate *im,
344					   void *private_data)
345{
346	struct tevent_req *req = talloc_get_type_abort(private_data,
347						       struct tevent_req);
348	struct smbd_smb2_notify_state *state = tevent_req_data(req,
349					       struct smbd_smb2_notify_state);
350
351	if (!NT_STATUS_IS_OK(state->status)) {
352		tevent_req_nterror(req, state->status);
353		return;
354	}
355
356	tevent_req_done(req);
357}
358
359static bool smbd_smb2_notify_cancel(struct tevent_req *req)
360{
361	struct smbd_smb2_notify_state *state = tevent_req_data(req,
362					       struct smbd_smb2_notify_state);
363
364	smbd_notify_cancel_by_smbreq(state->smb2req->sconn,
365				     state->smbreq);
366
367	return true;
368}
369
370static NTSTATUS smbd_smb2_notify_recv(struct tevent_req *req,
371				      TALLOC_CTX *mem_ctx,
372				      DATA_BLOB *out_output_buffer)
373{
374	NTSTATUS status;
375	struct smbd_smb2_notify_state *state = tevent_req_data(req,
376					       struct smbd_smb2_notify_state);
377
378	if (tevent_req_is_nterror(req, &status)) {
379		tevent_req_received(req);
380		return status;
381	}
382
383	*out_output_buffer = state->out_output_buffer;
384	talloc_steal(mem_ctx, out_output_buffer->data);
385
386	tevent_req_received(req);
387	return NT_STATUS_OK;
388}
389