#include "includes.h"
+/*
+ * Read an smb packet asynchronously, discard keepalives
+ */
+
+struct read_smb_state {
+ struct tevent_context *ev;
+ int fd;
+ uint8_t *buf;
+};
+
+static ssize_t read_smb_more(uint8_t *buf, size_t buflen, void *private_data);
+static void read_smb_done(struct tevent_req *subreq);
+
+static struct tevent_req *read_smb_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ int fd)
+{
+ struct tevent_req *result, *subreq;
+ struct read_smb_state *state;
+
+ result = tevent_req_create(mem_ctx, &state, struct read_smb_state);
+ if (result == NULL) {
+ return NULL;
+ }
+ state->ev = ev;
+ state->fd = fd;
+
+ subreq = read_packet_send(state, ev, fd, 4, read_smb_more, NULL);
+ if (subreq == NULL) {
+ goto fail;
+ }
+ tevent_req_set_callback(subreq, read_smb_done, result);
+ return result;
+ fail:
+ TALLOC_FREE(result);
+ return NULL;
+}
+
+static ssize_t read_smb_more(uint8_t *buf, size_t buflen, void *private_data)
+{
+ if (buflen > 4) {
+ return 0; /* We've been here, we're done */
+ }
+ return smb_len_large(buf);
+}
+
+static void read_smb_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct read_smb_state *state = tevent_req_data(
+ req, struct read_smb_state);
+ ssize_t len;
+ int err;
+
+ len = read_packet_recv(subreq, state, &state->buf, &err);
+ TALLOC_FREE(subreq);
+ if (len == -1) {
+ tevent_req_error(req, err);
+ return;
+ }
+
+ if (CVAL(state->buf, 0) == SMBkeepalive) {
+ subreq = read_packet_send(state, state->ev, state->fd, 4,
+ read_smb_more, NULL);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, read_smb_done, req);
+ return;
+ }
+ tevent_req_done(req);
+}
+
+static ssize_t read_smb_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+ uint8_t **pbuf, int *perrno)
+{
+ struct read_smb_state *state = tevent_req_data(
+ req, struct read_smb_state);
+
+ if (tevent_req_is_unix_error(req, perrno)) {
+ return -1;
+ }
+ *pbuf = talloc_move(mem_ctx, &state->buf);
+ return talloc_get_size(*pbuf);
+}
+
/**
* Fetch an error out of a NBT packet
* @param[in] buf The SMB packet
int i;
result = cli->mid++;
- if (result == 0) {
+ if ((result == 0) || (result == 0xffff)) {
continue;
}
SSVAL(state->bytecount_buf, 0, iov_len(bytes_iov, iov_count));
- state->iov[0].iov_base = state->header;
+ state->iov[0].iov_base = (void *)state->header;
state->iov[0].iov_len = sizeof(state->header);
- state->iov[1].iov_base = state->vwv;
+ state->iov[1].iov_base = (void *)state->vwv;
state->iov[1].iov_len = wct * sizeof(uint16_t);
- state->iov[2].iov_base = state->bytecount_buf;
+ state->iov[2].iov_base = (void *)state->bytecount_buf;
state->iov[2].iov_len = sizeof(uint16_t);
if (iov_count != 0) {
return result;
}
-static bool cli_signv(struct cli_state *cli, struct iovec *iov, int count,
- uint32_t *seqnum)
+static NTSTATUS cli_signv(struct cli_state *cli, struct iovec *iov, int count,
+ uint32_t *seqnum)
{
uint8_t *buf;
*/
if ((count <= 0) || (iov[0].iov_len < smb_wct)) {
- return false;
+ return NT_STATUS_INVALID_PARAMETER;
}
buf = iov_concat(talloc_tos(), iov, count);
if (buf == NULL) {
- return false;
+ return NT_STATUS_NO_MEMORY;
}
cli_calculate_sign_mac(cli, (char *)buf, seqnum);
memcpy(iov[0].iov_base, buf, iov[0].iov_len);
TALLOC_FREE(buf);
- return true;
+ return NT_STATUS_OK;
}
static void cli_smb_sent(struct tevent_req *subreq);
-static bool cli_smb_req_iov_send(struct tevent_req *req,
- struct cli_smb_state *state,
- struct iovec *iov, int iov_count)
+static NTSTATUS cli_smb_req_iov_send(struct tevent_req *req,
+ struct cli_smb_state *state,
+ struct iovec *iov, int iov_count)
{
struct tevent_req *subreq;
+ NTSTATUS status;
+
+ if (state->cli->fd == -1) {
+ return NT_STATUS_CONNECTION_INVALID;
+ }
if (iov[0].iov_len < smb_wct) {
- return false;
+ return NT_STATUS_INVALID_PARAMETER;
}
if (state->mid != 0) {
smb_setlen((char *)iov[0].iov_base, iov_len(iov, iov_count) - 4);
- if (!cli_signv(state->cli, iov, iov_count, &state->seqnum)) {
- return false;
+ status = cli_signv(state->cli, iov, iov_count, &state->seqnum);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
}
if (cli_encryption_on(state->cli)) {
- NTSTATUS status;
char *buf, *enc_buf;
buf = (char *)iov_concat(talloc_tos(), iov, iov_count);
if (buf == NULL) {
- return false;
+ return NT_STATUS_NO_MEMORY;
}
status = cli_encrypt_message(state->cli, (char *)buf,
&enc_buf);
if (!NT_STATUS_IS_OK(status)) {
DEBUG(0, ("Error in encrypting client message: %s\n",
nt_errstr(status)));
- return false;
+ return status;
}
buf = (char *)talloc_memdup(state, enc_buf,
smb_len(enc_buf)+4);
SAFE_FREE(enc_buf);
if (buf == NULL) {
- return false;
+ return NT_STATUS_NO_MEMORY;
}
- iov[0].iov_base = buf;
+ iov[0].iov_base = (void *)buf;
iov[0].iov_len = talloc_get_size(buf);
subreq = writev_send(state, state->ev, state->cli->outgoing,
- state->cli->fd, iov, 1);
+ state->cli->fd, false, iov, 1);
} else {
subreq = writev_send(state, state->ev, state->cli->outgoing,
- state->cli->fd, iov, iov_count);
+ state->cli->fd, false, iov, iov_count);
}
if (subreq == NULL) {
- return false;
+ return NT_STATUS_NO_MEMORY;
}
tevent_req_set_callback(subreq, cli_smb_sent, req);
- return true;
+ return NT_STATUS_OK;
}
-bool cli_smb_req_send(struct tevent_req *req)
+NTSTATUS cli_smb_req_send(struct tevent_req *req)
{
struct cli_smb_state *state = tevent_req_data(
req, struct cli_smb_state);
{
struct tevent_req *req;
struct iovec iov;
+ NTSTATUS status;
- iov.iov_base = CONST_DISCARD(char *, bytes);
+ iov.iov_base = CONST_DISCARD(void *, bytes);
iov.iov_len = num_bytes;
req = cli_smb_req_create(mem_ctx, ev, cli, smb_command,
if (req == NULL) {
return NULL;
}
- if (!cli_smb_req_send(req)) {
- TALLOC_FREE(req);
+
+ status = cli_smb_req_send(req);
+ if (!NT_STATUS_IS_OK(status)) {
+ tevent_req_nterror(req, status);
+ return tevent_req_post(req, ev);
}
return req;
}
nwritten = writev_recv(subreq, &err);
TALLOC_FREE(subreq);
if (nwritten == -1) {
+ if (state->cli->fd != -1) {
+ close(state->cli->fd);
+ state->cli->fd = -1;
+ }
tevent_req_nterror(req, map_nt_error_from_unix(err));
return;
}
int num_pending;
int i, err;
uint16_t mid;
+ bool oplock_break;
received = read_smb_recv(subreq, talloc_tos(), &inbuf, &err);
TALLOC_FREE(subreq);
if (received == -1) {
+ if (cli->fd != -1) {
+ close(cli->fd);
+ cli->fd = -1;
+ }
status = map_nt_error_from_unix(err);
goto fail;
}
goto done;
}
+ oplock_break = false;
+
+ if (mid == 0xffff) {
+ /*
+ * Paranoia checks that this is really an oplock break request.
+ */
+ oplock_break = (smb_len(inbuf) == 51); /* hdr + 8 words */
+ oplock_break &= ((CVAL(inbuf, smb_flg) & FLAG_REPLY) == 0);
+ oplock_break &= (CVAL(inbuf, smb_com) == SMBlockingX);
+ oplock_break &= (SVAL(inbuf, smb_vwv6) == 0);
+ oplock_break &= (SVAL(inbuf, smb_vwv7) == 0);
+
+ if (!oplock_break) {
+ /* Dump unexpected reply */
+ TALLOC_FREE(inbuf);
+ goto done;
+ }
+ }
+
req = cli->pending[i];
state = tevent_req_data(req, struct cli_smb_state);
ev = state->ev;
- if (!cli_check_sign_mac(cli, (char *)inbuf, state->seqnum+1)) {
+ if (!oplock_break /* oplock breaks are not signed */
+ && !cli_check_sign_mac(cli, (char *)inbuf, state->seqnum+1)) {
DEBUG(10, ("cli_check_sign_mac failed\n"));
TALLOC_FREE(inbuf);
status = NT_STATUS_ACCESS_DENIED;
return wct_ofs;
}
-bool cli_smb_chain_send(struct tevent_req **reqs, int num_reqs)
+NTSTATUS cli_smb_chain_send(struct tevent_req **reqs, int num_reqs)
{
struct cli_smb_state *first_state = tevent_req_data(
reqs[0], struct cli_smb_state);
int i, iovlen;
struct iovec *iov = NULL;
struct iovec *this_iov;
+ NTSTATUS status;
iovlen = 0;
for (i=0; i<num_reqs; i++) {
iov = talloc_array(last_state, struct iovec, iovlen);
if (iov == NULL) {
- goto fail;
+ return NT_STATUS_NO_MEMORY;
}
first_state->chained_requests = (struct tevent_req **)talloc_memdup(
last_state, reqs, sizeof(*reqs) * num_reqs);
if (first_state->chained_requests == NULL) {
- goto fail;
+ TALLOC_FREE(iov);
+ return NT_STATUS_NO_MEMORY;
}
wct_offset = smb_wct - 4;
if (i < num_reqs-1) {
if (!is_andx_req(CVAL(state->header, smb_com))
|| CVAL(state->header, smb_wct) < 2) {
- goto fail;
+ TALLOC_FREE(iov);
+ TALLOC_FREE(first_state->chained_requests);
+ return NT_STATUS_INVALID_PARAMETER;
}
}
* last byte.
*/
this_iov[0].iov_len = chain_padding+1;
- this_iov[0].iov_base = &state->header[
+ this_iov[0].iov_base = (void *)&state->header[
sizeof(state->header) - this_iov[0].iov_len];
memset(this_iov[0].iov_base, 0, this_iov[0].iov_len-1);
}
chain_padding = next_padding;
}
- if (!cli_smb_req_iov_send(reqs[0], last_state, iov, iovlen)) {
- goto fail;
+ status = cli_smb_req_iov_send(reqs[0], last_state, iov, iovlen);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(iov);
+ TALLOC_FREE(first_state->chained_requests);
+ return status;
}
- return true;
- fail:
- TALLOC_FREE(iov);
- return false;
+
+ return NT_STATUS_OK;
}
uint8_t *cli_smb_inbuf(struct tevent_req *req)
return ((tevent_queue_length(cli->outgoing) != 0)
|| (talloc_array_length(cli->pending) != 0));
}
+
+struct cli_smb_oplock_break_waiter_state {
+ uint16_t fnum;
+ uint8_t level;
+};
+
+static void cli_smb_oplock_break_waiter_done(struct tevent_req *subreq);
+
+struct tevent_req *cli_smb_oplock_break_waiter_send(TALLOC_CTX *mem_ctx,
+ struct event_context *ev,
+ struct cli_state *cli)
+{
+ struct tevent_req *req, *subreq;
+ struct cli_smb_oplock_break_waiter_state *state;
+ struct cli_smb_state *smb_state;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct cli_smb_oplock_break_waiter_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ /*
+ * Create a fake SMB request that we will never send out. This is only
+ * used to be set into the pending queue with the right mid.
+ */
+ subreq = cli_smb_req_create(mem_ctx, ev, cli, 0, 0, 0, NULL, 0, NULL);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ smb_state = tevent_req_data(subreq, struct cli_smb_state);
+ SSVAL(smb_state->header, smb_mid, 0xffff);
+
+ if (!cli_smb_req_set_pending(subreq)) {
+ tevent_req_nterror(req, NT_STATUS_NO_MEMORY);
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_smb_oplock_break_waiter_done, req);
+ return req;
+}
+
+static void cli_smb_oplock_break_waiter_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_smb_oplock_break_waiter_state *state = tevent_req_data(
+ req, struct cli_smb_oplock_break_waiter_state);
+ uint8_t wct;
+ uint16_t *vwv;
+ uint32_t num_bytes;
+ uint8_t *bytes;
+ NTSTATUS status;
+
+ status = cli_smb_recv(subreq, 8, &wct, &vwv, &num_bytes, &bytes);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(subreq);
+ tevent_req_nterror(req, status);
+ return;
+ }
+ state->fnum = SVAL(vwv+2, 0);
+ state->level = CVAL(vwv+3, 1);
+ tevent_req_done(req);
+}
+
+NTSTATUS cli_smb_oplock_break_waiter_recv(struct tevent_req *req,
+ uint16_t *pfnum,
+ uint8_t *plevel)
+{
+ struct cli_smb_oplock_break_waiter_state *state = tevent_req_data(
+ req, struct cli_smb_oplock_break_waiter_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ return status;
+ }
+ *pfnum = state->fnum;
+ *plevel = state->level;
+ return NT_STATUS_OK;
+}