static int cli_request_destructor(struct cli_request *req)
{
if (req->enc_state != NULL) {
- common_free_enc_buffer(req->enc_state, req->outbuf);
+ common_free_enc_buffer(req->enc_state, (char *)req->outbuf);
}
DLIST_REMOVE(req->cli->outstanding_requests, req);
if (req->cli->outstanding_requests == NULL) {
* to the chain. Find the offset to the place where we have to put our cmd.
*/
-static bool find_andx_cmd_ofs(char *buf, size_t *pofs)
+static bool find_andx_cmd_ofs(uint8_t *buf, size_t *pofs)
{
uint8_t cmd;
size_t ofs;
return true;
}
+/**
+ * @brief Do the smb chaining at a buffer level
+ * @param[in] poutbuf Pointer to the talloc'ed buffer to be modified
+ * @param[in] smb_command The command that we want to issue
+ * @param[in] wct How many words?
+ * @param[in] vwv The words, already in network order
+ * @param[in] bytes_alignment How shall we align "bytes"?
+ * @param[in] num_bytes How many bytes?
+ * @param[in] bytes The data the request ships
+ *
+ * smb_splice_chain() adds the vwv and bytes to the request already present in
+ * *poutbuf.
+ */
+
+bool smb_splice_chain(uint8_t **poutbuf, uint8_t smb_command,
+ uint8_t wct, const uint16_t *vwv,
+ size_t bytes_alignment,
+ uint32_t num_bytes, const uint8_t *bytes)
+{
+ uint8_t *outbuf;
+ size_t old_size, new_size;
+ size_t ofs;
+ size_t chain_padding = 0;
+ size_t bytes_padding = 0;
+ bool first_request;
+
+ old_size = talloc_get_size(*poutbuf);
+
+ /*
+ * old_size == smb_wct means we're pushing the first request in for
+ * libsmb/
+ */
+
+ first_request = (old_size == smb_wct);
+
+ if (!first_request && ((old_size % 4) != 0)) {
+ /*
+ * Align the wct field of subsequent requests to a 4-byte
+ * boundary
+ */
+ chain_padding = 4 - (old_size % 4);
+ }
+
+ /*
+ * After the old request comes the new wct field (1 byte), the vwv's
+ * and the num_bytes field. After at we might need to align the bytes
+ * given to us to "bytes_alignment", increasing the num_bytes value.
+ */
+
+ new_size = old_size + chain_padding + 1 + wct * sizeof(uint16_t) + 2;
+
+ if ((bytes_alignment != 0) && ((new_size % bytes_alignment) != 0)) {
+ bytes_padding = bytes_alignment - (new_size % bytes_alignment);
+ }
+
+ new_size += bytes_padding + num_bytes;
+
+ if ((smb_command != SMBwriteX) && (new_size > 0xffff)) {
+ DEBUG(1, ("splice_chain: %u bytes won't fit\n",
+ (unsigned)new_size));
+ return false;
+ }
+
+ outbuf = TALLOC_REALLOC_ARRAY(NULL, *poutbuf, uint8_t, new_size);
+ if (outbuf == NULL) {
+ DEBUG(0, ("talloc failed\n"));
+ return false;
+ }
+ *poutbuf = outbuf;
+
+ if (first_request) {
+ SCVAL(outbuf, smb_com, smb_command);
+ } else {
+ size_t andx_cmd_ofs;
+
+ if (!find_andx_cmd_ofs(outbuf, &andx_cmd_ofs)) {
+ DEBUG(1, ("invalid command chain\n"));
+ *poutbuf = TALLOC_REALLOC_ARRAY(
+ NULL, *poutbuf, uint8_t, old_size);
+ return false;
+ }
+
+ if (chain_padding != 0) {
+ memset(outbuf + old_size, 0, chain_padding);
+ old_size += chain_padding;
+ }
+
+ SCVAL(outbuf, andx_cmd_ofs, smb_command);
+ SSVAL(outbuf, andx_cmd_ofs + 2, old_size - 4);
+ }
+
+ ofs = old_size;
+
+ /*
+ * Push the chained request:
+ *
+ * wct field
+ */
+
+ SCVAL(outbuf, ofs, wct);
+ ofs += 1;
+
+ /*
+ * vwv array
+ */
+
+ memcpy(outbuf + ofs, vwv, sizeof(uint16_t) * wct);
+ ofs += sizeof(uint16_t) * wct;
+
+ /*
+ * bcc (byte count)
+ */
+
+ SSVAL(outbuf, ofs, num_bytes + bytes_padding);
+ ofs += sizeof(uint16_t);
+
+ /*
+ * padding
+ */
+
+ if (bytes_padding != 0) {
+ memset(outbuf + ofs, 0, bytes_padding);
+ ofs += bytes_padding;
+ }
+
+ /*
+ * The bytes field
+ */
+
+ memcpy(outbuf + ofs, bytes, num_bytes);
+
+ return true;
+}
+
/**
* @brief Destroy an async_req that is the visible part of a cli_request
* @param[in] req The request to kill
uint8_t smb_command,
uint8_t additional_flags,
uint8_t wct, const uint16_t *vwv,
- uint16_t num_bytes,
+ size_t bytes_alignment,
+ uint32_t num_bytes,
const uint8_t *bytes)
{
struct async_req **tmp_reqs;
- char *tmp_buf;
struct cli_request *req;
- size_t old_size, new_size;
- size_t ofs;
req = cli->chain_accumulator;
talloc_set_destructor(req->async[req->num_async-1],
cli_async_req_destructor);
- old_size = talloc_get_size(req->outbuf);
-
- /*
- * We need space for the wct field, the words, the byte count field
- * and the bytes themselves.
- */
- new_size = old_size + 1 + wct * sizeof(uint16_t) + 2 + num_bytes;
-
- if (new_size > 0xffff) {
- DEBUG(1, ("cli_request_chain: %u bytes won't fit\n",
- (unsigned)new_size));
+ if (!smb_splice_chain(&req->outbuf, smb_command, wct, vwv,
+ bytes_alignment, num_bytes, bytes)) {
goto fail;
}
- tmp_buf = TALLOC_REALLOC_ARRAY(NULL, req->outbuf, char, new_size);
- if (tmp_buf == NULL) {
- DEBUG(0, ("talloc failed\n"));
- goto fail;
- }
- req->outbuf = tmp_buf;
-
- if (old_size == smb_wct) {
- SCVAL(req->outbuf, smb_com, smb_command);
- } else {
- size_t andx_cmd_ofs;
- if (!find_andx_cmd_ofs(req->outbuf, &andx_cmd_ofs)) {
- DEBUG(1, ("invalid command chain\n"));
- goto fail;
- }
- SCVAL(req->outbuf, andx_cmd_ofs, smb_command);
- SSVAL(req->outbuf, andx_cmd_ofs + 2, old_size - 4);
- }
-
- ofs = old_size;
-
- SCVAL(req->outbuf, ofs, wct);
- ofs += 1;
-
- memcpy(req->outbuf + ofs, vwv, sizeof(uint16_t) * wct);
- ofs += sizeof(uint16_t) * wct;
-
- SSVAL(req->outbuf, ofs, num_bytes);
- ofs += sizeof(uint16_t);
-
- memcpy(req->outbuf + ofs, bytes, num_bytes);
-
return req->async[req->num_async-1];
fail:
if (size_hint == 0) {
size_hint = 100;
}
- req->outbuf = talloc_array(req, char, smb_wct + size_hint);
+ req->outbuf = talloc_array(req, uint8_t, smb_wct + size_hint);
if (req->outbuf == NULL) {
goto fail;
}
- req->outbuf = TALLOC_REALLOC_ARRAY(NULL, req->outbuf, char, smb_wct);
+ req->outbuf = TALLOC_REALLOC_ARRAY(NULL, req->outbuf, uint8_t,
+ smb_wct);
req->num_async = 0;
req->async = NULL;
req->recv_helper.fn = NULL;
SSVAL(req->outbuf, smb_tid, cli->cnum);
- cli_setup_packet_buf(cli, req->outbuf);
+ cli_setup_packet_buf(cli, (char *)req->outbuf);
req->mid = cli_new_mid(cli);
cli->chain_accumulator = NULL;
SSVAL(req->outbuf, smb_mid, req->mid);
- smb_setlen(req->outbuf, talloc_get_size(req->outbuf) - 4);
+ smb_setlen((char *)req->outbuf, talloc_get_size(req->outbuf) - 4);
- cli_calculate_sign_mac(cli, req->outbuf);
+ cli_calculate_sign_mac(cli, (char *)req->outbuf);
if (cli_encryption_on(cli)) {
NTSTATUS status;
char *enc_buf;
- status = cli_encrypt_message(cli, req->outbuf, &enc_buf);
+ status = cli_encrypt_message(cli, (char *)req->outbuf,
+ &enc_buf);
if (!NT_STATUS_IS_OK(status)) {
DEBUG(0, ("Error in encrypting client message. "
"Error %s\n", nt_errstr(status)));
TALLOC_FREE(req);
return;
}
- req->outbuf = enc_buf;
+ req->outbuf = (uint8_t *)enc_buf;
req->enc_state = cli->trans_enc_state;
}
* @param[in] additional_flags open_and_x wants to add oplock header flags
* @param[in] wct How many words?
* @param[in] vwv The words, already in network order
+ * @param[in] bytes_alignment How shall we align "bytes"?
* @param[in] num_bytes How many bytes?
* @param[in] bytes The data the request ships
*
uint8_t smb_command,
uint8_t additional_flags,
uint8_t wct, const uint16_t *vwv,
- uint16_t num_bytes, const uint8_t *bytes)
+ size_t bytes_alignment,
+ uint32_t num_bytes, const uint8_t *bytes)
{
struct async_req *result;
bool uncork = false;
}
result = cli_request_chain(mem_ctx, ev, cli, smb_command,
- additional_flags, wct, vwv,
+ additional_flags, wct, vwv, bytes_alignment,
num_bytes, bytes);
if (result == NULL) {
return NT_STATUS_OK;
}
+/**
+ * Decrypt a PDU, check the signature
+ * @param[in] cli The cli_state that received something
+ * @param[in] pdu The incoming bytes
+ * @retval error code
+ */
+
+
+static NTSTATUS validate_smb_crypto(struct cli_state *cli, char *pdu)
+{
+ NTSTATUS status;
+
+ if ((IVAL(pdu, 4) != 0x424d53ff) /* 0xFF"SMB" */
+ && (SVAL(pdu, 4) != 0x45ff)) /* 0xFF"E" */ {
+ DEBUG(10, ("Got non-SMB PDU\n"));
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ if (cli_encryption_on(cli) && CVAL(pdu, 0) == 0) {
+ uint16_t enc_ctx_num;
+
+ status = get_enc_ctx_num((uint8_t *)pdu, &enc_ctx_num);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(10, ("get_enc_ctx_num returned %s\n",
+ nt_errstr(status)));
+ return status;
+ }
+
+ if (enc_ctx_num != cli->trans_enc_state->enc_ctx_num) {
+ DEBUG(10, ("wrong enc_ctx %d, expected %d\n",
+ enc_ctx_num,
+ cli->trans_enc_state->enc_ctx_num));
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ status = common_decrypt_buffer(cli->trans_enc_state, pdu);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(10, ("common_decrypt_buffer returned %s\n",
+ nt_errstr(status)));
+ return status;
+ }
+ }
+
+ if (!cli_check_sign_mac(cli, pdu)) {
+ DEBUG(10, ("cli_check_sign_mac failed\n"));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ return NT_STATUS_OK;
+}
+
/**
* A PDU has arrived on cli->evt_inbuf
* @param[in] cli The cli_state that received something
goto invalidate_requests;
}
}
-
- }
-
- if ((IVAL(pdu, 4) != 0x424d53ff) /* 0xFF"SMB" */
- && (IVAL(pdu, 4) != 0x424d45ff)) /* 0xFF"EMB" */ {
- DEBUG(10, ("Got non-SMB PDU\n"));
- status = NT_STATUS_INVALID_NETWORK_RESPONSE;
- goto invalidate_requests;
- }
-
- /*
- * TODO: Handle oplock break requests
- */
-
- if (cli_encryption_on(cli) && CVAL(pdu, 0) == 0) {
- uint16_t enc_ctx_num;
-
- status = get_enc_ctx_num((uint8_t *)pdu, &enc_ctx_num);
- if (!NT_STATUS_IS_OK(status)) {
- DEBUG(10, ("get_enc_ctx_num returned %s\n",
- nt_errstr(status)));
- goto invalidate_requests;
- }
-
- if (enc_ctx_num != cli->trans_enc_state->enc_ctx_num) {
- DEBUG(10, ("wrong enc_ctx %d, expected %d\n",
- enc_ctx_num,
- cli->trans_enc_state->enc_ctx_num));
- status = NT_STATUS_INVALID_HANDLE;
- goto invalidate_requests;
- }
-
- status = common_decrypt_buffer(cli->trans_enc_state,
- pdu);
- if (!NT_STATUS_IS_OK(status)) {
- DEBUG(10, ("common_decrypt_buffer returned %s\n",
- nt_errstr(status)));
- goto invalidate_requests;
- }
}
- if (!cli_check_sign_mac(cli, pdu)) {
- DEBUG(10, ("cli_check_sign_mac failed\n"));
- status = NT_STATUS_ACCESS_DENIED;
+ status = validate_smb_crypto(cli, pdu);
+ if (!NT_STATUS_IS_OK(status)) {
goto invalidate_requests;
}
DEBUG(11, ("cli_state_handler called with flags %d\n", flags));
+ if (flags & EVENT_FD_WRITE) {
+ size_t to_send;
+ ssize_t sent;
+
+ for (req = cli->outstanding_requests; req; req = req->next) {
+ to_send = smb_len(req->outbuf)+4;
+ if (to_send > req->sent) {
+ break;
+ }
+ }
+
+ if (req == NULL) {
+ if (cli->fd_event != NULL) {
+ event_fd_set_not_writeable(cli->fd_event);
+ }
+ return;
+ }
+
+ sent = sys_send(cli->fd, req->outbuf + req->sent,
+ to_send - req->sent, 0);
+
+ if (sent < 0) {
+ status = map_nt_error_from_unix(errno);
+ goto sock_error;
+ }
+
+ req->sent += sent;
+
+ if (req->sent == to_send) {
+ return;
+ }
+ }
+
if (flags & EVENT_FD_READ) {
int res, available;
size_t old_size, new_size;
}
cli->evt_inbuf = tmp;
- res = recv(cli->fd, cli->evt_inbuf + old_size, available, 0);
+ res = sys_recv(cli->fd, cli->evt_inbuf + old_size, available, 0);
if (res == -1) {
DEBUG(10, ("recv failed: %s\n", strerror(errno)));
status = map_nt_error_from_unix(errno);
}
}
- if (flags & EVENT_FD_WRITE) {
- size_t to_send;
- ssize_t sent;
-
- for (req = cli->outstanding_requests; req; req = req->next) {
- to_send = smb_len(req->outbuf)+4;
- if (to_send > req->sent) {
- break;
- }
- }
-
- if (req == NULL) {
- if (cli->fd_event != NULL) {
- event_fd_set_not_writeable(cli->fd_event);
- }
- return;
- }
-
- sent = send(cli->fd, req->outbuf + req->sent,
- to_send - req->sent, 0);
-
- if (sent < 0) {
- status = map_nt_error_from_unix(errno);
- goto sock_error;
- }
-
- req->sent += sent;
-
- if (req->sent == to_send) {
- return;
- }
- }
return;
sock_error: