libcli/smb: split out smb2cli_ioctl_parse_buffer()
authorStefan Metzmacher <metze@samba.org>
Thu, 14 Jan 2021 16:27:21 +0000 (17:27 +0100)
committerKarolin Seeger <kseeger@samba.org>
Mon, 18 Jan 2021 07:57:14 +0000 (07:57 +0000)
BUG: https://bugzilla.samba.org/show_bug.cgi?id=14607

Pair-Programmed-With: Volker Lendecke <vl@samba.org>

Signed-off-by: Stefan Metzmacher <metze@samba.org>
Signed-off-by: Volker Lendecke <vl@samba.org>
(cherry picked from commit 508ed5b42c23f8b3d9730d838bd921cb73c61358)

libcli/smb/smb2cli_ioctl.c

index 2b572baeb2381a038a0d1d9a54efdd68ad246bbe..6368bd31bc2bd3145f048a338f859927f3db63bc 100644 (file)
@@ -160,6 +160,80 @@ struct tevent_req *smb2cli_ioctl_send(TALLOC_CTX *mem_ctx,
        return req;
 }
 
+static NTSTATUS smb2cli_ioctl_parse_buffer(uint32_t dyn_offset,
+                                          const DATA_BLOB dyn_buffer,
+                                          uint32_t min_offset,
+                                          uint32_t buffer_offset,
+                                          uint32_t buffer_length,
+                                          uint32_t max_length,
+                                          uint32_t *next_offset,
+                                          DATA_BLOB *buffer)
+{
+       uint32_t offset;
+       bool oob;
+
+       *buffer = data_blob_null;
+       *next_offset = dyn_offset;
+
+       if (buffer_offset == 0) {
+               /*
+                * If the offset is 0, we better ignore
+                * the buffer_length field.
+                */
+               return NT_STATUS_OK;
+       }
+
+       if (buffer_length == 0) {
+               /*
+                * If the length is 0, we better ignore
+                * the buffer_offset field.
+                */
+               return NT_STATUS_OK;
+       }
+
+       SMB_ASSERT(min_offset >= dyn_offset);
+       if (buffer_offset != min_offset) {
+               return NT_STATUS_INVALID_NETWORK_RESPONSE;
+       }
+
+       /*
+        * Make [input|output]_buffer_offset relative to "dyn_buffer"
+        */
+       offset = buffer_offset - dyn_offset;
+       oob = smb_buffer_oob(dyn_buffer.length, offset, buffer_length);
+       if (oob) {
+               return NT_STATUS_INVALID_NETWORK_RESPONSE;
+       }
+
+       /*
+        * Give the caller a hint what we consumed,
+        * the caller may need to add possible padding.
+        */
+       *next_offset = buffer_offset + buffer_length;
+
+       if (max_length == 0) {
+               /*
+                * If max_input_length is 0 we ignore the
+                * input_buffer_length, because Windows 2008 echos the
+                * DCERPC request from the requested input_buffer to
+                * the response input_buffer.
+                *
+                * We just use the same logic also for max_output_length...
+                */
+               buffer_length = 0;
+       }
+
+       if (buffer_length > max_length) {
+               return NT_STATUS_INVALID_NETWORK_RESPONSE;
+       }
+
+       *buffer = (DATA_BLOB) {
+               .data = dyn_buffer.data + offset,
+               .length = buffer_length,
+       };
+       return NT_STATUS_OK;
+}
+
 static void smb2cli_ioctl_done(struct tevent_req *subreq)
 {
        struct tevent_req *req =
@@ -169,15 +243,19 @@ static void smb2cli_ioctl_done(struct tevent_req *subreq)
                tevent_req_data(req,
                struct smb2cli_ioctl_state);
        NTSTATUS status;
+       NTSTATUS error;
        struct iovec *iov;
        uint8_t *fixed;
-       uint8_t *dyn;
-       size_t dyn_len;
+       DATA_BLOB dyn_buffer = data_blob_null;
        uint32_t dyn_ofs = SMB2_HDR_BODY + 0x30;
+       uint32_t input_min_offset;
        uint32_t input_buffer_offset;
        uint32_t input_buffer_length;
+       uint32_t input_next_offset;
+       uint32_t output_min_offset;
        uint32_t output_buffer_offset;
        uint32_t output_buffer_length;
+       uint32_t output_next_offset;
        static const struct smb2cli_req_expected_response expected[] = {
        {
                .status = NT_STATUS_OK,
@@ -247,92 +325,44 @@ static void smb2cli_ioctl_done(struct tevent_req *subreq)
 
        state->recv_iov = iov;
        fixed = (uint8_t *)iov[1].iov_base;
-       dyn = (uint8_t *)iov[2].iov_base;
-       dyn_len = iov[2].iov_len;
+       dyn_buffer = data_blob_const((uint8_t *)iov[2].iov_base,
+                                    iov[2].iov_len);
 
        input_buffer_offset = IVAL(fixed, 0x18);
        input_buffer_length = IVAL(fixed, 0x1C);
        output_buffer_offset = IVAL(fixed, 0x20);
        output_buffer_length = IVAL(fixed, 0x24);
 
-       if ((input_buffer_offset > 0) && (input_buffer_length > 0)) {
-               uint32_t ofs;
-
-               if (input_buffer_offset != dyn_ofs) {
-                       tevent_req_nterror(
-                               req, NT_STATUS_INVALID_NETWORK_RESPONSE);
-                       return;
-               }
-
-               ofs = input_buffer_length;
-               ofs = NDR_ROUND(ofs, 8);
-
-               if (state->max_input_length == 0) {
-                       /*
-                        * If max_input_length is 0 we ignore
-                        * the input_buffer_length, because
-                        * Windows 2008 echos the DCERPC request
-                        * from the requested input_buffer
-                        * to the response input_buffer.
-                        */
-                       input_buffer_length = 0;
-               }
-
-               if (input_buffer_length > dyn_len) {
-                       tevent_req_nterror(
-                               req, NT_STATUS_INVALID_NETWORK_RESPONSE);
-                       return;
-               }
-
-               if (input_buffer_length > state->max_input_length) {
-                       tevent_req_nterror(
-                               req, NT_STATUS_INVALID_NETWORK_RESPONSE);
-                       return;
-               }
-
-               state->out_input_buffer.data = dyn;
-               state->out_input_buffer.length = input_buffer_length;
-
-               if (ofs > dyn_len) {
-                       tevent_req_nterror(
-                               req, NT_STATUS_INVALID_NETWORK_RESPONSE);
-                       return;
-               }
-
-               dyn_ofs += ofs;
-               dyn += ofs;
-               dyn_len -= ofs;
+       input_min_offset = dyn_ofs;
+       input_next_offset = dyn_ofs;
+       error = smb2cli_ioctl_parse_buffer(dyn_ofs,
+                                          dyn_buffer,
+                                          input_min_offset,
+                                          input_buffer_offset,
+                                          input_buffer_length,
+                                          state->max_input_length,
+                                          &input_next_offset,
+                                          &state->out_input_buffer);
+       if (tevent_req_nterror(req, error)) {
+               return;
        }
 
-       if ((output_buffer_offset > 0) && (output_buffer_length > 0)) {
-               if (output_buffer_offset != dyn_ofs) {
-                       tevent_req_nterror(
-                               req, NT_STATUS_INVALID_NETWORK_RESPONSE);
-                       return;
-               }
-
-               if (state->max_output_length == 0) {
-                       /*
-                        * We do the same logic as for
-                        * max_input_length.
-                        */
-                       output_buffer_length = 0;
-               }
-
-               if (output_buffer_length > dyn_len) {
-                       tevent_req_nterror(
-                               req, NT_STATUS_INVALID_NETWORK_RESPONSE);
-                       return;
-               }
-
-               if (output_buffer_length > state->max_output_length) {
-                       tevent_req_nterror(
-                               req, NT_STATUS_INVALID_NETWORK_RESPONSE);
-                       return;
-               }
-
-               state->out_output_buffer.data = dyn;
-               state->out_output_buffer.length = output_buffer_length;
+       /*
+        * If output data is returned, the output offset MUST be set to
+        * InputOffset + InputCount rounded up to a multiple of 8.
+        */
+       output_min_offset = NDR_ROUND(input_next_offset, 8);
+       output_next_offset = 0; /* this variable is completely ignored */
+       error = smb2cli_ioctl_parse_buffer(dyn_ofs,
+                                          dyn_buffer,
+                                          output_min_offset,
+                                          output_buffer_offset,
+                                          output_buffer_length,
+                                          state->max_output_length,
+                                          &output_next_offset,
+                                          &state->out_output_buffer);
+       if (tevent_req_nterror(req, error)) {
+               return;
        }
 
        state->out_valid = true;