diff options
| -rw-r--r-- | source3/Makefile.in | 1 | ||||
| -rw-r--r-- | source3/include/client.h | 7 | ||||
| -rw-r--r-- | source3/libsmb/smb2cli_base.c | 531 | ||||
| -rw-r--r-- | source3/libsmb/smb2cli_base.h | 47 | ||||
| -rwxr-xr-x | source3/wscript_build | 1 | 
5 files changed, 587 insertions, 0 deletions
diff --git a/source3/Makefile.in b/source3/Makefile.in index 8dc5153340..2b2f9c4624 100644 --- a/source3/Makefile.in +++ b/source3/Makefile.in @@ -600,6 +600,7 @@ LIBSMB_OBJ = libsmb/clientgen.o libsmb/cliconnect.o libsmb/clifile.o \  	     libsmb/clioplock.o libsmb/clirap2.o \  	     libsmb/smb_seal.o libsmb/async_smb.o \  	     libsmb/read_smb.o \ +	     libsmb/smb2cli_base.o \  	     libsmb/cli_np_tstream.o \  	     libsmb/smbsock_connect.o \  	     $(LIBSAMBA_OBJ) \ diff --git a/source3/include/client.h b/source3/include/client.h index 6486c76f83..e94c63e51f 100644 --- a/source3/include/client.h +++ b/source3/include/client.h @@ -139,6 +139,13 @@ struct cli_state {  	struct tevent_queue *outgoing;  	struct tevent_req **pending; + +	struct { +		uint64_t mid; +		uint32_t pid; +		uint32_t tid; +		uint64_t uid; +	} smb2;  };  struct file_info { diff --git a/source3/libsmb/smb2cli_base.c b/source3/libsmb/smb2cli_base.c new file mode 100644 index 0000000000..318f1a250b --- /dev/null +++ b/source3/libsmb/smb2cli_base.c @@ -0,0 +1,531 @@ +/* +   Unix SMB/CIFS implementation. +   smb2 lib +   Copyright (C) Volker Lendecke 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 "read_smb.h" +#include "smb2cli_base.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; +	uint16_t dyn_len; + +	uint8_t nbt[4]; +	uint8_t hdr[64]; +	uint8_t pad[7];	/* padding space for compounding */ + +	uint8_t *inbuf; +	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->pending); +	int i; + +	if (num_pending == 1) { +		/* +		 * The pending read_smb tevent_req is a child of +		 * cli->pending. So if nothing is pending anymore, we need to +		 * delete the socket read fde. +		 */ +		TALLOC_FREE(cli->pending); +		return; +	} + +	for (i=0; i<num_pending; i++) { +		if (req == cli->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->pending. +		 */ +		return; +	} + +	/* +	 * Remove ourselves from the cli->pending array +	 */ +	if (num_pending > 1) { +		cli->pending[i] = cli->pending[num_pending-1]; +	} + +	/* +	 * No NULL check here, we're shrinking by sizeof(void *), and +	 * talloc_realloc just adjusts the size for this. +	 */ +	cli->pending = talloc_realloc(NULL, cli->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->pending); + +	pending = talloc_realloc(cli, cli->pending, struct tevent_req *, +				 num_pending+1); +	if (pending == NULL) { +		return false; +	} +	pending[num_pending] = req; +	cli->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->pending, state->ev, cli->fd); +	if (subreq == NULL) { +		smb2cli_req_unset_pending(req); +		return false; +	} +	tevent_req_set_callback(subreq, smb2cli_inbuf_received, cli); +	return true; +} + +struct tevent_req *smb2cli_req_create(TALLOC_CTX *mem_ctx, +				       struct tevent_context *ev, +				       struct cli_state *cli, +				       uint16_t cmd, +				       uint32_t flags, +				       const uint8_t *fixed, +				       uint16_t fixed_len, +				       const uint8_t *dyn, +				       uint16_t dyn_len) +{ +	struct tevent_req *result; +	struct smb2cli_req_state *state; + +	result = tevent_req_create(mem_ctx, &state, +				   struct smb2cli_req_state); +	if (result == NULL) { +		return NULL; +	} +	state->ev = ev; +	state->cli = cli; + +	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,		cli->smb2.pid); +	SIVAL(state->hdr, SMB2_HDR_TID,		cli->smb2.tid); +	SBVAL(state->hdr, SMB2_HDR_SESSION_ID,	cli->smb2.uid); + +	return result; +} + +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; + +		state = tevent_req_data(reqs[i], struct smb2cli_req_state); + +		SBVAL(state->hdr, SMB2_HDR_MESSAGE_ID, state->cli->smb2.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_large(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->outgoing, +			     state->cli->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 flags, +				    const uint8_t *fixed, +				    uint16_t fixed_len, +				    const uint8_t *dyn, +				    uint16_t dyn_len) +{ +	struct tevent_req *req; +	NTSTATUS status; + +	req = smb2cli_req_create(mem_ctx, ev, cli, cmd, flags, +				 fixed, fixed_len, dyn, dyn_len); +	if (req == NULL) { +		return NULL; +	} +	if (!tevent_req_is_in_progress(req)) { +		return req; +	} +	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) { +		if (state->cli->fd != -1) { +			close(state->cli->fd); +			state->cli->fd = -1; +		} +		tevent_req_nterror(req, map_nt_error_from_unix(err)); +		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; + +	num_iov = 1; + +	iov = talloc_array(mem_ctx, struct iovec, num_iov); +	if (iov == NULL) { +		return NT_STATUS_NO_MEMORY; +	} +	iov[0].iov_base = buf; +	iov[0].iov_len = 4; + +	buflen = smb_len_large(buf) + 4; +	taken = 4; + +	while (taken < buflen) { +		size_t len = buflen - taken; +		uint8_t *hdr = buf + 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->pending); +	int i; + +	for (i=0; i<num_pending; i++) { +		struct tevent_req *req = cli->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); +	struct tevent_req *req; +	struct iovec *iov; +	int i, num_iov; +	NTSTATUS status; +	uint8_t *inbuf; +	ssize_t received; +	int err; + +	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; +	} + +	status = smb2cli_inbuf_parse_compound(inbuf, talloc_tos(), +					      &iov, &num_iov); +	if (!NT_STATUS_IS_OK(status)) { +		goto fail; +	} + +	for (i=1; i<num_iov; i+=3) { +		struct iovec *cur = &iov[i]; +		uint8_t *inhdr = (uint8_t *)cur[0].iov_base; +		struct smb2cli_req_state *state; + +		req = cli_smb2_find_pending( +			cli, BVAL(inhdr, SMB2_HDR_MESSAGE_ID)); +		if (req == NULL) { +			/* +			 * oplock breaks ?? +			 */ +			goto fail; +		} +		state = tevent_req_data(req, struct smb2cli_req_state); +		if (i+3 >= num_iov) { +			/* last in chain */ +			state->inbuf = inbuf; +		} +		state->recv_iov = cur; +		tevent_req_done(req); +	} +	return; + fail: +	/* +	 * Cancel all pending requests. We don't do a for-loop walking +	 * cli->pending because that array changes in +	 * cli_smb_req_destructor(). +	 */ +	while (talloc_array_length(cli->pending) > 0) { +		req = cli->pending[0]; +		talloc_set_destructor(req, NULL); +		smb2cli_req_destructor(req); +		tevent_req_nterror(req, status); +	} +} + +NTSTATUS smb2cli_req_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, +			  struct iovec **piov, int body_size) +{ +	struct smb2cli_req_state *state = +		tevent_req_data(req, +		struct smb2cli_req_state); +	NTSTATUS status; + +	if (tevent_req_is_nterror(req, &status)) { +		return status; +	} +	if (body_size != 0) { +		if (body_size != SVAL(state->recv_iov[1].iov_base, 0)) { +			return NT_STATUS_INVALID_NETWORK_RESPONSE; +		} +	} +	talloc_steal(req, state->inbuf); +	if (piov != NULL) { +		*piov = state->recv_iov; +	} + +	return NT_STATUS(IVAL(state->recv_iov[0].iov_base, SMB2_HDR_STATUS)); +} + +uint8_t *smb2cli_req_inbuf(struct tevent_req *req) +{ +	struct smb2cli_req_state *state = tevent_req_data( +		req, struct smb2cli_req_state); + +	return state->inbuf; +} diff --git a/source3/libsmb/smb2cli_base.h b/source3/libsmb/smb2cli_base.h new file mode 100644 index 0000000000..09a6b7faaa --- /dev/null +++ b/source3/libsmb/smb2cli_base.h @@ -0,0 +1,47 @@ +/* +   Unix SMB/CIFS implementation. +   smb2 client routines +   Copyright (C) Volker Lendecke 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/>. +*/ + +#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 flags, +				      const uint8_t *fixed, +				      uint16_t fixed_len, +				      const uint8_t *dyn, +				      uint16_t dyn_len); +NTSTATUS smb2cli_req_compound_submit(struct tevent_req **reqs, +				     int num_reqs); +struct tevent_req *smb2cli_req_send(TALLOC_CTX *mem_ctx, +				    struct tevent_context *ev, +				    struct cli_state *cli, +				    uint16_t cmd, +				    uint32_t flags, +				    const uint8_t *fixed, +				    uint16_t fixed_len, +				    const uint8_t *dyn, +				    uint16_t dyn_len); +NTSTATUS smb2cli_req_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, +			  struct iovec **piov, int body_size); +uint8_t *smb2cli_req_inbuf(struct tevent_req *req); + +#endif diff --git a/source3/wscript_build b/source3/wscript_build index bf2d92bf8e..7a277aa2c5 100755 --- a/source3/wscript_build +++ b/source3/wscript_build @@ -136,6 +136,7 @@ LIBSMB_SRC = '''libsmb/clientgen.c libsmb/cliconnect.c libsmb/clifile.c               libsmb/clistr.c libsmb/cliquota.c libsmb/clifsinfo.c libsmb/clidfs.c               libsmb/clioplock.c libsmb/clirap2.c               libsmb/smb_seal.c libsmb/async_smb.c  libsmb/read_smb.c +             libsmb/smb2cli_base.c               libsmb/cli_np_tstream.c               libsmb/reparse_symlink.c               libsmb/clisymlink.c  | 
