#include "includes.h"
#include "libcli/raw/libcliraw.h"
#include "libcli/smb2/smb2.h"
-#include "lib/util/dlinklist.h"
+#include "../lib/util/dlinklist.h"
#include "lib/events/events.h"
#include "libcli/smb2/smb2_calls.h"
-#include "param/param.h"
/* fill in the bufinfo */
void smb2_setup_bufinfo(struct smb2_request *req)
}
}
+
+/* destroy a request structure */
+static int smb2_request_destructor(struct smb2_request *req)
+{
+ if (req->transport) {
+ /* remove it from the list of pending requests (a null op if
+ its not in the list) */
+ DLIST_REMOVE(req->transport->pending_recv, req);
+ }
+ return 0;
+}
+
/*
initialise a smb2 request
*/
{
struct smb2_request *req;
uint64_t seqnum;
+ uint32_t hdr_offset;
+ uint32_t flags = 0;
+ bool compound = false;
if (body_dynamic_present) {
if (body_dynamic_size == 0) {
req = talloc(transport, struct smb2_request);
if (req == NULL) return NULL;
- seqnum = transport->seqnum++;
- if (seqnum == UINT64_MAX) {
- seqnum = transport->seqnum++;
+ seqnum = transport->seqnum;
+ if (transport->credits.charge > 0) {
+ transport->seqnum += transport->credits.charge;
+ } else {
+ transport->seqnum += 1;
}
req->state = SMB2_REQUEST_INIT;
ZERO_STRUCT(req->cancel);
ZERO_STRUCT(req->in);
- req->out.size = SMB2_HDR_BODY+NBT_HDR_SIZE+body_fixed_size;
+ if (transport->compound.missing > 0) {
+ compound = true;
+ transport->compound.missing -= 1;
+ req->out = transport->compound.buffer;
+ ZERO_STRUCT(transport->compound.buffer);
+ if (transport->compound.related) {
+ flags |= SMB2_HDR_FLAG_CHAINED;
+ }
+ } else {
+ ZERO_STRUCT(req->out);
+ }
+
+ if (req->out.size > 0) {
+ hdr_offset = req->out.size;
+ } else {
+ hdr_offset = NBT_HDR_SIZE;
+ }
+ req->out.size = hdr_offset + SMB2_HDR_BODY + body_fixed_size;
req->out.allocated = req->out.size + body_dynamic_size;
- req->out.buffer = talloc_array(req, uint8_t, req->out.allocated);
+
+ req->out.buffer = talloc_realloc(req, req->out.buffer,
+ uint8_t, req->out.allocated);
if (req->out.buffer == NULL) {
talloc_free(req);
return NULL;
}
- req->out.hdr = req->out.buffer + NBT_HDR_SIZE;
+ req->out.hdr = req->out.buffer + hdr_offset;
req->out.body = req->out.hdr + SMB2_HDR_BODY;
req->out.body_fixed= body_fixed_size;
req->out.body_size = body_fixed_size;
SIVAL(req->out.hdr, 0, SMB2_MAGIC);
SSVAL(req->out.hdr, SMB2_HDR_LENGTH, SMB2_HDR_BODY);
- SSVAL(req->out.hdr, SMB2_HDR_EPOCH, 0);
+ SSVAL(req->out.hdr, SMB2_HDR_EPOCH, transport->credits.charge);
SIVAL(req->out.hdr, SMB2_HDR_STATUS, 0);
SSVAL(req->out.hdr, SMB2_HDR_OPCODE, opcode);
- SSVAL(req->out.hdr, SMB2_HDR_CREDIT, 0);
- SIVAL(req->out.hdr, SMB2_HDR_FLAGS, 0);
+ SSVAL(req->out.hdr, SMB2_HDR_CREDIT, transport->credits.ask_num);
+ SIVAL(req->out.hdr, SMB2_HDR_FLAGS, flags);
SIVAL(req->out.hdr, SMB2_HDR_NEXT_COMMAND, 0);
- SBVAL(req->out.hdr, SMB2_HDR_MESSAGE_ID, req->seqnum);
+ SBVAL(req->out.hdr, SMB2_HDR_MESSAGE_ID, req->seqnum);
SIVAL(req->out.hdr, SMB2_HDR_PID, 0);
SIVAL(req->out.hdr, SMB2_HDR_TID, 0);
SBVAL(req->out.hdr, SMB2_HDR_SESSION_ID, 0);
* if we have a dynamic part, make sure the first byte
* which is always be part of the packet is initialized
*/
- if (body_dynamic_size) {
+ if (body_dynamic_size && !compound) {
req->out.size += 1;
SCVAL(req->out.dynamic, 0, 0);
}
+ talloc_set_destructor(req, smb2_request_destructor);
+
return req;
}
if (req == NULL) return NULL;
SBVAL(req->out.hdr, SMB2_HDR_SESSION_ID, tree->session->uid);
+ SIVAL(req->out.hdr, SMB2_HDR_PID, tree->session->pid);
SIVAL(req->out.hdr, SMB2_HDR_TID, tree->tid);
req->session = tree->session;
req->tree = tree;
_send() call fails completely */
if (!req) return NT_STATUS_UNSUCCESSFUL;
- if (req->transport) {
- /* remove it from the list of pending requests (a null op if
- its not in the list) */
- DLIST_REMOVE(req->transport->pending_recv, req);
- }
-
if (req->state == SMB2_REQUEST_ERROR &&
NT_STATUS_IS_OK(req->status)) {
- req->status = NT_STATUS_INTERNAL_ERROR;
+ status = NT_STATUS_INTERNAL_ERROR;
+ } else {
+ status = req->status;
}
- status = req->status;
talloc_free(req);
return status;
}
return false;
}
/* be careful with wraparound! */
- if (ptr < buf->body ||
- ptr >= buf->body + buf->body_size ||
+ if ((uintptr_t)ptr < (uintptr_t)buf->body ||
+ (uintptr_t)ptr >= (uintptr_t)buf->body + buf->body_size ||
size > buf->body_size ||
- ptr + size > buf->body + buf->body_size) {
+ (uintptr_t)ptr + size > (uintptr_t)buf->body + buf->body_size) {
return true;
}
return false;
static size_t smb2_padding_fix(struct smb2_request_buffer *buf)
{
if (buf->dynamic == (buf->body + buf->body_fixed)) {
- return 1;
+ if (buf->dynamic != (buf->buffer + buf->size)) {
+ return 1;
+ }
}
return 0;
}
/*
grow a SMB2 buffer by the specified amount
*/
-static NTSTATUS smb2_grow_buffer(struct smb2_request_buffer *buf, size_t increase)
+NTSTATUS smb2_grow_buffer(struct smb2_request_buffer *buf, size_t increase)
{
+ size_t hdr_ofs;
size_t dynamic_ofs;
uint8_t *buffer_ptr;
uint32_t newsize = buf->size + increase;
if (newsize <= buf->allocated) return NT_STATUS_OK;
+ hdr_ofs = buf->hdr - buf->buffer;
dynamic_ofs = buf->dynamic - buf->buffer;
buffer_ptr = talloc_realloc(buf, buf->buffer, uint8_t, newsize);
NT_STATUS_HAVE_NO_MEMORY(buffer_ptr);
buf->buffer = buffer_ptr;
- buf->hdr = buf->buffer + NBT_HDR_SIZE;
+ buf->hdr = buf->buffer + hdr_ofs;
buf->body = buf->hdr + SMB2_HDR_BODY;
buf->dynamic = buf->buffer + dynamic_ofs;
buf->allocated = newsize;
{
uint16_t ofs, size;
if (smb2_oob(buf, ptr, 4)) {
- return NT_STATUS_BUFFER_TOO_SMALL;
+ return NT_STATUS_INVALID_PARAMETER;
}
ofs = SVAL(ptr, 0);
size = SVAL(ptr, 2);
return NT_STATUS_OK;
}
if (smb2_oob(buf, buf->hdr + ofs, size)) {
- return NT_STATUS_BUFFER_TOO_SMALL;
+ return NT_STATUS_INVALID_PARAMETER;
}
*blob = data_blob_talloc(mem_ctx, buf->hdr + ofs, size);
NT_STATUS_HAVE_NO_MEMORY(blob->data);
/* we have only 16 bit for the size */
if (blob.length > 0xFFFF) {
- return NT_STATUS_BUFFER_TOO_SMALL;
+ return NT_STATUS_INVALID_PARAMETER;
}
/* check if there're enough room for ofs and size */
if (smb2_oob(buf, ptr, 4)) {
- return NT_STATUS_BUFFER_TOO_SMALL;
+ return NT_STATUS_INVALID_PARAMETER;
}
if (blob.data == NULL) {
/* check if there're enough room for ofs and size */
if (smb2_oob(buf, ptr, 6)) {
- return NT_STATUS_BUFFER_TOO_SMALL;
+ return NT_STATUS_INVALID_PARAMETER;
}
if (blob.data == NULL) {
/* check if there're enough room for ofs and size */
if (smb2_oob(buf, ptr, 8)) {
- return NT_STATUS_BUFFER_TOO_SMALL;
+ return NT_STATUS_INVALID_PARAMETER;
}
if (blob.data == NULL) {
/* check if there're enough room for ofs and size */
if (smb2_oob(buf, ptr, 8)) {
- return NT_STATUS_BUFFER_TOO_SMALL;
+ return NT_STATUS_INVALID_PARAMETER;
}
if (blob.data == NULL) {
uint32_t size;
if (smb2_oob(buf, ptr, 6)) {
- return NT_STATUS_BUFFER_TOO_SMALL;
+ return NT_STATUS_INVALID_PARAMETER;
}
ofs = SVAL(ptr, 0);
size = IVAL(ptr, 2);
return NT_STATUS_OK;
}
if (smb2_oob(buf, buf->hdr + ofs, size)) {
- return NT_STATUS_BUFFER_TOO_SMALL;
+ return NT_STATUS_INVALID_PARAMETER;
}
*blob = data_blob_talloc(mem_ctx, buf->hdr + ofs, size);
NT_STATUS_HAVE_NO_MEMORY(blob->data);
{
uint32_t ofs, size;
if (smb2_oob(buf, ptr, 8)) {
- return NT_STATUS_BUFFER_TOO_SMALL;
+ return NT_STATUS_INVALID_PARAMETER;
}
ofs = IVAL(ptr, 0);
size = IVAL(ptr, 4);
return NT_STATUS_OK;
}
if (smb2_oob(buf, buf->hdr + ofs, size)) {
- return NT_STATUS_BUFFER_TOO_SMALL;
+ return NT_STATUS_INVALID_PARAMETER;
}
*blob = data_blob_talloc(mem_ctx, buf->hdr + ofs, size);
NT_STATUS_HAVE_NO_MEMORY(blob->data);
{
uint32_t ofs, size;
if (smb2_oob(buf, ptr, 8)) {
- return NT_STATUS_BUFFER_TOO_SMALL;
+ return NT_STATUS_INVALID_PARAMETER;
}
ofs = SVAL(ptr, 0);
size = IVAL(ptr, 4);
return NT_STATUS_OK;
}
if (smb2_oob(buf, buf->hdr + ofs, size)) {
- return NT_STATUS_BUFFER_TOO_SMALL;
+ return NT_STATUS_INVALID_PARAMETER;
}
*blob = data_blob_talloc(mem_ctx, buf->hdr + ofs, size);
NT_STATUS_HAVE_NO_MEMORY(blob->data);
{
uint32_t ofs, size;
if (smb2_oob(buf, ptr, 8)) {
- return NT_STATUS_BUFFER_TOO_SMALL;
+ return NT_STATUS_INVALID_PARAMETER;
}
size = IVAL(ptr, 0);
ofs = IVAL(ptr, 4);
return NT_STATUS_OK;
}
if (smb2_oob(buf, buf->hdr + ofs, size)) {
- return NT_STATUS_BUFFER_TOO_SMALL;
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ *blob = data_blob_talloc(mem_ctx, buf->hdr + ofs, size);
+ NT_STATUS_HAVE_NO_MEMORY(blob->data);
+ return NT_STATUS_OK;
+}
+
+/*
+ pull a uint32_t length/ uint16_t ofs/blob triple from a data blob
+ the ptr points to the start of the offset/length pair
+*/
+NTSTATUS smb2_pull_s32o16_blob(struct smb2_request_buffer *buf, TALLOC_CTX *mem_ctx, uint8_t *ptr, DATA_BLOB *blob)
+{
+ uint32_t ofs, size;
+ if (smb2_oob(buf, ptr, 8)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ size = IVAL(ptr, 0);
+ ofs = SVAL(ptr, 4);
+ if (ofs == 0) {
+ *blob = data_blob(NULL, 0);
+ return NT_STATUS_OK;
+ }
+ if (smb2_oob(buf, buf->hdr + ofs, size)) {
+ return NT_STATUS_INVALID_PARAMETER;
}
*blob = data_blob_talloc(mem_ctx, buf->hdr + ofs, size);
NT_STATUS_HAVE_NO_MEMORY(blob->data);
{
DATA_BLOB blob;
NTSTATUS status;
- ssize_t size;
void *vstr;
+ bool ret;
status = smb2_pull_o16s16_blob(buf, mem_ctx, ptr, &blob);
NT_STATUS_NOT_OK_RETURN(status);
return NT_STATUS_OK;
}
- size = convert_string_talloc(mem_ctx, lp_iconv_convenience(global_loadparm), CH_UTF16, CH_UNIX,
- blob.data, blob.length, &vstr);
+ ret = convert_string_talloc(mem_ctx, CH_UTF16, CH_UNIX,
+ blob.data, blob.length, &vstr, NULL);
data_blob_free(&blob);
(*str) = (char *)vstr;
- if (size == -1) {
+ if (!ret) {
return NT_STATUS_ILLEGAL_CHARACTER;
}
return NT_STATUS_OK;
{
DATA_BLOB blob;
NTSTATUS status;
- ssize_t size;
+ bool ret;
if (str == NULL) {
return smb2_push_o16s16_blob(buf, ofs, data_blob(NULL, 0));
}
- size = convert_string_talloc(buf->buffer, lp_iconv_convenience(global_loadparm), CH_UNIX, CH_UTF16,
- str, strlen(str), (void **)&blob.data);
- if (size == -1) {
+ if (*str == 0) {
+ blob.data = discard_const_p(uint8_t, str);
+ blob.length = 0;
+ return smb2_push_o16s16_blob(buf, ofs, blob);
+ }
+
+ ret = convert_string_talloc(buf->buffer, CH_UNIX, CH_UTF16,
+ str, strlen(str), (void **)&blob.data, &blob.length);
+ if (!ret) {
return NT_STATUS_ILLEGAL_CHARACTER;
}
- blob.length = size;
status = smb2_push_o16s16_blob(buf, ofs, blob);
data_blob_free(&blob);