]> git.ipfire.org Git - thirdparty/samba.git/commitdiff
libsmb: Parse the smb2 symlink error response in smb2cli_create()
authorVolker Lendecke <vl@samba.org>
Wed, 26 Oct 2022 11:58:56 +0000 (13:58 +0200)
committerJeremy Allison <jra@samba.org>
Tue, 22 Nov 2022 18:27:33 +0000 (18:27 +0000)
Signed-off-by: Volker Lendecke <vl@samba.org>
Reviewed-by: Jeremy Allison <jra@samba.org>
libcli/smb/smb2cli_create.c
libcli/smb/smb_constants.h

index ca73ca6efb9aed36281c7c725064070b0b62d0f5..6417de6332dea16d41a9cac1977b953f14133c96 100644 (file)
 #include "smb_common.h"
 #include "smbXcli_base.h"
 #include "smb2_create_blob.h"
+#include "reparse_symlink.h"
 
 struct smb2cli_create_state {
+       enum protocol_types protocol; /* for symlink error response parser */
        uint8_t *name_utf16;
        size_t name_utf16_len;
        uint8_t fixed[56];
@@ -33,6 +35,7 @@ struct smb2cli_create_state {
        uint64_t fid_volatile;
        struct smb_create_returns cr;
        struct smb2_create_blobs blobs;
+       struct symlink_reparse_struct *symlink;
        struct tevent_req *subreq;
 };
 
@@ -74,6 +77,7 @@ struct tevent_req *smb2cli_create_send(
        if (req == NULL) {
                return NULL;
        }
+       state->protocol = smbXcli_conn_protocol(conn);
 
        ok = convert_string_talloc(
                state,
@@ -182,6 +186,185 @@ static bool smb2cli_create_cancel(struct tevent_req *req)
        return tevent_req_cancel(state->subreq);
 }
 
+/*
+ * [MS-SMB2] 2.2.2.2.1 Symbolic Link Error Response
+ */
+
+static NTSTATUS smb2cli_parse_symlink_error_response(
+       TALLOC_CTX *mem_ctx,
+       const uint8_t *buf,
+       size_t buflen,
+       struct symlink_reparse_struct **psymlink)
+{
+       struct symlink_reparse_struct *symlink = NULL;
+       uint32_t symlink_length, error_tag;
+
+       if (buflen < 8) {
+               DBG_DEBUG("buffer too short: %zu bytes\n", buflen);
+               return NT_STATUS_INVALID_NETWORK_RESPONSE;
+       }
+
+       symlink_length = IVAL(buf, 0);
+       if (symlink_length != (buflen-4)) {
+               DBG_DEBUG("symlink_length=%"PRIu32", (buflen-4)=%zu",
+                         symlink_length, buflen-4);
+               return NT_STATUS_INVALID_NETWORK_RESPONSE;
+       }
+
+       error_tag = IVAL(buf, 4);
+       if (error_tag != SYMLINK_ERROR_TAG) {
+               DBG_DEBUG("error_tag=%"PRIu32", expected 0x%x\n",
+                         error_tag,
+                         SYMLINK_ERROR_TAG);
+               return NT_STATUS_INVALID_NETWORK_RESPONSE;
+       }
+
+       symlink = symlink_reparse_buffer_parse(
+               mem_ctx, buf+8, buflen-8);
+       if (symlink == NULL) {
+               DBG_DEBUG("symlink_reparse_buffer_parse failed\n");
+               return NT_STATUS_INVALID_NETWORK_RESPONSE;
+       }
+
+       *psymlink = symlink;
+       return NT_STATUS_OK;
+}
+
+/*
+ * [MS-SMB2] 2.2.2 ErrorData
+ *
+ * This is in theory a broad API, but as right now we only have a
+ * single [MS-SMB2] 2.2.2.2.1 symlink error response we can return
+ * just this.
+ */
+static NTSTATUS smb2cli_create_error_data_parse(
+       enum protocol_types protocol,
+       uint8_t error_context_count,
+       uint32_t byte_count,
+       const uint8_t *buf,
+       size_t buflen,
+       TALLOC_CTX *mem_ctx,
+       struct symlink_reparse_struct **_symlink)
+{
+       struct symlink_reparse_struct *symlink = NULL;
+       uint32_t error_data_length, error_id;
+       NTSTATUS status;
+
+       if (protocol != PROTOCOL_SMB3_11) {
+               if (error_context_count != 0) {
+                       DBG_DEBUG("Got error_context_count=%"PRIu8"\n",
+                                 error_context_count);
+                       return NT_STATUS_INVALID_NETWORK_RESPONSE;
+               }
+
+               status = smb2cli_parse_symlink_error_response(
+                       mem_ctx, buf, buflen, &symlink);
+               if (!NT_STATUS_IS_OK(status)) {
+                       return status;
+               }
+               *_symlink = symlink;
+               return NT_STATUS_OK;
+       }
+
+       /*
+        * The STOPPED_ON_SYMLINK that I've seen coming from W2k16 has
+        * just a single array element in the [MS-SMB2] 2.2.2
+        * ErrorData array. We'll need to adapt this if there actually
+        * comes an array of multiple ErrorData elements.
+        */
+
+       if (error_context_count != 1) {
+               DBG_DEBUG("Got error_context_count=%"PRIu8"\n",
+                         error_context_count);
+               return NT_STATUS_INVALID_NETWORK_RESPONSE;
+       }
+
+       if (byte_count != buflen) {
+               DBG_DEBUG("bytecount=%"PRIu32", "
+                         "buflen=%zu\n",
+                         byte_count,
+                         buflen);
+               return NT_STATUS_INVALID_NETWORK_RESPONSE;
+       }
+
+       if (buflen < 8) {
+               DBG_DEBUG("buflen=%zu\n", buflen);
+               return NT_STATUS_INVALID_NETWORK_RESPONSE;
+       }
+
+       error_data_length = IVAL(buf, 0);
+       if (error_data_length != (buflen - 8)) {
+               DBG_DEBUG("error_data_length=%"PRIu32", expected %zu\n",
+                         error_data_length,
+                         buflen - 8);
+               return NT_STATUS_INVALID_NETWORK_RESPONSE;
+       }
+
+       error_id = IVAL(buf, 4);
+       if (error_id != 0) {
+               DBG_DEBUG("error_id=%"PRIu32", expected 0\n", error_id);
+               return NT_STATUS_INVALID_NETWORK_RESPONSE;
+       }
+
+       status = smb2cli_parse_symlink_error_response(
+               mem_ctx, buf + 8, buflen - 8, &symlink);
+       if (!NT_STATUS_IS_OK(status)) {
+               DBG_DEBUG("smb2cli_parse_symlink_error_response failed: %s\n",
+                         nt_errstr(status));
+               return status;
+       }
+
+       *_symlink = symlink;
+       return NT_STATUS_OK;
+}
+
+static NTSTATUS smb2cli_create_unparsed_unix_len(
+       size_t unparsed_utf16_len,
+       uint8_t *name_utf16,
+       size_t name_utf16_len,
+       size_t *_unparsed_unix_len)
+{
+       uint8_t *unparsed_utf16 = NULL;
+       uint8_t *unparsed_unix = NULL;
+       size_t unparsed_unix_len = 0;
+       bool ok;
+
+       if (unparsed_utf16_len > name_utf16_len) {
+               DBG_DEBUG("unparsed_utf16_len=%zu, name_utf16_len=%zu\n",
+                         unparsed_utf16_len,
+                         name_utf16_len);
+               return NT_STATUS_INVALID_NETWORK_RESPONSE;
+       }
+
+       unparsed_utf16 = name_utf16 + name_utf16_len - unparsed_utf16_len;
+
+       ok = convert_string_talloc(
+               talloc_tos(),
+               CH_UTF16,
+               CH_UNIX,
+               unparsed_utf16,
+               unparsed_utf16_len,
+               &unparsed_unix,
+               &unparsed_unix_len);
+       if (!ok) {
+               NTSTATUS status = map_nt_error_from_unix_common(errno);
+               DBG_DEBUG("convert_string_talloc failed: %s\n",
+                         strerror(errno));
+               return status;
+       }
+
+       /*
+        * convert_string_talloc() returns a 0-terminated string
+        */
+       SMB_ASSERT(unparsed_unix_len > 0);
+       SMB_ASSERT(unparsed_unix[unparsed_unix_len-1] == '\0');
+
+       TALLOC_FREE(unparsed_unix);
+
+       *_unparsed_unix_len = (unparsed_unix_len-1);
+       return NT_STATUS_OK;
+}
+
 static void smb2cli_create_done(struct tevent_req *subreq)
 {
        struct tevent_req *req =
@@ -198,12 +381,51 @@ static void smb2cli_create_done(struct tevent_req *subreq)
        {
                .status = NT_STATUS_OK,
                .body_size = 0x59
+       },
+       {
+               .status = NT_STATUS_STOPPED_ON_SYMLINK,
+               .body_size = 0x9,
        }
        };
 
        status = smb2cli_req_recv(subreq, state, &iov,
                                  expected, ARRAY_SIZE(expected));
        TALLOC_FREE(subreq);
+
+       if (NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK)) {
+               uint16_t error_context_count = CVAL(iov[1].iov_base, 2);
+               uint32_t byte_count = IVAL(iov[1].iov_base, 4);
+               size_t unparsed_unix_len = 0;
+
+               NTSTATUS symlink_status;
+
+               symlink_status = smb2cli_create_error_data_parse(
+                       state->protocol,
+                       error_context_count,
+                       byte_count,
+                       iov[2].iov_base,
+                       iov[2].iov_len,
+                       state,
+                       &state->symlink);
+               if (tevent_req_nterror(req, symlink_status)) {
+                       return;
+               }
+
+               /*
+                * Our callers want to know the unparsed length in
+                * unix encoding.
+                */
+               symlink_status = smb2cli_create_unparsed_unix_len(
+                       state->symlink->unparsed_path_length,
+                       state->name_utf16,
+                       state->name_utf16_len,
+                       &unparsed_unix_len);
+               if (tevent_req_nterror(req, symlink_status)) {
+                       return;
+               }
+               state->symlink->unparsed_path_length = unparsed_unix_len;
+       }
+
        if (tevent_req_nterror(req, status)) {
                return;
        }
index 862bf49861b9825a694ab59337ddf79f7b635799..876ea63f2b9b03c1d3352030f64c5dfb21792e54 100644 (file)
@@ -619,4 +619,9 @@ enum csc_policy {
  */
 #define SYMLINK_FLAG_RELATIVE       0x00000001
 
+/*
+ * Symlink error tag from [MS-SMB2] 2.2.2.2.1 Symbolic Link Error Response
+ */
+#define SYMLINK_ERROR_TAG           0x4C4D5953
+
 #endif /* _SMB_CONSTANTS_H */