1 /*
2    Unix SMB/CIFS implementation.
3    client directory search routines
4    Copyright (C) James Myers 2003 <myersjj@samba.org>
5    Copyright (C) James Peach 2007
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 "libcli/raw/libcliraw.h"
23 #include "libcli/raw/raw_proto.h"
24 
25 /****************************************************************************
26  Old style search backend - process output.
27 ****************************************************************************/
smb_raw_search_backend(struct smbcli_request * req,TALLOC_CTX * mem_ctx,uint16_t count,void * private_data,smbcli_search_callback callback)28 static void smb_raw_search_backend(struct smbcli_request *req,
29 				   TALLOC_CTX *mem_ctx,
30 				   uint16_t count,
31 				   void *private_data,
32 				   smbcli_search_callback callback)
33 
34 {
35 	union smb_search_data search_data;
36 	int i;
37 	uint8_t *p;
38 
39 	if (req->in.data_size < 3 + count*43) {
40 		req->status = NT_STATUS_INVALID_PARAMETER;
41 		return;
42 	}
43 
44 	p = req->in.data + 3;
45 
46 	for (i=0; i < count; i++) {
47 		char *name;
48 
49 		search_data.search.id.reserved      = CVAL(p, 0);
50 		memcpy(search_data.search.id.name,    p+1, 11);
51 		search_data.search.id.handle        = CVAL(p, 12);
52 		search_data.search.id.server_cookie = IVAL(p, 13);
53 		search_data.search.id.client_cookie = IVAL(p, 17);
54 		search_data.search.attrib           = CVAL(p, 21);
55 		search_data.search.write_time       = raw_pull_dos_date(req->transport,
56 									p + 22);
57 		search_data.search.size             = IVAL(p, 26);
58 		smbcli_req_pull_ascii(&req->in.bufinfo, mem_ctx, &name, p+30, 13, STR_ASCII);
59 		search_data.search.name = name;
60 		if (!callback(private_data, &search_data)) {
61 			break;
62 		}
63 		p += 43;
64 	}
65 }
66 
67 /****************************************************************************
68  Old style search first.
69 ****************************************************************************/
smb_raw_search_first_old(struct smbcli_tree * tree,TALLOC_CTX * mem_ctx,union smb_search_first * io,void * private_data,smbcli_search_callback callback)70 static NTSTATUS smb_raw_search_first_old(struct smbcli_tree *tree,
71 					 TALLOC_CTX *mem_ctx,
72 					 union smb_search_first *io, void *private_data,
73 					 smbcli_search_callback callback)
74 
75 {
76 	struct smbcli_request *req;
77 	uint8_t op = SMBsearch;
78 
79 	if (io->generic.level == RAW_SEARCH_FFIRST) {
80 		op = SMBffirst;
81 	} else if (io->generic.level == RAW_SEARCH_FUNIQUE) {
82 		op = SMBfunique;
83 	}
84 
85 	req = smbcli_request_setup(tree, op, 2, 0);
86 	if (!req) {
87 		return NT_STATUS_NO_MEMORY;
88 	}
89 
90 	SSVAL(req->out.vwv, VWV(0), io->search_first.in.max_count);
91 	SSVAL(req->out.vwv, VWV(1), io->search_first.in.search_attrib);
92 	smbcli_req_append_ascii4(req, io->search_first.in.pattern, STR_TERMINATE);
93 	smbcli_req_append_var_block(req, NULL, 0);
94 
95 	if (!smbcli_request_send(req) ||
96 	    !smbcli_request_receive(req)) {
97 		return smbcli_request_destroy(req);
98 	}
99 
100 	if (NT_STATUS_IS_OK(req->status)) {
101 		io->search_first.out.count = SVAL(req->in.vwv, VWV(0));
102 		smb_raw_search_backend(req, mem_ctx, io->search_first.out.count, private_data, callback);
103 	}
104 
105 	return smbcli_request_destroy(req);
106 }
107 
108 /****************************************************************************
109  Old style search next.
110 ****************************************************************************/
smb_raw_search_next_old(struct smbcli_tree * tree,TALLOC_CTX * mem_ctx,union smb_search_next * io,void * private_data,smbcli_search_callback callback)111 static NTSTATUS smb_raw_search_next_old(struct smbcli_tree *tree,
112 					TALLOC_CTX *mem_ctx,
113 					union smb_search_next *io, void *private_data,
114 					smbcli_search_callback callback)
115 
116 {
117 	struct smbcli_request *req;
118 	uint8_t var_block[21];
119 	uint8_t op = SMBsearch;
120 
121 	if (io->generic.level == RAW_SEARCH_FFIRST) {
122 		op = SMBffirst;
123 	}
124 
125 	req = smbcli_request_setup(tree, op, 2, 0);
126 	if (!req) {
127 		return NT_STATUS_NO_MEMORY;
128 	}
129 
130 	SSVAL(req->out.vwv, VWV(0), io->search_next.in.max_count);
131 	SSVAL(req->out.vwv, VWV(1), io->search_next.in.search_attrib);
132 	smbcli_req_append_ascii4(req, "", STR_TERMINATE);
133 
134 	SCVAL(var_block,  0, io->search_next.in.id.reserved);
135 	memcpy(&var_block[1], io->search_next.in.id.name, 11);
136 	SCVAL(var_block, 12, io->search_next.in.id.handle);
137 	SIVAL(var_block, 13, io->search_next.in.id.server_cookie);
138 	SIVAL(var_block, 17, io->search_next.in.id.client_cookie);
139 
140 	smbcli_req_append_var_block(req, var_block, 21);
141 
142 	if (!smbcli_request_send(req) ||
143 	    !smbcli_request_receive(req)) {
144 		return smbcli_request_destroy(req);
145 	}
146 
147 	if (NT_STATUS_IS_OK(req->status)) {
148 		io->search_next.out.count = SVAL(req->in.vwv, VWV(0));
149 		smb_raw_search_backend(req, mem_ctx, io->search_next.out.count, private_data, callback);
150 	}
151 
152 	return smbcli_request_destroy(req);
153 }
154 
155 
156 /****************************************************************************
157  Old style search next.
158 ****************************************************************************/
smb_raw_search_close_old(struct smbcli_tree * tree,union smb_search_close * io)159 static NTSTATUS smb_raw_search_close_old(struct smbcli_tree *tree,
160 					 union smb_search_close *io)
161 {
162 	struct smbcli_request *req;
163 	uint8_t var_block[21];
164 
165 	req = smbcli_request_setup(tree, SMBfclose, 2, 0);
166 	if (!req) {
167 		return NT_STATUS_NO_MEMORY;
168 	}
169 
170 	SSVAL(req->out.vwv, VWV(0), io->fclose.in.max_count);
171 	SSVAL(req->out.vwv, VWV(1), io->fclose.in.search_attrib);
172 	smbcli_req_append_ascii4(req, "", STR_TERMINATE);
173 
174 	SCVAL(var_block,  0, io->fclose.in.id.reserved);
175 	memcpy(&var_block[1], io->fclose.in.id.name, 11);
176 	SCVAL(var_block, 12, io->fclose.in.id.handle);
177 	SIVAL(var_block, 13, io->fclose.in.id.server_cookie);
178 	SIVAL(var_block, 17, io->fclose.in.id.client_cookie);
179 
180 	smbcli_req_append_var_block(req, var_block, 21);
181 
182 	if (!smbcli_request_send(req) ||
183 	    !smbcli_request_receive(req)) {
184 		return smbcli_request_destroy(req);
185 	}
186 
187 	return smbcli_request_destroy(req);
188 }
189 
190 
191 
192 /****************************************************************************
193  Very raw search first - returns param/data blobs.
194 ****************************************************************************/
smb_raw_search_first_blob(struct smbcli_tree * tree,TALLOC_CTX * mem_ctx,union smb_search_first * io,DATA_BLOB * out_param_blob,DATA_BLOB * out_data_blob)195 static NTSTATUS smb_raw_search_first_blob(struct smbcli_tree *tree,
196 					  TALLOC_CTX *mem_ctx,	/* used to allocate output blobs */
197 					  union smb_search_first *io,
198 					  DATA_BLOB *out_param_blob,
199 					  DATA_BLOB *out_data_blob)
200 {
201 	struct smb_trans2 tp;
202 	uint16_t setup = TRANSACT2_FINDFIRST;
203 	NTSTATUS status;
204 
205 	tp.in.max_setup = 0;
206 	tp.in.flags = 0;
207 	tp.in.timeout = 0;
208 	tp.in.setup_count = 1;
209 	tp.in.data = data_blob(NULL, 0);
210 	tp.in.max_param = 10;
211 	tp.in.max_data = 0xFFFF;
212 	tp.in.setup = &setup;
213 
214 	if (io->t2ffirst.level != RAW_SEARCH_TRANS2) {
215 		return NT_STATUS_INVALID_LEVEL;
216 	}
217 
218 	if (io->t2ffirst.data_level >= RAW_SEARCH_DATA_GENERIC) {
219 		return NT_STATUS_INVALID_LEVEL;
220 	}
221 
222 	if (io->t2ffirst.data_level == RAW_SEARCH_DATA_EA_LIST) {
223 		if (!ea_push_name_list(mem_ctx,
224 				       &tp.in.data,
225 				       io->t2ffirst.in.num_names,
226 				       io->t2ffirst.in.ea_names)) {
227 			return NT_STATUS_NO_MEMORY;
228 		}
229 	}
230 
231 	tp.in.params = data_blob_talloc(mem_ctx, NULL, 12);
232 	if (!tp.in.params.data) {
233 		return NT_STATUS_NO_MEMORY;
234 	}
235 
236 	SSVAL(tp.in.params.data, 0, io->t2ffirst.in.search_attrib);
237 	SSVAL(tp.in.params.data, 2, io->t2ffirst.in.max_count);
238 	SSVAL(tp.in.params.data, 4, io->t2ffirst.in.flags);
239 	SSVAL(tp.in.params.data, 6, io->t2ffirst.data_level);
240 	SIVAL(tp.in.params.data, 8, io->t2ffirst.in.storage_type);
241 
242 	smbcli_blob_append_string(tree->session, mem_ctx, &tp.in.params,
243 				  io->t2ffirst.in.pattern, STR_TERMINATE);
244 
245 	status = smb_raw_trans2(tree, mem_ctx, &tp);
246 	if (!NT_STATUS_IS_OK(status)) {
247 		return status;
248 	}
249 
250 	out_param_blob->length = tp.out.params.length;
251 	out_param_blob->data = tp.out.params.data;
252 	out_data_blob->length = tp.out.data.length;
253 	out_data_blob->data = tp.out.data.data;
254 
255 	return NT_STATUS_OK;
256 }
257 
258 
259 /****************************************************************************
260  Very raw search first - returns param/data blobs.
261  Used in CIFS-on-CIFS NTVFS.
262 ****************************************************************************/
smb_raw_search_next_blob(struct smbcli_tree * tree,TALLOC_CTX * mem_ctx,union smb_search_next * io,DATA_BLOB * out_param_blob,DATA_BLOB * out_data_blob)263 static NTSTATUS smb_raw_search_next_blob(struct smbcli_tree *tree,
264 					 TALLOC_CTX *mem_ctx,
265 					 union smb_search_next *io,
266 					 DATA_BLOB *out_param_blob,
267 					 DATA_BLOB *out_data_blob)
268 {
269 	struct smb_trans2 tp;
270 	uint16_t setup = TRANSACT2_FINDNEXT;
271 	NTSTATUS status;
272 
273 	tp.in.max_setup = 0;
274 	tp.in.flags = 0;
275 	tp.in.timeout = 0;
276 	tp.in.setup_count = 1;
277 	tp.in.data = data_blob(NULL, 0);
278 	tp.in.max_param = 10;
279 	tp.in.max_data = 0xFFFF;
280 	tp.in.setup = &setup;
281 
282 	if (io->t2fnext.level != RAW_SEARCH_TRANS2) {
283 		return NT_STATUS_INVALID_LEVEL;
284 	}
285 
286 	if (io->t2fnext.data_level >= RAW_SEARCH_DATA_GENERIC) {
287 		return NT_STATUS_INVALID_LEVEL;
288 	}
289 
290 	if (io->t2fnext.data_level == RAW_SEARCH_DATA_EA_LIST) {
291 		if (!ea_push_name_list(mem_ctx,
292 				       &tp.in.data,
293 				       io->t2fnext.in.num_names,
294 				       io->t2fnext.in.ea_names)) {
295 			return NT_STATUS_NO_MEMORY;
296 		}
297 	}
298 
299 	tp.in.params = data_blob_talloc(mem_ctx, NULL, 12);
300 	if (!tp.in.params.data) {
301 		return NT_STATUS_NO_MEMORY;
302 	}
303 
304 	SSVAL(tp.in.params.data, 0, io->t2fnext.in.handle);
305 	SSVAL(tp.in.params.data, 2, io->t2fnext.in.max_count);
306 	SSVAL(tp.in.params.data, 4, io->t2fnext.data_level);
307 	SIVAL(tp.in.params.data, 6, io->t2fnext.in.resume_key);
308 	SSVAL(tp.in.params.data, 10, io->t2fnext.in.flags);
309 
310 	smbcli_blob_append_string(tree->session, mem_ctx, &tp.in.params,
311 			       io->t2fnext.in.last_name,
312 			       STR_TERMINATE);
313 
314 	status = smb_raw_trans2(tree, mem_ctx, &tp);
315 	if (!NT_STATUS_IS_OK(status)) {
316 		return status;
317 	}
318 
319 	out_param_blob->length = tp.out.params.length;
320 	out_param_blob->data = tp.out.params.data;
321 	out_data_blob->length = tp.out.data.length;
322 	out_data_blob->data = tp.out.data.data;
323 
324 	return NT_STATUS_OK;
325 }
326 
327 
328 /*
329   parse the wire search formats that are in common between SMB and
330   SMB2
331 */
smb_raw_search_common(TALLOC_CTX * mem_ctx,enum smb_search_data_level level,const DATA_BLOB * blob,union smb_search_data * data,unsigned int * next_ofs,unsigned int str_flags)332 NTSTATUS smb_raw_search_common(TALLOC_CTX *mem_ctx,
333 			       enum smb_search_data_level level,
334 			       const DATA_BLOB *blob,
335 			       union smb_search_data *data,
336 			       unsigned int *next_ofs,
337 			       unsigned int str_flags)
338 {
339 	unsigned int len, blen;
340 
341 	if (blob->length < 4) {
342 		return NT_STATUS_INFO_LENGTH_MISMATCH;
343 	}
344 
345 	*next_ofs = IVAL(blob->data, 0);
346 	if (*next_ofs != 0) {
347 		blen = *next_ofs;
348 	} else {
349 		blen = blob->length;
350 	}
351 
352 	switch (level) {
353 	case RAW_SEARCH_DATA_DIRECTORY_INFO:
354 		if (blen < 65) return NT_STATUS_INFO_LENGTH_MISMATCH;
355 		data->directory_info.file_index  = IVAL(blob->data,             4);
356 		data->directory_info.create_time = smbcli_pull_nttime(blob->data,  8);
357 		data->directory_info.access_time = smbcli_pull_nttime(blob->data, 16);
358 		data->directory_info.write_time  = smbcli_pull_nttime(blob->data, 24);
359 		data->directory_info.change_time = smbcli_pull_nttime(blob->data, 32);
360 		data->directory_info.size        = BVAL(blob->data,            40);
361 		data->directory_info.alloc_size  = BVAL(blob->data,            48);
362 		data->directory_info.attrib      = IVAL(blob->data,            56);
363 		len = smbcli_blob_pull_string(NULL, mem_ctx, blob,
364 					      &data->directory_info.name,
365 					      60, 64, str_flags);
366 		if (*next_ofs != 0 && *next_ofs < 64+len) {
367 			return NT_STATUS_INFO_LENGTH_MISMATCH;
368 		}
369 		return NT_STATUS_OK;
370 
371 	case RAW_SEARCH_DATA_FULL_DIRECTORY_INFO:
372 		if (blen < 69) return NT_STATUS_INFO_LENGTH_MISMATCH;
373 		data->full_directory_info.file_index  = IVAL(blob->data,                4);
374 		data->full_directory_info.create_time = smbcli_pull_nttime(blob->data,  8);
375 		data->full_directory_info.access_time = smbcli_pull_nttime(blob->data, 16);
376 		data->full_directory_info.write_time  = smbcli_pull_nttime(blob->data, 24);
377 		data->full_directory_info.change_time = smbcli_pull_nttime(blob->data, 32);
378 		data->full_directory_info.size        = BVAL(blob->data,               40);
379 		data->full_directory_info.alloc_size  = BVAL(blob->data,               48);
380 		data->full_directory_info.attrib      = IVAL(blob->data,               56);
381 		data->full_directory_info.ea_size     = IVAL(blob->data,               64);
382 		len = smbcli_blob_pull_string(NULL, mem_ctx, blob,
383 					      &data->full_directory_info.name,
384 					      60, 68, str_flags);
385 		if (*next_ofs != 0 && *next_ofs < 68+len) {
386 			return NT_STATUS_INFO_LENGTH_MISMATCH;
387 		}
388 		return NT_STATUS_OK;
389 
390 	case RAW_SEARCH_DATA_NAME_INFO:
391 		if (blen < 13) return NT_STATUS_INFO_LENGTH_MISMATCH;
392 		data->name_info.file_index  = IVAL(blob->data, 4);
393 		len = smbcli_blob_pull_string(NULL, mem_ctx, blob,
394 					      &data->name_info.name,
395 					      8, 12, str_flags);
396 		if (*next_ofs != 0 && *next_ofs < 12+len) {
397 			return NT_STATUS_INFO_LENGTH_MISMATCH;
398 		}
399 		return NT_STATUS_OK;
400 
401 
402 	case RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO:
403 		if (blen < 95) return NT_STATUS_INFO_LENGTH_MISMATCH;
404 		data->both_directory_info.file_index  = IVAL(blob->data,                4);
405 		data->both_directory_info.create_time = smbcli_pull_nttime(blob->data,  8);
406 		data->both_directory_info.access_time = smbcli_pull_nttime(blob->data, 16);
407 		data->both_directory_info.write_time  = smbcli_pull_nttime(blob->data, 24);
408 		data->both_directory_info.change_time = smbcli_pull_nttime(blob->data, 32);
409 		data->both_directory_info.size        = BVAL(blob->data,               40);
410 		data->both_directory_info.alloc_size  = BVAL(blob->data,               48);
411 		data->both_directory_info.attrib      = IVAL(blob->data,               56);
412 		data->both_directory_info.ea_size     = IVAL(blob->data,               64);
413 		smbcli_blob_pull_string(NULL, mem_ctx, blob,
414 					&data->both_directory_info.short_name,
415 					68, 70, STR_LEN8BIT | STR_UNICODE);
416 		len = smbcli_blob_pull_string(NULL, mem_ctx, blob,
417 					      &data->both_directory_info.name,
418 					      60, 94, str_flags);
419 		if (*next_ofs != 0 && *next_ofs < 94+len) {
420 			return NT_STATUS_INFO_LENGTH_MISMATCH;
421 		}
422 		return NT_STATUS_OK;
423 
424 
425 	case RAW_SEARCH_DATA_ID_FULL_DIRECTORY_INFO:
426 		if (blen < 81) return NT_STATUS_INFO_LENGTH_MISMATCH;
427 		data->id_full_directory_info.file_index  = IVAL(blob->data,             4);
428 		data->id_full_directory_info.create_time = smbcli_pull_nttime(blob->data,  8);
429 		data->id_full_directory_info.access_time = smbcli_pull_nttime(blob->data, 16);
430 		data->id_full_directory_info.write_time  = smbcli_pull_nttime(blob->data, 24);
431 		data->id_full_directory_info.change_time = smbcli_pull_nttime(blob->data, 32);
432 		data->id_full_directory_info.size        = BVAL(blob->data,            40);
433 		data->id_full_directory_info.alloc_size  = BVAL(blob->data,            48);
434 		data->id_full_directory_info.attrib      = IVAL(blob->data,            56);
435 		data->id_full_directory_info.ea_size     = IVAL(blob->data,            64);
436 		data->id_full_directory_info.file_id     = BVAL(blob->data,            72);
437 		len = smbcli_blob_pull_string(NULL, mem_ctx, blob,
438 					      &data->id_full_directory_info.name,
439 					      60, 80, str_flags);
440 		if (*next_ofs != 0 && *next_ofs < 80+len) {
441 			return NT_STATUS_INFO_LENGTH_MISMATCH;
442 		}
443 		return NT_STATUS_OK;
444 
445 	case RAW_SEARCH_DATA_ID_BOTH_DIRECTORY_INFO:
446 		if (blen < 105) return NT_STATUS_INFO_LENGTH_MISMATCH;
447 		data->id_both_directory_info.file_index  = IVAL(blob->data,             4);
448 		data->id_both_directory_info.create_time = smbcli_pull_nttime(blob->data,  8);
449 		data->id_both_directory_info.access_time = smbcli_pull_nttime(blob->data, 16);
450 		data->id_both_directory_info.write_time  = smbcli_pull_nttime(blob->data, 24);
451 		data->id_both_directory_info.change_time = smbcli_pull_nttime(blob->data, 32);
452 		data->id_both_directory_info.size        = BVAL(blob->data,            40);
453 		data->id_both_directory_info.alloc_size  = BVAL(blob->data,            48);
454 		data->id_both_directory_info.attrib      = SVAL(blob->data,            56);
455 		data->id_both_directory_info.ea_size     = IVAL(blob->data,            64);
456 		smbcli_blob_pull_string(NULL, mem_ctx, blob,
457 				     &data->id_both_directory_info.short_name,
458 				     68, 70, STR_LEN8BIT | STR_UNICODE);
459 		memcpy(data->id_both_directory_info.short_name_buf, blob->data + 70, 24);
460 		data->id_both_directory_info.file_id     = BVAL(blob->data,            96);
461 		len = smbcli_blob_pull_string(NULL, mem_ctx, blob,
462 					      &data->id_both_directory_info.name,
463 					      60, 104, str_flags);
464 		if (*next_ofs != 0 && *next_ofs < 104+len) {
465 			return NT_STATUS_INFO_LENGTH_MISMATCH;
466 		}
467 		return NT_STATUS_OK;
468 
469 	default:
470 		break;
471 	}
472 
473 	/* invalid level */
474 	return NT_STATUS_INVALID_INFO_CLASS;
475 }
476 
477 
478 /*
479   parse a trans2 search response.
480   Return the number of bytes consumed
481   return 0 for success with end of list
482   return -1 for a parse error
483 */
parse_trans2_search(struct smbcli_tree * tree,TALLOC_CTX * mem_ctx,enum smb_search_data_level level,uint16_t flags,DATA_BLOB * blob,union smb_search_data * data)484 static int parse_trans2_search(struct smbcli_tree *tree,
485 			       TALLOC_CTX *mem_ctx,
486 			       enum smb_search_data_level level,
487 			       uint16_t flags,
488 			       DATA_BLOB *blob,
489 			       union smb_search_data *data)
490 {
491 	unsigned int len, ofs;
492 	uint32_t ea_size;
493 	DATA_BLOB eablob;
494 	NTSTATUS status;
495 
496 	switch (level) {
497 	case RAW_SEARCH_DATA_GENERIC:
498 	case RAW_SEARCH_DATA_SEARCH:
499 		/* handled elsewhere */
500 		return -1;
501 
502 	case RAW_SEARCH_DATA_STANDARD:
503 		if (flags & FLAG_TRANS2_FIND_REQUIRE_RESUME) {
504 			if (blob->length < 4) return -1;
505 			data->standard.resume_key = IVAL(blob->data, 0);
506 			blob->data += 4;
507 			blob->length -= 4;
508 		}
509 		if (blob->length < 24) return -1;
510 		data->standard.create_time = raw_pull_dos_date2(tree->session->transport,
511 								blob->data + 0);
512 		data->standard.access_time = raw_pull_dos_date2(tree->session->transport,
513 								blob->data + 4);
514 		data->standard.write_time  = raw_pull_dos_date2(tree->session->transport,
515 								blob->data + 8);
516 		data->standard.size        = IVAL(blob->data, 12);
517 		data->standard.alloc_size  = IVAL(blob->data, 16);
518 		data->standard.attrib      = SVAL(blob->data, 20);
519 		len = smbcli_blob_pull_string(tree->session, mem_ctx, blob,
520 					   &data->standard.name,
521 					   22, 23, STR_LEN8BIT | STR_TERMINATE | STR_LEN_NOTERM);
522 		return len + 23;
523 
524 	case RAW_SEARCH_DATA_EA_SIZE:
525 		if (flags & FLAG_TRANS2_FIND_REQUIRE_RESUME) {
526 			if (blob->length < 4) return -1;
527 			data->ea_size.resume_key = IVAL(blob->data, 0);
528 			blob->data += 4;
529 			blob->length -= 4;
530 		}
531 		if (blob->length < 28) return -1;
532 		data->ea_size.create_time = raw_pull_dos_date2(tree->session->transport,
533 							       blob->data + 0);
534 		data->ea_size.access_time = raw_pull_dos_date2(tree->session->transport,
535 							       blob->data + 4);
536 		data->ea_size.write_time  = raw_pull_dos_date2(tree->session->transport,
537 							       blob->data + 8);
538 		data->ea_size.size        = IVAL(blob->data, 12);
539 		data->ea_size.alloc_size  = IVAL(blob->data, 16);
540 		data->ea_size.attrib      = SVAL(blob->data, 20);
541 		data->ea_size.ea_size     = IVAL(blob->data, 22);
542 		len = smbcli_blob_pull_string(tree->session, mem_ctx, blob,
543 					   &data->ea_size.name,
544 					   26, 27, STR_LEN8BIT | STR_TERMINATE | STR_NOALIGN);
545 		return len + 27 + 1;
546 
547 	case RAW_SEARCH_DATA_EA_LIST:
548 		if (flags & FLAG_TRANS2_FIND_REQUIRE_RESUME) {
549 			if (blob->length < 4) return -1;
550 			data->ea_list.resume_key = IVAL(blob->data, 0);
551 			blob->data += 4;
552 			blob->length -= 4;
553 		}
554 		if (blob->length < 28) return -1;
555 		data->ea_list.create_time = raw_pull_dos_date2(tree->session->transport,
556 							       blob->data + 0);
557 		data->ea_list.access_time = raw_pull_dos_date2(tree->session->transport,
558 							       blob->data + 4);
559 		data->ea_list.write_time  = raw_pull_dos_date2(tree->session->transport,
560 							       blob->data + 8);
561 		data->ea_list.size        = IVAL(blob->data, 12);
562 		data->ea_list.alloc_size  = IVAL(blob->data, 16);
563 		data->ea_list.attrib      = SVAL(blob->data, 20);
564 		ea_size                   = IVAL(blob->data, 22);
565 		if (ea_size > 0xFFFF) {
566 			return -1;
567 		}
568 		eablob.data = blob->data + 22;
569 		eablob.length = ea_size;
570 		if (eablob.length > blob->length - 24) {
571 			return -1;
572 		}
573 		status = ea_pull_list(&eablob, mem_ctx,
574 				      &data->ea_list.eas.num_eas,
575 				      &data->ea_list.eas.eas);
576 		if (!NT_STATUS_IS_OK(status)) {
577 			return -1;
578 		}
579 		len = smbcli_blob_pull_string(tree->session, mem_ctx, blob,
580 					      &data->ea_list.name,
581 					      22+ea_size, 23+ea_size,
582 					      STR_LEN8BIT | STR_NOALIGN);
583 		return len + ea_size + 23 + 1;
584 
585 	case RAW_SEARCH_DATA_UNIX_INFO:
586 		if (blob->length < 109) return -1;
587 		ofs                                  = IVAL(blob->data,             0);
588 		data->unix_info.file_index           = IVAL(blob->data,             4);
589 		data->unix_info.size                 = BVAL(blob->data,             8);
590 		data->unix_info.alloc_size           = BVAL(blob->data,            16);
591 		data->unix_info.status_change_time   = smbcli_pull_nttime(blob->data, 24);
592 		data->unix_info.access_time          = smbcli_pull_nttime(blob->data, 32);
593 		data->unix_info.change_time          = smbcli_pull_nttime(blob->data, 40);
594 		data->unix_info.uid                  = IVAL(blob->data,            48);
595 		data->unix_info.gid                  = IVAL(blob->data,            56);
596 		data->unix_info.file_type            = IVAL(blob->data,            64);
597 		data->unix_info.dev_major            = BVAL(blob->data,            68);
598 		data->unix_info.dev_minor            = BVAL(blob->data,            76);
599 		data->unix_info.unique_id            = BVAL(blob->data,            84);
600 		data->unix_info.permissions          = IVAL(blob->data,            92);
601 		data->unix_info.nlink                = IVAL(blob->data,           100);
602 		/* There is no length field for this name but we know it's null terminated. */
603 		len = smbcli_blob_pull_unix_string(tree->session, mem_ctx, blob,
604 					   &data->unix_info.name, 108, 0);
605 		if (ofs != 0 && ofs < 108+len) {
606 			return -1;
607 		}
608 		return ofs;
609 
610 	case RAW_SEARCH_DATA_UNIX_INFO2:
611 		/*   8 - size of ofs + file_index
612 		 * 116 - size of unix_info2
613 		 *   4 - size of name length
614 		 *   2 - "." is the shortest name
615 		 */
616 		if (blob->length < (116 + 8 + 4 + 2)) {
617 			return -1;
618 		}
619 
620 		ofs                                 = IVAL(blob->data,   0);
621 		data->unix_info2.file_index         = IVAL(blob->data,   4);
622 		data->unix_info2.end_of_file        = BVAL(blob->data,   8);
623 		data->unix_info2.num_bytes          = BVAL(blob->data,  16);
624 		data->unix_info2.status_change_time = smbcli_pull_nttime(blob->data, 24);
625 		data->unix_info2.access_time        = smbcli_pull_nttime(blob->data, 32);
626 		data->unix_info2.change_time        = smbcli_pull_nttime(blob->data, 40);
627 		data->unix_info2.uid                = IVAL(blob->data,  48);
628 		data->unix_info2.gid                = IVAL(blob->data,  56);
629 		data->unix_info2.file_type          = IVAL(blob->data,  64);
630 		data->unix_info2.dev_major          = BVAL(blob->data,  68);
631 		data->unix_info2.dev_minor          = BVAL(blob->data,  76);
632 		data->unix_info2.unique_id          = BVAL(blob->data,  84);
633 		data->unix_info2.permissions        = IVAL(blob->data,  92);
634 		data->unix_info2.nlink              = IVAL(blob->data, 100);
635 		data->unix_info2.create_time	    = smbcli_pull_nttime(blob->data, 108);
636 		data->unix_info2.file_flags	    = IVAL(blob->data, 116);
637 		data->unix_info2.flags_mask	    = IVAL(blob->data, 120);
638 
639 		/* There is a 4 byte length field for this name. The length
640 		 * does not include the NULL terminator.
641 		 */
642 		len = smbcli_blob_pull_string(tree->session, mem_ctx, blob,
643 				       &data->unix_info2.name,
644 				       8 + 116, /* offset to length */
645 				       8 + 116 + 4, /* offset to string */
646 				       0);
647 
648 		if (ofs != 0 && ofs < (8 + 116 + 4 + len)) {
649 			return -1;
650 		}
651 
652 		return ofs;
653 
654 		case RAW_SEARCH_DATA_DIRECTORY_INFO:
655 		case RAW_SEARCH_DATA_FULL_DIRECTORY_INFO:
656 		case RAW_SEARCH_DATA_NAME_INFO:
657 		case RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO:
658 		case RAW_SEARCH_DATA_ID_FULL_DIRECTORY_INFO:
659 		case RAW_SEARCH_DATA_ID_BOTH_DIRECTORY_INFO: {
660 			unsigned int str_flags = STR_UNICODE;
661 			if (!(tree->session->transport->negotiate.capabilities & CAP_UNICODE)) {
662 				str_flags = STR_ASCII;
663 			}
664 
665 		status = smb_raw_search_common(mem_ctx, level, blob, data, &ofs, str_flags);
666 		if (!NT_STATUS_IS_OK(status)) {
667 			return -1;
668 		}
669 		return ofs;
670 	}
671 	}
672 
673 	/* invalid level */
674 	return -1;
675 }
676 
677 /****************************************************************************
678  Trans2 search backend - process output.
679 ****************************************************************************/
smb_raw_t2search_backend(struct smbcli_tree * tree,TALLOC_CTX * mem_ctx,enum smb_search_data_level level,uint16_t flags,int16_t count,DATA_BLOB * blob,void * private_data,smbcli_search_callback callback)680 static NTSTATUS smb_raw_t2search_backend(struct smbcli_tree *tree,
681 					 TALLOC_CTX *mem_ctx,
682 					 enum smb_search_data_level level,
683 					 uint16_t flags,
684 					 int16_t count,
685 					 DATA_BLOB *blob,
686 					 void *private_data,
687 					 smbcli_search_callback callback)
688 
689 {
690 	int i;
691 	DATA_BLOB blob2;
692 
693 	blob2.data = blob->data;
694 	blob2.length = blob->length;
695 
696 	for (i=0; i < count; i++) {
697 		union smb_search_data search_data;
698 		unsigned int len;
699 
700 		len = parse_trans2_search(tree, mem_ctx, level, flags, &blob2, &search_data);
701 		if (len == -1) {
702 			return NT_STATUS_INVALID_PARAMETER;
703 		}
704 
705 		/* the callback function can tell us that no more will
706 		   fit - in that case we stop, but it isn't an error */
707 		if (!callback(private_data, &search_data)) {
708 			break;
709 		}
710 
711 		if (len == 0) break;
712 
713 		blob2.data += len;
714 		blob2.length -= len;
715 	}
716 
717 	return NT_STATUS_OK;
718 }
719 
720 
721 /* Implements trans2findfirst2 and old search
722  */
smb_raw_search_first(struct smbcli_tree * tree,TALLOC_CTX * mem_ctx,union smb_search_first * io,void * private_data,smbcli_search_callback callback)723 _PUBLIC_ NTSTATUS smb_raw_search_first(struct smbcli_tree *tree,
724 			      TALLOC_CTX *mem_ctx,
725 			      union smb_search_first *io, void *private_data,
726 			      smbcli_search_callback callback)
727 {
728 	DATA_BLOB p_blob = data_blob_null, d_blob = data_blob_null;
729 	NTSTATUS status;
730 
731 	switch (io->generic.level) {
732 	case RAW_SEARCH_SEARCH:
733 	case RAW_SEARCH_FFIRST:
734 	case RAW_SEARCH_FUNIQUE:
735 		return smb_raw_search_first_old(tree, mem_ctx, io, private_data, callback);
736 
737 	case RAW_SEARCH_TRANS2:
738 		break;
739 
740 	case RAW_SEARCH_SMB2:
741 		return NT_STATUS_INVALID_LEVEL;
742 	}
743 
744 	status = smb_raw_search_first_blob(tree, mem_ctx,
745 					   io, &p_blob, &d_blob);
746 	if (!NT_STATUS_IS_OK(status)) {
747 		return status;
748 	}
749 
750 	if (p_blob.length < 10) {
751 		DEBUG(1,("smb_raw_search_first: parms wrong size %d != expected_param_size\n",
752 			(int)p_blob.length));
753 		return NT_STATUS_INVALID_PARAMETER;
754 	}
755 
756 	/* process output data */
757 	io->t2ffirst.out.handle = SVAL(p_blob.data, 0);
758 	io->t2ffirst.out.count = SVAL(p_blob.data, 2);
759 	io->t2ffirst.out.end_of_search = SVAL(p_blob.data, 4);
760 
761 	status = smb_raw_t2search_backend(tree, mem_ctx,
762 					  io->generic.data_level,
763 					  io->t2ffirst.in.flags, io->t2ffirst.out.count,
764 					  &d_blob, private_data, callback);
765 
766 	return status;
767 }
768 
769 /* Implements trans2findnext2 and old smbsearch
770  */
smb_raw_search_next(struct smbcli_tree * tree,TALLOC_CTX * mem_ctx,union smb_search_next * io,void * private_data,smbcli_search_callback callback)771 NTSTATUS smb_raw_search_next(struct smbcli_tree *tree,
772 			     TALLOC_CTX *mem_ctx,
773 			     union smb_search_next *io, void *private_data,
774 			     smbcli_search_callback callback)
775 {
776 	DATA_BLOB p_blob = data_blob_null, d_blob = data_blob_null;
777 	NTSTATUS status;
778 
779 	switch (io->generic.level) {
780 	case RAW_SEARCH_SEARCH:
781 	case RAW_SEARCH_FFIRST:
782 		return smb_raw_search_next_old(tree, mem_ctx, io, private_data, callback);
783 
784 	case RAW_SEARCH_FUNIQUE:
785 		return NT_STATUS_INVALID_LEVEL;
786 
787 	case RAW_SEARCH_TRANS2:
788 		break;
789 
790 	case RAW_SEARCH_SMB2:
791 		return NT_STATUS_INVALID_LEVEL;
792 	}
793 
794 	status = smb_raw_search_next_blob(tree, mem_ctx,
795 					  io, &p_blob, &d_blob);
796 	if (!NT_STATUS_IS_OK(status)) {
797 		return status;
798 	}
799 
800 	if (p_blob.length != 8) {
801 		DEBUG(1,("smb_raw_search_next: parms wrong size %d != expected_param_size\n",
802 			(int)p_blob.length));
803 		return NT_STATUS_INVALID_PARAMETER;
804 	}
805 
806 	/* process output data */
807 	io->t2fnext.out.count = SVAL(p_blob.data, 0);
808 	io->t2fnext.out.end_of_search = SVAL(p_blob.data, 2);
809 
810 	status = smb_raw_t2search_backend(tree, mem_ctx,
811 					  io->generic.data_level,
812 					  io->t2fnext.in.flags, io->t2fnext.out.count,
813 					  &d_blob, private_data, callback);
814 
815 	return status;
816 }
817 
818 /*
819    Implements trans2findclose2
820  */
smb_raw_search_close(struct smbcli_tree * tree,union smb_search_close * io)821 NTSTATUS smb_raw_search_close(struct smbcli_tree *tree,
822 			      union smb_search_close *io)
823 {
824 	struct smbcli_request *req;
825 
826 	if (io->generic.level == RAW_FINDCLOSE_FCLOSE) {
827 		return smb_raw_search_close_old(tree, io);
828 	}
829 
830 	req = smbcli_request_setup(tree, SMBfindclose, 1, 0);
831 	if (!req) {
832 		return NT_STATUS_NO_MEMORY;
833 	}
834 
835 	SSVAL(req->out.vwv, VWV(0), io->findclose.in.handle);
836 
837 	if (smbcli_request_send(req)) {
838 		(void) smbcli_request_receive(req);
839 	}
840 
841 	return smbcli_request_destroy(req);
842 }
843