summaryrefslogtreecommitdiff
path: root/source4/libcli/smb2
diff options
context:
space:
mode:
Diffstat (limited to 'source4/libcli/smb2')
-rw-r--r--source4/libcli/smb2/break.c74
-rw-r--r--source4/libcli/smb2/cancel.c77
-rw-r--r--source4/libcli/smb2/close.c80
-rw-r--r--source4/libcli/smb2/config.mk10
-rw-r--r--source4/libcli/smb2/connect.c291
-rw-r--r--source4/libcli/smb2/create.c419
-rw-r--r--source4/libcli/smb2/find.c180
-rw-r--r--source4/libcli/smb2/flush.c70
-rw-r--r--source4/libcli/smb2/getinfo.c219
-rw-r--r--source4/libcli/smb2/ioctl.c109
-rw-r--r--source4/libcli/smb2/keepalive.c65
-rw-r--r--source4/libcli/smb2/lock.c82
-rw-r--r--source4/libcli/smb2/logoff.c69
-rw-r--r--source4/libcli/smb2/negprot.c113
-rw-r--r--source4/libcli/smb2/notify.c114
-rw-r--r--source4/libcli/smb2/read.c87
-rw-r--r--source4/libcli/smb2/request.c714
-rw-r--r--source4/libcli/smb2/session.c275
-rw-r--r--source4/libcli/smb2/setinfo.c122
-rw-r--r--source4/libcli/smb2/signing.c117
-rw-r--r--source4/libcli/smb2/smb2.h302
-rw-r--r--source4/libcli/smb2/smb2_calls.h110
-rw-r--r--source4/libcli/smb2/tcon.c112
-rw-r--r--source4/libcli/smb2/tdis.c65
-rw-r--r--source4/libcli/smb2/transport.c417
-rw-r--r--source4/libcli/smb2/util.c210
-rw-r--r--source4/libcli/smb2/write.c81
27 files changed, 4584 insertions, 0 deletions
diff --git a/source4/libcli/smb2/break.c b/source4/libcli/smb2/break.c
new file mode 100644
index 0000000000..fe0cceb829
--- /dev/null
+++ b/source4/libcli/smb2/break.c
@@ -0,0 +1,74 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ SMB2 client oplock break handling
+
+ Copyright (C) Stefan Metzmacher 2008
+
+ 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 "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+
+/*
+ send a break request
+*/
+struct smb2_request *smb2_break_send(struct smb2_tree *tree, struct smb2_break *io)
+{
+ struct smb2_request *req;
+
+ req = smb2_request_init_tree(tree, SMB2_OP_BREAK, 0x18, false, 0);
+ if (req == NULL) return NULL;
+
+ SCVAL(req->out.body, 0x02, io->in.oplock_level);
+ SCVAL(req->out.body, 0x03, io->in.reserved);
+ SIVAL(req->out.body, 0x04, io->in.reserved2);
+ smb2_push_handle(req->out.body+0x08, &io->in.file.handle);
+
+ smb2_transport_send(req);
+
+ return req;
+}
+
+
+/*
+ recv a break reply
+*/
+NTSTATUS smb2_break_recv(struct smb2_request *req, struct smb2_break *io)
+{
+ if (!smb2_request_receive(req) ||
+ !smb2_request_is_ok(req)) {
+ return smb2_request_destroy(req);
+ }
+
+ SMB2_CHECK_PACKET_RECV(req, 0x18, false);
+
+ io->out.oplock_level = CVAL(req->in.body, 0x02);
+ io->out.reserved = CVAL(req->in.body, 0x03);
+ io->out.reserved2 = IVAL(req->in.body, 0x04);
+ smb2_pull_handle(req->in.body+0x08, &io->out.file.handle);
+
+ return smb2_request_destroy(req);
+}
+
+/*
+ sync flush request
+*/
+NTSTATUS smb2_break(struct smb2_tree *tree, struct smb2_break *io)
+{
+ struct smb2_request *req = smb2_break_send(tree, io);
+ return smb2_break_recv(req, io);
+}
diff --git a/source4/libcli/smb2/cancel.c b/source4/libcli/smb2/cancel.c
new file mode 100644
index 0000000000..65f02187c1
--- /dev/null
+++ b/source4/libcli/smb2/cancel.c
@@ -0,0 +1,77 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ SMB2 client notify calls
+
+ Copyright (C) Stefan Metzmacher 2006
+
+ 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 "libcli/raw/libcliraw.h"
+#include "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+
+/*
+ send a cancel request
+*/
+NTSTATUS smb2_cancel(struct smb2_request *r)
+{
+ NTSTATUS status;
+ struct smb2_request *c;
+ uint32_t old_timeout;
+ uint64_t old_seqnum;
+
+ /*
+ * if we don't get a pending id yet, we just
+ * mark the request for pending, so that we directly
+ * send the cancel after getting the pending id
+ */
+ if (!r->cancel.can_cancel) {
+ r->cancel.do_cancel++;
+ return NT_STATUS_OK;
+ }
+
+ /* we don't want a seqmun for a SMB2 Cancel */
+ old_seqnum = r->transport->seqnum;
+ c = smb2_request_init(r->transport, SMB2_OP_CANCEL, 0x04, false, 0);
+ r->transport->seqnum = old_seqnum;
+ NT_STATUS_HAVE_NO_MEMORY(c);
+ c->seqnum = 0;
+
+ SIVAL(c->out.hdr, SMB2_HDR_FLAGS, 0x00000002);
+ SSVAL(c->out.hdr, SMB2_HDR_CREDIT, 0x0030);
+ SIVAL(c->out.hdr, SMB2_HDR_PID, r->cancel.pending_id);
+ SBVAL(c->out.hdr, SMB2_HDR_MESSAGE_ID, c->seqnum);
+ if (r->session) {
+ SBVAL(c->out.hdr, SMB2_HDR_SESSION_ID, r->session->uid);
+ }
+
+ SSVAL(c->out.body, 0x02, 0);
+
+ old_timeout = c->transport->options.request_timeout;
+ c->transport->options.request_timeout = 0;
+ smb2_transport_send(c);
+ c->transport->options.request_timeout = old_timeout;
+
+ if (c->state == SMB2_REQUEST_ERROR) {
+ status = c->status;
+ } else {
+ status = NT_STATUS_OK;
+ }
+
+ talloc_free(c);
+ return status;
+}
diff --git a/source4/libcli/smb2/close.c b/source4/libcli/smb2/close.c
new file mode 100644
index 0000000000..4e6f33095f
--- /dev/null
+++ b/source4/libcli/smb2/close.c
@@ -0,0 +1,80 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ SMB2 client close handling
+
+ Copyright (C) Andrew Tridgell 2005
+
+ 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 "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+
+/*
+ send a close request
+*/
+struct smb2_request *smb2_close_send(struct smb2_tree *tree, struct smb2_close *io)
+{
+ struct smb2_request *req;
+
+ req = smb2_request_init_tree(tree, SMB2_OP_CLOSE, 0x18, false, 0);
+ if (req == NULL) return NULL;
+
+ SSVAL(req->out.body, 0x02, io->in.flags);
+ SIVAL(req->out.body, 0x04, 0); /* pad */
+ smb2_push_handle(req->out.body+0x08, &io->in.file.handle);
+
+ smb2_transport_send(req);
+
+ return req;
+}
+
+
+/*
+ recv a close reply
+*/
+NTSTATUS smb2_close_recv(struct smb2_request *req, struct smb2_close *io)
+{
+ if (!smb2_request_receive(req) ||
+ !smb2_request_is_ok(req)) {
+ return smb2_request_destroy(req);
+ }
+
+ SMB2_CHECK_PACKET_RECV(req, 0x3C, false);
+
+ io->out.flags = SVAL(req->in.body, 0x02);
+ io->out._pad = IVAL(req->in.body, 0x04);
+ io->out.create_time = smbcli_pull_nttime(req->in.body, 0x08);
+ io->out.access_time = smbcli_pull_nttime(req->in.body, 0x10);
+ io->out.write_time = smbcli_pull_nttime(req->in.body, 0x18);
+ io->out.change_time = smbcli_pull_nttime(req->in.body, 0x20);
+ io->out.alloc_size = BVAL(req->in.body, 0x28);
+ io->out.size = BVAL(req->in.body, 0x30);
+ io->out.file_attr = IVAL(req->in.body, 0x38);
+
+ return smb2_request_destroy(req);
+}
+
+/*
+ sync close request
+*/
+NTSTATUS smb2_close(struct smb2_tree *tree, struct smb2_close *io)
+{
+ struct smb2_request *req = smb2_close_send(tree, io);
+ return smb2_close_recv(req, io);
+}
diff --git a/source4/libcli/smb2/config.mk b/source4/libcli/smb2/config.mk
new file mode 100644
index 0000000000..322bca1416
--- /dev/null
+++ b/source4/libcli/smb2/config.mk
@@ -0,0 +1,10 @@
+[SUBSYSTEM::LIBCLI_SMB2]
+PUBLIC_DEPENDENCIES = LIBCLI_RAW LIBPACKET gensec
+
+LIBCLI_SMB2_OBJ_FILES = $(addprefix $(libclisrcdir)/smb2/, \
+ transport.o request.o negprot.o session.o tcon.o \
+ create.o close.o connect.o getinfo.o write.o read.o \
+ setinfo.o find.o ioctl.o logoff.o tdis.o flush.o \
+ lock.o notify.o cancel.o keepalive.o break.o util.o signing.o)
+
+$(eval $(call proto_header_template,$(libclisrcdir)/smb2/smb2_proto.h,$(LIBCLI_SMB2_OBJ_FILES:.o=.c)))
diff --git a/source4/libcli/smb2/connect.c b/source4/libcli/smb2/connect.c
new file mode 100644
index 0000000000..43151943d3
--- /dev/null
+++ b/source4/libcli/smb2/connect.c
@@ -0,0 +1,291 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ SMB2 composite connection setup
+
+ Copyright (C) Andrew Tridgell 2005
+
+ 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 "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+#include "libcli/composite/composite.h"
+#include "libcli/resolve/resolve.h"
+#include "param/param.h"
+
+struct smb2_connect_state {
+ struct cli_credentials *credentials;
+ struct resolve_context *resolve_ctx;
+ const char *host;
+ const char *share;
+ struct smbcli_options options;
+ struct smb2_negprot negprot;
+ struct smb2_tree_connect tcon;
+ struct smb2_session *session;
+ struct smb2_tree *tree;
+};
+
+/*
+ continue after tcon reply
+*/
+static void continue_tcon(struct smb2_request *req)
+{
+ struct composite_context *c = talloc_get_type(req->async.private_data,
+ struct composite_context);
+ struct smb2_connect_state *state = talloc_get_type(c->private_data,
+ struct smb2_connect_state);
+
+ c->status = smb2_tree_connect_recv(req, &state->tcon);
+ if (!composite_is_ok(c)) return;
+
+ state->tree->tid = state->tcon.out.tid;
+
+ composite_done(c);
+}
+
+/*
+ continue after a session setup
+*/
+static void continue_session(struct composite_context *creq)
+{
+ struct composite_context *c = talloc_get_type(creq->async.private_data,
+ struct composite_context);
+ struct smb2_connect_state *state = talloc_get_type(c->private_data,
+ struct smb2_connect_state);
+ struct smb2_request *req;
+
+ c->status = smb2_session_setup_spnego_recv(creq);
+ if (!composite_is_ok(c)) return;
+
+ state->tree = smb2_tree_init(state->session, state, true);
+ if (composite_nomem(state->tree, c)) return;
+
+ state->tcon.in.reserved = 0;
+ state->tcon.in.path = talloc_asprintf(state, "\\\\%s\\%s",
+ state->host, state->share);
+ if (composite_nomem(state->tcon.in.path, c)) return;
+
+ req = smb2_tree_connect_send(state->tree, &state->tcon);
+ if (composite_nomem(req, c)) return;
+
+ req->async.fn = continue_tcon;
+ req->async.private_data = c;
+}
+
+/*
+ continue after negprot reply
+*/
+static void continue_negprot(struct smb2_request *req)
+{
+ struct composite_context *c = talloc_get_type(req->async.private_data,
+ struct composite_context);
+ struct smb2_connect_state *state = talloc_get_type(c->private_data,
+ struct smb2_connect_state);
+ struct smb2_transport *transport = req->transport;
+ struct composite_context *creq;
+
+ c->status = smb2_negprot_recv(req, c, &state->negprot);
+ if (!composite_is_ok(c)) return;
+
+ transport->negotiate.system_time = state->negprot.out.system_time;
+ transport->negotiate.server_start_time = state->negprot.out.server_start_time;
+ transport->negotiate.security_mode = state->negprot.out.security_mode;
+
+ switch (transport->options.signing) {
+ case SMB_SIGNING_OFF:
+ if (transport->negotiate.security_mode & SMB2_NEGOTIATE_SIGNING_REQUIRED) {
+ composite_error(c, NT_STATUS_ACCESS_DENIED);
+ return;
+ }
+ transport->signing_required = false;
+ break;
+ case SMB_SIGNING_SUPPORTED:
+ if (transport->negotiate.security_mode & SMB2_NEGOTIATE_SIGNING_REQUIRED) {
+ transport->signing_required = true;
+ } else {
+ transport->signing_required = false;
+ }
+ break;
+ case SMB_SIGNING_AUTO:
+ if (transport->negotiate.security_mode & SMB2_NEGOTIATE_SIGNING_ENABLED) {
+ transport->signing_required = true;
+ } else {
+ transport->signing_required = false;
+ }
+ break;
+ case SMB_SIGNING_REQUIRED:
+ if (transport->negotiate.security_mode & SMB2_NEGOTIATE_SIGNING_ENABLED) {
+ transport->signing_required = true;
+ } else {
+ composite_error(c, NT_STATUS_ACCESS_DENIED);
+ return;
+ }
+ break;
+ }
+
+
+ state->session = smb2_session_init(transport, global_loadparm, state, true);
+ if (composite_nomem(state->session, c)) return;
+
+ creq = smb2_session_setup_spnego_send(state->session, state->credentials);
+
+ composite_continue(c, creq, continue_session, c);
+}
+
+/*
+ continue after a socket connect completes
+*/
+static void continue_socket(struct composite_context *creq)
+{
+ struct composite_context *c = talloc_get_type(creq->async.private_data,
+ struct composite_context);
+ struct smb2_connect_state *state = talloc_get_type(c->private_data,
+ struct smb2_connect_state);
+ struct smbcli_socket *sock;
+ struct smb2_transport *transport;
+ struct smb2_request *req;
+ uint16_t dialects[2];
+
+ c->status = smbcli_sock_connect_recv(creq, state, &sock);
+ if (!composite_is_ok(c)) return;
+
+ transport = smb2_transport_init(sock, state, &state->options);
+ if (composite_nomem(transport, c)) return;
+
+ ZERO_STRUCT(state->negprot);
+ state->negprot.in.dialect_count = 2;
+ switch (transport->options.signing) {
+ case SMB_SIGNING_OFF:
+ state->negprot.in.security_mode = 0;
+ break;
+ case SMB_SIGNING_SUPPORTED:
+ case SMB_SIGNING_AUTO:
+ state->negprot.in.security_mode = SMB2_NEGOTIATE_SIGNING_ENABLED;
+ break;
+ case SMB_SIGNING_REQUIRED:
+ state->negprot.in.security_mode =
+ SMB2_NEGOTIATE_SIGNING_ENABLED | SMB2_NEGOTIATE_SIGNING_REQUIRED;
+ break;
+ }
+ state->negprot.in.capabilities = 0;
+ unix_to_nt_time(&state->negprot.in.start_time, time(NULL));
+ dialects[0] = 0;
+ dialects[1] = SMB2_DIALECT_REVISION;
+ state->negprot.in.dialects = dialects;
+
+ req = smb2_negprot_send(transport, &state->negprot);
+ if (composite_nomem(req, c)) return;
+
+ req->async.fn = continue_negprot;
+ req->async.private_data = c;
+}
+
+
+/*
+ continue after a resolve finishes
+*/
+static void continue_resolve(struct composite_context *creq)
+{
+ struct composite_context *c = talloc_get_type(creq->async.private_data,
+ struct composite_context);
+ struct smb2_connect_state *state = talloc_get_type(c->private_data,
+ struct smb2_connect_state);
+ const char *addr;
+ const char *ports[2] = { "445", NULL };
+
+ c->status = resolve_name_recv(creq, state, &addr);
+ if (!composite_is_ok(c)) return;
+
+ creq = smbcli_sock_connect_send(state, addr, ports, state->host, state->resolve_ctx, c->event_ctx);
+
+ composite_continue(c, creq, continue_socket, c);
+}
+
+/*
+ a composite function that does a full negprot/sesssetup/tcon, returning
+ a connected smb2_tree
+ */
+struct composite_context *smb2_connect_send(TALLOC_CTX *mem_ctx,
+ const char *host,
+ const char *share,
+ struct resolve_context *resolve_ctx,
+ struct cli_credentials *credentials,
+ struct event_context *ev,
+ struct smbcli_options *options)
+{
+ struct composite_context *c;
+ struct smb2_connect_state *state;
+ struct nbt_name name;
+ struct composite_context *creq;
+
+ c = composite_create(mem_ctx, ev);
+ if (c == NULL) return NULL;
+
+ state = talloc(c, struct smb2_connect_state);
+ if (composite_nomem(state, c)) return c;
+ c->private_data = state;
+
+ state->credentials = credentials;
+ state->options = *options;
+ state->host = talloc_strdup(c, host);
+ if (composite_nomem(state->host, c)) return c;
+ state->share = talloc_strdup(c, share);
+ if (composite_nomem(state->share, c)) return c;
+ state->resolve_ctx = talloc_reference(state, resolve_ctx);
+
+ ZERO_STRUCT(name);
+ name.name = host;
+
+ creq = resolve_name_send(resolve_ctx, &name, c->event_ctx);
+ composite_continue(c, creq, continue_resolve, c);
+ return c;
+}
+
+/*
+ receive a connect reply
+*/
+NTSTATUS smb2_connect_recv(struct composite_context *c, TALLOC_CTX *mem_ctx,
+ struct smb2_tree **tree)
+{
+ NTSTATUS status;
+ struct smb2_connect_state *state = talloc_get_type(c->private_data,
+ struct smb2_connect_state);
+ status = composite_wait(c);
+ if (NT_STATUS_IS_OK(status)) {
+ *tree = talloc_steal(mem_ctx, state->tree);
+ }
+ talloc_free(c);
+ return status;
+}
+
+/*
+ sync version of smb2_connect
+*/
+NTSTATUS smb2_connect(TALLOC_CTX *mem_ctx,
+ const char *host, const char *share,
+ struct resolve_context *resolve_ctx,
+ struct cli_credentials *credentials,
+ struct smb2_tree **tree,
+ struct event_context *ev,
+ struct smbcli_options *options)
+{
+ struct composite_context *c = smb2_connect_send(mem_ctx, host, share,
+ resolve_ctx,
+ credentials, ev, options);
+ return smb2_connect_recv(c, mem_ctx, tree);
+}
diff --git a/source4/libcli/smb2/create.c b/source4/libcli/smb2/create.c
new file mode 100644
index 0000000000..8a40e56a00
--- /dev/null
+++ b/source4/libcli/smb2/create.c
@@ -0,0 +1,419 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ SMB2 client tree handling
+
+ Copyright (C) Andrew Tridgell 2005
+
+ 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 "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+#include "librpc/gen_ndr/ndr_security.h"
+
+
+/*
+ parse a set of SMB2 create blobs
+*/
+NTSTATUS smb2_create_blob_parse(TALLOC_CTX *mem_ctx, const DATA_BLOB buffer,
+ struct smb2_create_blobs *blobs)
+{
+ const uint8_t *data = buffer.data;
+ uint32_t remaining = buffer.length;
+
+ while (remaining > 0) {
+ uint32_t next;
+ uint32_t name_offset, name_length;
+ uint32_t reserved, data_offset;
+ uint32_t data_length;
+ char *tag;
+ DATA_BLOB b;
+ NTSTATUS status;
+
+ if (remaining < 16) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ next = IVAL(data, 0);
+ name_offset = SVAL(data, 4);
+ name_length = SVAL(data, 6);
+ reserved = SVAL(data, 8);
+ data_offset = SVAL(data, 10);
+ data_length = IVAL(data, 12);
+
+ if ((next & 0x7) != 0 ||
+ next > remaining ||
+ name_offset < 16 ||
+ name_offset > remaining ||
+ name_length != 4 || /* windows enforces this */
+ name_offset + name_length > remaining ||
+ data_offset < name_offset + name_length ||
+ data_offset > remaining ||
+ data_offset + (uint64_t)data_length > remaining) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ tag = talloc_strndup(mem_ctx, (const char *)data + name_offset, name_length);
+ if (tag == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ b = data_blob_const(data+data_offset, data_length);
+ status = smb2_create_blob_add(mem_ctx, blobs, tag, b);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ talloc_free(tag);
+
+ if (next == 0) break;
+
+ remaining -= next;
+ data += next;
+
+ if (remaining < 16) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ add a blob to a smb2_create attribute blob
+*/
+static NTSTATUS smb2_create_blob_push_one(TALLOC_CTX *mem_ctx, DATA_BLOB *buffer,
+ const struct smb2_create_blob *blob,
+ bool last)
+{
+ uint32_t ofs = buffer->length;
+ size_t tag_length = strlen(blob->tag);
+ uint8_t pad = smb2_padding_size(blob->data.length+tag_length, 4);
+
+ if (!data_blob_realloc(mem_ctx, buffer,
+ buffer->length + 0x14 + tag_length + blob->data.length + pad))
+ return NT_STATUS_NO_MEMORY;
+
+ if (last) {
+ SIVAL(buffer->data, ofs+0x00, 0);
+ } else {
+ SIVAL(buffer->data, ofs+0x00, 0x14 + tag_length + blob->data.length + pad);
+ }
+ SSVAL(buffer->data, ofs+0x04, 0x10); /* offset of tag */
+ SIVAL(buffer->data, ofs+0x06, tag_length); /* tag length */
+ SSVAL(buffer->data, ofs+0x0A, 0x14 + tag_length); /* offset of data */
+ SIVAL(buffer->data, ofs+0x0C, blob->data.length);
+ memcpy(buffer->data+ofs+0x10, blob->tag, tag_length);
+ SIVAL(buffer->data, ofs+0x10+tag_length, 0); /* pad? */
+ memcpy(buffer->data+ofs+0x14+tag_length, blob->data.data, blob->data.length);
+ memset(buffer->data+ofs+0x14+tag_length+blob->data.length, 0, pad);
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ create a buffer of a set of create blobs
+*/
+NTSTATUS smb2_create_blob_push(TALLOC_CTX *mem_ctx, DATA_BLOB *buffer,
+ const struct smb2_create_blobs blobs)
+{
+ int i;
+ NTSTATUS status;
+
+ *buffer = data_blob(NULL, 0);
+ for (i=0; i < blobs.num_blobs; i++) {
+ bool last = false;
+ const struct smb2_create_blob *c;
+
+ if ((i + 1) == blobs.num_blobs) {
+ last = true;
+ }
+
+ c = &blobs.blobs[i];
+ status = smb2_create_blob_push_one(mem_ctx, buffer, c, last);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ }
+ return NT_STATUS_OK;
+}
+
+
+NTSTATUS smb2_create_blob_add(TALLOC_CTX *mem_ctx, struct smb2_create_blobs *b,
+ const char *tag, DATA_BLOB data)
+{
+ struct smb2_create_blob *array;
+
+ array = talloc_realloc(mem_ctx, b->blobs,
+ struct smb2_create_blob,
+ b->num_blobs + 1);
+ NT_STATUS_HAVE_NO_MEMORY(array);
+ b->blobs = array;
+
+ b->blobs[b->num_blobs].tag = talloc_strdup(b->blobs, tag);
+ NT_STATUS_HAVE_NO_MEMORY(b->blobs[b->num_blobs].tag);
+
+ if (data.data) {
+ b->blobs[b->num_blobs].data = data_blob_talloc(b->blobs,
+ data.data,
+ data.length);
+ NT_STATUS_HAVE_NO_MEMORY(b->blobs[b->num_blobs].data.data);
+ } else {
+ b->blobs[b->num_blobs].data = data_blob(NULL, 0);
+ }
+
+ b->num_blobs += 1;
+
+ return NT_STATUS_OK;
+}
+
+/*
+ send a create request
+*/
+struct smb2_request *smb2_create_send(struct smb2_tree *tree, struct smb2_create *io)
+{
+ struct smb2_request *req;
+ NTSTATUS status;
+ DATA_BLOB blob;
+ struct smb2_create_blobs blobs;
+ int i;
+
+ ZERO_STRUCT(blobs);
+
+ req = smb2_request_init_tree(tree, SMB2_OP_CREATE, 0x38, true, 0);
+ if (req == NULL) return NULL;
+
+ SCVAL(req->out.body, 0x02, io->in.security_flags);
+ SCVAL(req->out.body, 0x03, io->in.oplock_level);
+ SIVAL(req->out.body, 0x04, io->in.impersonation_level);
+ SBVAL(req->out.body, 0x08, io->in.create_flags);
+ SBVAL(req->out.body, 0x10, io->in.reserved);
+ SIVAL(req->out.body, 0x18, io->in.desired_access);
+ SIVAL(req->out.body, 0x1C, io->in.file_attributes);
+ SIVAL(req->out.body, 0x20, io->in.share_access);
+ SIVAL(req->out.body, 0x24, io->in.create_disposition);
+ SIVAL(req->out.body, 0x28, io->in.create_options);
+
+ status = smb2_push_o16s16_string(&req->out, 0x2C, io->in.fname);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(req);
+ return NULL;
+ }
+
+ /* now add all the optional blobs */
+ if (io->in.eas.num_eas != 0) {
+ DATA_BLOB b = data_blob_talloc(req, NULL,
+ ea_list_size_chained(io->in.eas.num_eas, io->in.eas.eas, 4));
+ ea_put_list_chained(b.data, io->in.eas.num_eas, io->in.eas.eas, 4);
+ status = smb2_create_blob_add(req, &blobs,
+ SMB2_CREATE_TAG_EXTA, b);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(req);
+ return NULL;
+ }
+ data_blob_free(&b);
+ }
+
+ /* an empty MxAc tag seems to be used to ask the server to
+ return the maximum access mask allowed on the file */
+ if (io->in.query_maximal_access) {
+ /* TODO: MS-SMB2 2.2.13.2.5 says this can contain a timestamp? What to do
+ with that if it doesn't match? */
+ status = smb2_create_blob_add(req, &blobs,
+ SMB2_CREATE_TAG_MXAC, data_blob(NULL, 0));
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(req);
+ return NULL;
+ }
+ }
+
+ if (io->in.alloc_size != 0) {
+ uint8_t data[8];
+ SBVAL(data, 0, io->in.alloc_size);
+ status = smb2_create_blob_add(req, &blobs,
+ SMB2_CREATE_TAG_ALSI, data_blob_const(data, 8));
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(req);
+ return NULL;
+ }
+ }
+
+ if (io->in.durable_open) {
+ status = smb2_create_blob_add(req, &blobs,
+ SMB2_CREATE_TAG_DHNQ, data_blob_talloc_zero(req, 16));
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(req);
+ return NULL;
+ }
+ }
+
+ if (io->in.durable_handle) {
+ uint8_t data[16];
+ smb2_push_handle(data, io->in.durable_handle);
+ status = smb2_create_blob_add(req, &blobs,
+ SMB2_CREATE_TAG_DHNC, data_blob_const(data, 16));
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(req);
+ return NULL;
+ }
+ }
+
+ if (io->in.timewarp) {
+ uint8_t data[8];
+ SBVAL(data, 0, io->in.timewarp);
+ status = smb2_create_blob_add(req, &blobs,
+ SMB2_CREATE_TAG_TWRP, data_blob_const(data, 8));
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(req);
+ return NULL;
+ }
+ }
+
+ if (io->in.sec_desc) {
+ enum ndr_err_code ndr_err;
+ DATA_BLOB sd_blob;
+ ndr_err = ndr_push_struct_blob(&sd_blob, req, NULL,
+ io->in.sec_desc,
+ (ndr_push_flags_fn_t)ndr_push_security_descriptor);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ talloc_free(req);
+ return NULL;
+ }
+ status = smb2_create_blob_add(req, &blobs,
+ SMB2_CREATE_TAG_SECD, sd_blob);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(req);
+ return NULL;
+ }
+ }
+
+ if (io->in.query_on_disk_id) {
+ status = smb2_create_blob_add(req, &blobs,
+ SMB2_CREATE_TAG_QFID, data_blob(NULL, 0));
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(req);
+ return NULL;
+ }
+ }
+
+ /* and any custom blobs */
+ for (i=0;i<io->in.blobs.num_blobs;i++) {
+ status = smb2_create_blob_add(req, &blobs,
+ io->in.blobs.blobs[i].tag,
+ io->in.blobs.blobs[i].data);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(req);
+ return NULL;
+ }
+ }
+
+
+ status = smb2_create_blob_push(req, &blob, blobs);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(req);
+ return NULL;
+ }
+
+ status = smb2_push_o32s32_blob(&req->out, 0x30, blob);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(req);
+ return NULL;
+ }
+
+ data_blob_free(&blob);
+
+ smb2_transport_send(req);
+
+ return req;
+}
+
+
+/*
+ recv a create reply
+*/
+NTSTATUS smb2_create_recv(struct smb2_request *req, TALLOC_CTX *mem_ctx, struct smb2_create *io)
+{
+ NTSTATUS status;
+ DATA_BLOB blob;
+ int i;
+
+ if (!smb2_request_receive(req) ||
+ !smb2_request_is_ok(req)) {
+ return smb2_request_destroy(req);
+ }
+
+ SMB2_CHECK_PACKET_RECV(req, 0x58, true);
+ ZERO_STRUCT(io->out);
+ io->out.oplock_level = CVAL(req->in.body, 0x02);
+ io->out.reserved = CVAL(req->in.body, 0x03);
+ io->out.create_action = IVAL(req->in.body, 0x04);
+ io->out.create_time = smbcli_pull_nttime(req->in.body, 0x08);
+ io->out.access_time = smbcli_pull_nttime(req->in.body, 0x10);
+ io->out.write_time = smbcli_pull_nttime(req->in.body, 0x18);
+ io->out.change_time = smbcli_pull_nttime(req->in.body, 0x20);
+ io->out.alloc_size = BVAL(req->in.body, 0x28);
+ io->out.size = BVAL(req->in.body, 0x30);
+ io->out.file_attr = IVAL(req->in.body, 0x38);
+ io->out.reserved2 = IVAL(req->in.body, 0x3C);
+ smb2_pull_handle(req->in.body+0x40, &io->out.file.handle);
+ status = smb2_pull_o32s32_blob(&req->in, mem_ctx, req->in.body+0x50, &blob);
+ if (!NT_STATUS_IS_OK(status)) {
+ smb2_request_destroy(req);
+ return status;
+ }
+
+ status = smb2_create_blob_parse(mem_ctx, blob, &io->out.blobs);
+ if (!NT_STATUS_IS_OK(status)) {
+ smb2_request_destroy(req);
+ return status;
+ }
+
+ /* pull out the parsed blobs */
+ for (i=0;i<io->out.blobs.num_blobs;i++) {
+ if (strcmp(io->out.blobs.blobs[i].tag, SMB2_CREATE_TAG_MXAC) == 0) {
+ /* TODO: this also contains a status field in
+ first 4 bytes */
+ if (io->out.blobs.blobs[i].data.length != 8) {
+ smb2_request_destroy(req);
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+ io->out.maximal_access = IVAL(io->out.blobs.blobs[i].data.data, 4);
+ }
+ if (strcmp(io->out.blobs.blobs[i].tag, SMB2_CREATE_TAG_QFID) == 0) {
+ if (io->out.blobs.blobs[i].data.length != 32) {
+ smb2_request_destroy(req);
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+ memcpy(io->out.on_disk_id, io->out.blobs.blobs[i].data.data, 32);
+ }
+ }
+
+ data_blob_free(&blob);
+
+ return smb2_request_destroy(req);
+}
+
+/*
+ sync create request
+*/
+NTSTATUS smb2_create(struct smb2_tree *tree, TALLOC_CTX *mem_ctx, struct smb2_create *io)
+{
+ struct smb2_request *req = smb2_create_send(tree, io);
+ return smb2_create_recv(req, mem_ctx, io);
+}
diff --git a/source4/libcli/smb2/find.c b/source4/libcli/smb2/find.c
new file mode 100644
index 0000000000..8ebfd81bcd
--- /dev/null
+++ b/source4/libcli/smb2/find.c
@@ -0,0 +1,180 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ SMB2 client find calls
+
+ Copyright (C) Andrew Tridgell 2005
+
+ 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 "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+
+/*
+ send a find request
+*/
+struct smb2_request *smb2_find_send(struct smb2_tree *tree, struct smb2_find *io)
+{
+ struct smb2_request *req;
+ NTSTATUS status;
+
+ req = smb2_request_init_tree(tree, SMB2_OP_FIND, 0x20, true, 0);
+ if (req == NULL) return NULL;
+
+ SCVAL(req->out.body, 0x02, io->in.level);
+ SCVAL(req->out.body, 0x03, io->in.continue_flags);
+ SIVAL(req->out.body, 0x04, io->in.file_index);
+ smb2_push_handle(req->out.body+0x08, &io->in.file.handle);
+
+ status = smb2_push_o16s16_string(&req->out, 0x18, io->in.pattern);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(req);
+ return NULL;
+ }
+
+ SIVAL(req->out.body, 0x1C, io->in.max_response_size);
+
+ smb2_transport_send(req);
+
+ return req;
+}
+
+
+/*
+ recv a find reply
+*/
+NTSTATUS smb2_find_recv(struct smb2_request *req, TALLOC_CTX *mem_ctx,
+ struct smb2_find *io)
+{
+ NTSTATUS status;
+
+ if (!smb2_request_receive(req) ||
+ smb2_request_is_error(req)) {
+ return smb2_request_destroy(req);
+ }
+
+ SMB2_CHECK_PACKET_RECV(req, 0x08, true);
+
+ status = smb2_pull_o16s32_blob(&req->in, mem_ctx,
+ req->in.body+0x02, &io->out.blob);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ return smb2_request_destroy(req);
+}
+
+/*
+ sync find request
+*/
+NTSTATUS smb2_find(struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
+ struct smb2_find *io)
+{
+ struct smb2_request *req = smb2_find_send(tree, io);
+ return smb2_find_recv(req, mem_ctx, io);
+}
+
+
+/*
+ a varient of smb2_find_recv that parses the resulting blob into
+ smb_search_data structures
+*/
+NTSTATUS smb2_find_level_recv(struct smb2_request *req, TALLOC_CTX *mem_ctx,
+ uint8_t level, uint_t *count,
+ union smb_search_data **io)
+{
+ struct smb2_find f;
+ NTSTATUS status;
+ DATA_BLOB b;
+ enum smb_search_data_level smb_level;
+ uint_t next_ofs=0;
+
+ switch (level) {
+ case SMB2_FIND_DIRECTORY_INFO:
+ smb_level = RAW_SEARCH_DATA_DIRECTORY_INFO;
+ break;
+ case SMB2_FIND_FULL_DIRECTORY_INFO:
+ smb_level = RAW_SEARCH_DATA_FULL_DIRECTORY_INFO;
+ break;
+ case SMB2_FIND_BOTH_DIRECTORY_INFO:
+ smb_level = RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO;
+ break;
+ case SMB2_FIND_NAME_INFO:
+ smb_level = RAW_SEARCH_DATA_NAME_INFO;
+ break;
+ case SMB2_FIND_ID_FULL_DIRECTORY_INFO:
+ smb_level = RAW_SEARCH_DATA_ID_FULL_DIRECTORY_INFO;
+ break;
+ case SMB2_FIND_ID_BOTH_DIRECTORY_INFO:
+ smb_level = RAW_SEARCH_DATA_ID_BOTH_DIRECTORY_INFO;
+ break;
+ default:
+ return NT_STATUS_INVALID_INFO_CLASS;
+ }
+
+ status = smb2_find_recv(req, mem_ctx, &f);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ b = f.out.blob;
+ *io = NULL;
+ *count = 0;
+
+ do {
+ union smb_search_data *io2;
+
+ io2 = talloc_realloc(mem_ctx, *io, union smb_search_data, (*count)+1);
+ if (io2 == NULL) {
+ data_blob_free(&f.out.blob);
+ talloc_free(*io);
+ return NT_STATUS_NO_MEMORY;
+ }
+ *io = io2;
+
+ status = smb_raw_search_common(*io, smb_level, &b, (*io) + (*count),
+ &next_ofs, STR_UNICODE);
+
+ if (NT_STATUS_IS_OK(status) &&
+ next_ofs >= b.length) {
+ data_blob_free(&f.out.blob);
+ talloc_free(*io);
+ return NT_STATUS_INFO_LENGTH_MISMATCH;
+ }
+
+ (*count)++;
+
+ b = data_blob_const(b.data+next_ofs, b.length - next_ofs);
+ } while (NT_STATUS_IS_OK(status) && next_ofs != 0);
+
+ data_blob_free(&f.out.blob);
+
+ return NT_STATUS_OK;
+}
+
+/*
+ a varient of smb2_find that parses the resulting blob into
+ smb_search_data structures
+*/
+NTSTATUS smb2_find_level(struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
+ struct smb2_find *f,
+ uint_t *count, union smb_search_data **io)
+{
+ struct smb2_request *req;
+
+ req = smb2_find_send(tree, f);
+ return smb2_find_level_recv(req, mem_ctx, f->in.level, count, io);
+}
diff --git a/source4/libcli/smb2/flush.c b/source4/libcli/smb2/flush.c
new file mode 100644
index 0000000000..577d1ba1ba
--- /dev/null
+++ b/source4/libcli/smb2/flush.c
@@ -0,0 +1,70 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ SMB2 client flush handling
+
+ Copyright (C) Andrew Tridgell 2005
+
+ 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 "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+
+/*
+ send a flush request
+*/
+struct smb2_request *smb2_flush_send(struct smb2_tree *tree, struct smb2_flush *io)
+{
+ struct smb2_request *req;
+
+ req = smb2_request_init_tree(tree, SMB2_OP_FLUSH, 0x18, false, 0);
+ if (req == NULL) return NULL;
+
+ SSVAL(req->out.body, 0x02, io->in.reserved1);
+ SIVAL(req->out.body, 0x04, io->in.reserved2);
+ smb2_push_handle(req->out.body+0x08, &io->in.file.handle);
+
+ smb2_transport_send(req);
+
+ return req;
+}
+
+
+/*
+ recv a flush reply
+*/
+NTSTATUS smb2_flush_recv(struct smb2_request *req, struct smb2_flush *io)
+{
+ if (!smb2_request_receive(req) ||
+ !smb2_request_is_ok(req)) {
+ return smb2_request_destroy(req);
+ }
+
+ SMB2_CHECK_PACKET_RECV(req, 0x04, false);
+
+ io->out.reserved = SVAL(req->in.body, 0x02);
+
+ return smb2_request_destroy(req);
+}
+
+/*
+ sync flush request
+*/
+NTSTATUS smb2_flush(struct smb2_tree *tree, struct smb2_flush *io)
+{
+ struct smb2_request *req = smb2_flush_send(tree, io);
+ return smb2_flush_recv(req, io);
+}
diff --git a/source4/libcli/smb2/getinfo.c b/source4/libcli/smb2/getinfo.c
new file mode 100644
index 0000000000..b462bab1de
--- /dev/null
+++ b/source4/libcli/smb2/getinfo.c
@@ -0,0 +1,219 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ SMB2 client getinfo calls
+
+ Copyright (C) Andrew Tridgell 2005
+
+ 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 "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+
+/*
+ send a getinfo request
+*/
+struct smb2_request *smb2_getinfo_send(struct smb2_tree *tree, struct smb2_getinfo *io)
+{
+ struct smb2_request *req;
+ NTSTATUS status;
+
+ req = smb2_request_init_tree(tree, SMB2_OP_GETINFO, 0x28, true,
+ io->in.blob.length);
+ if (req == NULL) return NULL;
+
+ SCVAL(req->out.body, 0x02, io->in.info_type);
+ SCVAL(req->out.body, 0x03, io->in.info_class);
+ SIVAL(req->out.body, 0x04, io->in.output_buffer_length);
+ SIVAL(req->out.body, 0x0C, io->in.reserved);
+ SIVAL(req->out.body, 0x08, io->in.input_buffer_length);
+ SIVAL(req->out.body, 0x10, io->in.additional_information);
+ SIVAL(req->out.body, 0x14, io->in.getinfo_flags);
+ smb2_push_handle(req->out.body+0x18, &io->in.file.handle);
+
+ /* this blob is used for quota queries */
+ status = smb2_push_o32s32_blob(&req->out, 0x08, io->in.blob);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(req);
+ return NULL;
+ }
+ smb2_transport_send(req);
+
+ return req;
+}
+
+
+/*
+ recv a getinfo reply
+*/
+NTSTATUS smb2_getinfo_recv(struct smb2_request *req, TALLOC_CTX *mem_ctx,
+ struct smb2_getinfo *io)
+{
+ NTSTATUS status;
+
+ if (!smb2_request_receive(req) ||
+ smb2_request_is_error(req)) {
+ return smb2_request_destroy(req);
+ }
+
+ SMB2_CHECK_PACKET_RECV(req, 0x08, true);
+
+ status = smb2_pull_o16s16_blob(&req->in, mem_ctx, req->in.body+0x02, &io->out.blob);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ return smb2_request_destroy(req);
+}
+
+/*
+ sync getinfo request
+*/
+NTSTATUS smb2_getinfo(struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
+ struct smb2_getinfo *io)
+{
+ struct smb2_request *req = smb2_getinfo_send(tree, io);
+ return smb2_getinfo_recv(req, mem_ctx, io);
+}
+
+
+/*
+ map a generic info level to a SMB2 info level
+*/
+uint16_t smb2_getinfo_map_level(uint16_t level, uint8_t class)
+{
+ if (class == SMB2_GETINFO_FILE &&
+ level == RAW_FILEINFO_SEC_DESC) {
+ return SMB2_GETINFO_SECURITY;
+ }
+ if ((level & 0xFF) == class) {
+ return level;
+ } else if (level > 1000) {
+ return ((level-1000)<<8) | class;
+ }
+ DEBUG(0,("Unable to map SMB2 info level 0x%04x of class %d\n", level, class));
+ return 0;
+}
+
+/*
+ level specific getinfo call - async send
+*/
+struct smb2_request *smb2_getinfo_file_send(struct smb2_tree *tree, union smb_fileinfo *io)
+{
+ struct smb2_getinfo b;
+ uint16_t smb2_level = smb2_getinfo_map_level(io->generic.level, SMB2_GETINFO_FILE);
+
+ if (smb2_level == 0) {
+ return NULL;
+ }
+
+ ZERO_STRUCT(b);
+ b.in.info_type = smb2_level & 0xFF;
+ b.in.info_class = smb2_level >> 8;
+ b.in.output_buffer_length = 0x10000;
+ b.in.input_buffer_length = 0;
+ b.in.file.handle = io->generic.in.file.handle;
+
+ if (io->generic.level == RAW_FILEINFO_SEC_DESC) {
+ b.in.additional_information = io->query_secdesc.in.secinfo_flags;
+ }
+ if (io->generic.level == RAW_FILEINFO_SMB2_ALL_EAS) {
+ b.in.getinfo_flags = io->all_eas.in.continue_flags;
+ }
+
+ return smb2_getinfo_send(tree, &b);
+}
+
+/*
+ recv a getinfo reply and parse the level info
+*/
+NTSTATUS smb2_getinfo_file_recv(struct smb2_request *req, TALLOC_CTX *mem_ctx,
+ union smb_fileinfo *io)
+{
+ struct smb2_getinfo b;
+ NTSTATUS status;
+
+ status = smb2_getinfo_recv(req, mem_ctx, &b);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ status = smb_raw_fileinfo_passthru_parse(&b.out.blob, mem_ctx, io->generic.level, io);
+ data_blob_free(&b.out.blob);
+
+ return status;
+}
+
+/*
+ level specific getinfo call
+*/
+NTSTATUS smb2_getinfo_file(struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
+ union smb_fileinfo *io)
+{
+ struct smb2_request *req = smb2_getinfo_file_send(tree, io);
+ return smb2_getinfo_file_recv(req, mem_ctx, io);
+}
+
+
+/*
+ level specific getinfo call - async send
+*/
+struct smb2_request *smb2_getinfo_fs_send(struct smb2_tree *tree, union smb_fsinfo *io)
+{
+ struct smb2_getinfo b;
+ uint16_t smb2_level = smb2_getinfo_map_level(io->generic.level, SMB2_GETINFO_FS);
+
+ if (smb2_level == 0) {
+ return NULL;
+ }
+
+ ZERO_STRUCT(b);
+ b.in.output_buffer_length = 0x10000;
+ b.in.file.handle = io->generic.handle;
+ b.in.info_type = smb2_level & 0xFF;
+ b.in.info_class = smb2_level >> 8;
+
+ return smb2_getinfo_send(tree, &b);
+}
+
+/*
+ recv a getinfo reply and parse the level info
+*/
+NTSTATUS smb2_getinfo_fs_recv(struct smb2_request *req, TALLOC_CTX *mem_ctx,
+ union smb_fsinfo *io)
+{
+ struct smb2_getinfo b;
+ NTSTATUS status;
+
+ status = smb2_getinfo_recv(req, mem_ctx, &b);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ status = smb_raw_fsinfo_passthru_parse(b.out.blob, mem_ctx, io->generic.level, io);
+ data_blob_free(&b.out.blob);
+
+ return status;
+}
+
+/*
+ level specific getinfo call
+*/
+NTSTATUS smb2_getinfo_fs(struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
+ union smb_fsinfo *io)
+{
+ struct smb2_request *req = smb2_getinfo_fs_send(tree, io);
+ return smb2_getinfo_fs_recv(req, mem_ctx, io);
+}
+
diff --git a/source4/libcli/smb2/ioctl.c b/source4/libcli/smb2/ioctl.c
new file mode 100644
index 0000000000..d81bca517f
--- /dev/null
+++ b/source4/libcli/smb2/ioctl.c
@@ -0,0 +1,109 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ SMB2 client ioctl call
+
+ Copyright (C) Andrew Tridgell 2005
+
+ 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 "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+
+/*
+ send a ioctl request
+*/
+struct smb2_request *smb2_ioctl_send(struct smb2_tree *tree, struct smb2_ioctl *io)
+{
+ NTSTATUS status;
+ struct smb2_request *req;
+
+ req = smb2_request_init_tree(tree, SMB2_OP_IOCTL, 0x38, true,
+ io->in.in.length+io->in.out.length);
+ if (req == NULL) return NULL;
+
+ SSVAL(req->out.body, 0x02, 0); /* pad */
+ SIVAL(req->out.body, 0x04, io->in.function);
+ smb2_push_handle(req->out.body+0x08, &io->in.file.handle);
+
+ status = smb2_push_o32s32_blob(&req->out, 0x18, io->in.out);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(req);
+ return NULL;
+ }
+
+ SIVAL(req->out.body, 0x20, io->in.unknown2);
+
+ status = smb2_push_o32s32_blob(&req->out, 0x24, io->in.in);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(req);
+ return NULL;
+ }
+
+ SIVAL(req->out.body, 0x2C, io->in.max_response_size);
+ SBVAL(req->out.body, 0x30, io->in.flags);
+
+ smb2_transport_send(req);
+
+ return req;
+}
+
+
+/*
+ recv a ioctl reply
+*/
+NTSTATUS smb2_ioctl_recv(struct smb2_request *req,
+ TALLOC_CTX *mem_ctx, struct smb2_ioctl *io)
+{
+ NTSTATUS status;
+
+ if (!smb2_request_receive(req) ||
+ smb2_request_is_error(req)) {
+ return smb2_request_destroy(req);
+ }
+
+ SMB2_CHECK_PACKET_RECV(req, 0x30, true);
+
+ io->out._pad = SVAL(req->in.body, 0x02);
+ io->out.function = IVAL(req->in.body, 0x04);
+ smb2_pull_handle(req->in.body+0x08, &io->out.file.handle);
+
+ status = smb2_pull_o32s32_blob(&req->in, mem_ctx, req->in.body+0x18, &io->out.in);
+ if (!NT_STATUS_IS_OK(status)) {
+ smb2_request_destroy(req);
+ return status;
+ }
+
+ status = smb2_pull_o32s32_blob(&req->in, mem_ctx, req->in.body+0x20, &io->out.out);
+ if (!NT_STATUS_IS_OK(status)) {
+ smb2_request_destroy(req);
+ return status;
+ }
+
+ io->out.unknown2 = IVAL(req->in.body, 0x28);
+ io->out.unknown3 = IVAL(req->in.body, 0x2C);
+
+ return smb2_request_destroy(req);
+}
+
+/*
+ sync ioctl request
+*/
+NTSTATUS smb2_ioctl(struct smb2_tree *tree, TALLOC_CTX *mem_ctx, struct smb2_ioctl *io)
+{
+ struct smb2_request *req = smb2_ioctl_send(tree, io);
+ return smb2_ioctl_recv(req, mem_ctx, io);
+}
diff --git a/source4/libcli/smb2/keepalive.c b/source4/libcli/smb2/keepalive.c
new file mode 100644
index 0000000000..402b063e81
--- /dev/null
+++ b/source4/libcli/smb2/keepalive.c
@@ -0,0 +1,65 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ SMB2 client keepalive handling
+
+ Copyright (C) Andrew Tridgell 2005
+
+ 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 "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+
+/*
+ send a keepalive request
+*/
+struct smb2_request *smb2_keepalive_send(struct smb2_transport *transport)
+{
+ struct smb2_request *req;
+
+ req = smb2_request_init(transport, SMB2_OP_KEEPALIVE, 0x04, false, 0);
+ if (req == NULL) return NULL;
+
+ SSVAL(req->out.body, 0x02, 0);
+
+ smb2_transport_send(req);
+
+ return req;
+}
+
+
+/*
+ recv a keepalive reply
+*/
+NTSTATUS smb2_keepalive_recv(struct smb2_request *req)
+{
+ if (!smb2_request_receive(req) ||
+ !smb2_request_is_ok(req)) {
+ return smb2_request_destroy(req);
+ }
+
+ SMB2_CHECK_PACKET_RECV(req, 0x04, false);
+ return smb2_request_destroy(req);
+}
+
+/*
+ sync keepalive request
+*/
+NTSTATUS smb2_keepalive(struct smb2_transport *transport)
+{
+ struct smb2_request *req = smb2_keepalive_send(transport);
+ return smb2_keepalive_recv(req);
+}
diff --git a/source4/libcli/smb2/lock.c b/source4/libcli/smb2/lock.c
new file mode 100644
index 0000000000..62c6e5dba7
--- /dev/null
+++ b/source4/libcli/smb2/lock.c
@@ -0,0 +1,82 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ SMB2 client lock handling
+
+ Copyright (C) Stefan Metzmacher 2006
+
+ 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 "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+
+/*
+ send a lock request
+*/
+struct smb2_request *smb2_lock_send(struct smb2_tree *tree, struct smb2_lock *io)
+{
+ struct smb2_request *req;
+ int i;
+
+ req = smb2_request_init_tree(tree, SMB2_OP_LOCK,
+ 24 + io->in.lock_count*24, false, 0);
+ if (req == NULL) return NULL;
+
+ /* this is quite bizarre - the spec says we must lie about the length! */
+ SSVAL(req->out.body, 0, 0x30);
+
+ SSVAL(req->out.body, 0x02, io->in.lock_count);
+ SIVAL(req->out.body, 0x04, io->in.reserved);
+ smb2_push_handle(req->out.body+0x08, &io->in.file.handle);
+
+ for (i=0;i<io->in.lock_count;i++) {
+ SBVAL(req->out.body, 0x18 + i*24, io->in.locks[i].offset);
+ SBVAL(req->out.body, 0x20 + i*24, io->in.locks[i].length);
+ SIVAL(req->out.body, 0x28 + i*24, io->in.locks[i].flags);
+ SIVAL(req->out.body, 0x2C + i*24, io->in.locks[i].reserved);
+ }
+
+ smb2_transport_send(req);
+
+ return req;
+}
+
+
+/*
+ recv a lock reply
+*/
+NTSTATUS smb2_lock_recv(struct smb2_request *req, struct smb2_lock *io)
+{
+ if (!smb2_request_receive(req) ||
+ smb2_request_is_error(req)) {
+ return smb2_request_destroy(req);
+ }
+
+ SMB2_CHECK_PACKET_RECV(req, 0x04, false);
+
+ io->out.reserved = SVAL(req->in.body, 0x02);
+
+ return smb2_request_destroy(req);
+}
+
+/*
+ sync lock request
+*/
+NTSTATUS smb2_lock(struct smb2_tree *tree, struct smb2_lock *io)
+{
+ struct smb2_request *req = smb2_lock_send(tree, io);
+ return smb2_lock_recv(req, io);
+}
diff --git a/source4/libcli/smb2/logoff.c b/source4/libcli/smb2/logoff.c
new file mode 100644
index 0000000000..e3f83f27d8
--- /dev/null
+++ b/source4/libcli/smb2/logoff.c
@@ -0,0 +1,69 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ SMB2 client logoff handling
+
+ Copyright (C) Andrew Tridgell 2005
+
+ 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 "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+
+/*
+ send a logoff request
+*/
+struct smb2_request *smb2_logoff_send(struct smb2_session *session)
+{
+ struct smb2_request *req;
+
+ req = smb2_request_init(session->transport, SMB2_OP_LOGOFF, 0x04, false, 0);
+ if (req == NULL) return NULL;
+
+ req->session = session;
+
+ SBVAL(req->out.hdr, SMB2_HDR_SESSION_ID, session->uid);
+
+ SSVAL(req->out.body, 0x02, 0);
+
+ smb2_transport_send(req);
+
+ return req;
+}
+
+
+/*
+ recv a logoff reply
+*/
+NTSTATUS smb2_logoff_recv(struct smb2_request *req)
+{
+ if (!smb2_request_receive(req) ||
+ !smb2_request_is_ok(req)) {
+ return smb2_request_destroy(req);
+ }
+
+ SMB2_CHECK_PACKET_RECV(req, 0x04, false);
+ return smb2_request_destroy(req);
+}
+
+/*
+ sync logoff request
+*/
+NTSTATUS smb2_logoff(struct smb2_session *session)
+{
+ struct smb2_request *req = smb2_logoff_send(session);
+ return smb2_logoff_recv(req);
+}
diff --git a/source4/libcli/smb2/negprot.c b/source4/libcli/smb2/negprot.c
new file mode 100644
index 0000000000..c1f0cf0b24
--- /dev/null
+++ b/source4/libcli/smb2/negprot.c
@@ -0,0 +1,113 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ SMB2 client negprot handling
+
+ Copyright (C) Andrew Tridgell 2005
+
+ 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 "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+#include "librpc/ndr/libndr.h"
+
+/*
+ send a negprot request
+*/
+struct smb2_request *smb2_negprot_send(struct smb2_transport *transport,
+ struct smb2_negprot *io)
+{
+ struct smb2_request *req;
+ uint16_t size = 0x24 + io->in.dialect_count*2;
+ enum ndr_err_code ndr_err;
+ int i;
+
+ req = smb2_request_init(transport, SMB2_OP_NEGPROT, size, false, 0);
+ if (req == NULL) return NULL;
+
+
+ SSVAL(req->out.body, 0x00, 0x24);
+ SSVAL(req->out.body, 0x02, io->in.dialect_count);
+ SSVAL(req->out.body, 0x04, io->in.security_mode);
+ SSVAL(req->out.body, 0x06, io->in.reserved);
+ SIVAL(req->out.body, 0x08, io->in.capabilities);
+ ndr_err = smbcli_push_guid(req->out.body, 0x0C, &io->in.client_guid);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ talloc_free(req);
+ return NULL;
+ }
+ smbcli_push_nttime(req->out.body, 0x1C, io->in.start_time);
+ for (i=0;i<io->in.dialect_count;i++) {
+ SSVAL(req->out.body, 0x24 + i*2, io->in.dialects[i]);
+ }
+
+ smb2_transport_send(req);
+
+ return req;
+}
+
+/*
+ recv a negprot reply
+*/
+NTSTATUS smb2_negprot_recv(struct smb2_request *req, TALLOC_CTX *mem_ctx,
+ struct smb2_negprot *io)
+{
+ NTSTATUS status;
+ enum ndr_err_code ndr_err;
+
+ if (!smb2_request_receive(req) ||
+ smb2_request_is_error(req)) {
+ return smb2_request_destroy(req);
+ }
+
+ SMB2_CHECK_PACKET_RECV(req, 0x40, true);
+
+ io->out.security_mode = SVAL(req->in.body, 0x02);
+ io->out.dialect_revision = SVAL(req->in.body, 0x04);
+ io->out.reserved = SVAL(req->in.body, 0x06);
+ ndr_err = smbcli_pull_guid(req->in.body, 0x08, &io->in.client_guid);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ smb2_request_destroy(req);
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+ io->out.capabilities = IVAL(req->in.body, 0x18);
+ io->out.max_transact_size = IVAL(req->in.body, 0x1C);
+ io->out.max_read_size = IVAL(req->in.body, 0x20);
+ io->out.max_write_size = IVAL(req->in.body, 0x24);
+ io->out.system_time = smbcli_pull_nttime(req->in.body, 0x28);
+ io->out.server_start_time = smbcli_pull_nttime(req->in.body, 0x30);
+ io->out.reserved2 = IVAL(req->in.body, 0x3C);
+
+ status = smb2_pull_o16s16_blob(&req->in, mem_ctx, req->in.body+0x38, &io->out.secblob);
+ if (!NT_STATUS_IS_OK(status)) {
+ smb2_request_destroy(req);
+ return status;
+ }
+
+ return smb2_request_destroy(req);
+}
+
+/*
+ sync negprot request
+*/
+NTSTATUS smb2_negprot(struct smb2_transport *transport,
+ TALLOC_CTX *mem_ctx, struct smb2_negprot *io)
+{
+ struct smb2_request *req = smb2_negprot_send(transport, io);
+ return smb2_negprot_recv(req, mem_ctx, io);
+}
diff --git a/source4/libcli/smb2/notify.c b/source4/libcli/smb2/notify.c
new file mode 100644
index 0000000000..ef7341cae8
--- /dev/null
+++ b/source4/libcli/smb2/notify.c
@@ -0,0 +1,114 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ SMB2 client notify calls
+
+ Copyright (C) Stefan Metzmacher 2006
+
+ 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 "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+
+/*
+ send a notify request
+*/
+struct smb2_request *smb2_notify_send(struct smb2_tree *tree, struct smb2_notify *io)
+{
+ struct smb2_request *req;
+ uint32_t old_timeout;
+
+ req = smb2_request_init_tree(tree, SMB2_OP_NOTIFY, 0x20, false, 0);
+ if (req == NULL) return NULL;
+
+ SSVAL(req->out.hdr, SMB2_HDR_CREDIT, 0x0030);
+
+ SSVAL(req->out.body, 0x02, io->in.recursive);
+ SIVAL(req->out.body, 0x04, io->in.buffer_size);
+ smb2_push_handle(req->out.body+0x08, &io->in.file.handle);
+ SIVAL(req->out.body, 0x18, io->in.completion_filter);
+ SIVAL(req->out.body, 0x1C, io->in.unknown);
+
+ old_timeout = req->transport->options.request_timeout;
+ req->transport->options.request_timeout = 0;
+ smb2_transport_send(req);
+ req->transport->options.request_timeout = old_timeout;
+
+ return req;
+}
+
+
+/*
+ recv a notify reply
+*/
+NTSTATUS smb2_notify_recv(struct smb2_request *req, TALLOC_CTX *mem_ctx,
+ struct smb2_notify *io)
+{
+ NTSTATUS status;
+ DATA_BLOB blob;
+ uint32_t ofs, i;
+
+ if (!smb2_request_receive(req) ||
+ !smb2_request_is_ok(req)) {
+ return smb2_request_destroy(req);
+ }
+
+ SMB2_CHECK_PACKET_RECV(req, 0x08, true);
+
+ status = smb2_pull_o16s32_blob(&req->in, mem_ctx, req->in.body+0x02, &blob);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ io->out.changes = NULL;
+ io->out.num_changes = 0;
+
+ /* count them */
+ for (ofs=0; blob.length - ofs > 12; ) {
+ uint32_t next = IVAL(blob.data, ofs);
+ io->out.num_changes++;
+ if (next == 0 || (ofs + next) >= blob.length) break;
+ ofs += next;
+ }
+
+ /* allocate array */
+ io->out.changes = talloc_array(mem_ctx, struct notify_changes, io->out.num_changes);
+ if (!io->out.changes) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i=ofs=0; i<io->out.num_changes; i++) {
+ io->out.changes[i].action = IVAL(blob.data, ofs+4);
+ smbcli_blob_pull_string(NULL, mem_ctx, &blob,
+ &io->out.changes[i].name,
+ ofs+8, ofs+12, STR_UNICODE);
+ ofs += IVAL(blob.data, ofs);
+ }
+
+ return smb2_request_destroy(req);
+}
+
+/*
+ sync notify request
+*/
+NTSTATUS smb2_notify(struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
+ struct smb2_notify *io)
+{
+ struct smb2_request *req = smb2_notify_send(tree, io);
+ return smb2_notify_recv(req, mem_ctx, io);
+}
diff --git a/source4/libcli/smb2/read.c b/source4/libcli/smb2/read.c
new file mode 100644
index 0000000000..9d40e32a4d
--- /dev/null
+++ b/source4/libcli/smb2/read.c
@@ -0,0 +1,87 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ SMB2 client read call
+
+ Copyright (C) Andrew Tridgell 2005
+
+ 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 "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+
+/*
+ send a read request
+*/
+struct smb2_request *smb2_read_send(struct smb2_tree *tree, struct smb2_read *io)
+{
+ struct smb2_request *req;
+
+ req = smb2_request_init_tree(tree, SMB2_OP_READ, 0x30, true, 0);
+ if (req == NULL) return NULL;
+
+ SCVAL(req->out.body, 0x02, 0); /* pad */
+ SCVAL(req->out.body, 0x03, 0); /* reserved */
+ SIVAL(req->out.body, 0x04, io->in.length);
+ SBVAL(req->out.body, 0x08, io->in.offset);
+ smb2_push_handle(req->out.body+0x10, &io->in.file.handle);
+ SIVAL(req->out.body, 0x20, io->in.min_count);
+ SIVAL(req->out.body, 0x24, io->in.channel);
+ SIVAL(req->out.body, 0x28, io->in.remaining);
+ SSVAL(req->out.body, 0x2C, io->in.channel_offset);
+ SSVAL(req->out.body, 0x2E, io->in.channel_length);
+
+ smb2_transport_send(req);
+
+ return req;
+}
+
+
+/*
+ recv a read reply
+*/
+NTSTATUS smb2_read_recv(struct smb2_request *req,
+ TALLOC_CTX *mem_ctx, struct smb2_read *io)
+{
+ NTSTATUS status;
+
+ if (!smb2_request_receive(req) ||
+ !smb2_request_is_ok(req)) {
+ return smb2_request_destroy(req);
+ }
+
+ SMB2_CHECK_PACKET_RECV(req, 0x10, true);
+
+ status = smb2_pull_o16s32_blob(&req->in, mem_ctx, req->in.body+0x02, &io->out.data);
+ if (!NT_STATUS_IS_OK(status)) {
+ smb2_request_destroy(req);
+ return status;
+ }
+
+ io->out.remaining = IVAL(req->in.body, 0x08);
+ io->out.reserved = IVAL(req->in.body, 0x0C);
+
+ return smb2_request_destroy(req);
+}
+
+/*
+ sync read request
+*/
+NTSTATUS smb2_read(struct smb2_tree *tree, TALLOC_CTX *mem_ctx, struct smb2_read *io)
+{
+ struct smb2_request *req = smb2_read_send(tree, io);
+ return smb2_read_recv(req, mem_ctx, io);
+}
diff --git a/source4/libcli/smb2/request.c b/source4/libcli/smb2/request.c
new file mode 100644
index 0000000000..64d427f889
--- /dev/null
+++ b/source4/libcli/smb2/request.c
@@ -0,0 +1,714 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ SMB2 client request handling
+
+ Copyright (C) Andrew Tridgell 2005
+ Copyright (C) Stefan Metzmacher 2005
+
+ 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 "libcli/raw/libcliraw.h"
+#include "libcli/smb2/smb2.h"
+#include "lib/util/dlinklist.h"
+#include "lib/events/events.h"
+#include "libcli/smb2/smb2_calls.h"
+#include "param/param.h"
+
+/* fill in the bufinfo */
+void smb2_setup_bufinfo(struct smb2_request *req)
+{
+ req->in.bufinfo.mem_ctx = req;
+ req->in.bufinfo.flags = BUFINFO_FLAG_UNICODE | BUFINFO_FLAG_SMB2;
+ req->in.bufinfo.align_base = req->in.buffer;
+ if (req->in.dynamic) {
+ req->in.bufinfo.data = req->in.dynamic;
+ req->in.bufinfo.data_size = req->in.body_size - req->in.body_fixed;
+ } else {
+ req->in.bufinfo.data = NULL;
+ req->in.bufinfo.data_size = 0;
+ }
+}
+
+
+/* destroy a request structure */
+static int smb2_request_destructor(struct smb2_request *req)
+{
+ if (req->transport) {
+ /* remove it from the list of pending requests (a null op if
+ its not in the list) */
+ DLIST_REMOVE(req->transport->pending_recv, req);
+ }
+ return 0;
+}
+
+/*
+ initialise a smb2 request
+*/
+struct smb2_request *smb2_request_init(struct smb2_transport *transport, uint16_t opcode,
+ uint16_t body_fixed_size, bool body_dynamic_present,
+ uint32_t body_dynamic_size)
+{
+ struct smb2_request *req;
+ uint64_t seqnum;
+
+ if (body_dynamic_present) {
+ if (body_dynamic_size == 0) {
+ body_dynamic_size = 1;
+ }
+ } else {
+ body_dynamic_size = 0;
+ }
+
+ req = talloc(transport, struct smb2_request);
+ if (req == NULL) return NULL;
+
+ seqnum = transport->seqnum++;
+ if (seqnum == UINT64_MAX) {
+ seqnum = transport->seqnum++;
+ }
+
+ req->state = SMB2_REQUEST_INIT;
+ req->transport = transport;
+ req->session = NULL;
+ req->tree = NULL;
+ req->seqnum = seqnum;
+ req->status = NT_STATUS_OK;
+ req->async.fn = NULL;
+ req->next = req->prev = NULL;
+
+ ZERO_STRUCT(req->cancel);
+ ZERO_STRUCT(req->in);
+
+ req->out.size = SMB2_HDR_BODY+NBT_HDR_SIZE+body_fixed_size;
+
+ req->out.allocated = req->out.size + body_dynamic_size;
+ req->out.buffer = talloc_array(req, uint8_t, req->out.allocated);
+ if (req->out.buffer == NULL) {
+ talloc_free(req);
+ return NULL;
+ }
+
+ req->out.hdr = req->out.buffer + NBT_HDR_SIZE;
+ req->out.body = req->out.hdr + SMB2_HDR_BODY;
+ req->out.body_fixed= body_fixed_size;
+ req->out.body_size = body_fixed_size;
+ req->out.dynamic = (body_dynamic_size ? req->out.body + body_fixed_size : NULL);
+
+ SIVAL(req->out.hdr, 0, SMB2_MAGIC);
+ SSVAL(req->out.hdr, SMB2_HDR_LENGTH, SMB2_HDR_BODY);
+ SSVAL(req->out.hdr, SMB2_HDR_EPOCH, 0);
+ SIVAL(req->out.hdr, SMB2_HDR_STATUS, 0);
+ SSVAL(req->out.hdr, SMB2_HDR_OPCODE, opcode);
+ SSVAL(req->out.hdr, SMB2_HDR_CREDIT, 0);
+ SIVAL(req->out.hdr, SMB2_HDR_FLAGS, 0);
+ SIVAL(req->out.hdr, SMB2_HDR_NEXT_COMMAND, 0);
+ SBVAL(req->out.hdr, SMB2_HDR_MESSAGE_ID, req->seqnum);
+ SIVAL(req->out.hdr, SMB2_HDR_PID, 0);
+ SIVAL(req->out.hdr, SMB2_HDR_TID, 0);
+ SBVAL(req->out.hdr, SMB2_HDR_SESSION_ID, 0);
+ memset(req->out.hdr+SMB2_HDR_SIGNATURE, 0, 16);
+
+ /* set the length of the fixed body part and +1 if there's a dynamic part also */
+ SSVAL(req->out.body, 0, body_fixed_size + (body_dynamic_size?1:0));
+
+ /*
+ * if we have a dynamic part, make sure the first byte
+ * which is always be part of the packet is initialized
+ */
+ if (body_dynamic_size) {
+ req->out.size += 1;
+ SCVAL(req->out.dynamic, 0, 0);
+ }
+
+ talloc_set_destructor(req, smb2_request_destructor);
+
+ return req;
+}
+
+/*
+ initialise a smb2 request for tree operations
+*/
+struct smb2_request *smb2_request_init_tree(struct smb2_tree *tree, uint16_t opcode,
+ uint16_t body_fixed_size, bool body_dynamic_present,
+ uint32_t body_dynamic_size)
+{
+ struct smb2_request *req = smb2_request_init(tree->session->transport, opcode,
+ body_fixed_size, body_dynamic_present,
+ body_dynamic_size);
+ if (req == NULL) return NULL;
+
+ SBVAL(req->out.hdr, SMB2_HDR_SESSION_ID, tree->session->uid);
+ SIVAL(req->out.hdr, SMB2_HDR_TID, tree->tid);
+ req->session = tree->session;
+ req->tree = tree;
+
+ return req;
+}
+
+/* destroy a request structure and return final status */
+NTSTATUS smb2_request_destroy(struct smb2_request *req)
+{
+ NTSTATUS status;
+
+ /* this is the error code we give the application for when a
+ _send() call fails completely */
+ if (!req) return NT_STATUS_UNSUCCESSFUL;
+
+ if (req->state == SMB2_REQUEST_ERROR &&
+ NT_STATUS_IS_OK(req->status)) {
+ status = NT_STATUS_INTERNAL_ERROR;
+ } else {
+ status = req->status;
+ }
+
+ talloc_free(req);
+ return status;
+}
+
+/*
+ receive a response to a packet
+*/
+bool smb2_request_receive(struct smb2_request *req)
+{
+ /* req can be NULL when a send has failed. This eliminates lots of NULL
+ checks in each module */
+ if (!req) return false;
+
+ /* keep receiving packets until this one is replied to */
+ while (req->state <= SMB2_REQUEST_RECV) {
+ if (event_loop_once(req->transport->socket->event.ctx) != 0) {
+ return false;
+ }
+ }
+
+ return req->state == SMB2_REQUEST_DONE;
+}
+
+/* Return true if the last packet was in error */
+bool smb2_request_is_error(struct smb2_request *req)
+{
+ return NT_STATUS_IS_ERR(req->status);
+}
+
+/* Return true if the last packet was OK */
+bool smb2_request_is_ok(struct smb2_request *req)
+{
+ return NT_STATUS_IS_OK(req->status);
+}
+
+/*
+ check if a range in the reply body is out of bounds
+*/
+bool smb2_oob(struct smb2_request_buffer *buf, const uint8_t *ptr, size_t size)
+{
+ if (size == 0) {
+ /* zero bytes is never out of range */
+ return false;
+ }
+ /* be careful with wraparound! */
+ if ((uintptr_t)ptr < (uintptr_t)buf->body ||
+ (uintptr_t)ptr >= (uintptr_t)buf->body + buf->body_size ||
+ size > buf->body_size ||
+ (uintptr_t)ptr + size > (uintptr_t)buf->body + buf->body_size) {
+ return true;
+ }
+ return false;
+}
+
+size_t smb2_padding_size(uint32_t offset, size_t n)
+{
+ if ((offset & (n-1)) == 0) return 0;
+ return n - (offset & (n-1));
+}
+
+static size_t smb2_padding_fix(struct smb2_request_buffer *buf)
+{
+ if (buf->dynamic == (buf->body + buf->body_fixed)) {
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ grow a SMB2 buffer by the specified amount
+*/
+static NTSTATUS smb2_grow_buffer(struct smb2_request_buffer *buf, size_t increase)
+{
+ size_t dynamic_ofs;
+ uint8_t *buffer_ptr;
+ uint32_t newsize = buf->size + increase;
+
+ /* a packet size should be limited a bit */
+ if (newsize >= 0x00FFFFFF) return NT_STATUS_MARSHALL_OVERFLOW;
+
+ if (newsize <= buf->allocated) return NT_STATUS_OK;
+
+ dynamic_ofs = buf->dynamic - buf->buffer;
+
+ buffer_ptr = talloc_realloc(buf, buf->buffer, uint8_t, newsize);
+ NT_STATUS_HAVE_NO_MEMORY(buffer_ptr);
+
+ buf->buffer = buffer_ptr;
+ buf->hdr = buf->buffer + NBT_HDR_SIZE;
+ buf->body = buf->hdr + SMB2_HDR_BODY;
+ buf->dynamic = buf->buffer + dynamic_ofs;
+ buf->allocated = newsize;
+
+ return NT_STATUS_OK;
+}
+
+/*
+ pull a uint16_t ofs/ uint16_t length/blob triple from a data blob
+ the ptr points to the start of the offset/length pair
+*/
+NTSTATUS smb2_pull_o16s16_blob(struct smb2_request_buffer *buf, TALLOC_CTX *mem_ctx, uint8_t *ptr, DATA_BLOB *blob)
+{
+ uint16_t ofs, size;
+ if (smb2_oob(buf, ptr, 4)) {
+ return NT_STATUS_BUFFER_TOO_SMALL;
+ }
+ ofs = SVAL(ptr, 0);
+ size = SVAL(ptr, 2);
+ if (ofs == 0) {
+ *blob = data_blob(NULL, 0);
+ return NT_STATUS_OK;
+ }
+ if (smb2_oob(buf, buf->hdr + ofs, size)) {
+ return NT_STATUS_BUFFER_TOO_SMALL;
+ }
+ *blob = data_blob_talloc(mem_ctx, buf->hdr + ofs, size);
+ NT_STATUS_HAVE_NO_MEMORY(blob->data);
+ return NT_STATUS_OK;
+}
+
+/*
+ push a uint16_t ofs/ uint16_t length/blob triple into a data blob
+ the ofs points to the start of the offset/length pair, and is relative
+ to the body start
+*/
+NTSTATUS smb2_push_o16s16_blob(struct smb2_request_buffer *buf,
+ uint16_t ofs, DATA_BLOB blob)
+{
+ NTSTATUS status;
+ size_t offset;
+ size_t padding_length;
+ size_t padding_fix;
+ uint8_t *ptr = buf->body+ofs;
+
+ if (buf->dynamic == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* we have only 16 bit for the size */
+ if (blob.length > 0xFFFF) {
+ return NT_STATUS_BUFFER_TOO_SMALL;
+ }
+
+ /* check if there're enough room for ofs and size */
+ if (smb2_oob(buf, ptr, 4)) {
+ return NT_STATUS_BUFFER_TOO_SMALL;
+ }
+
+ if (blob.data == NULL) {
+ if (blob.length != 0) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+ SSVAL(ptr, 0, 0);
+ SSVAL(ptr, 2, 0);
+ return NT_STATUS_OK;
+ }
+
+ offset = buf->dynamic - buf->hdr;
+ padding_length = smb2_padding_size(offset, 2);
+ offset += padding_length;
+ padding_fix = smb2_padding_fix(buf);
+
+ SSVAL(ptr, 0, offset);
+ SSVAL(ptr, 2, blob.length);
+
+ status = smb2_grow_buffer(buf, blob.length + padding_length - padding_fix);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ memset(buf->dynamic, 0, padding_length);
+ buf->dynamic += padding_length;
+
+ memcpy(buf->dynamic, blob.data, blob.length);
+ buf->dynamic += blob.length;
+
+ buf->size += blob.length + padding_length - padding_fix;
+ buf->body_size += blob.length + padding_length;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ push a uint16_t ofs/ uint32_t length/blob triple into a data blob
+ the ofs points to the start of the offset/length pair, and is relative
+ to the body start
+*/
+NTSTATUS smb2_push_o16s32_blob(struct smb2_request_buffer *buf,
+ uint16_t ofs, DATA_BLOB blob)
+{
+ NTSTATUS status;
+ size_t offset;
+ size_t padding_length;
+ size_t padding_fix;
+ uint8_t *ptr = buf->body+ofs;
+
+ if (buf->dynamic == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* check if there're enough room for ofs and size */
+ if (smb2_oob(buf, ptr, 6)) {
+ return NT_STATUS_BUFFER_TOO_SMALL;
+ }
+
+ if (blob.data == NULL) {
+ if (blob.length != 0) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+ SSVAL(ptr, 0, 0);
+ SIVAL(ptr, 2, 0);
+ return NT_STATUS_OK;
+ }
+
+ offset = buf->dynamic - buf->hdr;
+ padding_length = smb2_padding_size(offset, 2);
+ offset += padding_length;
+ padding_fix = smb2_padding_fix(buf);
+
+ SSVAL(ptr, 0, offset);
+ SIVAL(ptr, 2, blob.length);
+
+ status = smb2_grow_buffer(buf, blob.length + padding_length - padding_fix);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ memset(buf->dynamic, 0, padding_length);
+ buf->dynamic += padding_length;
+
+ memcpy(buf->dynamic, blob.data, blob.length);
+ buf->dynamic += blob.length;
+
+ buf->size += blob.length + padding_length - padding_fix;
+ buf->body_size += blob.length + padding_length;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ push a uint32_t ofs/ uint32_t length/blob triple into a data blob
+ the ofs points to the start of the offset/length pair, and is relative
+ to the body start
+*/
+NTSTATUS smb2_push_o32s32_blob(struct smb2_request_buffer *buf,
+ uint32_t ofs, DATA_BLOB blob)
+{
+ NTSTATUS status;
+ size_t offset;
+ size_t padding_length;
+ size_t padding_fix;
+ uint8_t *ptr = buf->body+ofs;
+
+ if (buf->dynamic == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* check if there're enough room for ofs and size */
+ if (smb2_oob(buf, ptr, 8)) {
+ return NT_STATUS_BUFFER_TOO_SMALL;
+ }
+
+ if (blob.data == NULL) {
+ if (blob.length != 0) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+ SIVAL(ptr, 0, 0);
+ SIVAL(ptr, 4, 0);
+ return NT_STATUS_OK;
+ }
+
+ offset = buf->dynamic - buf->hdr;
+ padding_length = smb2_padding_size(offset, 8);
+ offset += padding_length;
+ padding_fix = smb2_padding_fix(buf);
+
+ SIVAL(ptr, 0, offset);
+ SIVAL(ptr, 4, blob.length);
+
+ status = smb2_grow_buffer(buf, blob.length + padding_length - padding_fix);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ memset(buf->dynamic, 0, padding_length);
+ buf->dynamic += padding_length;
+
+ memcpy(buf->dynamic, blob.data, blob.length);
+ buf->dynamic += blob.length;
+
+ buf->size += blob.length + padding_length - padding_fix;
+ buf->body_size += blob.length + padding_length;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ push a uint32_t length/ uint32_t ofs/blob triple into a data blob
+ the ofs points to the start of the length/offset pair, and is relative
+ to the body start
+*/
+NTSTATUS smb2_push_s32o32_blob(struct smb2_request_buffer *buf,
+ uint32_t ofs, DATA_BLOB blob)
+{
+ NTSTATUS status;
+ size_t offset;
+ size_t padding_length;
+ size_t padding_fix;
+ uint8_t *ptr = buf->body+ofs;
+
+ if (buf->dynamic == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* check if there're enough room for ofs and size */
+ if (smb2_oob(buf, ptr, 8)) {
+ return NT_STATUS_BUFFER_TOO_SMALL;
+ }
+
+ if (blob.data == NULL) {
+ if (blob.length != 0) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+ SIVAL(ptr, 0, 0);
+ SIVAL(ptr, 4, 0);
+ return NT_STATUS_OK;
+ }
+
+ offset = buf->dynamic - buf->hdr;
+ padding_length = smb2_padding_size(offset, 8);
+ offset += padding_length;
+ padding_fix = smb2_padding_fix(buf);
+
+ SIVAL(ptr, 0, blob.length);
+ SIVAL(ptr, 4, offset);
+
+ status = smb2_grow_buffer(buf, blob.length + padding_length - padding_fix);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ memset(buf->dynamic, 0, padding_length);
+ buf->dynamic += padding_length;
+
+ memcpy(buf->dynamic, blob.data, blob.length);
+ buf->dynamic += blob.length;
+
+ buf->size += blob.length + padding_length - padding_fix;
+ buf->body_size += blob.length + padding_length;
+
+ return NT_STATUS_OK;
+}
+
+/*
+ pull a uint16_t ofs/ uint32_t length/blob triple from a data blob
+ the ptr points to the start of the offset/length pair
+*/
+NTSTATUS smb2_pull_o16s32_blob(struct smb2_request_buffer *buf, TALLOC_CTX *mem_ctx, uint8_t *ptr, DATA_BLOB *blob)
+{
+ uint16_t ofs;
+ uint32_t size;
+
+ if (smb2_oob(buf, ptr, 6)) {
+ return NT_STATUS_BUFFER_TOO_SMALL;
+ }
+ ofs = SVAL(ptr, 0);
+ size = IVAL(ptr, 2);
+ if (ofs == 0) {
+ *blob = data_blob(NULL, 0);
+ return NT_STATUS_OK;
+ }
+ if (smb2_oob(buf, buf->hdr + ofs, size)) {
+ return NT_STATUS_BUFFER_TOO_SMALL;
+ }
+ *blob = data_blob_talloc(mem_ctx, buf->hdr + ofs, size);
+ NT_STATUS_HAVE_NO_MEMORY(blob->data);
+ return NT_STATUS_OK;
+}
+
+/*
+ pull a uint32_t ofs/ uint32_t length/blob triple from a data blob
+ the ptr points to the start of the offset/length pair
+*/
+NTSTATUS smb2_pull_o32s32_blob(struct smb2_request_buffer *buf, TALLOC_CTX *mem_ctx, uint8_t *ptr, DATA_BLOB *blob)
+{
+ uint32_t ofs, size;
+ if (smb2_oob(buf, ptr, 8)) {
+ return NT_STATUS_BUFFER_TOO_SMALL;
+ }
+ ofs = IVAL(ptr, 0);
+ size = IVAL(ptr, 4);
+ if (ofs == 0) {
+ *blob = data_blob(NULL, 0);
+ return NT_STATUS_OK;
+ }
+ if (smb2_oob(buf, buf->hdr + ofs, size)) {
+ return NT_STATUS_BUFFER_TOO_SMALL;
+ }
+ *blob = data_blob_talloc(mem_ctx, buf->hdr + ofs, size);
+ NT_STATUS_HAVE_NO_MEMORY(blob->data);
+ return NT_STATUS_OK;
+}
+
+/*
+ pull a uint16_t ofs/ uint32_t length/blob triple from a data blob
+ the ptr points to the start of the offset/length pair
+
+ In this varient the uint16_t is padded by an extra 2 bytes, making
+ the size aligned on 4 byte boundary
+*/
+NTSTATUS smb2_pull_o16As32_blob(struct smb2_request_buffer *buf, TALLOC_CTX *mem_ctx, uint8_t *ptr, DATA_BLOB *blob)
+{
+ uint32_t ofs, size;
+ if (smb2_oob(buf, ptr, 8)) {
+ return NT_STATUS_BUFFER_TOO_SMALL;
+ }
+ ofs = SVAL(ptr, 0);
+ size = IVAL(ptr, 4);
+ if (ofs == 0) {
+ *blob = data_blob(NULL, 0);
+ return NT_STATUS_OK;
+ }
+ if (smb2_oob(buf, buf->hdr + ofs, size)) {
+ return NT_STATUS_BUFFER_TOO_SMALL;
+ }
+ *blob = data_blob_talloc(mem_ctx, buf->hdr + ofs, size);
+ NT_STATUS_HAVE_NO_MEMORY(blob->data);
+ return NT_STATUS_OK;
+}
+
+/*
+ pull a uint32_t length/ uint32_t ofs/blob triple from a data blob
+ the ptr points to the start of the offset/length pair
+*/
+NTSTATUS smb2_pull_s32o32_blob(struct smb2_request_buffer *buf, TALLOC_CTX *mem_ctx, uint8_t *ptr, DATA_BLOB *blob)
+{
+ uint32_t ofs, size;
+ if (smb2_oob(buf, ptr, 8)) {
+ return NT_STATUS_BUFFER_TOO_SMALL;
+ }
+ size = IVAL(ptr, 0);
+ ofs = IVAL(ptr, 4);
+ if (ofs == 0) {
+ *blob = data_blob(NULL, 0);
+ return NT_STATUS_OK;
+ }
+ if (smb2_oob(buf, buf->hdr + ofs, size)) {
+ return NT_STATUS_BUFFER_TOO_SMALL;
+ }
+ *blob = data_blob_talloc(mem_ctx, buf->hdr + ofs, size);
+ NT_STATUS_HAVE_NO_MEMORY(blob->data);
+ return NT_STATUS_OK;
+}
+
+/*
+ pull a string in a uint16_t ofs/ uint16_t length/blob format
+ UTF-16 without termination
+*/
+NTSTATUS smb2_pull_o16s16_string(struct smb2_request_buffer *buf, TALLOC_CTX *mem_ctx,
+ uint8_t *ptr, const char **str)
+{
+ DATA_BLOB blob;
+ NTSTATUS status;
+ ssize_t size;
+ void *vstr;
+
+ status = smb2_pull_o16s16_blob(buf, mem_ctx, ptr, &blob);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ if (blob.data == NULL) {
+ *str = NULL;
+ return NT_STATUS_OK;
+ }
+
+ if (blob.length == 0) {
+ char *s;
+ s = talloc_strdup(mem_ctx, "");
+ NT_STATUS_HAVE_NO_MEMORY(s);
+ *str = s;
+ return NT_STATUS_OK;
+ }
+
+ size = convert_string_talloc(mem_ctx, lp_iconv_convenience(global_loadparm), CH_UTF16, CH_UNIX,
+ blob.data, blob.length, &vstr);
+ data_blob_free(&blob);
+ (*str) = (char *)vstr;
+ if (size == -1) {
+ return NT_STATUS_ILLEGAL_CHARACTER;
+ }
+ return NT_STATUS_OK;
+}
+
+/*
+ push a string in a uint16_t ofs/ uint16_t length/blob format
+ UTF-16 without termination
+*/
+NTSTATUS smb2_push_o16s16_string(struct smb2_request_buffer *buf,
+ uint16_t ofs, const char *str)
+{
+ DATA_BLOB blob;
+ NTSTATUS status;
+ ssize_t size;
+
+ if (str == NULL) {
+ return smb2_push_o16s16_blob(buf, ofs, data_blob(NULL, 0));
+ }
+
+ if (*str == 0) {
+ blob.data = discard_const(str);
+ blob.length = 0;
+ return smb2_push_o16s16_blob(buf, ofs, blob);
+ }
+
+ size = convert_string_talloc(buf->buffer, lp_iconv_convenience(global_loadparm), CH_UNIX, CH_UTF16,
+ str, strlen(str), (void **)&blob.data);
+ if (size == -1) {
+ return NT_STATUS_ILLEGAL_CHARACTER;
+ }
+ blob.length = size;
+
+ status = smb2_push_o16s16_blob(buf, ofs, blob);
+ data_blob_free(&blob);
+ return status;
+}
+
+/*
+ push a file handle into a buffer
+*/
+void smb2_push_handle(uint8_t *data, struct smb2_handle *h)
+{
+ SBVAL(data, 0, h->data[0]);
+ SBVAL(data, 8, h->data[1]);
+}
+
+/*
+ pull a file handle from a buffer
+*/
+void smb2_pull_handle(uint8_t *ptr, struct smb2_handle *h)
+{
+ h->data[0] = BVAL(ptr, 0);
+ h->data[1] = BVAL(ptr, 8);
+}
diff --git a/source4/libcli/smb2/session.c b/source4/libcli/smb2/session.c
new file mode 100644
index 0000000000..31b3e942e9
--- /dev/null
+++ b/source4/libcli/smb2/session.c
@@ -0,0 +1,275 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ SMB2 client session handling
+
+ Copyright (C) Andrew Tridgell 2005
+
+ 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 "libcli/raw/libcliraw.h"
+#include "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+#include "libcli/composite/composite.h"
+#include "auth/gensec/gensec.h"
+#include "param/param.h"
+
+/**
+ initialise a smb2_session structure
+ */
+struct smb2_session *smb2_session_init(struct smb2_transport *transport,
+ struct loadparm_context *lp_ctx,
+ TALLOC_CTX *parent_ctx, bool primary)
+{
+ struct smb2_session *session;
+ NTSTATUS status;
+
+ session = talloc_zero(parent_ctx, struct smb2_session);
+ if (!session) {
+ return NULL;
+ }
+ if (primary) {
+ session->transport = talloc_steal(session, transport);
+ } else {
+ session->transport = talloc_reference(session, transport);
+ }
+
+ /* prepare a gensec context for later use */
+ status = gensec_client_start(session, &session->gensec,
+ session->transport->socket->event.ctx,
+ lp_ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(session);
+ return NULL;
+ }
+
+ gensec_want_feature(session->gensec, GENSEC_FEATURE_SESSION_KEY);
+
+ return session;
+}
+
+/**
+ send a session setup request
+*/
+struct smb2_request *smb2_session_setup_send(struct smb2_session *session,
+ struct smb2_session_setup *io)
+{
+ struct smb2_request *req;
+ NTSTATUS status;
+
+ req = smb2_request_init(session->transport, SMB2_OP_SESSSETUP,
+ 0x18, true, io->in.secblob.length);
+ if (req == NULL) return NULL;
+
+ SBVAL(req->out.hdr, SMB2_HDR_SESSION_ID, session->uid);
+ SCVAL(req->out.body, 0x02, io->in.vc_number);
+ SCVAL(req->out.body, 0x03, io->in.security_mode);
+ SIVAL(req->out.body, 0x04, io->in.capabilities);
+ SIVAL(req->out.body, 0x08, io->in.channel);
+ SBVAL(req->out.body, 0x10, io->in.previous_sessionid);
+
+ req->session = session;
+
+ status = smb2_push_o16s16_blob(&req->out, 0x0C, io->in.secblob);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(req);
+ return NULL;
+ }
+
+ smb2_transport_send(req);
+
+ return req;
+}
+
+
+/**
+ recv a session setup reply
+*/
+NTSTATUS smb2_session_setup_recv(struct smb2_request *req, TALLOC_CTX *mem_ctx,
+ struct smb2_session_setup *io)
+{
+ NTSTATUS status;
+
+ if (!smb2_request_receive(req) ||
+ (smb2_request_is_error(req) &&
+ !NT_STATUS_EQUAL(req->status, NT_STATUS_MORE_PROCESSING_REQUIRED))) {
+ return smb2_request_destroy(req);
+ }
+
+ SMB2_CHECK_PACKET_RECV(req, 0x08, true);
+
+ io->out.session_flags = SVAL(req->in.body, 0x02);
+ io->out.uid = BVAL(req->in.hdr, SMB2_HDR_SESSION_ID);
+
+ status = smb2_pull_o16s16_blob(&req->in, mem_ctx, req->in.body+0x04, &io->out.secblob);
+ if (!NT_STATUS_IS_OK(status)) {
+ smb2_request_destroy(req);
+ return status;
+ }
+
+ return smb2_request_destroy(req);
+}
+
+/*
+ sync session setup request
+*/
+NTSTATUS smb2_session_setup(struct smb2_session *session,
+ TALLOC_CTX *mem_ctx, struct smb2_session_setup *io)
+{
+ struct smb2_request *req = smb2_session_setup_send(session, io);
+ return smb2_session_setup_recv(req, mem_ctx, io);
+}
+
+
+struct smb2_session_state {
+ struct smb2_session_setup io;
+ struct smb2_request *req;
+ NTSTATUS gensec_status;
+};
+
+/*
+ handle continuations of the spnego session setup
+*/
+static void session_request_handler(struct smb2_request *req)
+{
+ struct composite_context *c = talloc_get_type(req->async.private_data,
+ struct composite_context);
+ struct smb2_session_state *state = talloc_get_type(c->private_data,
+ struct smb2_session_state);
+ struct smb2_session *session = req->session;
+
+ c->status = smb2_session_setup_recv(req, c, &state->io);
+ if (NT_STATUS_EQUAL(c->status, NT_STATUS_MORE_PROCESSING_REQUIRED) ||
+ (NT_STATUS_IS_OK(c->status) &&
+ NT_STATUS_EQUAL(state->gensec_status, NT_STATUS_MORE_PROCESSING_REQUIRED))) {
+ NTSTATUS session_key_err;
+ DATA_BLOB session_key;
+ c->status = gensec_update(session->gensec, c,
+ state->io.out.secblob,
+ &state->io.in.secblob);
+ state->gensec_status = c->status;
+
+ session_key_err = gensec_session_key(session->gensec, &session_key);
+ if (NT_STATUS_IS_OK(session_key_err)) {
+ session->session_key = session_key;
+ }
+ }
+
+ session->uid = state->io.out.uid;
+
+ if (NT_STATUS_EQUAL(c->status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ state->req = smb2_session_setup_send(session, &state->io);
+ if (state->req == NULL) {
+ composite_error(c, NT_STATUS_NO_MEMORY);
+ return;
+ }
+
+ state->req->async.fn = session_request_handler;
+ state->req->async.private_data = c;
+ return;
+ }
+
+ if (!NT_STATUS_IS_OK(c->status)) {
+ composite_error(c, c->status);
+ return;
+ }
+
+ if (session->transport->signing_required) {
+ if (session->session_key.length == 0) {
+ DEBUG(0,("Wrong session key length %u for SMB2 signing\n",
+ (unsigned)session->session_key.length));
+ composite_error(c, NT_STATUS_ACCESS_DENIED);
+ return;
+ }
+ session->signing_active = true;
+ }
+
+ composite_done(c);
+}
+
+/*
+ a composite function that does a full SPNEGO session setup
+ */
+struct composite_context *smb2_session_setup_spnego_send(struct smb2_session *session,
+ struct cli_credentials *credentials)
+{
+ struct composite_context *c;
+ struct smb2_session_state *state;
+
+ c = composite_create(session, session->transport->socket->event.ctx);
+ if (c == NULL) return NULL;
+
+ state = talloc(c, struct smb2_session_state);
+ if (composite_nomem(state, c)) return c;
+ c->private_data = state;
+
+ ZERO_STRUCT(state->io);
+ state->io.in.vc_number = 0;
+ if (session->transport->signing_required) {
+ state->io.in.security_mode =
+ SMB2_NEGOTIATE_SIGNING_ENABLED | SMB2_NEGOTIATE_SIGNING_REQUIRED;
+ }
+ state->io.in.capabilities = 0;
+ state->io.in.channel = 0;
+ state->io.in.previous_sessionid = 0;
+
+ c->status = gensec_set_credentials(session->gensec, credentials);
+ if (!composite_is_ok(c)) return c;
+
+ c->status = gensec_set_target_hostname(session->gensec,
+ session->transport->socket->hostname);
+ if (!composite_is_ok(c)) return c;
+
+ c->status = gensec_set_target_service(session->gensec, "cifs");
+ if (!composite_is_ok(c)) return c;
+
+ c->status = gensec_start_mech_by_oid(session->gensec, GENSEC_OID_SPNEGO);
+ if (!composite_is_ok(c)) return c;
+
+ c->status = gensec_update(session->gensec, c,
+ session->transport->negotiate.secblob,
+ &state->io.in.secblob);
+ if (!NT_STATUS_EQUAL(c->status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ composite_error(c, c->status);
+ return c;
+ }
+ state->gensec_status = c->status;
+
+ state->req = smb2_session_setup_send(session, &state->io);
+ composite_continue_smb2(c, state->req, session_request_handler, c);
+ return c;
+}
+
+/*
+ receive a composite session setup reply
+*/
+NTSTATUS smb2_session_setup_spnego_recv(struct composite_context *c)
+{
+ NTSTATUS status;
+ status = composite_wait(c);
+ talloc_free(c);
+ return status;
+}
+
+/*
+ sync version of smb2_session_setup_spnego
+*/
+NTSTATUS smb2_session_setup_spnego(struct smb2_session *session,
+ struct cli_credentials *credentials)
+{
+ struct composite_context *c = smb2_session_setup_spnego_send(session, credentials);
+ return smb2_session_setup_spnego_recv(c);
+}
diff --git a/source4/libcli/smb2/setinfo.c b/source4/libcli/smb2/setinfo.c
new file mode 100644
index 0000000000..69c0f45b63
--- /dev/null
+++ b/source4/libcli/smb2/setinfo.c
@@ -0,0 +1,122 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ SMB2 client setinfo calls
+
+ Copyright (C) Andrew Tridgell 2005
+
+ 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 "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+
+/*
+ send a setinfo request
+*/
+struct smb2_request *smb2_setinfo_send(struct smb2_tree *tree, struct smb2_setinfo *io)
+{
+ NTSTATUS status;
+ struct smb2_request *req;
+
+ req = smb2_request_init_tree(tree, SMB2_OP_SETINFO, 0x20, true, io->in.blob.length);
+ if (req == NULL) return NULL;
+
+ SSVAL(req->out.body, 0x02, io->in.level);
+
+ status = smb2_push_s32o32_blob(&req->out, 0x04, io->in.blob);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(req);
+ return NULL;
+ }
+
+ SIVAL(req->out.body, 0x0C, io->in.flags);
+ smb2_push_handle(req->out.body+0x10, &io->in.file.handle);
+
+ smb2_transport_send(req);
+
+ return req;
+}
+
+
+/*
+ recv a setinfo reply
+*/
+NTSTATUS smb2_setinfo_recv(struct smb2_request *req)
+{
+ if (!smb2_request_receive(req) ||
+ !smb2_request_is_ok(req)) {
+ return smb2_request_destroy(req);
+ }
+
+ SMB2_CHECK_PACKET_RECV(req, 0x02, false);
+
+ return smb2_request_destroy(req);
+}
+
+/*
+ sync setinfo request
+*/
+NTSTATUS smb2_setinfo(struct smb2_tree *tree, struct smb2_setinfo *io)
+{
+ struct smb2_request *req = smb2_setinfo_send(tree, io);
+ return smb2_setinfo_recv(req);
+}
+
+/*
+ level specific file setinfo call - async send
+*/
+struct smb2_request *smb2_setinfo_file_send(struct smb2_tree *tree, union smb_setfileinfo *io)
+{
+ struct smb2_setinfo b;
+ uint16_t smb2_level = smb2_getinfo_map_level(io->generic.level, SMB2_GETINFO_FILE);
+ struct smb2_request *req;
+
+ if (smb2_level == 0) {
+ return NULL;
+ }
+
+ ZERO_STRUCT(b);
+ b.in.level = smb2_level;
+ b.in.file.handle = io->generic.in.file.handle;
+
+ /* change levels so the parsers know it is SMB2 */
+ if (io->generic.level == RAW_SFILEINFO_RENAME_INFORMATION) {
+ io->generic.level = RAW_SFILEINFO_RENAME_INFORMATION_SMB2;
+ }
+
+ if (!smb_raw_setfileinfo_passthru(tree, io->generic.level, io, &b.in.blob)) {
+ return NULL;
+ }
+
+ if (io->generic.level == RAW_SFILEINFO_SEC_DESC) {
+ b.in.flags = io->set_secdesc.in.secinfo_flags;
+ }
+
+ req = smb2_setinfo_send(tree, &b);
+ data_blob_free(&b.in.blob);
+ return req;
+}
+
+/*
+ level specific file setinfo call - sync
+*/
+NTSTATUS smb2_setinfo_file(struct smb2_tree *tree, union smb_setfileinfo *io)
+{
+ struct smb2_request *req = smb2_setinfo_file_send(tree, io);
+ return smb2_setinfo_recv(req);
+}
diff --git a/source4/libcli/smb2/signing.c b/source4/libcli/smb2/signing.c
new file mode 100644
index 0000000000..de9e1e9d29
--- /dev/null
+++ b/source4/libcli/smb2/signing.c
@@ -0,0 +1,117 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ SMB2 Signing Code
+
+ Copyright (C) Andrew Tridgell <tridge@samba.org> 2008
+
+ 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 "libcli/raw/libcliraw.h"
+#include "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+#include "lib/crypto/crypto.h"
+
+/*
+ sign an outgoing message
+ */
+NTSTATUS smb2_sign_message(struct smb2_request_buffer *buf, DATA_BLOB session_key)
+{
+ struct HMACSHA256Context m;
+ uint8_t res[32];
+ uint64_t session_id;
+
+ if (buf->size < NBT_HDR_SIZE + SMB2_HDR_SIGNATURE + 16) {
+ /* can't sign non-SMB2 messages */
+ return NT_STATUS_OK;
+ }
+
+ session_id = BVAL(buf->hdr, SMB2_HDR_SESSION_ID);
+ if (session_id == 0) {
+ /* we don't sign messages with a zero session_id. See
+ MS-SMB2 3.2.4.1.1 */
+ return NT_STATUS_OK;
+ }
+
+ if (session_key.length == 0) {
+ DEBUG(2,("Wrong session key length %u for SMB2 signing\n",
+ (unsigned)session_key.length));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ memset(buf->hdr + SMB2_HDR_SIGNATURE, 0, 16);
+
+ SIVAL(buf->hdr, SMB2_HDR_FLAGS, IVAL(buf->hdr, SMB2_HDR_FLAGS) | SMB2_HDR_FLAG_SIGNED);
+
+ ZERO_STRUCT(m);
+ hmac_sha256_init(session_key.data, MIN(session_key.length, 16), &m);
+ hmac_sha256_update(buf->buffer+NBT_HDR_SIZE, buf->size-NBT_HDR_SIZE, &m);
+ hmac_sha256_final(res, &m);
+ DEBUG(5,("signed SMB2 message of size %u\n", (unsigned)buf->size - NBT_HDR_SIZE));
+
+ memcpy(buf->hdr + SMB2_HDR_SIGNATURE, res, 16);
+
+ return NT_STATUS_OK;
+}
+
+/*
+ check an incoming signature
+ */
+NTSTATUS smb2_check_signature(struct smb2_request_buffer *buf, DATA_BLOB session_key)
+{
+ uint64_t session_id;
+ struct HMACSHA256Context m;
+ uint8_t res[SHA256_DIGEST_LENGTH];
+ uint8_t sig[16];
+
+ if (buf->size < NBT_HDR_SIZE + SMB2_HDR_SIGNATURE + 16) {
+ /* can't check non-SMB2 messages */
+ return NT_STATUS_OK;
+ }
+
+ session_id = BVAL(buf->hdr, SMB2_HDR_SESSION_ID);
+ if (session_id == 0) {
+ /* don't sign messages with a zero session_id. See
+ MS-SMB2 3.2.4.1.1 */
+ return NT_STATUS_OK;
+ }
+
+ if (session_key.length == 0) {
+ /* we don't have the session key yet */
+ return NT_STATUS_OK;
+ }
+
+ memcpy(sig, buf->hdr+SMB2_HDR_SIGNATURE, 16);
+
+ memset(buf->hdr + SMB2_HDR_SIGNATURE, 0, 16);
+
+ ZERO_STRUCT(m);
+ hmac_sha256_init(session_key.data, MIN(session_key.length, 16), &m);
+ hmac_sha256_update(buf->hdr, buf->size-NBT_HDR_SIZE, &m);
+ hmac_sha256_final(res, &m);
+
+ memcpy(buf->hdr+SMB2_HDR_SIGNATURE, sig, 16);
+
+ if (memcmp(res, sig, 16) != 0) {
+ DEBUG(0,("Bad SMB2 signature for message of size %u\n",
+ (unsigned)buf->size-NBT_HDR_SIZE));
+ dump_data(0, sig, 16);
+ dump_data(0, res, 16);
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ return NT_STATUS_OK;
+}
diff --git a/source4/libcli/smb2/smb2.h b/source4/libcli/smb2/smb2.h
new file mode 100644
index 0000000000..f00107de60
--- /dev/null
+++ b/source4/libcli/smb2/smb2.h
@@ -0,0 +1,302 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ SMB2 client library header
+
+ Copyright (C) Andrew Tridgell 2005
+
+ 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 __LIBCLI_SMB2_SMB2_H__
+#define __LIBCLI_SMB2_SMB2_H__
+
+#include "libcli/raw/request.h"
+#include "libcli/raw/libcliraw.h"
+
+struct smb2_handle;
+
+/*
+ information returned from the negotiate process
+*/
+struct smb2_negotiate {
+ DATA_BLOB secblob;
+ NTTIME system_time;
+ NTTIME server_start_time;
+ uint16_t security_mode;
+};
+
+/* this is the context for the smb2 transport layer */
+struct smb2_transport {
+ /* socket level info */
+ struct smbcli_socket *socket;
+
+ struct smb2_negotiate negotiate;
+
+ /* next seqnum to allocate */
+ uint64_t seqnum;
+
+ /* a list of requests that are pending for receive on this
+ connection */
+ struct smb2_request *pending_recv;
+
+ /* context of the stream -> packet parser */
+ struct packet_context *packet;
+
+ /* an idle function - if this is defined then it will be
+ called once every period microseconds while we are waiting
+ for a packet */
+ struct {
+ void (*func)(struct smb2_transport *, void *);
+ void *private;
+ uint_t period;
+ } idle;
+
+ struct {
+ /* a oplock break request handler */
+ bool (*handler)(struct smb2_transport *transport,
+ const struct smb2_handle *handle,
+ uint8_t level, void *private_data);
+ /* private data passed to the oplock handler */
+ void *private_data;
+ } oplock;
+
+ struct smbcli_options options;
+
+ bool signing_required;
+};
+
+
+/*
+ SMB2 tree context
+*/
+struct smb2_tree {
+ struct smb2_session *session;
+ uint32_t tid;
+};
+
+/*
+ SMB2 session context
+*/
+struct smb2_session {
+ struct smb2_transport *transport;
+ struct gensec_security *gensec;
+ uint64_t uid;
+ DATA_BLOB session_key;
+ bool signing_active;
+};
+
+
+struct smb2_request_buffer {
+ /* the raw SMB2 buffer, including the 4 byte length header */
+ uint8_t *buffer;
+
+ /* the size of the raw buffer, including 4 byte header */
+ size_t size;
+
+ /* how much has been allocated - on reply the buffer is over-allocated to
+ prevent too many realloc() calls
+ */
+ size_t allocated;
+
+ /* the start of the SMB2 header - this is always buffer+4 */
+ uint8_t *hdr;
+
+ /* the packet body */
+ uint8_t *body;
+ size_t body_fixed;
+ size_t body_size;
+
+ /* this point to the next dynamic byte that can be used
+ * this will be moved when some dynamic data is pushed
+ */
+ uint8_t *dynamic;
+
+ /* this is used to range check and align strings and buffers */
+ struct request_bufinfo bufinfo;
+};
+
+
+/*
+ a client request moves between the following 4 states.
+*/
+enum smb2_request_state {SMB2_REQUEST_INIT, /* we are creating the request */
+ SMB2_REQUEST_RECV, /* we are waiting for a matching reply */
+ SMB2_REQUEST_DONE, /* the request is finished */
+ SMB2_REQUEST_ERROR}; /* a packet or transport level error has occurred */
+
+/* the context for a single SMB2 request */
+struct smb2_request {
+ /* allow a request to be part of a list of requests */
+ struct smb2_request *next, *prev;
+
+ /* each request is in one of 3 possible states */
+ enum smb2_request_state state;
+
+ struct smb2_transport *transport;
+ struct smb2_session *session;
+ struct smb2_tree *tree;
+
+ uint64_t seqnum;
+
+ struct {
+ bool do_cancel;
+ bool can_cancel;
+ uint32_t pending_id;
+ } cancel;
+
+ /* the NT status for this request. Set by packet receive code
+ or code detecting error. */
+ NTSTATUS status;
+
+ struct smb2_request_buffer in;
+ struct smb2_request_buffer out;
+
+ /* information on what to do with a reply when it is received
+ asyncronously. If this is not setup when a reply is received then
+ the reply is discarded
+
+ The private pointer is private to the caller of the client
+ library (the application), not private to the library
+ */
+ struct {
+ void (*fn)(struct smb2_request *);
+ void *private_data;
+ } async;
+};
+
+
+#define SMB2_MIN_SIZE 0x42
+
+/* offsets into header elements for a sync SMB2 request */
+#define SMB2_HDR_PROTOCOL_ID 0x00
+#define SMB2_HDR_LENGTH 0x04
+#define SMB2_HDR_EPOCH 0x06
+#define SMB2_HDR_STATUS 0x08
+#define SMB2_HDR_OPCODE 0x0c
+#define SMB2_HDR_CREDIT 0x0e
+#define SMB2_HDR_FLAGS 0x10
+#define SMB2_HDR_NEXT_COMMAND 0x14
+#define SMB2_HDR_MESSAGE_ID 0x18
+#define SMB2_HDR_PID 0x20
+#define SMB2_HDR_TID 0x24
+#define SMB2_HDR_SESSION_ID 0x28
+#define SMB2_HDR_SIGNATURE 0x30 /* 16 bytes */
+#define SMB2_HDR_BODY 0x40
+
+/* header flags */
+#define SMB2_HDR_FLAG_REDIRECT 0x01
+#define SMB2_HDR_FLAG_ASYNC 0x02
+#define SMB2_HDR_FLAG_CHAINED 0x04
+#define SMB2_HDR_FLAG_SIGNED 0x08
+#define SMB2_HDR_FLAG_DFS 0x10000000
+
+/* SMB2 opcodes */
+#define SMB2_OP_NEGPROT 0x00
+#define SMB2_OP_SESSSETUP 0x01
+#define SMB2_OP_LOGOFF 0x02
+#define SMB2_OP_TCON 0x03
+#define SMB2_OP_TDIS 0x04
+#define SMB2_OP_CREATE 0x05
+#define SMB2_OP_CLOSE 0x06
+#define SMB2_OP_FLUSH 0x07
+#define SMB2_OP_READ 0x08
+#define SMB2_OP_WRITE 0x09
+#define SMB2_OP_LOCK 0x0a
+#define SMB2_OP_IOCTL 0x0b
+#define SMB2_OP_CANCEL 0x0c
+#define SMB2_OP_KEEPALIVE 0x0d
+#define SMB2_OP_FIND 0x0e
+#define SMB2_OP_NOTIFY 0x0f
+#define SMB2_OP_GETINFO 0x10
+#define SMB2_OP_SETINFO 0x11
+#define SMB2_OP_BREAK 0x12
+
+#define SMB2_MAGIC 0x424D53FE /* 0xFE 'S' 'M' 'B' */
+
+/* the dialect we support */
+#define SMB2_DIALECT_REVISION 0x202
+
+/* SMB2 negotiate security_mode */
+#define SMB2_NEGOTIATE_SIGNING_ENABLED 0x01
+#define SMB2_NEGOTIATE_SIGNING_REQUIRED 0x02
+
+/* SMB2 capabilities - only 1 so far. I'm sure more will be added */
+#define SMB2_CAP_DFS 0x0
+/* so we can spot new caps as added */
+#define SMB2_CAP_ALL SMB2_CAP_DFS
+
+/* SMB2 share flags */
+#define SMB2_SHAREFLAG_MANUAL_CACHING 0x0000
+#define SMB2_SHAREFLAG_AUTO_CACHING 0x0010
+#define SMB2_SHAREFLAG_VDO_CACHING 0x0020
+#define SMB2_SHAREFLAG_NO_CACHING 0x0030
+#define SMB2_SHAREFLAG_DFS 0x0001
+#define SMB2_SHAREFLAG_DFS_ROOT 0x0002
+#define SMB2_SHAREFLAG_RESTRICT_EXCLUSIVE_OPENS 0x0100
+#define SMB2_SHAREFLAG_FORCE_SHARED_DELETE 0x0200
+#define SMB2_SHAREFLAG_ALLOW_NAMESPACE_CACHING 0x0400
+#define SMB2_SHAREFLAG_ACCESS_BASED_DIRECTORY_ENUM 0x0800
+#define SMB2_SHAREFLAG_ALL 0x0F33
+
+/* SMB2 create security flags */
+#define SMB2_SECURITY_DYNAMIC_TRACKING 0x01
+#define SMB2_SECURITY_EFFECTIVE_ONLY 0x02
+
+/* SMB2 requested oplock levels */
+#define SMB2_OPLOCK_LEVEL_NONE 0x00
+#define SMB2_OPLOCK_LEVEL_II 0x01
+#define SMB2_OPLOCK_LEVEL_EXCLUSIVE 0x08
+#define SMB2_OPLOCK_LEVEL_BATCH 0x09
+
+/* SMB2 impersonation levels */
+#define SMB2_IMPERSONATION_ANONYMOUS 0x00
+#define SMB2_IMPERSONATION_IDENTIFICATION 0x01
+#define SMB2_IMPERSONATION_IMPERSONATION 0x02
+#define SMB2_IMPERSONATION_DELEGATE 0x03
+
+/* SMB2 create tags */
+#define SMB2_CREATE_TAG_EXTA "ExtA"
+#define SMB2_CREATE_TAG_MXAC "MxAc"
+#define SMB2_CREATE_TAG_SECD "SecD"
+#define SMB2_CREATE_TAG_DHNQ "DHnQ"
+#define SMB2_CREATE_TAG_DHNC "DHnC"
+#define SMB2_CREATE_TAG_ALSI "AlSi"
+#define SMB2_CREATE_TAG_TWRP "TWrp"
+#define SMB2_CREATE_TAG_QFID "QFid"
+
+/* SMB2 Create ignore some more create_options */
+#define SMB2_CREATE_OPTIONS_NOT_SUPPORTED_MASK (NTCREATEX_OPTIONS_TREE_CONNECTION | \
+ NTCREATEX_OPTIONS_OPFILTER)
+
+/*
+ check that a body has the expected size
+*/
+#define SMB2_CHECK_PACKET_RECV(req, size, dynamic) do { \
+ size_t is_size = req->in.body_size; \
+ uint16_t field_size = SVAL(req->in.body, 0); \
+ uint16_t want_size = ((dynamic)?(size)+1:(size)); \
+ if (is_size < (size)) { \
+ DEBUG(0,("%s: buffer too small 0x%x. Expected 0x%x\n", \
+ __location__, (unsigned)is_size, (unsigned)want_size)); \
+ return NT_STATUS_BUFFER_TOO_SMALL; \
+ }\
+ if (field_size != want_size) { \
+ DEBUG(0,("%s: unexpected fixed body size 0x%x. Expected 0x%x\n", \
+ __location__, (unsigned)field_size, (unsigned)want_size)); \
+ return NT_STATUS_INVALID_PARAMETER; \
+ } \
+} while (0)
+
+#endif
diff --git a/source4/libcli/smb2/smb2_calls.h b/source4/libcli/smb2/smb2_calls.h
new file mode 100644
index 0000000000..f66236af30
--- /dev/null
+++ b/source4/libcli/smb2/smb2_calls.h
@@ -0,0 +1,110 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ SMB2 client calls
+
+ Copyright (C) Andrew Tridgell 2005
+
+ 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 "libcli/raw/interfaces.h"
+
+struct smb2_negprot {
+ struct {
+ uint16_t dialect_count; /* size of dialects array */
+ uint16_t security_mode; /* 0==signing disabled
+ 1==signing enabled */
+ uint16_t reserved;
+ uint32_t capabilities;
+ struct GUID client_guid;
+ NTTIME start_time;
+ uint16_t *dialects;
+ } in;
+ struct {
+ /* static body buffer 64 (0x40) bytes */
+ /* uint16_t buffer_code; 0x41 = 0x40 + 1 */
+ uint16_t security_mode; /* SMB2_NEGOTIATE_SIGNING_* */
+ uint16_t dialect_revision;
+ uint16_t reserved;
+ struct GUID server_guid;
+ uint32_t capabilities;
+ uint32_t max_transact_size;
+ uint32_t max_read_size;
+ uint32_t max_write_size;
+ NTTIME system_time;
+ NTTIME server_start_time;
+ /* uint16_t secblob_ofs */
+ /* uint16_t secblob_size */
+ uint32_t reserved2;
+ DATA_BLOB secblob;
+ } out;
+};
+
+/* getinfo classes */
+#define SMB2_GETINFO_FILE 0x01
+#define SMB2_GETINFO_FS 0x02
+#define SMB2_GETINFO_SECURITY 0x03
+#define SMB2_GETINFO_QUOTA 0x04
+
+#define SMB2_GETINFO_ADD_OWNER_SECURITY 0x01
+#define SMB2_GETINFO_ADD_GROUP_SECURITY 0x02
+#define SMB2_GETINFO_ADD_DACL_SECURITY 0x04
+#define SMB2_GETINFO_ADD_SACL_SECURITY 0x08
+#define SMB2_GETINFO_ADD_LABEL_SECURITY 0x10
+
+/* NOTE! the getinfo fs and file levels exactly match up with the
+ 'passthru' SMB levels, which are levels >= 1000. The SMB2 client
+ lib uses the names from the libcli/raw/ library */
+
+struct smb2_getinfo {
+ struct {
+ /* static body buffer 40 (0x28) bytes */
+ /* uint16_t buffer_code; 0x29 = 0x28 + 1 */
+ uint8_t info_type;
+ uint8_t info_class;
+ uint32_t output_buffer_length;
+ /* uint32_t input_buffer_offset; */
+ uint32_t reserved;
+ uint32_t input_buffer_length;
+ uint32_t additional_information; /* SMB2_GETINFO_ADD_* */
+ uint32_t getinfo_flags; /* level specific */
+ union smb_handle file;
+ DATA_BLOB blob;
+ } in;
+
+ struct {
+ /* static body buffer 8 (0x08) bytes */
+ /* uint16_t buffer_code; 0x09 = 0x08 + 1 */
+ /* uint16_t blob_ofs; */
+ /* uint16_t blob_size; */
+
+ /* dynamic body */
+ DATA_BLOB blob;
+ } out;
+};
+
+struct smb2_setinfo {
+ struct {
+ uint16_t level;
+ uint32_t flags;
+ union smb_handle file;
+ DATA_BLOB blob;
+ } in;
+};
+
+struct cli_credentials;
+struct event_context;
+struct resolve_context;
+#include "libcli/smb2/smb2_proto.h"
diff --git a/source4/libcli/smb2/tcon.c b/source4/libcli/smb2/tcon.c
new file mode 100644
index 0000000000..ec7152b264
--- /dev/null
+++ b/source4/libcli/smb2/tcon.c
@@ -0,0 +1,112 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ SMB2 client tree handling
+
+ Copyright (C) Andrew Tridgell 2005
+
+ 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 "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+
+/*
+ initialise a smb2_session structure
+ */
+struct smb2_tree *smb2_tree_init(struct smb2_session *session,
+ TALLOC_CTX *parent_ctx, bool primary)
+{
+ struct smb2_tree *tree;
+
+ tree = talloc_zero(parent_ctx, struct smb2_tree);
+ if (!session) {
+ return NULL;
+ }
+ if (primary) {
+ tree->session = talloc_steal(tree, session);
+ } else {
+ tree->session = talloc_reference(tree, session);
+ }
+ return tree;
+}
+
+/*
+ send a tree connect
+*/
+struct smb2_request *smb2_tree_connect_send(struct smb2_tree *tree,
+ struct smb2_tree_connect *io)
+{
+ struct smb2_request *req;
+ NTSTATUS status;
+
+ req = smb2_request_init(tree->session->transport, SMB2_OP_TCON,
+ 0x08, true, 0);
+ if (req == NULL) return NULL;
+
+ SBVAL(req->out.hdr, SMB2_HDR_SESSION_ID, tree->session->uid);
+ req->session = tree->session;
+
+ SSVAL(req->out.body, 0x02, io->in.reserved);
+ status = smb2_push_o16s16_string(&req->out, 0x04, io->in.path);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(req);
+ return NULL;
+ }
+
+ smb2_transport_send(req);
+
+ return req;
+}
+
+
+/*
+ recv a tree connect reply
+*/
+NTSTATUS smb2_tree_connect_recv(struct smb2_request *req, struct smb2_tree_connect *io)
+{
+ if (!smb2_request_receive(req) ||
+ smb2_request_is_error(req)) {
+ return smb2_request_destroy(req);
+ }
+
+ SMB2_CHECK_PACKET_RECV(req, 0x10, false);
+
+ io->out.tid = IVAL(req->in.hdr, SMB2_HDR_TID);
+
+ io->out.share_type = CVAL(req->in.body, 0x02);
+ io->out.reserved = CVAL(req->in.body, 0x03);
+ io->out.flags = IVAL(req->in.body, 0x04);
+ io->out.capabilities= IVAL(req->in.body, 0x08);
+ io->out.access_mask = IVAL(req->in.body, 0x0C);
+
+ if (io->out.capabilities & ~SMB2_CAP_ALL) {
+ DEBUG(0,("Unknown capabilities mask 0x%x\n", io->out.capabilities));
+ }
+ if (io->out.flags & ~SMB2_SHAREFLAG_ALL) {
+ DEBUG(0,("Unknown tcon shareflag 0x%x\n", io->out.flags));
+ }
+
+ return smb2_request_destroy(req);
+}
+
+/*
+ sync tree connect request
+*/
+NTSTATUS smb2_tree_connect(struct smb2_tree *tree, struct smb2_tree_connect *io)
+{
+ struct smb2_request *req = smb2_tree_connect_send(tree, io);
+ return smb2_tree_connect_recv(req, io);
+}
diff --git a/source4/libcli/smb2/tdis.c b/source4/libcli/smb2/tdis.c
new file mode 100644
index 0000000000..5adad9dc6e
--- /dev/null
+++ b/source4/libcli/smb2/tdis.c
@@ -0,0 +1,65 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ SMB2 client tdis handling
+
+ Copyright (C) Andrew Tridgell 2005
+
+ 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 "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+
+/*
+ send a tdis request
+*/
+struct smb2_request *smb2_tdis_send(struct smb2_tree *tree)
+{
+ struct smb2_request *req;
+
+ req = smb2_request_init_tree(tree, SMB2_OP_TDIS, 0x04, false, 0);
+ if (req == NULL) return NULL;
+
+ SSVAL(req->out.body, 0x02, 0);
+
+ smb2_transport_send(req);
+
+ return req;
+}
+
+
+/*
+ recv a tdis reply
+*/
+NTSTATUS smb2_tdis_recv(struct smb2_request *req)
+{
+ if (!smb2_request_receive(req) ||
+ !smb2_request_is_ok(req)) {
+ return smb2_request_destroy(req);
+ }
+
+ SMB2_CHECK_PACKET_RECV(req, 0x04, false);
+ return smb2_request_destroy(req);
+}
+
+/*
+ sync tdis request
+*/
+NTSTATUS smb2_tdis(struct smb2_tree *tree)
+{
+ struct smb2_request *req = smb2_tdis_send(tree);
+ return smb2_tdis_recv(req);
+}
diff --git a/source4/libcli/smb2/transport.c b/source4/libcli/smb2/transport.c
new file mode 100644
index 0000000000..b946a102c8
--- /dev/null
+++ b/source4/libcli/smb2/transport.c
@@ -0,0 +1,417 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ SMB2 client transport context management functions
+
+ Copyright (C) Andrew Tridgell 2005
+
+ 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 "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+#include "lib/socket/socket.h"
+#include "lib/events/events.h"
+#include "lib/stream/packet.h"
+#include "lib/util/dlinklist.h"
+
+
+/*
+ an event has happened on the socket
+*/
+static void smb2_transport_event_handler(struct event_context *ev,
+ struct fd_event *fde,
+ uint16_t flags, void *private)
+{
+ struct smb2_transport *transport = talloc_get_type(private,
+ struct smb2_transport);
+ if (flags & EVENT_FD_READ) {
+ packet_recv(transport->packet);
+ return;
+ }
+ if (flags & EVENT_FD_WRITE) {
+ packet_queue_run(transport->packet);
+ }
+}
+
+/*
+ destroy a transport
+ */
+static int transport_destructor(struct smb2_transport *transport)
+{
+ smb2_transport_dead(transport, NT_STATUS_LOCAL_DISCONNECT);
+ return 0;
+}
+
+
+/*
+ handle receive errors
+*/
+static void smb2_transport_error(void *private, NTSTATUS status)
+{
+ struct smb2_transport *transport = talloc_get_type(private,
+ struct smb2_transport);
+ smb2_transport_dead(transport, status);
+}
+
+static NTSTATUS smb2_transport_finish_recv(void *private, DATA_BLOB blob);
+
+/*
+ create a transport structure based on an established socket
+*/
+struct smb2_transport *smb2_transport_init(struct smbcli_socket *sock,
+ TALLOC_CTX *parent_ctx,
+ struct smbcli_options *options)
+{
+ struct smb2_transport *transport;
+
+ transport = talloc_zero(parent_ctx, struct smb2_transport);
+ if (!transport) return NULL;
+
+ transport->socket = talloc_steal(transport, sock);
+ transport->options = *options;
+
+ /* setup the stream -> packet parser */
+ transport->packet = packet_init(transport);
+ if (transport->packet == NULL) {
+ talloc_free(transport);
+ return NULL;
+ }
+ packet_set_private(transport->packet, transport);
+ packet_set_socket(transport->packet, transport->socket->sock);
+ packet_set_callback(transport->packet, smb2_transport_finish_recv);
+ packet_set_full_request(transport->packet, packet_full_request_nbt);
+ packet_set_error_handler(transport->packet, smb2_transport_error);
+ packet_set_event_context(transport->packet, transport->socket->event.ctx);
+ packet_set_nofree(transport->packet);
+
+ /* take over event handling from the socket layer - it only
+ handles events up until we are connected */
+ talloc_free(transport->socket->event.fde);
+ transport->socket->event.fde = event_add_fd(transport->socket->event.ctx,
+ transport->socket,
+ socket_get_fd(transport->socket->sock),
+ EVENT_FD_READ,
+ smb2_transport_event_handler,
+ transport);
+
+ packet_set_fde(transport->packet, transport->socket->event.fde);
+ packet_set_serialise(transport->packet);
+
+ talloc_set_destructor(transport, transport_destructor);
+
+ return transport;
+}
+
+/*
+ mark the transport as dead
+*/
+void smb2_transport_dead(struct smb2_transport *transport, NTSTATUS status)
+{
+ smbcli_sock_dead(transport->socket);
+
+ if (NT_STATUS_EQUAL(NT_STATUS_UNSUCCESSFUL, status)) {
+ status = NT_STATUS_UNEXPECTED_NETWORK_ERROR;
+ }
+
+ /* kill all pending receives */
+ while (transport->pending_recv) {
+ struct smb2_request *req = transport->pending_recv;
+ req->state = SMB2_REQUEST_ERROR;
+ req->status = status;
+ DLIST_REMOVE(transport->pending_recv, req);
+ if (req->async.fn) {
+ req->async.fn(req);
+ }
+ }
+}
+
+static NTSTATUS smb2_handle_oplock_break(struct smb2_transport *transport,
+ const DATA_BLOB *blob)
+{
+ uint8_t *hdr;
+ uint16_t opcode;
+
+ hdr = blob->data+NBT_HDR_SIZE;
+
+ if (blob->length < (SMB2_MIN_SIZE+0x18)) {
+ DEBUG(1,("Discarding smb2 oplock reply of size %u\n",
+ (unsigned)blob->length));
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ opcode = SVAL(hdr, SMB2_HDR_OPCODE);
+
+ if (opcode != SMB2_OP_BREAK) {
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ if (transport->oplock.handler) {
+ uint8_t *body = hdr+SMB2_HDR_BODY;
+ struct smb2_handle h;
+ uint8_t level;
+
+ level = CVAL(body, 0x02);
+ smb2_pull_handle(body+0x08, &h);
+
+ transport->oplock.handler(transport, &h, level,
+ transport->oplock.private_data);
+ } else {
+ DEBUG(5,("Got SMB2 oplock break with no handler\n"));
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ we have a full request in our receive buffer - match it to a pending request
+ and process
+ */
+static NTSTATUS smb2_transport_finish_recv(void *private, DATA_BLOB blob)
+{
+ struct smb2_transport *transport = talloc_get_type(private,
+ struct smb2_transport);
+ uint8_t *buffer, *hdr;
+ int len;
+ struct smb2_request *req = NULL;
+ uint64_t seqnum;
+ uint32_t flags;
+ uint16_t buffer_code;
+ uint32_t dynamic_size;
+ uint32_t i;
+ NTSTATUS status;
+
+ buffer = blob.data;
+ len = blob.length;
+
+ hdr = buffer+NBT_HDR_SIZE;
+
+ if (len < SMB2_MIN_SIZE) {
+ DEBUG(1,("Discarding smb2 reply of size %d\n", len));
+ goto error;
+ }
+
+ flags = IVAL(hdr, SMB2_HDR_FLAGS);
+ seqnum = BVAL(hdr, SMB2_HDR_MESSAGE_ID);
+
+ /* see MS-SMB2 3.2.5.19 */
+ if (seqnum == UINT64_MAX) {
+ return smb2_handle_oplock_break(transport, &blob);
+ }
+
+ /* match the incoming request against the list of pending requests */
+ for (req=transport->pending_recv; req; req=req->next) {
+ if (req->seqnum == seqnum) break;
+ }
+
+ if (!req) {
+ DEBUG(1,("Discarding unmatched reply with seqnum 0x%llx op %d\n",
+ (long long)seqnum, SVAL(hdr, SMB2_HDR_OPCODE)));
+ goto error;
+ }
+
+ /* fill in the 'in' portion of the matching request */
+ req->in.buffer = buffer;
+ talloc_steal(req, buffer);
+ req->in.size = len;
+ req->in.allocated = req->in.size;
+
+ req->in.hdr = hdr;
+ req->in.body = hdr+SMB2_HDR_BODY;
+ req->in.body_size = req->in.size - (SMB2_HDR_BODY+NBT_HDR_SIZE);
+ req->status = NT_STATUS(IVAL(hdr, SMB2_HDR_STATUS));
+
+ if ((flags & SMB2_HDR_FLAG_ASYNC) &&
+ NT_STATUS_EQUAL(req->status, STATUS_PENDING)) {
+ req->cancel.can_cancel = true;
+ req->cancel.pending_id = IVAL(hdr, SMB2_HDR_PID);
+ for (i=0; i< req->cancel.do_cancel; i++) {
+ smb2_cancel(req);
+ }
+ talloc_free(buffer);
+ return NT_STATUS_OK;
+ }
+
+ if (req->session && req->session->signing_active) {
+ status = smb2_check_signature(&req->in,
+ req->session->session_key);
+ if (!NT_STATUS_IS_OK(status)) {
+ /* the spec says to ignore packets with a bad signature */
+ talloc_free(buffer);
+ return status;
+ }
+ }
+
+ buffer_code = SVAL(req->in.body, 0);
+ req->in.body_fixed = (buffer_code & ~1);
+ req->in.dynamic = NULL;
+ dynamic_size = req->in.body_size - req->in.body_fixed;
+ if (dynamic_size != 0 && (buffer_code & 1)) {
+ req->in.dynamic = req->in.body + req->in.body_fixed;
+ if (smb2_oob(&req->in, req->in.dynamic, dynamic_size)) {
+ DEBUG(1,("SMB2 request invalid dynamic size 0x%x\n",
+ dynamic_size));
+ goto error;
+ }
+ }
+
+ smb2_setup_bufinfo(req);
+
+ DEBUG(2, ("SMB2 RECV seqnum=0x%llx\n", (long long)req->seqnum));
+ dump_data(5, req->in.body, req->in.body_size);
+
+ /* if this request has an async handler then call that to
+ notify that the reply has been received. This might destroy
+ the request so it must happen last */
+ DLIST_REMOVE(transport->pending_recv, req);
+ req->state = SMB2_REQUEST_DONE;
+ if (req->async.fn) {
+ req->async.fn(req);
+ }
+ return NT_STATUS_OK;
+
+error:
+ dump_data(5, buffer, len);
+ if (req) {
+ DLIST_REMOVE(transport->pending_recv, req);
+ req->state = SMB2_REQUEST_ERROR;
+ if (req->async.fn) {
+ req->async.fn(req);
+ }
+ } else {
+ talloc_free(buffer);
+ }
+ return NT_STATUS_UNSUCCESSFUL;
+}
+
+/*
+ handle timeouts of individual smb requests
+*/
+static void smb2_timeout_handler(struct event_context *ev, struct timed_event *te,
+ struct timeval t, void *private)
+{
+ struct smb2_request *req = talloc_get_type(private, struct smb2_request);
+
+ if (req->state == SMB2_REQUEST_RECV) {
+ DLIST_REMOVE(req->transport->pending_recv, req);
+ }
+ req->status = NT_STATUS_IO_TIMEOUT;
+ req->state = SMB2_REQUEST_ERROR;
+ if (req->async.fn) {
+ req->async.fn(req);
+ }
+}
+
+
+/*
+ destroy a request
+*/
+static int smb2_request_destructor(struct smb2_request *req)
+{
+ if (req->state == SMB2_REQUEST_RECV) {
+ DLIST_REMOVE(req->transport->pending_recv, req);
+ }
+ return 0;
+}
+
+
+/*
+ put a request into the send queue
+*/
+void smb2_transport_send(struct smb2_request *req)
+{
+ DATA_BLOB blob;
+ NTSTATUS status;
+
+ _smb2_setlen(req->out.buffer, req->out.size - NBT_HDR_SIZE);
+
+ DEBUG(2, ("SMB2 send seqnum=0x%llx\n", (long long)req->seqnum));
+ dump_data(5, req->out.body, req->out.body_size);
+
+ /* check if the transport is dead */
+ if (req->transport->socket->sock == NULL) {
+ req->state = SMB2_REQUEST_ERROR;
+ req->status = NT_STATUS_NET_WRITE_FAULT;
+ return;
+ }
+
+ /* possibly sign the message */
+ if (req->session && req->session->signing_active) {
+ status = smb2_sign_message(&req->out, req->session->session_key);
+ if (!NT_STATUS_IS_OK(status)) {
+ req->state = SMB2_REQUEST_ERROR;
+ req->status = status;
+ return;
+ }
+ }
+
+ blob = data_blob_const(req->out.buffer, req->out.size);
+ status = packet_send(req->transport->packet, blob);
+ if (!NT_STATUS_IS_OK(status)) {
+ req->state = SMB2_REQUEST_ERROR;
+ req->status = status;
+ return;
+ }
+
+ req->state = SMB2_REQUEST_RECV;
+ DLIST_ADD(req->transport->pending_recv, req);
+
+ /* add a timeout */
+ if (req->transport->options.request_timeout) {
+ event_add_timed(req->transport->socket->event.ctx, req,
+ timeval_current_ofs(req->transport->options.request_timeout, 0),
+ smb2_timeout_handler, req);
+ }
+
+ talloc_set_destructor(req, smb2_request_destructor);
+}
+
+static void idle_handler(struct event_context *ev,
+ struct timed_event *te, struct timeval t, void *private)
+{
+ struct smb2_transport *transport = talloc_get_type(private,
+ struct smb2_transport);
+ struct timeval next = timeval_add(&t, 0, transport->idle.period);
+ transport->socket->event.te = event_add_timed(transport->socket->event.ctx,
+ transport,
+ next,
+ idle_handler, transport);
+ transport->idle.func(transport, transport->idle.private);
+}
+
+/*
+ setup the idle handler for a transport
+ the period is in microseconds
+*/
+void smb2_transport_idle_handler(struct smb2_transport *transport,
+ void (*idle_func)(struct smb2_transport *, void *),
+ uint64_t period,
+ void *private)
+{
+ transport->idle.func = idle_func;
+ transport->idle.private = private;
+ transport->idle.period = period;
+
+ if (transport->socket->event.te != NULL) {
+ talloc_free(transport->socket->event.te);
+ }
+
+ transport->socket->event.te = event_add_timed(transport->socket->event.ctx,
+ transport,
+ timeval_current_ofs(0, period),
+ idle_handler, transport);
+}
diff --git a/source4/libcli/smb2/util.c b/source4/libcli/smb2/util.c
new file mode 100644
index 0000000000..311cea94a0
--- /dev/null
+++ b/source4/libcli/smb2/util.c
@@ -0,0 +1,210 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ SMB2 client utility functions
+
+ Copyright (C) Andrew Tridgell 2005
+
+ 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 "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+#include "libcli/smb_composite/smb_composite.h"
+
+/*
+ simple close wrapper with SMB2
+*/
+NTSTATUS smb2_util_close(struct smb2_tree *tree, struct smb2_handle h)
+{
+ struct smb2_close c;
+
+ ZERO_STRUCT(c);
+ c.in.file.handle = h;
+
+ return smb2_close(tree, &c);
+}
+
+/*
+ unlink a file with SMB2
+*/
+NTSTATUS smb2_util_unlink(struct smb2_tree *tree, const char *fname)
+{
+ union smb_unlink io;
+
+ ZERO_STRUCT(io);
+ io.unlink.in.pattern = fname;
+
+ return smb2_composite_unlink(tree, &io);
+}
+
+
+/*
+ rmdir with SMB2
+*/
+NTSTATUS smb2_util_rmdir(struct smb2_tree *tree, const char *dname)
+{
+ struct smb_rmdir io;
+
+ ZERO_STRUCT(io);
+ io.in.path = dname;
+
+ return smb2_composite_rmdir(tree, &io);
+}
+
+
+/*
+ mkdir with SMB2
+*/
+NTSTATUS smb2_util_mkdir(struct smb2_tree *tree, const char *dname)
+{
+ union smb_mkdir io;
+
+ ZERO_STRUCT(io);
+ io.mkdir.level = RAW_MKDIR_MKDIR;
+ io.mkdir.in.path = dname;
+
+ return smb2_composite_mkdir(tree, &io);
+}
+
+
+/*
+ set file attribute with SMB2
+*/
+NTSTATUS smb2_util_setatr(struct smb2_tree *tree, const char *name, uint32_t attrib)
+{
+ union smb_setfileinfo io;
+
+ ZERO_STRUCT(io);
+ io.basic_info.level = RAW_SFILEINFO_BASIC_INFORMATION;
+ io.basic_info.in.file.path = name;
+ io.basic_info.in.attrib = attrib;
+
+ return smb2_composite_setpathinfo(tree, &io);
+}
+
+
+
+
+/*
+ recursively descend a tree deleting all files
+ returns the number of files deleted, or -1 on error
+*/
+int smb2_deltree(struct smb2_tree *tree, const char *dname)
+{
+ NTSTATUS status;
+ uint32_t total_deleted = 0;
+ uint_t count, i;
+ union smb_search_data *list;
+ TALLOC_CTX *tmp_ctx = talloc_new(tree);
+ struct smb2_find f;
+ struct smb2_create create_parm;
+
+ /* it might be a file */
+ status = smb2_util_unlink(tree, dname);
+ if (NT_STATUS_IS_OK(status)) {
+ talloc_free(tmp_ctx);
+ return 1;
+ }
+ if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND) ||
+ NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_PATH_NOT_FOUND) ||
+ NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_FILE)) {
+ talloc_free(tmp_ctx);
+ return 0;
+ }
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_CANNOT_DELETE)) {
+ /* it could be read-only */
+ status = smb2_util_setatr(tree, dname, FILE_ATTRIBUTE_NORMAL);
+ status = smb2_util_unlink(tree, dname);
+ }
+ if (NT_STATUS_IS_OK(status)) {
+ talloc_free(tmp_ctx);
+ return 1;
+ }
+
+ ZERO_STRUCT(create_parm);
+ create_parm.in.desired_access = SEC_FLAG_MAXIMUM_ALLOWED;
+ create_parm.in.share_access =
+ NTCREATEX_SHARE_ACCESS_READ|
+ NTCREATEX_SHARE_ACCESS_WRITE;
+ create_parm.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
+ create_parm.in.create_disposition = NTCREATEX_DISP_OPEN;
+ create_parm.in.fname = dname;
+
+ status = smb2_create(tree, tmp_ctx, &create_parm);
+ if (NT_STATUS_IS_ERR(status)) {
+ DEBUG(2,("Failed to open %s - %s\n", dname, nt_errstr(status)));
+ talloc_free(tmp_ctx);
+ return -1;
+ }
+
+
+ ZERO_STRUCT(f);
+ f.in.file.handle = create_parm.out.file.handle;
+ f.in.max_response_size = 0x10000;
+ f.in.level = SMB2_FIND_NAME_INFO;
+ f.in.pattern = "*";
+
+ status = smb2_find_level(tree, tmp_ctx, &f, &count, &list);
+ if (NT_STATUS_IS_ERR(status)) {
+ DEBUG(2,("Failed to list %s - %s\n",
+ dname, nt_errstr(status)));
+ smb2_util_close(tree, create_parm.out.file.handle);
+ talloc_free(tmp_ctx);
+ return -1;
+ }
+
+ for (i=0;i<count;i++) {
+ char *name;
+ if (strcmp(".", list[i].name_info.name.s) == 0 ||
+ strcmp("..", list[i].name_info.name.s) == 0) {
+ continue;
+ }
+ name = talloc_asprintf(tmp_ctx, "%s\\%s", dname, list[i].name_info.name.s);
+ status = smb2_util_unlink(tree, name);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_CANNOT_DELETE)) {
+ /* it could be read-only */
+ status = smb2_util_setatr(tree, name, FILE_ATTRIBUTE_NORMAL);
+ status = smb2_util_unlink(tree, name);
+ }
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_FILE_IS_A_DIRECTORY)) {
+ int ret;
+ ret = smb2_deltree(tree, name);
+ if (ret > 0) total_deleted += ret;
+ }
+ talloc_free(name);
+ if (NT_STATUS_IS_OK(status)) {
+ total_deleted++;
+ }
+ }
+
+ smb2_util_close(tree, create_parm.out.file.handle);
+
+ status = smb2_util_rmdir(tree, dname);
+ if (NT_STATUS_IS_ERR(status)) {
+ DEBUG(2,("Failed to delete %s - %s\n",
+ dname, nt_errstr(status)));
+ talloc_free(tmp_ctx);
+ return -1;
+ }
+
+ talloc_free(tmp_ctx);
+
+ return total_deleted;
+}
diff --git a/source4/libcli/smb2/write.c b/source4/libcli/smb2/write.c
new file mode 100644
index 0000000000..bc283370d7
--- /dev/null
+++ b/source4/libcli/smb2/write.c
@@ -0,0 +1,81 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ SMB2 client write call
+
+ Copyright (C) Andrew Tridgell 2005
+
+ 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 "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+
+/*
+ send a write request
+*/
+struct smb2_request *smb2_write_send(struct smb2_tree *tree, struct smb2_write *io)
+{
+ NTSTATUS status;
+ struct smb2_request *req;
+
+ req = smb2_request_init_tree(tree, SMB2_OP_WRITE, 0x30, true, io->in.data.length);
+ if (req == NULL) return NULL;
+
+ status = smb2_push_o16s32_blob(&req->out, 0x02, io->in.data);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(req);
+ return NULL;
+ }
+
+ SBVAL(req->out.body, 0x08, io->in.offset);
+ smb2_push_handle(req->out.body+0x10, &io->in.file.handle);
+
+ SBVAL(req->out.body, 0x20, io->in.unknown1);
+ SBVAL(req->out.body, 0x28, io->in.unknown2);
+
+ smb2_transport_send(req);
+
+ return req;
+}
+
+
+/*
+ recv a write reply
+*/
+NTSTATUS smb2_write_recv(struct smb2_request *req, struct smb2_write *io)
+{
+ if (!smb2_request_receive(req) ||
+ smb2_request_is_error(req)) {
+ return smb2_request_destroy(req);
+ }
+
+ SMB2_CHECK_PACKET_RECV(req, 0x10, true);
+
+ io->out._pad = SVAL(req->in.body, 0x02);
+ io->out.nwritten = IVAL(req->in.body, 0x04);
+ io->out.unknown1 = BVAL(req->in.body, 0x08);
+
+ return smb2_request_destroy(req);
+}
+
+/*
+ sync write request
+*/
+NTSTATUS smb2_write(struct smb2_tree *tree, struct smb2_write *io)
+{
+ struct smb2_request *req = smb2_write_send(tree, io);
+ return smb2_write_recv(req, io);
+}