/src/samba/source4/libcli/smb2/request.c
Line | Count | Source |
1 | | /* |
2 | | Unix SMB/CIFS implementation. |
3 | | |
4 | | SMB2 client request handling |
5 | | |
6 | | Copyright (C) Andrew Tridgell 2005 |
7 | | Copyright (C) Stefan Metzmacher 2005 |
8 | | |
9 | | This program is free software; you can redistribute it and/or modify |
10 | | it under the terms of the GNU General Public License as published by |
11 | | the Free Software Foundation; either version 3 of the License, or |
12 | | (at your option) any later version. |
13 | | |
14 | | This program is distributed in the hope that it will be useful, |
15 | | but WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
17 | | GNU General Public License for more details. |
18 | | |
19 | | You should have received a copy of the GNU General Public License |
20 | | along with this program. If not, see <http://www.gnu.org/licenses/>. |
21 | | */ |
22 | | |
23 | | #include "includes.h" |
24 | | #include "libcli/raw/libcliraw.h" |
25 | | #include "libcli/smb2/smb2.h" |
26 | | #include "../lib/util/dlinklist.h" |
27 | | #include "lib/events/events.h" |
28 | | #include "libcli/smb2/smb2_calls.h" |
29 | | |
30 | | /* fill in the bufinfo */ |
31 | | void smb2_setup_bufinfo(struct smb2_request *req) |
32 | 0 | { |
33 | 0 | req->in.bufinfo.mem_ctx = req; |
34 | 0 | req->in.bufinfo.flags = BUFINFO_FLAG_UNICODE | BUFINFO_FLAG_SMB2; |
35 | 0 | req->in.bufinfo.align_base = req->in.buffer; |
36 | 0 | if (req->in.dynamic) { |
37 | 0 | req->in.bufinfo.data = req->in.dynamic; |
38 | 0 | req->in.bufinfo.data_size = req->in.body_size - req->in.body_fixed; |
39 | 0 | } else { |
40 | 0 | req->in.bufinfo.data = NULL; |
41 | 0 | req->in.bufinfo.data_size = 0; |
42 | 0 | } |
43 | 0 | } |
44 | | |
45 | | /* |
46 | | initialise a smb2 request |
47 | | */ |
48 | | struct smb2_request *smb2_request_init(struct smb2_transport *transport, uint16_t opcode, |
49 | | uint16_t body_fixed_size, bool body_dynamic_present, |
50 | | uint32_t body_dynamic_size) |
51 | 0 | { |
52 | 0 | struct smb2_request *req; |
53 | 0 | uint32_t hdr_offset; |
54 | 0 | bool compound = false; |
55 | |
|
56 | 0 | if (body_dynamic_present) { |
57 | 0 | if (body_dynamic_size == 0) { |
58 | 0 | body_dynamic_size = 1; |
59 | 0 | } |
60 | 0 | } else { |
61 | 0 | body_dynamic_size = 0; |
62 | 0 | } |
63 | |
|
64 | 0 | req = talloc_zero(transport, struct smb2_request); |
65 | 0 | if (req == NULL) return NULL; |
66 | | |
67 | 0 | req->state = SMB2_REQUEST_INIT; |
68 | 0 | req->transport = transport; |
69 | |
|
70 | 0 | hdr_offset = NBT_HDR_SIZE; |
71 | |
|
72 | 0 | req->out.size = hdr_offset + SMB2_HDR_BODY + body_fixed_size; |
73 | 0 | req->out.allocated = req->out.size + body_dynamic_size; |
74 | |
|
75 | 0 | req->out.buffer = talloc_realloc(req, req->out.buffer, |
76 | 0 | uint8_t, req->out.allocated); |
77 | 0 | if (req->out.buffer == NULL) { |
78 | 0 | talloc_free(req); |
79 | 0 | return NULL; |
80 | 0 | } |
81 | | |
82 | 0 | req->out.hdr = req->out.buffer + hdr_offset; |
83 | 0 | req->out.body = req->out.hdr + SMB2_HDR_BODY; |
84 | 0 | req->out.body_fixed= body_fixed_size; |
85 | 0 | req->out.body_size = body_fixed_size; |
86 | 0 | req->out.dynamic = (body_dynamic_size ? req->out.body + body_fixed_size : NULL); |
87 | |
|
88 | 0 | SIVAL(req->out.hdr, 0, SMB2_MAGIC); |
89 | 0 | SSVAL(req->out.hdr, SMB2_HDR_LENGTH, SMB2_HDR_BODY); |
90 | 0 | SSVAL(req->out.hdr, SMB2_HDR_CREDIT_CHARGE, 0); |
91 | 0 | SIVAL(req->out.hdr, SMB2_HDR_STATUS, 0); |
92 | 0 | SSVAL(req->out.hdr, SMB2_HDR_OPCODE, opcode); |
93 | 0 | SSVAL(req->out.hdr, SMB2_HDR_CREDIT, 0); |
94 | 0 | SIVAL(req->out.hdr, SMB2_HDR_FLAGS, 0); |
95 | 0 | SIVAL(req->out.hdr, SMB2_HDR_NEXT_COMMAND, 0); |
96 | 0 | SBVAL(req->out.hdr, SMB2_HDR_MESSAGE_ID, 0); |
97 | 0 | SIVAL(req->out.hdr, SMB2_HDR_PID, 0); |
98 | 0 | SIVAL(req->out.hdr, SMB2_HDR_TID, 0); |
99 | 0 | SBVAL(req->out.hdr, SMB2_HDR_SESSION_ID, 0); |
100 | 0 | memset(req->out.hdr+SMB2_HDR_SIGNATURE, 0, 16); |
101 | | |
102 | | /* set the length of the fixed body part and +1 if there's a dynamic part also */ |
103 | 0 | SSVAL(req->out.body, 0, body_fixed_size + (body_dynamic_size?1:0)); |
104 | | |
105 | | /* |
106 | | * if we have a dynamic part, make sure the first byte |
107 | | * which is always be part of the packet is initialized |
108 | | */ |
109 | 0 | if (body_dynamic_size && !compound) { |
110 | 0 | req->out.size += 1; |
111 | 0 | SCVAL(req->out.dynamic, 0, 0); |
112 | 0 | } |
113 | |
|
114 | 0 | return req; |
115 | 0 | } |
116 | | |
117 | | /* |
118 | | initialise a smb2 request for tree operations |
119 | | */ |
120 | | struct smb2_request *smb2_request_init_tree(struct smb2_tree *tree, uint16_t opcode, |
121 | | uint16_t body_fixed_size, bool body_dynamic_present, |
122 | | uint32_t body_dynamic_size) |
123 | 0 | { |
124 | 0 | struct smb2_request *req = smb2_request_init(tree->session->transport, opcode, |
125 | 0 | body_fixed_size, body_dynamic_present, |
126 | 0 | body_dynamic_size); |
127 | 0 | if (req == NULL) return NULL; |
128 | | |
129 | 0 | req->session = tree->session; |
130 | 0 | req->tree = tree; |
131 | |
|
132 | 0 | return req; |
133 | 0 | } |
134 | | |
135 | | /* destroy a request structure and return final status */ |
136 | | NTSTATUS smb2_request_destroy(struct smb2_request *req) |
137 | 0 | { |
138 | 0 | NTSTATUS status; |
139 | | |
140 | | /* this is the error code we give the application for when a |
141 | | _send() call fails completely */ |
142 | 0 | if (!req) return NT_STATUS_UNSUCCESSFUL; |
143 | | |
144 | 0 | if (req->state == SMB2_REQUEST_ERROR && |
145 | 0 | NT_STATUS_IS_OK(req->status)) { |
146 | 0 | status = NT_STATUS_INTERNAL_ERROR; |
147 | 0 | } else { |
148 | 0 | status = req->status; |
149 | 0 | } |
150 | |
|
151 | 0 | talloc_free(req); |
152 | 0 | return status; |
153 | 0 | } |
154 | | |
155 | | /* |
156 | | receive a response to a packet |
157 | | */ |
158 | | bool smb2_request_receive(struct smb2_request *req) |
159 | 0 | { |
160 | | /* req can be NULL when a send has failed. This eliminates lots of NULL |
161 | | checks in each module */ |
162 | 0 | if (!req) return false; |
163 | | |
164 | | /* keep receiving packets until this one is replied to */ |
165 | 0 | while (req->state <= SMB2_REQUEST_RECV) { |
166 | 0 | if (tevent_loop_once(req->transport->ev) != 0) { |
167 | 0 | return false; |
168 | 0 | } |
169 | 0 | } |
170 | | |
171 | 0 | return req->state == SMB2_REQUEST_DONE; |
172 | 0 | } |
173 | | |
174 | | /* Return true if the last packet was in error */ |
175 | | bool smb2_request_is_error(struct smb2_request *req) |
176 | 0 | { |
177 | 0 | return NT_STATUS_IS_ERR(req->status); |
178 | 0 | } |
179 | | |
180 | | /* Return true if the last packet was OK */ |
181 | | bool smb2_request_is_ok(struct smb2_request *req) |
182 | 0 | { |
183 | 0 | return NT_STATUS_IS_OK(req->status); |
184 | 0 | } |
185 | | |
186 | | /* |
187 | | check if a range in the reply body is out of bounds |
188 | | */ |
189 | | bool smb2_oob(struct smb2_request_buffer *buf, const uint8_t *ptr, size_t size) |
190 | 0 | { |
191 | 0 | if (size == 0) { |
192 | | /* zero bytes is never out of range */ |
193 | 0 | return false; |
194 | 0 | } |
195 | | /* be careful with wraparound! */ |
196 | 0 | if ((uintptr_t)ptr < (uintptr_t)buf->body || |
197 | 0 | (uintptr_t)ptr >= (uintptr_t)buf->body + buf->body_size || |
198 | 0 | size > buf->body_size || |
199 | 0 | (uintptr_t)ptr + size > (uintptr_t)buf->body + buf->body_size) { |
200 | 0 | return true; |
201 | 0 | } |
202 | 0 | return false; |
203 | 0 | } |
204 | | |
205 | | size_t smb2_padding_size(uint32_t offset, size_t n) |
206 | 0 | { |
207 | 0 | if ((offset & (n-1)) == 0) return 0; |
208 | 0 | return n - (offset & (n-1)); |
209 | 0 | } |
210 | | |
211 | | static size_t smb2_padding_fix(struct smb2_request_buffer *buf) |
212 | 0 | { |
213 | 0 | if (buf->dynamic == (buf->body + buf->body_fixed)) { |
214 | 0 | if (buf->dynamic != (buf->buffer + buf->size)) { |
215 | 0 | return 1; |
216 | 0 | } |
217 | 0 | } |
218 | 0 | return 0; |
219 | 0 | } |
220 | | |
221 | | /* |
222 | | grow a SMB2 buffer by the specified amount |
223 | | */ |
224 | | NTSTATUS smb2_grow_buffer(struct smb2_request_buffer *buf, size_t increase) |
225 | 0 | { |
226 | 0 | size_t hdr_ofs; |
227 | 0 | size_t dynamic_ofs; |
228 | 0 | uint8_t *buffer_ptr; |
229 | 0 | uint32_t newsize = buf->size + increase; |
230 | | |
231 | | /* a packet size should be limited a bit */ |
232 | 0 | if (newsize >= 0x00FFFFFF) return NT_STATUS_MARSHALL_OVERFLOW; |
233 | | |
234 | 0 | if (newsize <= buf->allocated) return NT_STATUS_OK; |
235 | | |
236 | 0 | hdr_ofs = buf->hdr - buf->buffer; |
237 | 0 | dynamic_ofs = buf->dynamic - buf->buffer; |
238 | |
|
239 | 0 | buffer_ptr = talloc_realloc(buf, buf->buffer, uint8_t, newsize); |
240 | 0 | NT_STATUS_HAVE_NO_MEMORY(buffer_ptr); |
241 | | |
242 | 0 | buf->buffer = buffer_ptr; |
243 | 0 | buf->hdr = buf->buffer + hdr_ofs; |
244 | 0 | buf->body = buf->hdr + SMB2_HDR_BODY; |
245 | 0 | buf->dynamic = buf->buffer + dynamic_ofs; |
246 | 0 | buf->allocated = newsize; |
247 | |
|
248 | 0 | return NT_STATUS_OK; |
249 | 0 | } |
250 | | |
251 | | /* |
252 | | pull a uint16_t ofs/ uint16_t length/blob triple from a data blob |
253 | | the ptr points to the start of the offset/length pair |
254 | | */ |
255 | | NTSTATUS smb2_pull_o16s16_blob(struct smb2_request_buffer *buf, TALLOC_CTX *mem_ctx, uint8_t *ptr, DATA_BLOB *blob) |
256 | 0 | { |
257 | 0 | uint16_t ofs, size; |
258 | 0 | if (smb2_oob(buf, ptr, 4)) { |
259 | 0 | return NT_STATUS_INVALID_PARAMETER; |
260 | 0 | } |
261 | 0 | ofs = SVAL(ptr, 0); |
262 | 0 | size = SVAL(ptr, 2); |
263 | 0 | if (ofs == 0) { |
264 | 0 | *blob = data_blob(NULL, 0); |
265 | 0 | return NT_STATUS_OK; |
266 | 0 | } |
267 | 0 | if (smb2_oob(buf, buf->hdr + ofs, size)) { |
268 | 0 | return NT_STATUS_INVALID_PARAMETER; |
269 | 0 | } |
270 | 0 | *blob = data_blob_talloc(mem_ctx, buf->hdr + ofs, size); |
271 | 0 | NT_STATUS_HAVE_NO_MEMORY(blob->data); |
272 | 0 | return NT_STATUS_OK; |
273 | 0 | } |
274 | | |
275 | | /* |
276 | | push a uint16_t ofs/ uint16_t length/blob triple into a data blob |
277 | | the ofs points to the start of the offset/length pair, and is relative |
278 | | to the body start |
279 | | */ |
280 | | NTSTATUS smb2_push_o16s16_blob(struct smb2_request_buffer *buf, |
281 | | uint16_t ofs, DATA_BLOB blob) |
282 | 0 | { |
283 | 0 | NTSTATUS status; |
284 | 0 | size_t offset; |
285 | 0 | size_t padding_length; |
286 | 0 | size_t padding_fix; |
287 | 0 | uint8_t *ptr = buf->body+ofs; |
288 | |
|
289 | 0 | if (buf->dynamic == NULL) { |
290 | 0 | return NT_STATUS_INVALID_PARAMETER; |
291 | 0 | } |
292 | | |
293 | | /* we have only 16 bit for the size */ |
294 | 0 | if (blob.length > 0xFFFF) { |
295 | 0 | return NT_STATUS_INVALID_PARAMETER; |
296 | 0 | } |
297 | | |
298 | | /* check if there're enough room for ofs and size */ |
299 | 0 | if (smb2_oob(buf, ptr, 4)) { |
300 | 0 | return NT_STATUS_INVALID_PARAMETER; |
301 | 0 | } |
302 | | |
303 | 0 | if (blob.data == NULL) { |
304 | 0 | if (blob.length != 0) { |
305 | 0 | return NT_STATUS_INTERNAL_ERROR; |
306 | 0 | } |
307 | 0 | SSVAL(ptr, 0, 0); |
308 | 0 | SSVAL(ptr, 2, 0); |
309 | 0 | return NT_STATUS_OK; |
310 | 0 | } |
311 | | |
312 | 0 | offset = buf->dynamic - buf->hdr; |
313 | 0 | padding_length = smb2_padding_size(offset, 2); |
314 | 0 | offset += padding_length; |
315 | 0 | padding_fix = smb2_padding_fix(buf); |
316 | |
|
317 | 0 | SSVAL(ptr, 0, offset); |
318 | 0 | SSVAL(ptr, 2, blob.length); |
319 | |
|
320 | 0 | status = smb2_grow_buffer(buf, blob.length + padding_length - padding_fix); |
321 | 0 | NT_STATUS_NOT_OK_RETURN(status); |
322 | | |
323 | 0 | memset(buf->dynamic, 0, padding_length); |
324 | 0 | buf->dynamic += padding_length; |
325 | |
|
326 | 0 | memcpy(buf->dynamic, blob.data, blob.length); |
327 | 0 | buf->dynamic += blob.length; |
328 | |
|
329 | 0 | buf->size += blob.length + padding_length - padding_fix; |
330 | 0 | buf->body_size += blob.length + padding_length; |
331 | |
|
332 | 0 | return NT_STATUS_OK; |
333 | 0 | } |
334 | | |
335 | | |
336 | | /* |
337 | | push a uint16_t ofs/ uint32_t length/blob triple into a data blob |
338 | | the ofs points to the start of the offset/length pair, and is relative |
339 | | to the body start |
340 | | */ |
341 | | NTSTATUS smb2_push_o16s32_blob(struct smb2_request_buffer *buf, |
342 | | uint16_t ofs, DATA_BLOB blob) |
343 | 0 | { |
344 | 0 | NTSTATUS status; |
345 | 0 | size_t offset; |
346 | 0 | size_t padding_length; |
347 | 0 | size_t padding_fix; |
348 | 0 | uint8_t *ptr = buf->body+ofs; |
349 | |
|
350 | 0 | if (buf->dynamic == NULL) { |
351 | 0 | return NT_STATUS_INVALID_PARAMETER; |
352 | 0 | } |
353 | | |
354 | | /* check if there're enough room for ofs and size */ |
355 | 0 | if (smb2_oob(buf, ptr, 6)) { |
356 | 0 | return NT_STATUS_INVALID_PARAMETER; |
357 | 0 | } |
358 | | |
359 | 0 | if (blob.data == NULL) { |
360 | 0 | if (blob.length != 0) { |
361 | 0 | return NT_STATUS_INTERNAL_ERROR; |
362 | 0 | } |
363 | 0 | SSVAL(ptr, 0, 0); |
364 | 0 | SIVAL(ptr, 2, 0); |
365 | 0 | return NT_STATUS_OK; |
366 | 0 | } |
367 | | |
368 | 0 | offset = buf->dynamic - buf->hdr; |
369 | 0 | padding_length = smb2_padding_size(offset, 2); |
370 | 0 | offset += padding_length; |
371 | 0 | padding_fix = smb2_padding_fix(buf); |
372 | |
|
373 | 0 | SSVAL(ptr, 0, offset); |
374 | 0 | SIVAL(ptr, 2, blob.length); |
375 | |
|
376 | 0 | status = smb2_grow_buffer(buf, blob.length + padding_length - padding_fix); |
377 | 0 | NT_STATUS_NOT_OK_RETURN(status); |
378 | | |
379 | 0 | memset(buf->dynamic, 0, padding_length); |
380 | 0 | buf->dynamic += padding_length; |
381 | |
|
382 | 0 | memcpy(buf->dynamic, blob.data, blob.length); |
383 | 0 | buf->dynamic += blob.length; |
384 | |
|
385 | 0 | buf->size += blob.length + padding_length - padding_fix; |
386 | 0 | buf->body_size += blob.length + padding_length; |
387 | |
|
388 | 0 | return NT_STATUS_OK; |
389 | 0 | } |
390 | | |
391 | | |
392 | | /* |
393 | | push a uint32_t ofs/ uint32_t length/blob triple into a data blob |
394 | | the ofs points to the start of the offset/length pair, and is relative |
395 | | to the body start |
396 | | */ |
397 | | NTSTATUS smb2_push_o32s32_blob(struct smb2_request_buffer *buf, |
398 | | uint32_t ofs, DATA_BLOB blob) |
399 | 0 | { |
400 | 0 | NTSTATUS status; |
401 | 0 | size_t offset; |
402 | 0 | size_t padding_length; |
403 | 0 | size_t padding_fix; |
404 | 0 | uint8_t *ptr = buf->body+ofs; |
405 | |
|
406 | 0 | if (buf->dynamic == NULL) { |
407 | 0 | return NT_STATUS_INVALID_PARAMETER; |
408 | 0 | } |
409 | | |
410 | | /* check if there're enough room for ofs and size */ |
411 | 0 | if (smb2_oob(buf, ptr, 8)) { |
412 | 0 | return NT_STATUS_INVALID_PARAMETER; |
413 | 0 | } |
414 | | |
415 | 0 | if (blob.data == NULL) { |
416 | 0 | if (blob.length != 0) { |
417 | 0 | return NT_STATUS_INTERNAL_ERROR; |
418 | 0 | } |
419 | 0 | SIVAL(ptr, 0, 0); |
420 | 0 | SIVAL(ptr, 4, 0); |
421 | 0 | return NT_STATUS_OK; |
422 | 0 | } |
423 | | |
424 | 0 | offset = buf->dynamic - buf->hdr; |
425 | 0 | padding_length = smb2_padding_size(offset, 8); |
426 | 0 | offset += padding_length; |
427 | 0 | padding_fix = smb2_padding_fix(buf); |
428 | |
|
429 | 0 | SIVAL(ptr, 0, offset); |
430 | 0 | SIVAL(ptr, 4, blob.length); |
431 | |
|
432 | 0 | status = smb2_grow_buffer(buf, blob.length + padding_length - padding_fix); |
433 | 0 | NT_STATUS_NOT_OK_RETURN(status); |
434 | | |
435 | 0 | memset(buf->dynamic, 0, padding_length); |
436 | 0 | buf->dynamic += padding_length; |
437 | |
|
438 | 0 | memcpy(buf->dynamic, blob.data, blob.length); |
439 | 0 | buf->dynamic += blob.length; |
440 | |
|
441 | 0 | buf->size += blob.length + padding_length - padding_fix; |
442 | 0 | buf->body_size += blob.length + padding_length; |
443 | |
|
444 | 0 | return NT_STATUS_OK; |
445 | 0 | } |
446 | | |
447 | | |
448 | | /* |
449 | | push a uint32_t length/ uint32_t ofs/blob triple into a data blob |
450 | | the ofs points to the start of the length/offset pair, and is relative |
451 | | to the body start |
452 | | */ |
453 | | NTSTATUS smb2_push_s32o32_blob(struct smb2_request_buffer *buf, |
454 | | uint32_t ofs, DATA_BLOB blob) |
455 | 0 | { |
456 | 0 | NTSTATUS status; |
457 | 0 | size_t offset; |
458 | 0 | size_t padding_length; |
459 | 0 | size_t padding_fix; |
460 | 0 | uint8_t *ptr = buf->body+ofs; |
461 | |
|
462 | 0 | if (buf->dynamic == NULL) { |
463 | 0 | return NT_STATUS_INVALID_PARAMETER; |
464 | 0 | } |
465 | | |
466 | | /* check if there're enough room for ofs and size */ |
467 | 0 | if (smb2_oob(buf, ptr, 8)) { |
468 | 0 | return NT_STATUS_INVALID_PARAMETER; |
469 | 0 | } |
470 | | |
471 | 0 | if (blob.data == NULL) { |
472 | 0 | if (blob.length != 0) { |
473 | 0 | return NT_STATUS_INTERNAL_ERROR; |
474 | 0 | } |
475 | 0 | SIVAL(ptr, 0, 0); |
476 | 0 | SIVAL(ptr, 4, 0); |
477 | 0 | return NT_STATUS_OK; |
478 | 0 | } |
479 | | |
480 | 0 | offset = buf->dynamic - buf->hdr; |
481 | 0 | padding_length = smb2_padding_size(offset, 8); |
482 | 0 | offset += padding_length; |
483 | 0 | padding_fix = smb2_padding_fix(buf); |
484 | |
|
485 | 0 | SIVAL(ptr, 0, blob.length); |
486 | 0 | SIVAL(ptr, 4, offset); |
487 | |
|
488 | 0 | status = smb2_grow_buffer(buf, blob.length + padding_length - padding_fix); |
489 | 0 | NT_STATUS_NOT_OK_RETURN(status); |
490 | | |
491 | 0 | memset(buf->dynamic, 0, padding_length); |
492 | 0 | buf->dynamic += padding_length; |
493 | |
|
494 | 0 | memcpy(buf->dynamic, blob.data, blob.length); |
495 | 0 | buf->dynamic += blob.length; |
496 | |
|
497 | 0 | buf->size += blob.length + padding_length - padding_fix; |
498 | 0 | buf->body_size += blob.length + padding_length; |
499 | |
|
500 | 0 | return NT_STATUS_OK; |
501 | 0 | } |
502 | | |
503 | | /* |
504 | | pull a uint16_t ofs/ uint32_t length/blob triple from a data blob |
505 | | the ptr points to the start of the offset/length pair |
506 | | */ |
507 | | NTSTATUS smb2_pull_o16s32_blob(struct smb2_request_buffer *buf, TALLOC_CTX *mem_ctx, uint8_t *ptr, DATA_BLOB *blob) |
508 | 0 | { |
509 | 0 | uint16_t ofs; |
510 | 0 | uint32_t size; |
511 | |
|
512 | 0 | if (smb2_oob(buf, ptr, 6)) { |
513 | 0 | return NT_STATUS_INVALID_PARAMETER; |
514 | 0 | } |
515 | 0 | ofs = SVAL(ptr, 0); |
516 | 0 | size = IVAL(ptr, 2); |
517 | 0 | if (ofs == 0) { |
518 | 0 | *blob = data_blob(NULL, 0); |
519 | 0 | return NT_STATUS_OK; |
520 | 0 | } |
521 | 0 | if (smb2_oob(buf, buf->hdr + ofs, size)) { |
522 | 0 | return NT_STATUS_INVALID_PARAMETER; |
523 | 0 | } |
524 | 0 | *blob = data_blob_talloc(mem_ctx, buf->hdr + ofs, size); |
525 | 0 | NT_STATUS_HAVE_NO_MEMORY(blob->data); |
526 | 0 | return NT_STATUS_OK; |
527 | 0 | } |
528 | | |
529 | | /* |
530 | | pull a uint32_t ofs/ uint32_t length/blob triple from a data blob |
531 | | the ptr points to the start of the offset/length pair |
532 | | */ |
533 | | NTSTATUS smb2_pull_o32s32_blob(struct smb2_request_buffer *buf, TALLOC_CTX *mem_ctx, uint8_t *ptr, DATA_BLOB *blob) |
534 | 0 | { |
535 | 0 | uint32_t ofs, size; |
536 | 0 | if (smb2_oob(buf, ptr, 8)) { |
537 | 0 | return NT_STATUS_INVALID_PARAMETER; |
538 | 0 | } |
539 | 0 | ofs = IVAL(ptr, 0); |
540 | 0 | size = IVAL(ptr, 4); |
541 | 0 | if (ofs == 0) { |
542 | 0 | *blob = data_blob(NULL, 0); |
543 | 0 | return NT_STATUS_OK; |
544 | 0 | } |
545 | 0 | if (smb2_oob(buf, buf->hdr + ofs, size)) { |
546 | 0 | return NT_STATUS_INVALID_PARAMETER; |
547 | 0 | } |
548 | 0 | *blob = data_blob_talloc(mem_ctx, buf->hdr + ofs, size); |
549 | 0 | NT_STATUS_HAVE_NO_MEMORY(blob->data); |
550 | 0 | return NT_STATUS_OK; |
551 | 0 | } |
552 | | |
553 | | /* |
554 | | pull a uint16_t ofs/ uint32_t length/blob triple from a data blob |
555 | | the ptr points to the start of the offset/length pair |
556 | | |
557 | | In this variant the uint16_t is padded by an extra 2 bytes, making |
558 | | the size aligned on 4 byte boundary |
559 | | */ |
560 | | NTSTATUS smb2_pull_o16As32_blob(struct smb2_request_buffer *buf, TALLOC_CTX *mem_ctx, uint8_t *ptr, DATA_BLOB *blob) |
561 | 0 | { |
562 | 0 | uint32_t ofs, size; |
563 | 0 | if (smb2_oob(buf, ptr, 8)) { |
564 | 0 | return NT_STATUS_INVALID_PARAMETER; |
565 | 0 | } |
566 | 0 | ofs = SVAL(ptr, 0); |
567 | 0 | size = IVAL(ptr, 4); |
568 | 0 | if (ofs == 0) { |
569 | 0 | *blob = data_blob(NULL, 0); |
570 | 0 | return NT_STATUS_OK; |
571 | 0 | } |
572 | 0 | if (smb2_oob(buf, buf->hdr + ofs, size)) { |
573 | 0 | return NT_STATUS_INVALID_PARAMETER; |
574 | 0 | } |
575 | 0 | *blob = data_blob_talloc(mem_ctx, buf->hdr + ofs, size); |
576 | 0 | NT_STATUS_HAVE_NO_MEMORY(blob->data); |
577 | 0 | return NT_STATUS_OK; |
578 | 0 | } |
579 | | |
580 | | /* |
581 | | pull a uint32_t length/ uint32_t ofs/blob triple from a data blob |
582 | | the ptr points to the start of the offset/length pair |
583 | | */ |
584 | | NTSTATUS smb2_pull_s32o32_blob(struct smb2_request_buffer *buf, TALLOC_CTX *mem_ctx, uint8_t *ptr, DATA_BLOB *blob) |
585 | 0 | { |
586 | 0 | uint32_t ofs, size; |
587 | 0 | if (smb2_oob(buf, ptr, 8)) { |
588 | 0 | return NT_STATUS_INVALID_PARAMETER; |
589 | 0 | } |
590 | 0 | size = IVAL(ptr, 0); |
591 | 0 | ofs = IVAL(ptr, 4); |
592 | 0 | if (ofs == 0) { |
593 | 0 | *blob = data_blob(NULL, 0); |
594 | 0 | return NT_STATUS_OK; |
595 | 0 | } |
596 | 0 | if (smb2_oob(buf, buf->hdr + ofs, size)) { |
597 | 0 | return NT_STATUS_INVALID_PARAMETER; |
598 | 0 | } |
599 | 0 | *blob = data_blob_talloc(mem_ctx, buf->hdr + ofs, size); |
600 | 0 | NT_STATUS_HAVE_NO_MEMORY(blob->data); |
601 | 0 | return NT_STATUS_OK; |
602 | 0 | } |
603 | | |
604 | | /* |
605 | | pull a uint32_t length/ uint16_t ofs/blob triple from a data blob |
606 | | the ptr points to the start of the offset/length pair |
607 | | */ |
608 | | NTSTATUS smb2_pull_s32o16_blob(struct smb2_request_buffer *buf, TALLOC_CTX *mem_ctx, uint8_t *ptr, DATA_BLOB *blob) |
609 | 0 | { |
610 | 0 | uint32_t ofs, size; |
611 | 0 | if (smb2_oob(buf, ptr, 8)) { |
612 | 0 | return NT_STATUS_INVALID_PARAMETER; |
613 | 0 | } |
614 | 0 | size = IVAL(ptr, 0); |
615 | 0 | ofs = SVAL(ptr, 4); |
616 | 0 | if (ofs == 0) { |
617 | 0 | *blob = data_blob(NULL, 0); |
618 | 0 | return NT_STATUS_OK; |
619 | 0 | } |
620 | 0 | if (smb2_oob(buf, buf->hdr + ofs, size)) { |
621 | 0 | return NT_STATUS_INVALID_PARAMETER; |
622 | 0 | } |
623 | 0 | *blob = data_blob_talloc(mem_ctx, buf->hdr + ofs, size); |
624 | 0 | NT_STATUS_HAVE_NO_MEMORY(blob->data); |
625 | 0 | return NT_STATUS_OK; |
626 | 0 | } |
627 | | |
628 | | /* |
629 | | pull a string in a uint16_t ofs/ uint16_t length/blob format |
630 | | UTF-16 without termination |
631 | | */ |
632 | | NTSTATUS smb2_pull_o16s16_string(struct smb2_request_buffer *buf, TALLOC_CTX *mem_ctx, |
633 | | uint8_t *ptr, const char **str) |
634 | 0 | { |
635 | 0 | DATA_BLOB blob; |
636 | 0 | NTSTATUS status; |
637 | 0 | void *vstr; |
638 | 0 | size_t converted_size = 0; |
639 | 0 | bool ret; |
640 | |
|
641 | 0 | status = smb2_pull_o16s16_blob(buf, mem_ctx, ptr, &blob); |
642 | 0 | NT_STATUS_NOT_OK_RETURN(status); |
643 | | |
644 | 0 | if (blob.data == NULL) { |
645 | 0 | *str = NULL; |
646 | 0 | return NT_STATUS_OK; |
647 | 0 | } |
648 | | |
649 | 0 | if (blob.length == 0) { |
650 | 0 | char *s; |
651 | 0 | s = talloc_strdup(mem_ctx, ""); |
652 | 0 | NT_STATUS_HAVE_NO_MEMORY(s); |
653 | 0 | *str = s; |
654 | 0 | return NT_STATUS_OK; |
655 | 0 | } |
656 | | |
657 | 0 | ret = convert_string_talloc(mem_ctx, CH_UTF16, CH_UNIX, |
658 | 0 | blob.data, blob.length, &vstr, &converted_size); |
659 | 0 | data_blob_free(&blob); |
660 | 0 | (*str) = (char *)vstr; |
661 | 0 | if (!ret) { |
662 | 0 | return NT_STATUS_ILLEGAL_CHARACTER; |
663 | 0 | } |
664 | 0 | return NT_STATUS_OK; |
665 | 0 | } |
666 | | |
667 | | /* |
668 | | push a string in a uint16_t ofs/ uint16_t length/blob format |
669 | | UTF-16 without termination |
670 | | */ |
671 | | NTSTATUS smb2_push_o16s16_string(struct smb2_request_buffer *buf, |
672 | | uint16_t ofs, const char *str) |
673 | 0 | { |
674 | 0 | DATA_BLOB blob; |
675 | 0 | NTSTATUS status; |
676 | 0 | bool ret; |
677 | 0 | void *ptr = NULL; |
678 | |
|
679 | 0 | if (str == NULL) { |
680 | 0 | return smb2_push_o16s16_blob(buf, ofs, data_blob(NULL, 0)); |
681 | 0 | } |
682 | | |
683 | 0 | if (*str == 0) { |
684 | 0 | blob.data = discard_const_p(uint8_t, str); |
685 | 0 | blob.length = 0; |
686 | 0 | return smb2_push_o16s16_blob(buf, ofs, blob); |
687 | 0 | } |
688 | | |
689 | 0 | ret = convert_string_talloc(buf->buffer, CH_UNIX, CH_UTF16, |
690 | 0 | str, strlen(str), &ptr, &blob.length); |
691 | 0 | if (!ret) { |
692 | 0 | return NT_STATUS_ILLEGAL_CHARACTER; |
693 | 0 | } |
694 | 0 | blob.data = (uint8_t *)ptr; |
695 | |
|
696 | 0 | status = smb2_push_o16s16_blob(buf, ofs, blob); |
697 | 0 | data_blob_free(&blob); |
698 | 0 | return status; |
699 | 0 | } |
700 | | |
701 | | /* |
702 | | push a file handle into a buffer |
703 | | */ |
704 | | void smb2_push_handle(uint8_t *data, struct smb2_handle *h) |
705 | 0 | { |
706 | 0 | SBVAL(data, 0, h->data[0]); |
707 | 0 | SBVAL(data, 8, h->data[1]); |
708 | 0 | } |
709 | | |
710 | | /* |
711 | | pull a file handle from a buffer |
712 | | */ |
713 | | void smb2_pull_handle(uint8_t *ptr, struct smb2_handle *h) |
714 | 0 | { |
715 | 0 | h->data[0] = BVAL(ptr, 0); |
716 | 0 | h->data[1] = BVAL(ptr, 8); |
717 | 0 | } |