diff options
author | Volker Lendecke <vl@samba.org> | 2022-10-26 13:58:56 +0200 |
---|---|---|
committer | Jeremy Allison <jra@samba.org> | 2022-11-22 18:27:33 +0000 |
commit | 218baae2d364dff581bb88ccd2a773e617ec8be8 (patch) | |
tree | ad1757933e3f2542e3acc23053683b39c31749aa /libcli | |
parent | 3fbd4b27cb32f2f93793b8c6c44eb5d37034fcf8 (diff) | |
download | samba-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.c | 222 | ||||
-rw-r--r-- | libcli/smb/smb_constants.h | 5 |
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 */ |