Line data Source code
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);
52 60 : NTSTATUS smbd_smb2_request_process_notify(struct smbd_smb2_request *req)
53 : {
54 60 : 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 60 : status = smbd_smb2_request_verify_sizes(req, 0x20);
66 60 : if (!NT_STATUS_IS_OK(status)) {
67 0 : return smbd_smb2_request_error(req, status);
68 : }
69 60 : inbody = SMBD_SMB2_IN_BODY_PTR(req);
70 :
71 60 : in_flags = SVAL(inbody, 0x02);
72 60 : in_output_buffer_length = IVAL(inbody, 0x04);
73 60 : in_file_id_persistent = BVAL(inbody, 0x08);
74 60 : in_file_id_volatile = BVAL(inbody, 0x10);
75 60 : in_completion_filter = IVAL(inbody, 0x18);
76 :
77 : /*
78 : * 0x00010000 is what Windows 7 uses,
79 : * Windows 2008 uses 0x00080000
80 : */
81 60 : if (in_output_buffer_length > xconn->smb2.server.max_trans) {
82 0 : return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
83 : }
84 :
85 60 : status = smbd_smb2_request_verify_creditcharge(req,
86 : in_output_buffer_length);
87 :
88 60 : if (!NT_STATUS_IS_OK(status)) {
89 0 : return smbd_smb2_request_error(req, status);
90 : }
91 :
92 60 : in_fsp = file_fsp_smb2(req, in_file_id_persistent, in_file_id_volatile);
93 60 : if (in_fsp == NULL) {
94 0 : return smbd_smb2_request_error(req, NT_STATUS_FILE_CLOSED);
95 : }
96 :
97 60 : 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 60 : if (subreq == NULL) {
103 0 : return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
104 : }
105 60 : tevent_req_set_callback(subreq, smbd_smb2_request_notify_done, req);
106 :
107 60 : return smbd_smb2_request_pending_queue(req, subreq, 500);
108 : }
109 :
110 60 : static void smbd_smb2_request_notify_done(struct tevent_req *subreq)
111 : {
112 60 : 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 60 : DATA_BLOB out_output_buffer = data_blob_null;
118 : NTSTATUS status;
119 : NTSTATUS error; /* transport error */
120 :
121 60 : status = smbd_smb2_notify_recv(subreq,
122 : req,
123 : &out_output_buffer);
124 60 : TALLOC_FREE(subreq);
125 60 : if (!NT_STATUS_IS_OK(status)) {
126 30 : error = smbd_smb2_request_error(req, status);
127 30 : if (!NT_STATUS_IS_OK(error)) {
128 0 : smbd_server_connection_terminate(req->xconn,
129 : nt_errstr(error));
130 15 : return;
131 : }
132 30 : return;
133 : }
134 :
135 30 : out_output_buffer_offset = SMB2_HDR_BODY + 0x08;
136 :
137 30 : outbody = smbd_smb2_generate_outbody(req, 0x08);
138 30 : if (outbody.data == NULL) {
139 0 : error = smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
140 0 : if (!NT_STATUS_IS_OK(error)) {
141 0 : smbd_server_connection_terminate(req->xconn,
142 : nt_errstr(error));
143 0 : return;
144 : }
145 0 : return;
146 : }
147 :
148 30 : SSVAL(outbody.data, 0x00, 0x08 + 1); /* struct size */
149 30 : SSVAL(outbody.data, 0x02,
150 : out_output_buffer_offset); /* output buffer offset */
151 30 : SIVAL(outbody.data, 0x04,
152 : out_output_buffer.length); /* output buffer length */
153 :
154 30 : outdyn = out_output_buffer;
155 :
156 30 : error = smbd_smb2_request_done(req, outbody, &outdyn);
157 30 : if (!NT_STATUS_IS_OK(error)) {
158 0 : smbd_server_connection_terminate(req->xconn,
159 : nt_errstr(error));
160 0 : 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 :
169 60 : static int smbd_smb2_notify_state_destructor(struct smbd_smb2_notify_state *state)
170 : {
171 60 : if (!state->has_request) {
172 60 : return 0;
173 : }
174 :
175 0 : state->skip_reply = true;
176 0 : smbd_notify_cancel_by_smbreq(state->smbreq);
177 0 : return 0;
178 : }
179 :
180 60 : static int smbd_smb2_notify_smbreq_destructor(struct smb_request *smbreq)
181 : {
182 60 : struct tevent_req *req = talloc_get_type_abort(smbreq->async_priv,
183 : struct tevent_req);
184 60 : 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 60 : 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 60 : talloc_steal(smbreq->smb2req, smbreq);
199 60 : talloc_set_destructor(smbreq, NULL);
200 :
201 : /*
202 : * We want to keep smbreq!
203 : */
204 60 : return -1;
205 : }
206 :
207 60 : 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 60 : connection_struct *conn = smb2req->tcon->compat;
219 60 : bool recursive = (in_flags & SMB2_WATCH_TREE) ? true : false;
220 : NTSTATUS status;
221 :
222 60 : req = tevent_req_create(mem_ctx, &state,
223 : struct smbd_smb2_notify_state);
224 60 : if (req == NULL) {
225 0 : return NULL;
226 : }
227 60 : state->smb2req = smb2req;
228 60 : state->status = NT_STATUS_INTERNAL_ERROR;
229 60 : state->out_output_buffer = data_blob_null;
230 60 : talloc_set_destructor(state, smbd_smb2_notify_state_destructor);
231 :
232 60 : DEBUG(10,("smbd_smb2_notify_send: %s - %s\n",
233 : fsp_str_dbg(fsp), fsp_fnum_dbg(fsp)));
234 :
235 60 : smbreq = smbd_smb2_fake_smb_request(smb2req);
236 60 : if (tevent_req_nomem(smbreq, req)) {
237 0 : return tevent_req_post(req, ev);
238 : }
239 :
240 60 : state->smbreq = smbreq;
241 60 : smbreq->async_priv = (void *)req;
242 :
243 60 : if (DEBUGLEVEL >= 3) {
244 : char *filter_string;
245 :
246 0 : filter_string = notify_filter_string(NULL, in_completion_filter);
247 0 : if (tevent_req_nomem(filter_string, req)) {
248 0 : return tevent_req_post(req, ev);
249 : }
250 :
251 0 : 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 0 : TALLOC_FREE(filter_string);
256 : }
257 :
258 60 : if ((!fsp->fsp_flags.is_directory) || (conn != fsp->conn)) {
259 0 : tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
260 0 : return tevent_req_post(req, ev);
261 : }
262 :
263 60 : if (fsp->notify == NULL) {
264 :
265 30 : status = change_notify_create(fsp,
266 : in_output_buffer_length,
267 : in_completion_filter,
268 : recursive);
269 30 : if (!NT_STATUS_IS_OK(status)) {
270 0 : DEBUG(10, ("change_notify_create returned %s\n",
271 : nt_errstr(status)));
272 0 : tevent_req_nterror(req, status);
273 0 : return tevent_req_post(req, ev);
274 : }
275 : }
276 :
277 60 : 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 0 : change_notify_reply(smbreq,
289 0 : 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 0 : return tevent_req_post(req, ev);
299 : }
300 :
301 : /*
302 : * No changes pending, queue the request
303 : */
304 :
305 60 : status = change_notify_add_request(smbreq,
306 : in_output_buffer_length,
307 : in_completion_filter,
308 : recursive, fsp,
309 : smbd_smb2_notify_reply);
310 60 : if (!NT_STATUS_IS_OK(status)) {
311 0 : tevent_req_nterror(req, status);
312 0 : 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 60 : state->has_request = true;
323 60 : talloc_set_destructor(smbreq, smbd_smb2_notify_smbreq_destructor);
324 :
325 : /* allow this request to be canceled */
326 60 : tevent_req_set_cancel_fn(req, smbd_smb2_notify_cancel);
327 :
328 60 : SMBPROFILE_IOBYTES_ASYNC_SET_IDLE(state->smb2req->profile);
329 60 : return req;
330 : }
331 :
332 60 : static void smbd_smb2_notify_reply(struct smb_request *smbreq,
333 : NTSTATUS error_code,
334 : uint8_t *buf, size_t len)
335 : {
336 60 : struct tevent_req *req = talloc_get_type_abort(smbreq->async_priv,
337 : struct tevent_req);
338 60 : struct smbd_smb2_notify_state *state = tevent_req_data(req,
339 : struct smbd_smb2_notify_state);
340 :
341 60 : if (state->skip_reply) {
342 0 : return;
343 : }
344 :
345 60 : SMBPROFILE_IOBYTES_ASYNC_SET_BUSY(state->smb2req->profile);
346 :
347 60 : state->status = error_code;
348 60 : if (!NT_STATUS_IS_OK(error_code)) {
349 : /* nothing */
350 30 : } else if (len == 0) {
351 0 : state->status = NT_STATUS_NOTIFY_ENUM_DIR;
352 : } else {
353 30 : state->out_output_buffer = data_blob_talloc(state, buf, len);
354 30 : if (state->out_output_buffer.data == NULL) {
355 0 : state->status = NT_STATUS_NO_MEMORY;
356 : }
357 : }
358 :
359 60 : tevent_req_defer_callback(req, state->smb2req->sconn->ev_ctx);
360 :
361 60 : if (!NT_STATUS_IS_OK(state->status)) {
362 30 : tevent_req_nterror(req, state->status);
363 30 : return;
364 : }
365 :
366 30 : tevent_req_done(req);
367 : }
368 :
369 18 : static bool smbd_smb2_notify_cancel(struct tevent_req *req)
370 : {
371 18 : struct smbd_smb2_notify_state *state = tevent_req_data(req,
372 : struct smbd_smb2_notify_state);
373 :
374 18 : smbd_notify_cancel_by_smbreq(state->smbreq);
375 :
376 18 : return true;
377 : }
378 :
379 60 : 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 60 : struct smbd_smb2_notify_state *state = tevent_req_data(req,
385 : struct smbd_smb2_notify_state);
386 :
387 60 : if (tevent_req_is_nterror(req, &status)) {
388 30 : tevent_req_received(req);
389 30 : return status;
390 : }
391 :
392 30 : *out_output_buffer = state->out_output_buffer;
393 30 : talloc_steal(mem_ctx, out_output_buffer->data);
394 :
395 30 : tevent_req_received(req);
396 30 : return NT_STATUS_OK;
397 : }
|