s3-tevent: only include ../lib/util/tevent wrappers where needed.
[bbaumbach/samba-autobuild/.git] / source3 / smbd / smb2_find.c
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/smbd.h"
23 #include "smbd/globals.h"
24 #include "../libcli/smb/smb_common.h"
25 #include "trans2.h"
26 #include "../lib/util/tevent_ntstatus.h"
27
28 static struct tevent_req *smbd_smb2_find_send(TALLOC_CTX *mem_ctx,
29                                               struct tevent_context *ev,
30                                               struct smbd_smb2_request *smb2req,
31                                               uint8_t in_file_info_class,
32                                               uint8_t in_flags,
33                                               uint32_t in_file_index,
34                                               uint64_t in_file_id_volatile,
35                                               uint32_t in_output_buffer_length,
36                                               const char *in_file_name);
37 static NTSTATUS smbd_smb2_find_recv(struct tevent_req *req,
38                                     TALLOC_CTX *mem_ctx,
39                                     DATA_BLOB *out_output_buffer);
40
41 static void smbd_smb2_request_find_done(struct tevent_req *subreq);
42 NTSTATUS smbd_smb2_request_process_find(struct smbd_smb2_request *req)
43 {
44         const uint8_t *inhdr;
45         const uint8_t *inbody;
46         int i = req->current_idx;
47         size_t expected_body_size = 0x21;
48         size_t body_size;
49         uint8_t in_file_info_class;
50         uint8_t in_flags;
51         uint32_t in_file_index;
52         uint64_t in_file_id_persistent;
53         uint64_t in_file_id_volatile;
54         uint16_t in_file_name_offset;
55         uint16_t in_file_name_length;
56         DATA_BLOB in_file_name_buffer;
57         char *in_file_name_string;
58         size_t in_file_name_string_size;
59         uint32_t in_output_buffer_length;
60         struct tevent_req *subreq;
61         bool ok;
62
63         inhdr = (const uint8_t *)req->in.vector[i+0].iov_base;
64         if (req->in.vector[i+1].iov_len != (expected_body_size & 0xFFFFFFFE)) {
65                 return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
66         }
67
68         inbody = (const uint8_t *)req->in.vector[i+1].iov_base;
69
70         body_size = SVAL(inbody, 0x00);
71         if (body_size != expected_body_size) {
72                 return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
73         }
74
75         in_file_info_class              = CVAL(inbody, 0x02);
76         in_flags                        = CVAL(inbody, 0x03);
77         in_file_index                   = IVAL(inbody, 0x04);
78         in_file_id_persistent           = BVAL(inbody, 0x08);
79         in_file_id_volatile             = BVAL(inbody, 0x10);
80         in_file_name_offset             = SVAL(inbody, 0x18);
81         in_file_name_length             = SVAL(inbody, 0x1A);
82         in_output_buffer_length         = IVAL(inbody, 0x1C);
83
84         if (in_file_name_offset == 0 && in_file_name_length == 0) {
85                 /* This is ok */
86         } else if (in_file_name_offset !=
87                    (SMB2_HDR_BODY + (body_size & 0xFFFFFFFE))) {
88                 return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
89         }
90
91         if (in_file_name_length > req->in.vector[i+2].iov_len) {
92                 return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
93         }
94
95         /* The output header is 8 bytes. */
96         if (in_output_buffer_length <= 8) {
97                 return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
98         }
99
100         DEBUG(10,("smbd_smb2_request_find_done: in_output_buffer_length = %u\n",
101                 (unsigned int)in_output_buffer_length ));
102
103         /* Take into account the output header. */
104         in_output_buffer_length -= 8;
105
106         in_file_name_buffer.data = (uint8_t *)req->in.vector[i+2].iov_base;
107         in_file_name_buffer.length = in_file_name_length;
108
109         ok = convert_string_talloc(req, CH_UTF16, CH_UNIX,
110                                    in_file_name_buffer.data,
111                                    in_file_name_buffer.length,
112                                    &in_file_name_string,
113                                    &in_file_name_string_size);
114         if (!ok) {
115                 return smbd_smb2_request_error(req, NT_STATUS_ILLEGAL_CHARACTER);
116         }
117
118         if (req->compat_chain_fsp) {
119                 /* skip check */
120         } else if (in_file_id_persistent != in_file_id_volatile) {
121                 return smbd_smb2_request_error(req, NT_STATUS_FILE_CLOSED);
122         }
123
124         subreq = smbd_smb2_find_send(req,
125                                      req->sconn->smb2.event_ctx,
126                                      req,
127                                      in_file_info_class,
128                                      in_flags,
129                                      in_file_index,
130                                      in_file_id_volatile,
131                                      in_output_buffer_length,
132                                      in_file_name_string);
133         if (subreq == NULL) {
134                 return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
135         }
136         tevent_req_set_callback(subreq, smbd_smb2_request_find_done, req);
137
138         return smbd_smb2_request_pending_queue(req, subreq);
139 }
140
141 static void smbd_smb2_request_find_done(struct tevent_req *subreq)
142 {
143         struct smbd_smb2_request *req = tevent_req_callback_data(subreq,
144                                         struct smbd_smb2_request);
145         int i = req->current_idx;
146         uint8_t *outhdr;
147         DATA_BLOB outbody;
148         DATA_BLOB outdyn;
149         uint16_t out_output_buffer_offset;
150         DATA_BLOB out_output_buffer = data_blob_null;
151         NTSTATUS status;
152         NTSTATUS error; /* transport error */
153
154         status = smbd_smb2_find_recv(subreq,
155                                      req,
156                                      &out_output_buffer);
157         TALLOC_FREE(subreq);
158         if (!NT_STATUS_IS_OK(status)) {
159                 error = smbd_smb2_request_error(req, status);
160                 if (!NT_STATUS_IS_OK(error)) {
161                         smbd_server_connection_terminate(req->sconn,
162                                                          nt_errstr(error));
163                         return;
164                 }
165                 return;
166         }
167
168         out_output_buffer_offset = SMB2_HDR_BODY + 0x08;
169
170         outhdr = (uint8_t *)req->out.vector[i].iov_base;
171
172         outbody = data_blob_talloc(req->out.vector, NULL, 0x08);
173         if (outbody.data == NULL) {
174                 error = smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
175                 if (!NT_STATUS_IS_OK(error)) {
176                         smbd_server_connection_terminate(req->sconn,
177                                                          nt_errstr(error));
178                         return;
179                 }
180                 return;
181         }
182
183         SSVAL(outbody.data, 0x00, 0x08 + 1);    /* struct size */
184         SSVAL(outbody.data, 0x02,
185               out_output_buffer_offset);        /* output buffer offset */
186         SIVAL(outbody.data, 0x04,
187               out_output_buffer.length);        /* output buffer length */
188
189         DEBUG(10,("smbd_smb2_request_find_done: out_output_buffer.length = %u\n",
190                 (unsigned int)out_output_buffer.length ));
191
192         outdyn = out_output_buffer;
193
194         error = smbd_smb2_request_done(req, outbody, &outdyn);
195         if (!NT_STATUS_IS_OK(error)) {
196                 smbd_server_connection_terminate(req->sconn,
197                                                  nt_errstr(error));
198                 return;
199         }
200 }
201
202 struct smbd_smb2_find_state {
203         struct smbd_smb2_request *smb2req;
204         DATA_BLOB out_output_buffer;
205 };
206
207 static struct tevent_req *smbd_smb2_find_send(TALLOC_CTX *mem_ctx,
208                                               struct tevent_context *ev,
209                                               struct smbd_smb2_request *smb2req,
210                                               uint8_t in_file_info_class,
211                                               uint8_t in_flags,
212                                               uint32_t in_file_index,
213                                               uint64_t in_file_id_volatile,
214                                               uint32_t in_output_buffer_length,
215                                               const char *in_file_name)
216 {
217         struct tevent_req *req;
218         struct smbd_smb2_find_state *state;
219         struct smb_request *smbreq;
220         connection_struct *conn = smb2req->tcon->compat_conn;
221         files_struct *fsp;
222         NTSTATUS status;
223         NTSTATUS empty_status;
224         uint32_t info_level;
225         uint32_t max_count;
226         char *pdata;
227         char *base_data;
228         char *end_data;
229         int last_entry_off = 0;
230         int off = 0;
231         uint32_t num = 0;
232         uint32_t dirtype = FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_DIRECTORY;
233         bool dont_descend = false;
234         bool ask_sharemode = true;
235
236         req = tevent_req_create(mem_ctx, &state,
237                                 struct smbd_smb2_find_state);
238         if (req == NULL) {
239                 return NULL;
240         }
241         state->smb2req = smb2req;
242         state->out_output_buffer = data_blob_null;
243
244         DEBUG(10,("smbd_smb2_find_send: file_id[0x%016llX]\n",
245                   (unsigned long long)in_file_id_volatile));
246
247         smbreq = smbd_smb2_fake_smb_request(smb2req);
248         if (tevent_req_nomem(smbreq, req)) {
249                 return tevent_req_post(req, ev);
250         }
251
252         fsp = file_fsp(smbreq, (uint16_t)in_file_id_volatile);
253         if (fsp == NULL) {
254                 tevent_req_nterror(req, NT_STATUS_FILE_CLOSED);
255                 return tevent_req_post(req, ev);
256         }
257         if (conn != fsp->conn) {
258                 tevent_req_nterror(req, NT_STATUS_FILE_CLOSED);
259                 return tevent_req_post(req, ev);
260         }
261         if (smb2req->session->vuid != fsp->vuid) {
262                 tevent_req_nterror(req, NT_STATUS_FILE_CLOSED);
263                 return tevent_req_post(req, ev);
264         }
265
266         if (!fsp->is_directory) {
267                 tevent_req_nterror(req, NT_STATUS_NOT_SUPPORTED);
268                 return tevent_req_post(req, ev);
269         }
270
271         if (strcmp(in_file_name, "") == 0) {
272                 tevent_req_nterror(req, NT_STATUS_OBJECT_NAME_INVALID);
273                 return tevent_req_post(req, ev);
274         }
275         if (strcmp(in_file_name, "\\") == 0) {
276                 tevent_req_nterror(req, NT_STATUS_OBJECT_NAME_INVALID);
277                 return tevent_req_post(req, ev);
278         }
279         if (strcmp(in_file_name, "/") == 0) {
280                 tevent_req_nterror(req, NT_STATUS_OBJECT_NAME_INVALID);
281                 return tevent_req_post(req, ev);
282         }
283
284         if (in_output_buffer_length > 0x10000) {
285                 tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
286                 return tevent_req_post(req, ev);
287         }
288
289         switch (in_file_info_class) {
290         case SMB2_FIND_DIRECTORY_INFO:
291                 info_level = SMB_FIND_FILE_DIRECTORY_INFO;
292                 break;
293
294         case SMB2_FIND_FULL_DIRECTORY_INFO:
295                 info_level = SMB_FIND_FILE_FULL_DIRECTORY_INFO;
296                 break;
297
298         case SMB2_FIND_BOTH_DIRECTORY_INFO:
299                 info_level = SMB_FIND_FILE_BOTH_DIRECTORY_INFO;
300                 break;
301
302         case SMB2_FIND_NAME_INFO:
303                 info_level = SMB_FIND_FILE_NAMES_INFO;
304                 break;
305
306         case SMB2_FIND_ID_BOTH_DIRECTORY_INFO:
307                 info_level = SMB_FIND_ID_BOTH_DIRECTORY_INFO;
308                 break;
309
310         case SMB2_FIND_ID_FULL_DIRECTORY_INFO:
311                 info_level = SMB_FIND_ID_FULL_DIRECTORY_INFO;
312                 break;
313
314         default:
315                 tevent_req_nterror(req, NT_STATUS_INVALID_INFO_CLASS);
316                 return tevent_req_post(req, ev);
317         }
318
319         if (in_flags & SMB2_CONTINUE_FLAG_REOPEN) {
320                 dptr_CloseDir(fsp);
321         }
322
323         if (fsp->dptr == NULL) {
324                 bool wcard_has_wild;
325
326                 if (!(fsp->access_mask & SEC_DIR_LIST)) {
327                         tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
328                         return tevent_req_post(req, ev);
329                 }
330
331                 wcard_has_wild = ms_has_wild(in_file_name);
332
333                 status = dptr_create(conn,
334                                      fsp,
335                                      fsp->fsp_name->base_name,
336                                      false, /* old_handle */
337                                      false, /* expect_close */
338                                      0, /* spid */
339                                      in_file_name, /* wcard */
340                                      wcard_has_wild,
341                                      dirtype,
342                                      &fsp->dptr);
343                 if (!NT_STATUS_IS_OK(status)) {
344                         tevent_req_nterror(req, status);
345                         return tevent_req_post(req, ev);
346                 }
347
348                 empty_status = NT_STATUS_NO_SUCH_FILE;
349         } else {
350                 empty_status = STATUS_NO_MORE_FILES;
351         }
352
353         if (in_flags & SMB2_CONTINUE_FLAG_RESTART) {
354                 dptr_SeekDir(fsp->dptr, 0);
355         }
356
357         if (in_flags & SMB2_CONTINUE_FLAG_SINGLE) {
358                 max_count = 1;
359         } else {
360                 max_count = UINT16_MAX;
361         }
362
363 #define DIR_ENTRY_SAFETY_MARGIN 4096
364
365         state->out_output_buffer = data_blob_talloc(state, NULL,
366                         in_output_buffer_length + DIR_ENTRY_SAFETY_MARGIN);
367         if (tevent_req_nomem(state->out_output_buffer.data, req)) {
368                 return tevent_req_post(req, ev);
369         }
370
371         state->out_output_buffer.length = 0;
372         pdata = (char *)state->out_output_buffer.data;
373         base_data = pdata;
374         /*
375          * end_data must include the safety margin as it's what is
376          * used to determine if pushed strings have been truncated.
377          */
378         end_data = pdata + in_output_buffer_length + DIR_ENTRY_SAFETY_MARGIN - 1;
379         last_entry_off = 0;
380         off = 0;
381         num = 0;
382
383         DEBUG(8,("smbd_smb2_find_send: dirpath=<%s> dontdescend=<%s>, "
384                 "in_output_buffer_length = %u\n",
385                 fsp->fsp_name->base_name, lp_dontdescend(SNUM(conn)),
386                 (unsigned int)in_output_buffer_length ));
387         if (in_list(fsp->fsp_name->base_name,lp_dontdescend(SNUM(conn)),
388                         conn->case_sensitive)) {
389                 dont_descend = true;
390         }
391
392         ask_sharemode = lp_parm_bool(SNUM(conn),
393                                      "smbd", "search ask sharemode",
394                                      true);
395
396         while (true) {
397                 bool ok;
398                 bool got_exact_match = false;
399                 bool out_of_space = false;
400                 int space_remaining = in_output_buffer_length - off;
401
402                 SMB_ASSERT(space_remaining >= 0);
403
404                 ok = smbd_dirptr_lanman2_entry(state,
405                                                conn,
406                                                fsp->dptr,
407                                                smbreq->flags2,
408                                                in_file_name,
409                                                dirtype,
410                                                info_level,
411                                                false, /* requires_resume_key */
412                                                dont_descend,
413                                                ask_sharemode,
414                                                8, /* align to 8 bytes */
415                                                false, /* no padding */
416                                                &pdata,
417                                                base_data,
418                                                end_data,
419                                                space_remaining,
420                                                &out_of_space,
421                                                &got_exact_match,
422                                                &last_entry_off,
423                                                NULL);
424
425                 off = (int)PTR_DIFF(pdata, base_data);
426
427                 if (!ok) {
428                         if (num > 0) {
429                                 SIVAL(state->out_output_buffer.data, last_entry_off, 0);
430                                 tevent_req_done(req);
431                                 return tevent_req_post(req, ev);
432                         } else if (out_of_space) {
433                                 tevent_req_nterror(req, NT_STATUS_INFO_LENGTH_MISMATCH);
434                                 return tevent_req_post(req, ev);
435                         } else {
436                                 tevent_req_nterror(req, empty_status);
437                                 return tevent_req_post(req, ev);
438                         }
439                 }
440
441                 num++;
442                 state->out_output_buffer.length = off;
443
444                 if (num < max_count) {
445                         continue;
446                 }
447
448                 SIVAL(state->out_output_buffer.data, last_entry_off, 0);
449                 tevent_req_done(req);
450                 return tevent_req_post(req, ev);
451         }
452
453         tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
454         return tevent_req_post(req, ev);
455 }
456
457 static NTSTATUS smbd_smb2_find_recv(struct tevent_req *req,
458                                     TALLOC_CTX *mem_ctx,
459                                     DATA_BLOB *out_output_buffer)
460 {
461         NTSTATUS status;
462         struct smbd_smb2_find_state *state = tevent_req_data(req,
463                                              struct smbd_smb2_find_state);
464
465         if (tevent_req_is_nterror(req, &status)) {
466                 tevent_req_received(req);
467                 return status;
468         }
469
470         *out_output_buffer = state->out_output_buffer;
471         talloc_steal(mem_ctx, out_output_buffer->data);
472
473         tevent_req_received(req);
474         return NT_STATUS_OK;
475 }