2 Unix SMB/CIFS implementation.
4 SMB2 client request handling
6 Copyright (C) Andrew Tridgell 2005
7 Copyright (C) Stefan Metzmacher 2005
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.
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.
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/>.
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"
30 /* fill in the bufinfo */
31 void smb2_setup_bufinfo(struct smb2_request *req)
33 req->in.bufinfo.mem_ctx = req;
34 req->in.bufinfo.flags = BUFINFO_FLAG_UNICODE | BUFINFO_FLAG_SMB2;
35 req->in.bufinfo.align_base = req->in.buffer;
36 if (req->in.dynamic) {
37 req->in.bufinfo.data = req->in.dynamic;
38 req->in.bufinfo.data_size = req->in.body_size - req->in.body_fixed;
40 req->in.bufinfo.data = NULL;
41 req->in.bufinfo.data_size = 0;
46 /* destroy a request structure */
47 static int smb2_request_destructor(struct smb2_request *req)
50 /* remove it from the list of pending requests (a null op if
51 its not in the list) */
52 DLIST_REMOVE(req->transport->pending_recv, req);
58 initialise a smb2 request
60 struct smb2_request *smb2_request_init(struct smb2_transport *transport, uint16_t opcode,
61 uint16_t body_fixed_size, bool body_dynamic_present,
62 uint32_t body_dynamic_size)
64 struct smb2_request *req;
68 bool compound = false;
70 if (body_dynamic_present) {
71 if (body_dynamic_size == 0) {
72 body_dynamic_size = 1;
75 body_dynamic_size = 0;
78 req = talloc(transport, struct smb2_request);
79 if (req == NULL) return NULL;
81 seqnum = transport->seqnum++;
82 if (seqnum == UINT64_MAX) {
83 seqnum = transport->seqnum++;
86 req->state = SMB2_REQUEST_INIT;
87 req->transport = transport;
91 req->status = NT_STATUS_OK;
93 req->next = req->prev = NULL;
95 ZERO_STRUCT(req->cancel);
98 if (transport->compound.missing > 0) {
100 transport->compound.missing -= 1;
101 req->out = transport->compound.buffer;
102 ZERO_STRUCT(transport->compound.buffer);
103 if (transport->compound.related) {
104 flags |= SMB2_HDR_FLAG_CHAINED;
107 ZERO_STRUCT(req->out);
110 if (req->out.size > 0) {
111 hdr_offset = req->out.size;
113 hdr_offset = NBT_HDR_SIZE;
116 req->out.size = hdr_offset + SMB2_HDR_BODY + body_fixed_size;
117 req->out.allocated = req->out.size + body_dynamic_size;
119 req->out.buffer = talloc_realloc(req, req->out.buffer,
120 uint8_t, req->out.allocated);
121 if (req->out.buffer == NULL) {
126 req->out.hdr = req->out.buffer + hdr_offset;
127 req->out.body = req->out.hdr + SMB2_HDR_BODY;
128 req->out.body_fixed= body_fixed_size;
129 req->out.body_size = body_fixed_size;
130 req->out.dynamic = (body_dynamic_size ? req->out.body + body_fixed_size : NULL);
132 SIVAL(req->out.hdr, 0, SMB2_MAGIC);
133 SSVAL(req->out.hdr, SMB2_HDR_LENGTH, SMB2_HDR_BODY);
134 SSVAL(req->out.hdr, SMB2_HDR_EPOCH, 0);
135 SIVAL(req->out.hdr, SMB2_HDR_STATUS, 0);
136 SSVAL(req->out.hdr, SMB2_HDR_OPCODE, opcode);
137 SSVAL(req->out.hdr, SMB2_HDR_CREDIT, transport->credits.ask_num);
138 SIVAL(req->out.hdr, SMB2_HDR_FLAGS, flags);
139 SIVAL(req->out.hdr, SMB2_HDR_NEXT_COMMAND, 0);
140 SBVAL(req->out.hdr, SMB2_HDR_MESSAGE_ID, req->seqnum);
141 SIVAL(req->out.hdr, SMB2_HDR_PID, 0);
142 SIVAL(req->out.hdr, SMB2_HDR_TID, 0);
143 SBVAL(req->out.hdr, SMB2_HDR_SESSION_ID, 0);
144 memset(req->out.hdr+SMB2_HDR_SIGNATURE, 0, 16);
146 /* set the length of the fixed body part and +1 if there's a dynamic part also */
147 SSVAL(req->out.body, 0, body_fixed_size + (body_dynamic_size?1:0));
150 * if we have a dynamic part, make sure the first byte
151 * which is always be part of the packet is initialized
153 if (body_dynamic_size && !compound) {
155 SCVAL(req->out.dynamic, 0, 0);
158 talloc_set_destructor(req, smb2_request_destructor);
164 initialise a smb2 request for tree operations
166 struct smb2_request *smb2_request_init_tree(struct smb2_tree *tree, uint16_t opcode,
167 uint16_t body_fixed_size, bool body_dynamic_present,
168 uint32_t body_dynamic_size)
170 struct smb2_request *req = smb2_request_init(tree->session->transport, opcode,
171 body_fixed_size, body_dynamic_present,
173 if (req == NULL) return NULL;
175 SBVAL(req->out.hdr, SMB2_HDR_SESSION_ID, tree->session->uid);
176 SIVAL(req->out.hdr, SMB2_HDR_TID, tree->tid);
177 req->session = tree->session;
183 /* destroy a request structure and return final status */
184 NTSTATUS smb2_request_destroy(struct smb2_request *req)
188 /* this is the error code we give the application for when a
189 _send() call fails completely */
190 if (!req) return NT_STATUS_UNSUCCESSFUL;
192 if (req->state == SMB2_REQUEST_ERROR &&
193 NT_STATUS_IS_OK(req->status)) {
194 status = NT_STATUS_INTERNAL_ERROR;
196 status = req->status;
204 receive a response to a packet
206 bool smb2_request_receive(struct smb2_request *req)
208 /* req can be NULL when a send has failed. This eliminates lots of NULL
209 checks in each module */
210 if (!req) return false;
212 /* keep receiving packets until this one is replied to */
213 while (req->state <= SMB2_REQUEST_RECV) {
214 if (event_loop_once(req->transport->socket->event.ctx) != 0) {
219 return req->state == SMB2_REQUEST_DONE;
222 /* Return true if the last packet was in error */
223 bool smb2_request_is_error(struct smb2_request *req)
225 return NT_STATUS_IS_ERR(req->status);
228 /* Return true if the last packet was OK */
229 bool smb2_request_is_ok(struct smb2_request *req)
231 return NT_STATUS_IS_OK(req->status);
235 check if a range in the reply body is out of bounds
237 bool smb2_oob(struct smb2_request_buffer *buf, const uint8_t *ptr, size_t size)
240 /* zero bytes is never out of range */
243 /* be careful with wraparound! */
244 if ((uintptr_t)ptr < (uintptr_t)buf->body ||
245 (uintptr_t)ptr >= (uintptr_t)buf->body + buf->body_size ||
246 size > buf->body_size ||
247 (uintptr_t)ptr + size > (uintptr_t)buf->body + buf->body_size) {
253 size_t smb2_padding_size(uint32_t offset, size_t n)
255 if ((offset & (n-1)) == 0) return 0;
256 return n - (offset & (n-1));
259 static size_t smb2_padding_fix(struct smb2_request_buffer *buf)
261 if (buf->dynamic == (buf->body + buf->body_fixed)) {
262 if (buf->dynamic != (buf->buffer + buf->size)) {
270 grow a SMB2 buffer by the specified amount
272 NTSTATUS smb2_grow_buffer(struct smb2_request_buffer *buf, size_t increase)
277 uint32_t newsize = buf->size + increase;
279 /* a packet size should be limited a bit */
280 if (newsize >= 0x00FFFFFF) return NT_STATUS_MARSHALL_OVERFLOW;
282 if (newsize <= buf->allocated) return NT_STATUS_OK;
284 hdr_ofs = buf->hdr - buf->buffer;
285 dynamic_ofs = buf->dynamic - buf->buffer;
287 buffer_ptr = talloc_realloc(buf, buf->buffer, uint8_t, newsize);
288 NT_STATUS_HAVE_NO_MEMORY(buffer_ptr);
290 buf->buffer = buffer_ptr;
291 buf->hdr = buf->buffer + hdr_ofs;
292 buf->body = buf->hdr + SMB2_HDR_BODY;
293 buf->dynamic = buf->buffer + dynamic_ofs;
294 buf->allocated = newsize;
300 pull a uint16_t ofs/ uint16_t length/blob triple from a data blob
301 the ptr points to the start of the offset/length pair
303 NTSTATUS smb2_pull_o16s16_blob(struct smb2_request_buffer *buf, TALLOC_CTX *mem_ctx, uint8_t *ptr, DATA_BLOB *blob)
306 if (smb2_oob(buf, ptr, 4)) {
307 return NT_STATUS_INVALID_PARAMETER;
312 *blob = data_blob(NULL, 0);
315 if (smb2_oob(buf, buf->hdr + ofs, size)) {
316 return NT_STATUS_INVALID_PARAMETER;
318 *blob = data_blob_talloc(mem_ctx, buf->hdr + ofs, size);
319 NT_STATUS_HAVE_NO_MEMORY(blob->data);
324 push a uint16_t ofs/ uint16_t length/blob triple into a data blob
325 the ofs points to the start of the offset/length pair, and is relative
328 NTSTATUS smb2_push_o16s16_blob(struct smb2_request_buffer *buf,
329 uint16_t ofs, DATA_BLOB blob)
333 size_t padding_length;
335 uint8_t *ptr = buf->body+ofs;
337 if (buf->dynamic == NULL) {
338 return NT_STATUS_INVALID_PARAMETER;
341 /* we have only 16 bit for the size */
342 if (blob.length > 0xFFFF) {
343 return NT_STATUS_INVALID_PARAMETER;
346 /* check if there're enough room for ofs and size */
347 if (smb2_oob(buf, ptr, 4)) {
348 return NT_STATUS_INVALID_PARAMETER;
351 if (blob.data == NULL) {
352 if (blob.length != 0) {
353 return NT_STATUS_INTERNAL_ERROR;
360 offset = buf->dynamic - buf->hdr;
361 padding_length = smb2_padding_size(offset, 2);
362 offset += padding_length;
363 padding_fix = smb2_padding_fix(buf);
365 SSVAL(ptr, 0, offset);
366 SSVAL(ptr, 2, blob.length);
368 status = smb2_grow_buffer(buf, blob.length + padding_length - padding_fix);
369 NT_STATUS_NOT_OK_RETURN(status);
371 memset(buf->dynamic, 0, padding_length);
372 buf->dynamic += padding_length;
374 memcpy(buf->dynamic, blob.data, blob.length);
375 buf->dynamic += blob.length;
377 buf->size += blob.length + padding_length - padding_fix;
378 buf->body_size += blob.length + padding_length;
385 push a uint16_t ofs/ uint32_t length/blob triple into a data blob
386 the ofs points to the start of the offset/length pair, and is relative
389 NTSTATUS smb2_push_o16s32_blob(struct smb2_request_buffer *buf,
390 uint16_t ofs, DATA_BLOB blob)
394 size_t padding_length;
396 uint8_t *ptr = buf->body+ofs;
398 if (buf->dynamic == NULL) {
399 return NT_STATUS_INVALID_PARAMETER;
402 /* check if there're enough room for ofs and size */
403 if (smb2_oob(buf, ptr, 6)) {
404 return NT_STATUS_INVALID_PARAMETER;
407 if (blob.data == NULL) {
408 if (blob.length != 0) {
409 return NT_STATUS_INTERNAL_ERROR;
416 offset = buf->dynamic - buf->hdr;
417 padding_length = smb2_padding_size(offset, 2);
418 offset += padding_length;
419 padding_fix = smb2_padding_fix(buf);
421 SSVAL(ptr, 0, offset);
422 SIVAL(ptr, 2, blob.length);
424 status = smb2_grow_buffer(buf, blob.length + padding_length - padding_fix);
425 NT_STATUS_NOT_OK_RETURN(status);
427 memset(buf->dynamic, 0, padding_length);
428 buf->dynamic += padding_length;
430 memcpy(buf->dynamic, blob.data, blob.length);
431 buf->dynamic += blob.length;
433 buf->size += blob.length + padding_length - padding_fix;
434 buf->body_size += blob.length + padding_length;
441 push a uint32_t ofs/ uint32_t length/blob triple into a data blob
442 the ofs points to the start of the offset/length pair, and is relative
445 NTSTATUS smb2_push_o32s32_blob(struct smb2_request_buffer *buf,
446 uint32_t ofs, DATA_BLOB blob)
450 size_t padding_length;
452 uint8_t *ptr = buf->body+ofs;
454 if (buf->dynamic == NULL) {
455 return NT_STATUS_INVALID_PARAMETER;
458 /* check if there're enough room for ofs and size */
459 if (smb2_oob(buf, ptr, 8)) {
460 return NT_STATUS_INVALID_PARAMETER;
463 if (blob.data == NULL) {
464 if (blob.length != 0) {
465 return NT_STATUS_INTERNAL_ERROR;
472 offset = buf->dynamic - buf->hdr;
473 padding_length = smb2_padding_size(offset, 8);
474 offset += padding_length;
475 padding_fix = smb2_padding_fix(buf);
477 SIVAL(ptr, 0, offset);
478 SIVAL(ptr, 4, blob.length);
480 status = smb2_grow_buffer(buf, blob.length + padding_length - padding_fix);
481 NT_STATUS_NOT_OK_RETURN(status);
483 memset(buf->dynamic, 0, padding_length);
484 buf->dynamic += padding_length;
486 memcpy(buf->dynamic, blob.data, blob.length);
487 buf->dynamic += blob.length;
489 buf->size += blob.length + padding_length - padding_fix;
490 buf->body_size += blob.length + padding_length;
497 push a uint32_t length/ uint32_t ofs/blob triple into a data blob
498 the ofs points to the start of the length/offset pair, and is relative
501 NTSTATUS smb2_push_s32o32_blob(struct smb2_request_buffer *buf,
502 uint32_t ofs, DATA_BLOB blob)
506 size_t padding_length;
508 uint8_t *ptr = buf->body+ofs;
510 if (buf->dynamic == NULL) {
511 return NT_STATUS_INVALID_PARAMETER;
514 /* check if there're enough room for ofs and size */
515 if (smb2_oob(buf, ptr, 8)) {
516 return NT_STATUS_INVALID_PARAMETER;
519 if (blob.data == NULL) {
520 if (blob.length != 0) {
521 return NT_STATUS_INTERNAL_ERROR;
528 offset = buf->dynamic - buf->hdr;
529 padding_length = smb2_padding_size(offset, 8);
530 offset += padding_length;
531 padding_fix = smb2_padding_fix(buf);
533 SIVAL(ptr, 0, blob.length);
534 SIVAL(ptr, 4, offset);
536 status = smb2_grow_buffer(buf, blob.length + padding_length - padding_fix);
537 NT_STATUS_NOT_OK_RETURN(status);
539 memset(buf->dynamic, 0, padding_length);
540 buf->dynamic += padding_length;
542 memcpy(buf->dynamic, blob.data, blob.length);
543 buf->dynamic += blob.length;
545 buf->size += blob.length + padding_length - padding_fix;
546 buf->body_size += blob.length + padding_length;
552 pull a uint16_t ofs/ uint32_t length/blob triple from a data blob
553 the ptr points to the start of the offset/length pair
555 NTSTATUS smb2_pull_o16s32_blob(struct smb2_request_buffer *buf, TALLOC_CTX *mem_ctx, uint8_t *ptr, DATA_BLOB *blob)
560 if (smb2_oob(buf, ptr, 6)) {
561 return NT_STATUS_INVALID_PARAMETER;
566 *blob = data_blob(NULL, 0);
569 if (smb2_oob(buf, buf->hdr + ofs, size)) {
570 return NT_STATUS_INVALID_PARAMETER;
572 *blob = data_blob_talloc(mem_ctx, buf->hdr + ofs, size);
573 NT_STATUS_HAVE_NO_MEMORY(blob->data);
578 pull a uint32_t ofs/ uint32_t length/blob triple from a data blob
579 the ptr points to the start of the offset/length pair
581 NTSTATUS smb2_pull_o32s32_blob(struct smb2_request_buffer *buf, TALLOC_CTX *mem_ctx, uint8_t *ptr, DATA_BLOB *blob)
584 if (smb2_oob(buf, ptr, 8)) {
585 return NT_STATUS_INVALID_PARAMETER;
590 *blob = data_blob(NULL, 0);
593 if (smb2_oob(buf, buf->hdr + ofs, size)) {
594 return NT_STATUS_INVALID_PARAMETER;
596 *blob = data_blob_talloc(mem_ctx, buf->hdr + ofs, size);
597 NT_STATUS_HAVE_NO_MEMORY(blob->data);
602 pull a uint16_t ofs/ uint32_t length/blob triple from a data blob
603 the ptr points to the start of the offset/length pair
605 In this varient the uint16_t is padded by an extra 2 bytes, making
606 the size aligned on 4 byte boundary
608 NTSTATUS smb2_pull_o16As32_blob(struct smb2_request_buffer *buf, TALLOC_CTX *mem_ctx, uint8_t *ptr, DATA_BLOB *blob)
611 if (smb2_oob(buf, ptr, 8)) {
612 return NT_STATUS_INVALID_PARAMETER;
617 *blob = data_blob(NULL, 0);
620 if (smb2_oob(buf, buf->hdr + ofs, size)) {
621 return NT_STATUS_INVALID_PARAMETER;
623 *blob = data_blob_talloc(mem_ctx, buf->hdr + ofs, size);
624 NT_STATUS_HAVE_NO_MEMORY(blob->data);
629 pull a uint32_t length/ uint32_t ofs/blob triple from a data blob
630 the ptr points to the start of the offset/length pair
632 NTSTATUS smb2_pull_s32o32_blob(struct smb2_request_buffer *buf, TALLOC_CTX *mem_ctx, uint8_t *ptr, DATA_BLOB *blob)
635 if (smb2_oob(buf, ptr, 8)) {
636 return NT_STATUS_INVALID_PARAMETER;
641 *blob = data_blob(NULL, 0);
644 if (smb2_oob(buf, buf->hdr + ofs, size)) {
645 return NT_STATUS_INVALID_PARAMETER;
647 *blob = data_blob_talloc(mem_ctx, buf->hdr + ofs, size);
648 NT_STATUS_HAVE_NO_MEMORY(blob->data);
653 pull a uint32_t length/ uint16_t ofs/blob triple from a data blob
654 the ptr points to the start of the offset/length pair
656 NTSTATUS smb2_pull_s32o16_blob(struct smb2_request_buffer *buf, TALLOC_CTX *mem_ctx, uint8_t *ptr, DATA_BLOB *blob)
659 if (smb2_oob(buf, ptr, 8)) {
660 return NT_STATUS_INVALID_PARAMETER;
665 *blob = data_blob(NULL, 0);
668 if (smb2_oob(buf, buf->hdr + ofs, size)) {
669 return NT_STATUS_INVALID_PARAMETER;
671 *blob = data_blob_talloc(mem_ctx, buf->hdr + ofs, size);
672 NT_STATUS_HAVE_NO_MEMORY(blob->data);
677 pull a string in a uint16_t ofs/ uint16_t length/blob format
678 UTF-16 without termination
680 NTSTATUS smb2_pull_o16s16_string(struct smb2_request_buffer *buf, TALLOC_CTX *mem_ctx,
681 uint8_t *ptr, const char **str)
688 status = smb2_pull_o16s16_blob(buf, mem_ctx, ptr, &blob);
689 NT_STATUS_NOT_OK_RETURN(status);
691 if (blob.data == NULL) {
696 if (blob.length == 0) {
698 s = talloc_strdup(mem_ctx, "");
699 NT_STATUS_HAVE_NO_MEMORY(s);
704 ret = convert_string_talloc(mem_ctx, CH_UTF16, CH_UNIX,
705 blob.data, blob.length, &vstr, NULL, false);
706 data_blob_free(&blob);
707 (*str) = (char *)vstr;
709 return NT_STATUS_ILLEGAL_CHARACTER;
715 push a string in a uint16_t ofs/ uint16_t length/blob format
716 UTF-16 without termination
718 NTSTATUS smb2_push_o16s16_string(struct smb2_request_buffer *buf,
719 uint16_t ofs, const char *str)
726 return smb2_push_o16s16_blob(buf, ofs, data_blob(NULL, 0));
730 blob.data = discard_const_p(uint8_t, str);
732 return smb2_push_o16s16_blob(buf, ofs, blob);
735 ret = convert_string_talloc(buf->buffer, CH_UNIX, CH_UTF16,
736 str, strlen(str), (void **)&blob.data, &blob.length,
739 return NT_STATUS_ILLEGAL_CHARACTER;
742 status = smb2_push_o16s16_blob(buf, ofs, blob);
743 data_blob_free(&blob);
748 push a file handle into a buffer
750 void smb2_push_handle(uint8_t *data, struct smb2_handle *h)
752 SBVAL(data, 0, h->data[0]);
753 SBVAL(data, 8, h->data[1]);
757 pull a file handle from a buffer
759 void smb2_pull_handle(uint8_t *ptr, struct smb2_handle *h)
761 h->data[0] = BVAL(ptr, 0);
762 h->data[1] = BVAL(ptr, 8);