1 /*
2    Unix SMB/CIFS implementation.
3    Core SMB2 server
4 
5    Copyright (C) Stefan Metzmacher 2009
6    Copyright (C) Jeremy Allison 2010
7 
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 3 of the License, or
11    (at your option) any later version.
12 
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17 
18    You should have received a copy of the GNU General Public License
19    along with this program.  If not, see <http://www.gnu.org/licenses/>.
20 */
21 
22 #include "includes.h"
23 #include "smbd/smbd.h"
24 #include "smbd/globals.h"
25 #include "../libcli/smb/smb_common.h"
26 #include "../lib/util/tevent_ntstatus.h"
27 
28 #undef DBGC_CLASS
29 #define DBGC_CLASS DBGC_SMB2
30 
31 struct smbd_smb2_notify_state {
32 	struct smbd_smb2_request *smb2req;
33 	struct smb_request *smbreq;
34 	bool has_request;
35 	bool skip_reply;
36 	NTSTATUS status;
37 	DATA_BLOB out_output_buffer;
38 };
39 
40 static struct tevent_req *smbd_smb2_notify_send(TALLOC_CTX *mem_ctx,
41 						struct tevent_context *ev,
42 						struct smbd_smb2_request *smb2req,
43 						struct files_struct *in_fsp,
44 						uint16_t in_flags,
45 						uint32_t in_output_buffer_length,
46 						uint64_t in_completion_filter);
47 static NTSTATUS smbd_smb2_notify_recv(struct tevent_req *req,
48 				      TALLOC_CTX *mem_ctx,
49 				      DATA_BLOB *out_output_buffer);
50 
51 static void smbd_smb2_request_notify_done(struct tevent_req *subreq);
smbd_smb2_request_process_notify(struct smbd_smb2_request * req)52 NTSTATUS smbd_smb2_request_process_notify(struct smbd_smb2_request *req)
53 {
54 	struct smbXsrv_connection *xconn = req->xconn;
55 	NTSTATUS status;
56 	const uint8_t *inbody;
57 	uint16_t in_flags;
58 	uint32_t in_output_buffer_length;
59 	uint64_t in_file_id_persistent;
60 	uint64_t in_file_id_volatile;
61 	struct files_struct *in_fsp;
62 	uint64_t in_completion_filter;
63 	struct tevent_req *subreq;
64 
65 	status = smbd_smb2_request_verify_sizes(req, 0x20);
66 	if (!NT_STATUS_IS_OK(status)) {
67 		return smbd_smb2_request_error(req, status);
68 	}
69 	inbody = SMBD_SMB2_IN_BODY_PTR(req);
70 
71 	in_flags		= SVAL(inbody, 0x02);
72 	in_output_buffer_length	= IVAL(inbody, 0x04);
73 	in_file_id_persistent	= BVAL(inbody, 0x08);
74 	in_file_id_volatile	= BVAL(inbody, 0x10);
75 	in_completion_filter	= IVAL(inbody, 0x18);
76 
77 	/*
78 	 * 0x00010000 is what Windows 7 uses,
79 	 * Windows 2008 uses 0x00080000
80 	 */
81 	if (in_output_buffer_length > xconn->smb2.server.max_trans) {
82 		return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
83 	}
84 
85 	status = smbd_smb2_request_verify_creditcharge(req,
86 						in_output_buffer_length);
87 
88 	if (!NT_STATUS_IS_OK(status)) {
89 		return smbd_smb2_request_error(req, status);
90 	}
91 
92 	in_fsp = file_fsp_smb2(req, in_file_id_persistent, in_file_id_volatile);
93 	if (in_fsp == NULL) {
94 		return smbd_smb2_request_error(req, NT_STATUS_FILE_CLOSED);
95 	}
96 
97 	subreq = smbd_smb2_notify_send(req, req->sconn->ev_ctx,
98 				       req, in_fsp,
99 				       in_flags,
100 				       in_output_buffer_length,
101 				       in_completion_filter);
102 	if (subreq == NULL) {
103 		return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
104 	}
105 	tevent_req_set_callback(subreq, smbd_smb2_request_notify_done, req);
106 
107 	return smbd_smb2_request_pending_queue(req, subreq, 500);
108 }
109 
smbd_smb2_request_notify_done(struct tevent_req * subreq)110 static void smbd_smb2_request_notify_done(struct tevent_req *subreq)
111 {
112 	struct smbd_smb2_request *req = tevent_req_callback_data(subreq,
113 					struct smbd_smb2_request);
114 	DATA_BLOB outbody;
115 	DATA_BLOB outdyn;
116 	uint16_t out_output_buffer_offset;
117 	DATA_BLOB out_output_buffer = data_blob_null;
118 	NTSTATUS status;
119 	NTSTATUS error; /* transport error */
120 
121 	status = smbd_smb2_notify_recv(subreq,
122 				       req,
123 				       &out_output_buffer);
124 	TALLOC_FREE(subreq);
125 	if (!NT_STATUS_IS_OK(status)) {
126 		error = smbd_smb2_request_error(req, status);
127 		if (!NT_STATUS_IS_OK(error)) {
128 			smbd_server_connection_terminate(req->xconn,
129 							 nt_errstr(error));
130 			return;
131 		}
132 		return;
133 	}
134 
135 	out_output_buffer_offset = SMB2_HDR_BODY + 0x08;
136 
137 	outbody = smbd_smb2_generate_outbody(req, 0x08);
138 	if (outbody.data == NULL) {
139 		error = smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
140 		if (!NT_STATUS_IS_OK(error)) {
141 			smbd_server_connection_terminate(req->xconn,
142 							 nt_errstr(error));
143 			return;
144 		}
145 		return;
146 	}
147 
148 	SSVAL(outbody.data, 0x00, 0x08 + 1);	/* struct size */
149 	SSVAL(outbody.data, 0x02,
150 	      out_output_buffer_offset);	/* output buffer offset */
151 	SIVAL(outbody.data, 0x04,
152 	      out_output_buffer.length);	/* output buffer length */
153 
154 	outdyn = out_output_buffer;
155 
156 	error = smbd_smb2_request_done(req, outbody, &outdyn);
157 	if (!NT_STATUS_IS_OK(error)) {
158 		smbd_server_connection_terminate(req->xconn,
159 						 nt_errstr(error));
160 		return;
161 	}
162 }
163 
164 static void smbd_smb2_notify_reply(struct smb_request *smbreq,
165 				   NTSTATUS error_code,
166 				   uint8_t *buf, size_t len);
167 static bool smbd_smb2_notify_cancel(struct tevent_req *req);
168 
smbd_smb2_notify_state_destructor(struct smbd_smb2_notify_state * state)169 static int smbd_smb2_notify_state_destructor(struct smbd_smb2_notify_state *state)
170 {
171 	if (!state->has_request) {
172 		return 0;
173 	}
174 
175 	state->skip_reply = true;
176 	smbd_notify_cancel_by_smbreq(state->smbreq);
177 	return 0;
178 }
179 
smbd_smb2_notify_smbreq_destructor(struct smb_request * smbreq)180 static int smbd_smb2_notify_smbreq_destructor(struct smb_request *smbreq)
181 {
182 	struct tevent_req *req = talloc_get_type_abort(smbreq->async_priv,
183 						       struct tevent_req);
184 	struct smbd_smb2_notify_state *state = tevent_req_data(req,
185 					       struct smbd_smb2_notify_state);
186 
187 	/*
188 	 * Our temporary parent from change_notify_add_request()
189 	 * goes away.
190 	 */
191 	state->has_request = false;
192 
193 	/*
194 	 * move it back to its original parent,
195 	 * which means we no longer need the destructor
196 	 * to protect it.
197 	 */
198 	talloc_steal(smbreq->smb2req, smbreq);
199 	talloc_set_destructor(smbreq, NULL);
200 
201 	/*
202 	 * We want to keep smbreq!
203 	 */
204 	return -1;
205 }
206 
smbd_smb2_notify_send(TALLOC_CTX * mem_ctx,struct tevent_context * ev,struct smbd_smb2_request * smb2req,struct files_struct * fsp,uint16_t in_flags,uint32_t in_output_buffer_length,uint64_t in_completion_filter)207 static struct tevent_req *smbd_smb2_notify_send(TALLOC_CTX *mem_ctx,
208 						struct tevent_context *ev,
209 						struct smbd_smb2_request *smb2req,
210 						struct files_struct *fsp,
211 						uint16_t in_flags,
212 						uint32_t in_output_buffer_length,
213 						uint64_t in_completion_filter)
214 {
215 	struct tevent_req *req;
216 	struct smbd_smb2_notify_state *state;
217 	struct smb_request *smbreq;
218 	connection_struct *conn = smb2req->tcon->compat;
219 	bool recursive = (in_flags & SMB2_WATCH_TREE) ? true : false;
220 	NTSTATUS status;
221 
222 	req = tevent_req_create(mem_ctx, &state,
223 				struct smbd_smb2_notify_state);
224 	if (req == NULL) {
225 		return NULL;
226 	}
227 	state->smb2req = smb2req;
228 	state->status = NT_STATUS_INTERNAL_ERROR;
229 	state->out_output_buffer = data_blob_null;
230 	talloc_set_destructor(state, smbd_smb2_notify_state_destructor);
231 
232 	DEBUG(10,("smbd_smb2_notify_send: %s - %s\n",
233 		  fsp_str_dbg(fsp), fsp_fnum_dbg(fsp)));
234 
235 	smbreq = smbd_smb2_fake_smb_request(smb2req);
236 	if (tevent_req_nomem(smbreq, req)) {
237 		return tevent_req_post(req, ev);
238 	}
239 
240 	state->smbreq = smbreq;
241 	smbreq->async_priv = (void *)req;
242 
243 	if (DEBUGLEVEL >= 3) {
244 		char *filter_string;
245 
246 		filter_string = notify_filter_string(NULL, in_completion_filter);
247 		if (tevent_req_nomem(filter_string, req)) {
248 			return tevent_req_post(req, ev);
249 		}
250 
251 		DEBUG(3,("smbd_smb2_notify_send: notify change "
252 			 "called on %s, filter = %s, recursive = %d\n",
253 			 fsp_str_dbg(fsp), filter_string, recursive));
254 
255 		TALLOC_FREE(filter_string);
256 	}
257 
258 	if ((!fsp->is_directory) || (conn != fsp->conn)) {
259 		tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
260 		return tevent_req_post(req, ev);
261 	}
262 
263 	if (fsp->notify == NULL) {
264 
265 		status = change_notify_create(fsp,
266 					      in_output_buffer_length,
267 					      in_completion_filter,
268 					      recursive);
269 		if (!NT_STATUS_IS_OK(status)) {
270 			DEBUG(10, ("change_notify_create returned %s\n",
271 				   nt_errstr(status)));
272 			tevent_req_nterror(req, status);
273 			return tevent_req_post(req, ev);
274 		}
275 	}
276 
277 	if (change_notify_fsp_has_changes(fsp)) {
278 
279 		/*
280 		 * We've got changes pending, respond immediately
281 		 */
282 
283 		/*
284 		 * TODO: write a torture test to check the filtering behaviour
285 		 * here.
286 		 */
287 
288 		change_notify_reply(smbreq,
289 				    NT_STATUS_OK,
290 				    in_output_buffer_length,
291 				    fsp->notify,
292 				    smbd_smb2_notify_reply);
293 
294 		/*
295 		 * change_notify_reply() above has independently
296 		 * called tevent_req_done().
297 		 */
298 		return tevent_req_post(req, ev);
299 	}
300 
301 	/*
302 	 * No changes pending, queue the request
303 	 */
304 
305 	status = change_notify_add_request(smbreq,
306 			in_output_buffer_length,
307 			in_completion_filter,
308 			recursive, fsp,
309 			smbd_smb2_notify_reply);
310 	if (!NT_STATUS_IS_OK(status)) {
311 		tevent_req_nterror(req, status);
312 		return tevent_req_post(req, ev);
313 	}
314 
315 	/*
316 	 * This is a HACK!
317 	 *
318 	 * change_notify_add_request() talloc_moves()
319 	 * smbreq away from us, so we need a destructor
320 	 * which moves it back at the end.
321 	 */
322 	state->has_request = true;
323 	talloc_set_destructor(smbreq, smbd_smb2_notify_smbreq_destructor);
324 
325 	/* allow this request to be canceled */
326 	tevent_req_set_cancel_fn(req, smbd_smb2_notify_cancel);
327 
328 	SMBPROFILE_IOBYTES_ASYNC_SET_IDLE(state->smb2req->profile);
329 	return req;
330 }
331 
smbd_smb2_notify_reply(struct smb_request * smbreq,NTSTATUS error_code,uint8_t * buf,size_t len)332 static void smbd_smb2_notify_reply(struct smb_request *smbreq,
333 				   NTSTATUS error_code,
334 				   uint8_t *buf, size_t len)
335 {
336 	struct tevent_req *req = talloc_get_type_abort(smbreq->async_priv,
337 						       struct tevent_req);
338 	struct smbd_smb2_notify_state *state = tevent_req_data(req,
339 					       struct smbd_smb2_notify_state);
340 
341 	if (state->skip_reply) {
342 		return;
343 	}
344 
345 	SMBPROFILE_IOBYTES_ASYNC_SET_BUSY(state->smb2req->profile);
346 
347 	state->status = error_code;
348 	if (!NT_STATUS_IS_OK(error_code)) {
349 		/* nothing */
350 	} else if (len == 0) {
351 		state->status = STATUS_NOTIFY_ENUM_DIR;
352 	} else {
353 		state->out_output_buffer = data_blob_talloc(state, buf, len);
354 		if (state->out_output_buffer.data == NULL) {
355 			state->status = NT_STATUS_NO_MEMORY;
356 		}
357 	}
358 
359 	tevent_req_defer_callback(req, state->smb2req->sconn->ev_ctx);
360 
361 	if (!NT_STATUS_IS_OK(state->status)) {
362 		tevent_req_nterror(req, state->status);
363 		return;
364 	}
365 
366 	tevent_req_done(req);
367 }
368 
smbd_smb2_notify_cancel(struct tevent_req * req)369 static bool smbd_smb2_notify_cancel(struct tevent_req *req)
370 {
371 	struct smbd_smb2_notify_state *state = tevent_req_data(req,
372 					       struct smbd_smb2_notify_state);
373 
374 	smbd_notify_cancel_by_smbreq(state->smbreq);
375 
376 	return true;
377 }
378 
smbd_smb2_notify_recv(struct tevent_req * req,TALLOC_CTX * mem_ctx,DATA_BLOB * out_output_buffer)379 static NTSTATUS smbd_smb2_notify_recv(struct tevent_req *req,
380 				      TALLOC_CTX *mem_ctx,
381 				      DATA_BLOB *out_output_buffer)
382 {
383 	NTSTATUS status;
384 	struct smbd_smb2_notify_state *state = tevent_req_data(req,
385 					       struct smbd_smb2_notify_state);
386 
387 	if (tevent_req_is_nterror(req, &status)) {
388 		tevent_req_received(req);
389 		return status;
390 	}
391 
392 	*out_output_buffer = state->out_output_buffer;
393 	talloc_steal(mem_ctx, out_output_buffer->data);
394 
395 	tevent_req_received(req);
396 	return NT_STATUS_OK;
397 }
398