summaryrefslogtreecommitdiff
path: root/libcli
diff options
context:
space:
mode:
authorVolker Lendecke <vl@samba.org>2022-10-26 13:58:56 +0200
committerJeremy Allison <jra@samba.org>2022-11-22 18:27:33 +0000
commit218baae2d364dff581bb88ccd2a773e617ec8be8 (patch)
treead1757933e3f2542e3acc23053683b39c31749aa /libcli
parent3fbd4b27cb32f2f93793b8c6c44eb5d37034fcf8 (diff)
downloadsamba-218baae2d364dff581bb88ccd2a773e617ec8be8.tar.gz
libsmb: Parse the smb2 symlink error response in smb2cli_create()
Signed-off-by: Volker Lendecke <vl@samba.org> Reviewed-by: Jeremy Allison <jra@samba.org>
Diffstat (limited to 'libcli')
-rw-r--r--libcli/smb/smb2cli_create.c222
-rw-r--r--libcli/smb/smb_constants.h5
2 files changed, 227 insertions, 0 deletions
diff --git a/libcli/smb/smb2cli_create.c b/libcli/smb/smb2cli_create.c
index ca73ca6efb9..6417de6332d 100644
--- a/libcli/smb/smb2cli_create.c
+++ b/libcli/smb/smb2cli_create.c
@@ -23,8 +23,10 @@
#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;
}
diff --git a/libcli/smb/smb_constants.h b/libcli/smb/smb_constants.h
index 862bf49861b..876ea63f2b9 100644
--- a/libcli/smb/smb_constants.h
+++ b/libcli/smb/smb_constants.h
@@ -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 */