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 1738 : NTSTATUS smbd_smb2_request_process_notify(struct smbd_smb2_request *req)
53 : {
54 1738 : struct smbXsrv_connection *xconn = req->xconn;
55 16 : NTSTATUS status;
56 16 : const uint8_t *inbody;
57 16 : uint16_t in_flags;
58 16 : uint32_t in_output_buffer_length;
59 16 : uint64_t in_file_id_persistent;
60 16 : uint64_t in_file_id_volatile;
61 16 : struct files_struct *in_fsp;
62 16 : uint64_t in_completion_filter;
63 16 : struct tevent_req *subreq;
64 :
65 1738 : status = smbd_smb2_request_verify_sizes(req, 0x20);
66 1738 : if (!NT_STATUS_IS_OK(status)) {
67 0 : return smbd_smb2_request_error(req, status);
68 : }
69 1738 : inbody = SMBD_SMB2_IN_BODY_PTR(req);
70 :
71 1738 : in_flags = SVAL(inbody, 0x02);
72 1738 : in_output_buffer_length = IVAL(inbody, 0x04);
73 1738 : in_file_id_persistent = BVAL(inbody, 0x08);
74 1738 : in_file_id_volatile = BVAL(inbody, 0x10);
75 1738 : in_completion_filter = IVAL(inbody, 0x18);
76 :
77 : /*
78 : * 0x00010000 is what Windows 7 uses,
79 : * Windows 2008 uses 0x00080000
80 : */
81 1738 : 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 1738 : status = smbd_smb2_request_verify_creditcharge(req,
86 : in_output_buffer_length);
87 :
88 1738 : if (!NT_STATUS_IS_OK(status)) {
89 0 : return smbd_smb2_request_error(req, status);
90 : }
91 :
92 1738 : in_fsp = file_fsp_smb2(req, in_file_id_persistent, in_file_id_volatile);
93 1738 : if (in_fsp == NULL) {
94 0 : return smbd_smb2_request_error(req, NT_STATUS_FILE_CLOSED);
95 : }
96 :
97 1738 : 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 1738 : if (subreq == NULL) {
103 0 : return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
104 : }
105 1738 : tevent_req_set_callback(subreq, smbd_smb2_request_notify_done, req);
106 :
107 1738 : return smbd_smb2_request_pending_queue(req, subreq, 500);
108 : }
109 :
110 1734 : static void smbd_smb2_request_notify_done(struct tevent_req *subreq)
111 : {
112 1734 : struct smbd_smb2_request *req = tevent_req_callback_data(subreq,
113 : struct smbd_smb2_request);
114 16 : DATA_BLOB outbody;
115 16 : DATA_BLOB outdyn;
116 16 : uint16_t out_output_buffer_offset;
117 1734 : DATA_BLOB out_output_buffer = data_blob_null;
118 16 : NTSTATUS status;
119 16 : NTSTATUS error; /* transport error */
120 :
121 1734 : status = smbd_smb2_notify_recv(subreq,
122 : req,
123 : &out_output_buffer);
124 1734 : TALLOC_FREE(subreq);
125 1734 : if (!NT_STATUS_IS_OK(status)) {
126 1612 : error = smbd_smb2_request_error(req, status);
127 1612 : if (!NT_STATUS_IS_OK(error)) {
128 0 : smbd_server_connection_terminate(req->xconn,
129 : nt_errstr(error));
130 1612 : return;
131 : }
132 1596 : return;
133 : }
134 :
135 122 : out_output_buffer_offset = SMB2_HDR_BODY + 0x08;
136 :
137 122 : outbody = smbd_smb2_generate_outbody(req, 0x08);
138 122 : 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 122 : SSVAL(outbody.data, 0x00, 0x08 + 1); /* struct size */
149 122 : SSVAL(outbody.data, 0x02,
150 : out_output_buffer_offset); /* output buffer offset */
151 122 : SIVAL(outbody.data, 0x04,
152 : out_output_buffer.length); /* output buffer length */
153 :
154 122 : outdyn = out_output_buffer;
155 :
156 122 : error = smbd_smb2_request_done(req, outbody, &outdyn);
157 122 : 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 1738 : static int smbd_smb2_notify_state_destructor(struct smbd_smb2_notify_state *state)
170 : {
171 1738 : if (!state->has_request) {
172 1722 : 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 1678 : static int smbd_smb2_notify_smbreq_destructor(struct smb_request *smbreq)
181 : {
182 1678 : struct tevent_req *req = talloc_get_type_abort(smbreq->async_priv,
183 : struct tevent_req);
184 1678 : 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 1678 : 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 1678 : talloc_steal(smbreq->smb2req, smbreq);
199 1678 : talloc_set_destructor(smbreq, NULL);
200 :
201 : /*
202 : * We want to keep smbreq!
203 : */
204 1678 : return -1;
205 : }
206 :
207 1738 : 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 16 : struct tevent_req *req;
216 16 : struct smbd_smb2_notify_state *state;
217 16 : struct smb_request *smbreq;
218 1738 : connection_struct *conn = smb2req->tcon->compat;
219 1738 : bool recursive = (in_flags & SMB2_WATCH_TREE) ? true : false;
220 16 : NTSTATUS status;
221 :
222 1738 : req = tevent_req_create(mem_ctx, &state,
223 : struct smbd_smb2_notify_state);
224 1738 : if (req == NULL) {
225 0 : return NULL;
226 : }
227 1738 : state->smb2req = smb2req;
228 1738 : state->status = NT_STATUS_INTERNAL_ERROR;
229 1738 : state->out_output_buffer = data_blob_null;
230 1738 : talloc_set_destructor(state, smbd_smb2_notify_state_destructor);
231 :
232 1738 : DEBUG(10,("smbd_smb2_notify_send: %s - %s\n",
233 : fsp_str_dbg(fsp), fsp_fnum_dbg(fsp)));
234 :
235 1738 : smbreq = smbd_smb2_fake_smb_request(smb2req, fsp);
236 1738 : if (tevent_req_nomem(smbreq, req)) {
237 0 : return tevent_req_post(req, ev);
238 : }
239 :
240 1738 : state->smbreq = smbreq;
241 1738 : smbreq->async_priv = (void *)req;
242 :
243 1738 : if (DEBUGLEVEL >= 3) {
244 0 : 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 1738 : if ((!fsp->fsp_flags.is_directory) || (conn != fsp->conn)) {
259 2 : tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
260 2 : return tevent_req_post(req, ev);
261 : }
262 :
263 1736 : if (fsp->notify == NULL) {
264 :
265 906 : status = change_notify_create(fsp,
266 : in_output_buffer_length,
267 : in_completion_filter,
268 : recursive);
269 906 : if (tevent_req_nterror(req, status)) {
270 4 : DEBUG(10, ("change_notify_create returned %s\n",
271 : nt_errstr(status)));
272 4 : return tevent_req_post(req, ev);
273 : }
274 : }
275 :
276 1732 : if (change_notify_fsp_has_changes(fsp)) {
277 :
278 : /*
279 : * We've got changes pending, respond immediately
280 : */
281 :
282 : /*
283 : * TODO: write a torture test to check the filtering behaviour
284 : * here.
285 : */
286 :
287 54 : change_notify_reply(smbreq,
288 54 : NT_STATUS_OK,
289 : in_output_buffer_length,
290 : fsp->notify,
291 : smbd_smb2_notify_reply);
292 :
293 : /*
294 : * change_notify_reply() above has independently
295 : * called tevent_req_done().
296 : */
297 54 : return tevent_req_post(req, ev);
298 : }
299 :
300 : /*
301 : * No changes pending, queue the request
302 : */
303 :
304 1678 : status = change_notify_add_request(smbreq,
305 : in_output_buffer_length,
306 : in_completion_filter,
307 : recursive, fsp,
308 : smbd_smb2_notify_reply);
309 1678 : if (tevent_req_nterror(req, status)) {
310 0 : return tevent_req_post(req, ev);
311 : }
312 :
313 : /*
314 : * This is a HACK!
315 : *
316 : * change_notify_add_request() talloc_moves()
317 : * smbreq away from us, so we need a destructor
318 : * which moves it back at the end.
319 : */
320 1678 : state->has_request = true;
321 1678 : talloc_set_destructor(smbreq, smbd_smb2_notify_smbreq_destructor);
322 :
323 : /* allow this request to be canceled */
324 1678 : tevent_req_set_cancel_fn(req, smbd_smb2_notify_cancel);
325 :
326 1678 : SMBPROFILE_IOBYTES_ASYNC_SET_IDLE(state->smb2req->profile);
327 1662 : return req;
328 : }
329 :
330 1732 : static void smbd_smb2_notify_reply(struct smb_request *smbreq,
331 : NTSTATUS error_code,
332 : uint8_t *buf, size_t len)
333 : {
334 1732 : struct tevent_req *req = talloc_get_type_abort(smbreq->async_priv,
335 : struct tevent_req);
336 1732 : struct smbd_smb2_notify_state *state = tevent_req_data(req,
337 : struct smbd_smb2_notify_state);
338 :
339 1732 : if (state->skip_reply) {
340 0 : return;
341 : }
342 :
343 1732 : SMBPROFILE_IOBYTES_ASYNC_SET_BUSY(state->smb2req->profile);
344 :
345 1732 : state->status = error_code;
346 1732 : if (!NT_STATUS_IS_OK(error_code)) {
347 : /* nothing */
348 126 : } else if (len == 0) {
349 4 : state->status = NT_STATUS_NOTIFY_ENUM_DIR;
350 : } else {
351 122 : state->out_output_buffer = data_blob_talloc(state, buf, len);
352 122 : if (state->out_output_buffer.data == NULL) {
353 0 : state->status = NT_STATUS_NO_MEMORY;
354 : }
355 : }
356 :
357 1732 : tevent_req_defer_callback(req, state->smb2req->sconn->ev_ctx);
358 :
359 1732 : if (tevent_req_nterror(req, state->status)) {
360 1594 : return;
361 : }
362 :
363 122 : tevent_req_done(req);
364 : }
365 :
366 1578 : static bool smbd_smb2_notify_cancel(struct tevent_req *req)
367 : {
368 1578 : struct smbd_smb2_notify_state *state = tevent_req_data(req,
369 : struct smbd_smb2_notify_state);
370 :
371 1578 : smbd_notify_cancel_by_smbreq(state->smbreq);
372 :
373 1578 : return true;
374 : }
375 :
376 1734 : static NTSTATUS smbd_smb2_notify_recv(struct tevent_req *req,
377 : TALLOC_CTX *mem_ctx,
378 : DATA_BLOB *out_output_buffer)
379 : {
380 16 : NTSTATUS status;
381 1734 : struct smbd_smb2_notify_state *state = tevent_req_data(req,
382 : struct smbd_smb2_notify_state);
383 :
384 1734 : if (tevent_req_is_nterror(req, &status)) {
385 1612 : tevent_req_received(req);
386 1612 : return status;
387 : }
388 :
389 122 : *out_output_buffer = state->out_output_buffer;
390 122 : talloc_steal(mem_ctx, out_output_buffer->data);
391 :
392 122 : tevent_req_received(req);
393 122 : return NT_STATUS_OK;
394 : }
|