auth: Reorder arguments to generate_session_info
[ira/wip.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         NTSTATUS status;
45         const uint8_t *inbody;
46         int i = req->current_idx;
47         uint8_t in_file_info_class;
48         uint8_t in_flags;
49         uint32_t in_file_index;
50         uint64_t in_file_id_persistent;
51         uint64_t in_file_id_volatile;
52         uint16_t in_file_name_offset;
53         uint16_t in_file_name_length;
54         DATA_BLOB in_file_name_buffer;
55         char *in_file_name_string;
56         size_t in_file_name_string_size;
57         uint32_t in_output_buffer_length;
58         struct tevent_req *subreq;
59         bool ok;
60
61         status = smbd_smb2_request_verify_sizes(req, 0x21);
62         if (!NT_STATUS_IS_OK(status)) {
63                 return smbd_smb2_request_error(req, status);
64         }
65         inbody = (const uint8_t *)req->in.vector[i+1].iov_base;
66
67         in_file_info_class              = CVAL(inbody, 0x02);
68         in_flags                        = CVAL(inbody, 0x03);
69         in_file_index                   = IVAL(inbody, 0x04);
70         in_file_id_persistent           = BVAL(inbody, 0x08);
71         in_file_id_volatile             = BVAL(inbody, 0x10);
72         in_file_name_offset             = SVAL(inbody, 0x18);
73         in_file_name_length             = SVAL(inbody, 0x1A);
74         in_output_buffer_length         = IVAL(inbody, 0x1C);
75
76         if (in_file_name_offset == 0 && in_file_name_length == 0) {
77                 /* This is ok */
78         } else if (in_file_name_offset !=
79                    (SMB2_HDR_BODY + req->in.vector[i+1].iov_len)) {
80                 return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
81         }
82
83         if (in_file_name_length > req->in.vector[i+2].iov_len) {
84                 return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
85         }
86
87         /* The output header is 8 bytes. */
88         if (in_output_buffer_length <= 8) {
89                 return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
90         }
91
92         DEBUG(10,("smbd_smb2_request_find_done: in_output_buffer_length = %u\n",
93                 (unsigned int)in_output_buffer_length ));
94
95         /* Take into account the output header. */
96         in_output_buffer_length -= 8;
97
98         in_file_name_buffer.data = (uint8_t *)req->in.vector[i+2].iov_base;
99         in_file_name_buffer.length = in_file_name_length;
100
101         ok = convert_string_talloc(req, CH_UTF16, CH_UNIX,
102                                    in_file_name_buffer.data,
103                                    in_file_name_buffer.length,
104                                    &in_file_name_string,
105                                    &in_file_name_string_size);
106         if (!ok) {
107                 return smbd_smb2_request_error(req, NT_STATUS_ILLEGAL_CHARACTER);
108         }
109
110         if (in_file_name_buffer.length == 0) {
111                 in_file_name_string_size = 0;
112         }
113
114         if (strlen(in_file_name_string) != in_file_name_string_size) {
115                 return smbd_smb2_request_error(req, NT_STATUS_OBJECT_NAME_INVALID);
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->ev_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, 500);
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 > smb2req->sconn->smb2.max_trans) {
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                 wcard_has_wild = ms_has_wild(in_file_name);
327
328                 status = dptr_create(conn,
329                                      fsp,
330                                      fsp->fsp_name->base_name,
331                                      false, /* old_handle */
332                                      false, /* expect_close */
333                                      0, /* spid */
334                                      in_file_name, /* wcard */
335                                      wcard_has_wild,
336                                      dirtype,
337                                      &fsp->dptr);
338                 if (!NT_STATUS_IS_OK(status)) {
339                         tevent_req_nterror(req, status);
340                         return tevent_req_post(req, ev);
341                 }
342
343                 empty_status = NT_STATUS_NO_SUCH_FILE;
344         } else {
345                 empty_status = STATUS_NO_MORE_FILES;
346         }
347
348         if (in_flags & SMB2_CONTINUE_FLAG_RESTART) {
349                 dptr_SeekDir(fsp->dptr, 0);
350         }
351
352         if (in_flags & SMB2_CONTINUE_FLAG_SINGLE) {
353                 max_count = 1;
354         } else {
355                 max_count = UINT16_MAX;
356         }
357
358 #define DIR_ENTRY_SAFETY_MARGIN 4096
359
360         state->out_output_buffer = data_blob_talloc(state, NULL,
361                         in_output_buffer_length + DIR_ENTRY_SAFETY_MARGIN);
362         if (tevent_req_nomem(state->out_output_buffer.data, req)) {
363                 return tevent_req_post(req, ev);
364         }
365
366         state->out_output_buffer.length = 0;
367         pdata = (char *)state->out_output_buffer.data;
368         base_data = pdata;
369         /*
370          * end_data must include the safety margin as it's what is
371          * used to determine if pushed strings have been truncated.
372          */
373         end_data = pdata + in_output_buffer_length + DIR_ENTRY_SAFETY_MARGIN - 1;
374         last_entry_off = 0;
375         off = 0;
376         num = 0;
377
378         DEBUG(8,("smbd_smb2_find_send: dirpath=<%s> dontdescend=<%s>, "
379                 "in_output_buffer_length = %u\n",
380                 fsp->fsp_name->base_name, lp_dontdescend(SNUM(conn)),
381                 (unsigned int)in_output_buffer_length ));
382         if (in_list(fsp->fsp_name->base_name,lp_dontdescend(SNUM(conn)),
383                         conn->case_sensitive)) {
384                 dont_descend = true;
385         }
386
387         ask_sharemode = lp_parm_bool(SNUM(conn),
388                                      "smbd", "search ask sharemode",
389                                      true);
390
391         while (true) {
392                 bool ok;
393                 bool got_exact_match = false;
394                 bool out_of_space = false;
395                 int space_remaining = in_output_buffer_length - off;
396
397                 SMB_ASSERT(space_remaining >= 0);
398
399                 ok = smbd_dirptr_lanman2_entry(state,
400                                                conn,
401                                                fsp->dptr,
402                                                smbreq->flags2,
403                                                in_file_name,
404                                                dirtype,
405                                                info_level,
406                                                false, /* requires_resume_key */
407                                                dont_descend,
408                                                ask_sharemode,
409                                                8, /* align to 8 bytes */
410                                                false, /* no padding */
411                                                &pdata,
412                                                base_data,
413                                                end_data,
414                                                space_remaining,
415                                                &out_of_space,
416                                                &got_exact_match,
417                                                &last_entry_off,
418                                                NULL);
419
420                 off = (int)PTR_DIFF(pdata, base_data);
421
422                 if (!ok) {
423                         if (num > 0) {
424                                 SIVAL(state->out_output_buffer.data, last_entry_off, 0);
425                                 tevent_req_done(req);
426                                 return tevent_req_post(req, ev);
427                         } else if (out_of_space) {
428                                 tevent_req_nterror(req, NT_STATUS_INFO_LENGTH_MISMATCH);
429                                 return tevent_req_post(req, ev);
430                         } else {
431                                 tevent_req_nterror(req, empty_status);
432                                 return tevent_req_post(req, ev);
433                         }
434                 }
435
436                 num++;
437                 state->out_output_buffer.length = off;
438
439                 if (num < max_count) {
440                         continue;
441                 }
442
443                 SIVAL(state->out_output_buffer.data, last_entry_off, 0);
444                 tevent_req_done(req);
445                 return tevent_req_post(req, ev);
446         }
447
448         tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
449         return tevent_req_post(req, ev);
450 }
451
452 static NTSTATUS smbd_smb2_find_recv(struct tevent_req *req,
453                                     TALLOC_CTX *mem_ctx,
454                                     DATA_BLOB *out_output_buffer)
455 {
456         NTSTATUS status;
457         struct smbd_smb2_find_state *state = tevent_req_data(req,
458                                              struct smbd_smb2_find_state);
459
460         if (tevent_req_is_nterror(req, &status)) {
461                 tevent_req_received(req);
462                 return status;
463         }
464
465         *out_output_buffer = state->out_output_buffer;
466         talloc_steal(mem_ctx, out_output_buffer->data);
467
468         tevent_req_received(req);
469         return NT_STATUS_OK;
470 }