charcnv: removed the allow_badcharcnv and allow_bad_conv options to convert_string*()
[samba.git] / source4 / libcli / smb2 / request.c
index 1de0531d9a947c38adf7b1ffe7a6983d4150c023..bf96d05bea05284edac6ffd71094e60addd135ff 100644 (file)
 #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)
@@ -43,6 +42,18 @@ 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
 */
@@ -52,6 +63,9 @@ struct smb2_request *smb2_request_init(struct smb2_transport *transport, uint16_
 {
        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) {
@@ -64,9 +78,11 @@ struct smb2_request *smb2_request_init(struct smb2_transport *transport, uint16_
        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;
@@ -81,16 +97,35 @@ struct smb2_request *smb2_request_init(struct smb2_transport *transport, uint16_
        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;
@@ -98,13 +133,13 @@ struct smb2_request *smb2_request_init(struct smb2_transport *transport, uint16_
 
        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);
@@ -117,11 +152,13 @@ struct smb2_request *smb2_request_init(struct smb2_transport *transport, uint16_
         * 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;
 }
 
@@ -138,6 +175,7 @@ struct smb2_request *smb2_request_init_tree(struct smb2_tree *tree, uint16_t opc
        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;
@@ -154,18 +192,13 @@ NTSTATUS smb2_request_destroy(struct smb2_request *req)
           _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;
 }
@@ -211,10 +244,10 @@ bool smb2_oob(struct smb2_request_buffer *buf, const uint8_t *ptr, size_t size)
                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;
@@ -229,7 +262,9 @@ size_t smb2_padding_size(uint32_t offset, size_t n)
 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;
 }
@@ -237,8 +272,9 @@ static size_t smb2_padding_fix(struct smb2_request_buffer *buf)
 /*
   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;
@@ -248,13 +284,14 @@ static NTSTATUS smb2_grow_buffer(struct smb2_request_buffer *buf, size_t increas
 
        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;
@@ -270,7 +307,7 @@ NTSTATUS smb2_pull_o16s16_blob(struct smb2_request_buffer *buf, TALLOC_CTX *mem_
 {
        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);
@@ -279,7 +316,7 @@ NTSTATUS smb2_pull_o16s16_blob(struct smb2_request_buffer *buf, TALLOC_CTX *mem_
                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);
@@ -306,12 +343,12 @@ NTSTATUS smb2_push_o16s16_blob(struct smb2_request_buffer *buf,
 
        /* 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) {
@@ -367,7 +404,7 @@ NTSTATUS smb2_push_o16s32_blob(struct smb2_request_buffer *buf,
 
        /* 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) {
@@ -423,7 +460,7 @@ NTSTATUS smb2_push_o32s32_blob(struct smb2_request_buffer *buf,
 
        /* 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) {
@@ -479,7 +516,7 @@ NTSTATUS smb2_push_s32o32_blob(struct smb2_request_buffer *buf,
 
        /* 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) {
@@ -524,7 +561,7 @@ NTSTATUS smb2_pull_o16s32_blob(struct smb2_request_buffer *buf, TALLOC_CTX *mem_
        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);
@@ -533,7 +570,7 @@ NTSTATUS smb2_pull_o16s32_blob(struct smb2_request_buffer *buf, TALLOC_CTX *mem_
                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);
@@ -548,7 +585,7 @@ NTSTATUS smb2_pull_o32s32_blob(struct smb2_request_buffer *buf, TALLOC_CTX *mem_
 {
        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);
@@ -557,7 +594,7 @@ NTSTATUS smb2_pull_o32s32_blob(struct smb2_request_buffer *buf, TALLOC_CTX *mem_
                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);
@@ -575,7 +612,7 @@ NTSTATUS smb2_pull_o16As32_blob(struct smb2_request_buffer *buf, TALLOC_CTX *mem
 {
        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);
@@ -584,7 +621,7 @@ NTSTATUS smb2_pull_o16As32_blob(struct smb2_request_buffer *buf, TALLOC_CTX *mem
                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);
@@ -599,7 +636,7 @@ NTSTATUS smb2_pull_s32o32_blob(struct smb2_request_buffer *buf, TALLOC_CTX *mem_
 {
        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);
@@ -608,7 +645,31 @@ NTSTATUS smb2_pull_s32o32_blob(struct smb2_request_buffer *buf, TALLOC_CTX *mem_
                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);
@@ -624,8 +685,8 @@ NTSTATUS smb2_pull_o16s16_string(struct smb2_request_buffer *buf, TALLOC_CTX *me
 {
        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);
@@ -643,11 +704,11 @@ NTSTATUS smb2_pull_o16s16_string(struct smb2_request_buffer *buf, TALLOC_CTX *me
                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;
@@ -662,18 +723,23 @@ NTSTATUS smb2_push_o16s16_string(struct smb2_request_buffer *buf,
 {
        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);