diff options
Diffstat (limited to 'source3/libsmb')
-rw-r--r-- | source3/libsmb/smb2cli_base.c | 752 | ||||
-rw-r--r-- | source3/libsmb/smb2cli_base.h | 63 |
2 files changed, 35 insertions, 780 deletions
diff --git a/source3/libsmb/smb2cli_base.c b/source3/libsmb/smb2cli_base.c deleted file mode 100644 index 3a59af0e11..0000000000 --- a/source3/libsmb/smb2cli_base.c +++ /dev/null @@ -1,752 +0,0 @@ -/* - Unix SMB/CIFS implementation. - smb2 lib - Copyright (C) Volker Lendecke 2011 - Copyright (C) Stefan Metzmacher 2011 - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include "includes.h" -#include "client.h" -#include "libcli/smb/read_smb.h" -#include "smb2cli_base.h" -#include "libsmb/proto.h" -#include "lib/async_req/async_sock.h" -#include "lib/util/tevent_ntstatus.h" - -struct smb2cli_req_state { - struct tevent_context *ev; - struct cli_state *cli; - - const uint8_t *fixed; - uint16_t fixed_len; - const uint8_t *dyn; - uint32_t dyn_len; - - uint8_t nbt[4]; - uint8_t hdr[64]; - uint8_t pad[7]; /* padding space for compounding */ - - /* always an array of 3 talloc elements */ - struct iovec *recv_iov; -}; - -static void smb2cli_req_unset_pending(struct tevent_req *req) -{ - struct smb2cli_req_state *state = - tevent_req_data(req, - struct smb2cli_req_state); - struct cli_state *cli = state->cli; - int num_pending = talloc_array_length(cli->conn.pending); - int i; - - talloc_set_destructor(req, NULL); - - if (num_pending == 1) { - /* - * The pending read_smb tevent_req is a child of - * cli->conn.pending. So if nothing is pending anymore, - * we need to delete the socket read fde. - */ - TALLOC_FREE(cli->conn.pending); - return; - } - - for (i=0; i<num_pending; i++) { - if (req == cli->conn.pending[i]) { - break; - } - } - if (i == num_pending) { - /* - * Something's seriously broken. Just returning here is the - * right thing nevertheless, the point of this routine is to - * remove ourselves from cli->conn.pending. - */ - return; - } - - /* - * Remove ourselves from the cli->pending array - */ - for (; i < (num_pending - 1); i++) { - cli->conn.pending[i] = cli->conn.pending[i+1]; - } - - /* - * No NULL check here, we're shrinking by sizeof(void *), and - * talloc_realloc just adjusts the size for this. - */ - cli->conn.pending = talloc_realloc(NULL, cli->conn.pending, - struct tevent_req *, - num_pending - 1); - return; -} - -static int smb2cli_req_destructor(struct tevent_req *req) -{ - smb2cli_req_unset_pending(req); - return 0; -} - -static void smb2cli_inbuf_received(struct tevent_req *subreq); - -static bool smb2cli_req_set_pending(struct tevent_req *req) -{ - struct smb2cli_req_state *state = - tevent_req_data(req, - struct smb2cli_req_state); - struct cli_state *cli; - struct tevent_req **pending; - int num_pending; - struct tevent_req *subreq; - - cli = state->cli; - num_pending = talloc_array_length(cli->conn.pending); - - pending = talloc_realloc(cli, cli->conn.pending, struct tevent_req *, - num_pending+1); - if (pending == NULL) { - return false; - } - pending[num_pending] = req; - cli->conn.pending = pending; - talloc_set_destructor(req, smb2cli_req_destructor); - - if (num_pending > 0) { - return true; - } - - /* - * We're the first ones, add the read_smb request that waits for the - * answer from the server - */ - subreq = read_smb_send(cli->conn.pending, state->ev, cli->conn.fd); - if (subreq == NULL) { - smb2cli_req_unset_pending(req); - return false; - } - tevent_req_set_callback(subreq, smb2cli_inbuf_received, cli); - return true; -} - -static void smb2cli_notify_pending(struct cli_state *cli, NTSTATUS status) -{ - if (cli->conn.fd != -1) { - close(cli->conn.fd); - } - cli->conn.fd = -1; - - /* - * Cancel all pending requests. We don't do a for-loop walking - * cli->conn.pending because that array changes in - * cli_smb_req_destructor(). - */ - while (talloc_array_length(cli->conn.pending) > 0) { - struct tevent_req *req; - struct smb2cli_req_state *state; - - req = cli->conn.pending[0]; - state = tevent_req_data(req, struct smb2cli_req_state); - - smb2cli_req_unset_pending(req); - - /* - * we need to defer the callback, because we may notify more - * then one caller. - */ - tevent_req_defer_callback(req, state->ev); - tevent_req_nterror(req, status); - } -} - -struct tevent_req *smb2cli_req_create(TALLOC_CTX *mem_ctx, - struct tevent_context *ev, - struct cli_state *cli, - uint16_t cmd, - uint32_t additional_flags, - uint32_t clear_flags, - unsigned int timeout, - uint32_t pid, - uint32_t tid, - uint64_t uid, - const uint8_t *fixed, - uint16_t fixed_len, - const uint8_t *dyn, - uint32_t dyn_len) -{ - struct tevent_req *req; - struct smb2cli_req_state *state; - uint32_t flags = 0; - - req = tevent_req_create(mem_ctx, &state, - struct smb2cli_req_state); - if (req == NULL) { - return NULL; - } - state->ev = ev; - state->cli = cli; - - state->recv_iov = talloc_zero_array(state, struct iovec, 3); - if (state->recv_iov == NULL) { - TALLOC_FREE(req); - return NULL; - } - - flags |= additional_flags; - flags &= ~clear_flags; - - state->fixed = fixed; - state->fixed_len = fixed_len; - state->dyn = dyn; - state->dyn_len = dyn_len; - - SIVAL(state->hdr, SMB2_HDR_PROTOCOL_ID, SMB2_MAGIC); - SSVAL(state->hdr, SMB2_HDR_LENGTH, SMB2_HDR_BODY); - SSVAL(state->hdr, SMB2_HDR_EPOCH, 1); - SIVAL(state->hdr, SMB2_HDR_STATUS, NT_STATUS_V(NT_STATUS_OK)); - SSVAL(state->hdr, SMB2_HDR_OPCODE, cmd); - SSVAL(state->hdr, SMB2_HDR_CREDIT, 31); - SIVAL(state->hdr, SMB2_HDR_FLAGS, flags); - SIVAL(state->hdr, SMB2_HDR_PID, pid); - SIVAL(state->hdr, SMB2_HDR_TID, tid); - SBVAL(state->hdr, SMB2_HDR_SESSION_ID, uid); - - if (timeout > 0) { - struct timeval endtime; - - endtime = timeval_current_ofs_msec(timeout); - if (!tevent_req_set_endtime(req, ev, endtime)) { - return req; - } - } - - return req; -} - -static void smb2cli_writev_done(struct tevent_req *subreq); - -NTSTATUS smb2cli_req_compound_submit(struct tevent_req **reqs, - int num_reqs) -{ - struct smb2cli_req_state *state; - struct tevent_req *subreq; - struct iovec *iov; - int i, num_iov, nbt_len; - - /* - * 1 for the nbt length - * per request: HDR, fixed, dyn, padding - * -1 because the last one does not need padding - */ - - iov = talloc_array(reqs[0], struct iovec, 1 + 4*num_reqs - 1); - if (iov == NULL) { - return NT_STATUS_NO_MEMORY; - } - - num_iov = 1; - nbt_len = 0; - - for (i=0; i<num_reqs; i++) { - size_t reqlen; - bool ret; - uint64_t mid; - - if (!tevent_req_is_in_progress(reqs[i])) { - return NT_STATUS_INTERNAL_ERROR; - } - - state = tevent_req_data(reqs[i], struct smb2cli_req_state); - - if (!cli_state_is_connected(state->cli)) { - return NT_STATUS_CONNECTION_DISCONNECTED; - } - - if (state->cli->smb2.mid == UINT64_MAX) { - return NT_STATUS_CONNECTION_ABORTED; - } - - mid = state->cli->smb2.mid; - state->cli->smb2.mid += 1; - - SBVAL(state->hdr, SMB2_HDR_MESSAGE_ID, mid); - - iov[num_iov].iov_base = state->hdr; - iov[num_iov].iov_len = sizeof(state->hdr); - num_iov += 1; - - iov[num_iov].iov_base = discard_const(state->fixed); - iov[num_iov].iov_len = state->fixed_len; - num_iov += 1; - - if (state->dyn != NULL) { - iov[num_iov].iov_base = discard_const(state->dyn); - iov[num_iov].iov_len = state->dyn_len; - num_iov += 1; - } - - reqlen = sizeof(state->hdr) + state->fixed_len + - state->dyn_len; - - if (i < num_reqs-1) { - if ((reqlen % 8) > 0) { - uint8_t pad = 8 - (reqlen % 8); - iov[num_iov].iov_base = state->pad; - iov[num_iov].iov_len = pad; - num_iov += 1; - reqlen += pad; - } - SIVAL(state->hdr, SMB2_HDR_NEXT_COMMAND, reqlen); - } - nbt_len += reqlen; - - ret = smb2cli_req_set_pending(reqs[i]); - if (!ret) { - return NT_STATUS_NO_MEMORY; - } - } - - /* - * TODO: Do signing here - */ - - state = tevent_req_data(reqs[0], struct smb2cli_req_state); - _smb_setlen_tcp(state->nbt, nbt_len); - iov[0].iov_base = state->nbt; - iov[0].iov_len = sizeof(state->nbt); - - subreq = writev_send(state, state->ev, state->cli->conn.outgoing, - state->cli->conn.fd, false, iov, num_iov); - if (subreq == NULL) { - return NT_STATUS_NO_MEMORY; - } - tevent_req_set_callback(subreq, smb2cli_writev_done, reqs[0]); - return NT_STATUS_OK; -} - -struct tevent_req *smb2cli_req_send(TALLOC_CTX *mem_ctx, - struct tevent_context *ev, - struct cli_state *cli, - uint16_t cmd, - uint32_t additional_flags, - uint32_t clear_flags, - unsigned int timeout, - uint32_t pid, - uint32_t tid, - uint64_t uid, - const uint8_t *fixed, - uint16_t fixed_len, - const uint8_t *dyn, - uint32_t dyn_len) -{ - struct tevent_req *req; - NTSTATUS status; - - req = smb2cli_req_create(mem_ctx, ev, cli, cmd, - additional_flags, clear_flags, - timeout, - pid, tid, uid, - fixed, fixed_len, dyn, dyn_len); - if (req == NULL) { - return NULL; - } - if (!tevent_req_is_in_progress(req)) { - return tevent_req_post(req, ev); - } - status = smb2cli_req_compound_submit(&req, 1); - if (tevent_req_nterror(req, status)) { - return tevent_req_post(req, ev); - } - return req; -} - -static void smb2cli_writev_done(struct tevent_req *subreq) -{ - struct tevent_req *req = - tevent_req_callback_data(subreq, - struct tevent_req); - struct smb2cli_req_state *state = - tevent_req_data(req, - struct smb2cli_req_state); - ssize_t nwritten; - int err; - - nwritten = writev_recv(subreq, &err); - TALLOC_FREE(subreq); - if (nwritten == -1) { - /* here, we need to notify all pending requests */ - NTSTATUS status = map_nt_error_from_unix_common(err); - smb2cli_notify_pending(state->cli, status); - return; - } -} - -static NTSTATUS smb2cli_inbuf_parse_compound(uint8_t *buf, TALLOC_CTX *mem_ctx, - struct iovec **piov, int *pnum_iov) -{ - struct iovec *iov; - int num_iov; - size_t buflen; - size_t taken; - uint8_t *first_hdr; - - num_iov = 0; - - iov = talloc_array(mem_ctx, struct iovec, num_iov); - if (iov == NULL) { - return NT_STATUS_NO_MEMORY; - } - - buflen = smb_len_tcp(buf); - taken = 0; - first_hdr = buf + NBT_HDR_SIZE; - - while (taken < buflen) { - size_t len = buflen - taken; - uint8_t *hdr = first_hdr + taken; - struct iovec *cur; - size_t full_size; - size_t next_command_ofs; - uint16_t body_size; - struct iovec *iov_tmp; - - /* - * We need the header plus the body length field - */ - - if (len < SMB2_HDR_BODY + 2) { - DEBUG(10, ("%d bytes left, expected at least %d\n", - (int)len, SMB2_HDR_BODY)); - goto inval; - } - if (IVAL(hdr, 0) != SMB2_MAGIC) { - DEBUG(10, ("Got non-SMB2 PDU: %x\n", - IVAL(hdr, 0))); - goto inval; - } - if (SVAL(hdr, 4) != SMB2_HDR_BODY) { - DEBUG(10, ("Got HDR len %d, expected %d\n", - SVAL(hdr, 4), SMB2_HDR_BODY)); - goto inval; - } - - full_size = len; - next_command_ofs = IVAL(hdr, SMB2_HDR_NEXT_COMMAND); - body_size = SVAL(hdr, SMB2_HDR_BODY); - - if (next_command_ofs != 0) { - if (next_command_ofs < (SMB2_HDR_BODY + 2)) { - goto inval; - } - if (next_command_ofs > full_size) { - goto inval; - } - full_size = next_command_ofs; - } - if (body_size < 2) { - goto inval; - } - body_size &= 0xfffe; - - if (body_size > (full_size - SMB2_HDR_BODY)) { - goto inval; - } - - iov_tmp = talloc_realloc(mem_ctx, iov, struct iovec, - num_iov + 3); - if (iov_tmp == NULL) { - TALLOC_FREE(iov); - return NT_STATUS_NO_MEMORY; - } - iov = iov_tmp; - cur = &iov[num_iov]; - num_iov += 3; - - cur[0].iov_base = hdr; - cur[0].iov_len = SMB2_HDR_BODY; - cur[1].iov_base = hdr + SMB2_HDR_BODY; - cur[1].iov_len = body_size; - cur[2].iov_base = hdr + SMB2_HDR_BODY + body_size; - cur[2].iov_len = full_size - (SMB2_HDR_BODY + body_size); - - taken += full_size; - } - - *piov = iov; - *pnum_iov = num_iov; - return NT_STATUS_OK; - -inval: - TALLOC_FREE(iov); - return NT_STATUS_INVALID_NETWORK_RESPONSE; -} - -static struct tevent_req *cli_smb2_find_pending(struct cli_state *cli, - uint64_t mid) -{ - int num_pending = talloc_array_length(cli->conn.pending); - int i; - - for (i=0; i<num_pending; i++) { - struct tevent_req *req = cli->conn.pending[i]; - struct smb2cli_req_state *state = - tevent_req_data(req, - struct smb2cli_req_state); - - if (mid == BVAL(state->hdr, SMB2_HDR_MESSAGE_ID)) { - return req; - } - } - return NULL; -} - -static void smb2cli_inbuf_received(struct tevent_req *subreq) -{ - struct cli_state *cli = - tevent_req_callback_data(subreq, - struct cli_state); - TALLOC_CTX *frame = talloc_stackframe(); - struct tevent_req *req; - struct smb2cli_req_state *state = NULL; - struct iovec *iov; - int i, num_iov; - NTSTATUS status; - uint8_t *inbuf; - ssize_t received; - int err; - size_t num_pending; - bool defer = true; - - received = read_smb_recv(subreq, frame, &inbuf, &err); - TALLOC_FREE(subreq); - if (received == -1) { - /* - * We need to close the connection and notify - * all pending requests. - */ - status = map_nt_error_from_unix_common(err); - smb2cli_notify_pending(cli, status); - TALLOC_FREE(frame); - return; - } - - status = smb2cli_inbuf_parse_compound(inbuf, frame, - &iov, &num_iov); - if (!NT_STATUS_IS_OK(status)) { - /* - * if we cannot parse the incoming pdu, - * the connection becomes unusable. - * - * We need to close the connection and notify - * all pending requests. - */ - smb2cli_notify_pending(cli, status); - TALLOC_FREE(frame); - return; - } - - for (i=0; i<num_iov; i+=3) { - uint8_t *inbuf_ref = NULL; - struct iovec *cur = &iov[i]; - uint8_t *inhdr = (uint8_t *)cur[0].iov_base; - uint16_t opcode = SVAL(inhdr, SMB2_HDR_OPCODE); - uint32_t flags = IVAL(inhdr, SMB2_HDR_FLAGS); - uint64_t mid = BVAL(inhdr, SMB2_HDR_MESSAGE_ID); - uint16_t req_opcode; - - req = cli_smb2_find_pending(cli, mid); - if (req == NULL) { - /* - * TODO: handle oplock breaks and async responses - */ - - /* - * We need to close the connection and notify - * all pending requests. - */ - status = NT_STATUS_INVALID_NETWORK_RESPONSE; - smb2cli_notify_pending(cli, status); - TALLOC_FREE(frame); - return; - } - state = tevent_req_data(req, struct smb2cli_req_state); - - req_opcode = SVAL(state->hdr, SMB2_HDR_OPCODE); - if (opcode != req_opcode) { - status = NT_STATUS_INVALID_NETWORK_RESPONSE; - smb2cli_notify_pending(cli, status); - TALLOC_FREE(frame); - return; - } - - if (!(flags & SMB2_HDR_FLAG_REDIRECT)) { - status = NT_STATUS_INVALID_NETWORK_RESPONSE; - smb2cli_notify_pending(cli, status); - TALLOC_FREE(frame); - return; - } - - status = NT_STATUS(IVAL(inhdr, SMB2_HDR_STATUS)); - if ((flags & SMB2_HDR_FLAG_ASYNC) && - NT_STATUS_EQUAL(status, STATUS_PENDING)) { - uint32_t req_flags = IVAL(state->hdr, SMB2_HDR_FLAGS); - uint64_t async_id = BVAL(inhdr, SMB2_HDR_ASYNC_ID); - - req_flags |= SMB2_HDR_FLAG_ASYNC; - SBVAL(state->hdr, SMB2_HDR_FLAGS, req_flags); - SBVAL(state->hdr, SMB2_HDR_ASYNC_ID, async_id); - continue; - } - - smb2cli_req_unset_pending(req); - - /* - * There might be more than one response - * we need to defer the notifications - */ - if ((num_iov == 4) && (talloc_array_length(cli->conn.pending) == 0)) { - defer = false; - } - - if (defer) { - tevent_req_defer_callback(req, state->ev); - } - - /* - * Note: here we use talloc_reference() in a way - * that does not expose it to the caller. - */ - inbuf_ref = talloc_reference(state->recv_iov, inbuf); - if (tevent_req_nomem(inbuf_ref, req)) { - continue; - } - - /* copy the related buffers */ - state->recv_iov[0] = cur[0]; - state->recv_iov[1] = cur[1]; - state->recv_iov[2] = cur[2]; - - tevent_req_done(req); - } - - TALLOC_FREE(frame); - - if (!defer) { - return; - } - - num_pending = talloc_array_length(cli->conn.pending); - if (num_pending == 0) { - if (state->cli->smb2.mid < UINT64_MAX) { - /* no more pending requests, so we are done for now */ - return; - } - - /* - * If there are no more requests possible, - * because we are out of message ids, - * we need to disconnect. - */ - smb2cli_notify_pending(cli, NT_STATUS_CONNECTION_ABORTED); - return; - } - req = cli->conn.pending[0]; - state = tevent_req_data(req, struct smb2cli_req_state); - - /* - * add the read_smb request that waits for the - * next answer from the server - */ - subreq = read_smb_send(cli->conn.pending, state->ev, cli->conn.fd); - if (subreq == NULL) { - smb2cli_notify_pending(cli, NT_STATUS_NO_MEMORY); - return; - } - tevent_req_set_callback(subreq, smb2cli_inbuf_received, cli); -} - -NTSTATUS smb2cli_req_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, - struct iovec **piov, - const struct smb2cli_req_expected_response *expected, - size_t num_expected) -{ - struct smb2cli_req_state *state = - tevent_req_data(req, - struct smb2cli_req_state); - NTSTATUS status; - size_t body_size; - bool found_status = false; - bool found_size = false; - size_t i; - - if (piov != NULL) { - *piov = NULL; - } - - if (tevent_req_is_nterror(req, &status)) { - for (i=0; i < num_expected; i++) { - if (NT_STATUS_EQUAL(status, expected[i].status)) { - found_status = true; - break; - } - } - - if (found_status) { - return NT_STATUS_UNEXPECTED_NETWORK_ERROR; - } - - return status; - } - - if (num_expected == 0) { - found_status = true; - found_size = true; - } - - status = NT_STATUS(IVAL(state->recv_iov[0].iov_base, SMB2_HDR_STATUS)); - body_size = SVAL(state->recv_iov[1].iov_base, 0); - - for (i=0; i < num_expected; i++) { - if (!NT_STATUS_EQUAL(status, expected[i].status)) { - continue; - } - - found_status = true; - if (expected[i].body_size == 0) { - found_size = true; - break; - } - - if (expected[i].body_size == body_size) { - found_size = true; - break; - } - } - - if (!found_status) { - return status; - } - - if (!found_size) { - return NT_STATUS_INVALID_NETWORK_RESPONSE; - } - - if (piov != NULL) { - *piov = talloc_move(mem_ctx, &state->recv_iov); - } - - return status; -} diff --git a/source3/libsmb/smb2cli_base.h b/source3/libsmb/smb2cli_base.h index 105f8ebe1c..efef350f5d 100644 --- a/source3/libsmb/smb2cli_base.h +++ b/source3/libsmb/smb2cli_base.h @@ -20,45 +20,52 @@ #ifndef __SMB2CLI_BASE_H__ #define __SMB2CLI_BASE_H__ -struct tevent_req *smb2cli_req_create(TALLOC_CTX *mem_ctx, - struct tevent_context *ev, - struct cli_state *cli, - uint16_t cmd, - uint32_t additional_flags, - uint32_t clear_flags, - unsigned int timeout, - uint32_t pid, - uint32_t tid, - uint64_t uid, - const uint8_t *fixed, - uint16_t fixed_len, - const uint8_t *dyn, - uint32_t dyn_len); -NTSTATUS smb2cli_req_compound_submit(struct tevent_req **reqs, - int num_reqs); +#include "../libcli/smb/smbXcli_base.h" -struct smb2cli_req_expected_response { - NTSTATUS status; - uint16_t body_size; -}; - -struct tevent_req *smb2cli_req_send(TALLOC_CTX *mem_ctx, +static inline struct tevent_req *cli_state_smb2cli_req_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct cli_state *cli, uint16_t cmd, uint32_t additional_flags, uint32_t clear_flags, - unsigned int timeout, + uint32_t timeout_msec, uint32_t pid, uint32_t tid, uint64_t uid, const uint8_t *fixed, uint16_t fixed_len, const uint8_t *dyn, - uint32_t dyn_len); -NTSTATUS smb2cli_req_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, - struct iovec **piov, - const struct smb2cli_req_expected_response *expected, - size_t num_expected); + uint32_t dyn_len) +{ + if (cli->smb2.conn == NULL) { + cli->smb2.conn = smbXcli_conn_create(cli, + cli->conn.fd, + cli->conn.remote_name, + SMB_SIGNING_OFF, + 0); /* smb1_capabilities */ + if (cli->smb2.conn == NULL) { + return NULL; + } + } + + return smb2cli_req_send(mem_ctx, ev, + cli->smb2.conn, cmd, + additional_flags, clear_flags, + timeout_msec, + pid, tid, uid, + fixed, fixed_len, + dyn, dyn_len); +} + +#define smb2cli_req_send(mem_ctx, ev, cli, cmd, \ + additional_flags, clear_flags, \ + timeout_msec, \ + pid, tid, uid, \ + fixed, fixed_len, dyn, dyn_len) \ + cli_state_smb2cli_req_send(mem_ctx, ev, cli, cmd, \ + additional_flags, clear_flags, \ + timeout_msec, \ + pid, tid, uid, \ + fixed, fixed_len, dyn, dyn_len) #endif |