summaryrefslogtreecommitdiffstats
path: root/source4/libcli/smb2
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-05 17:47:29 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-05 17:47:29 +0000
commit4f5791ebd03eaec1c7da0865a383175b05102712 (patch)
tree8ce7b00f7a76baa386372422adebbe64510812d4 /source4/libcli/smb2
parentInitial commit. (diff)
downloadsamba-upstream.tar.xz
samba-upstream.zip
Adding upstream version 2:4.17.12+dfsg.upstream/2%4.17.12+dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--source4/libcli/smb2/break.c74
-rw-r--r--source4/libcli/smb2/cancel.c45
-rw-r--r--source4/libcli/smb2/close.c80
-rw-r--r--source4/libcli/smb2/connect.c483
-rw-r--r--source4/libcli/smb2/create.c446
-rw-r--r--source4/libcli/smb2/find.c181
-rw-r--r--source4/libcli/smb2/flush.c70
-rw-r--r--source4/libcli/smb2/getinfo.c239
-rw-r--r--source4/libcli/smb2/ioctl.c151
-rw-r--r--source4/libcli/smb2/keepalive.c68
-rw-r--r--source4/libcli/smb2/lease_break.c81
-rw-r--r--source4/libcli/smb2/lock.c82
-rw-r--r--source4/libcli/smb2/logoff.c67
-rw-r--r--source4/libcli/smb2/notify.c116
-rw-r--r--source4/libcli/smb2/read.c89
-rw-r--r--source4/libcli/smb2/request.c717
-rw-r--r--source4/libcli/smb2/session.c477
-rw-r--r--source4/libcli/smb2/setinfo.c123
-rw-r--r--source4/libcli/smb2/signing.c139
-rw-r--r--source4/libcli/smb2/smb2.h204
-rw-r--r--source4/libcli/smb2/smb2_calls.h99
-rw-r--r--source4/libcli/smb2/tcon.c52
-rw-r--r--source4/libcli/smb2/tdis.c65
-rw-r--r--source4/libcli/smb2/transport.c557
-rw-r--r--source4/libcli/smb2/util.c363
-rw-r--r--source4/libcli/smb2/write.c81
-rw-r--r--source4/libcli/smb2/wscript_build10
27 files changed, 5159 insertions, 0 deletions
diff --git a/source4/libcli/smb2/break.c b/source4/libcli/smb2/break.c
new file mode 100644
index 0000000..fe0cceb
--- /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 0000000..cc40b34
--- /dev/null
+++ b/source4/libcli/smb2/cancel.c
@@ -0,0 +1,45 @@
+/*
+ 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 <tevent.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)
+{
+ bool ok;
+
+ if (r->subreq == NULL) {
+ return NT_STATUS_OK;
+ }
+
+ ok = tevent_req_cancel(r->subreq);
+ if (!ok) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ return NT_STATUS_OK;
+}
diff --git a/source4/libcli/smb2/close.c b/source4/libcli/smb2/close.c
new file mode 100644
index 0000000..4e6f330
--- /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/connect.c b/source4/libcli/smb2/connect.c
new file mode 100644
index 0000000..64b6786
--- /dev/null
+++ b/source4/libcli/smb2/connect.c
@@ -0,0 +1,483 @@
+/*
+ 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 <tevent.h>
+#include "lib/util/tevent_ntstatus.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"
+#include "auth/credentials/credentials.h"
+#include "../libcli/smb/smbXcli_base.h"
+#include "smb2_constants.h"
+
+struct smb2_connect_state {
+ struct tevent_context *ev;
+ struct cli_credentials *credentials;
+ bool fallback_to_anonymous;
+ uint64_t previous_session_id;
+ struct resolve_context *resolve_ctx;
+ const char *host;
+ const char *share;
+ const char *unc;
+ const char **ports;
+ const char *socket_options;
+ struct nbt_name calling, called;
+ struct gensec_settings *gensec_settings;
+ struct smbcli_options options;
+ struct smb2_transport *transport;
+ struct smb2_session *session;
+ struct smb2_tree *tree;
+};
+
+static void smb2_connect_session_start(struct tevent_req *req);
+static void smb2_connect_socket_done(struct composite_context *creq);
+
+/*
+ a composite function that does a full negprot/sesssetup/tcon, returning
+ a connected smb2_tree
+ */
+struct tevent_req *smb2_connect_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ const char *host,
+ const char **ports,
+ const char *share,
+ struct resolve_context *resolve_ctx,
+ struct cli_credentials *credentials,
+ bool fallback_to_anonymous,
+ struct smbXcli_conn **existing_conn,
+ uint64_t previous_session_id,
+ const struct smbcli_options *options,
+ const char *socket_options,
+ struct gensec_settings *gensec_settings)
+{
+ struct tevent_req *req;
+ struct smb2_connect_state *state;
+ struct composite_context *creq;
+ static const char *default_ports[] = { "445", "139", NULL };
+ enum smb_encryption_setting encryption_state =
+ cli_credentials_get_smb_encryption(credentials);
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct smb2_connect_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ state->ev = ev;
+ state->credentials = credentials;
+ state->fallback_to_anonymous = fallback_to_anonymous;
+ state->previous_session_id = previous_session_id;
+ state->options = *options;
+ state->host = host;
+ state->ports = ports;
+ state->share = share;
+ state->resolve_ctx = resolve_ctx;
+ state->socket_options = socket_options;
+ state->gensec_settings = gensec_settings;
+
+ if (state->ports == NULL) {
+ state->ports = default_ports;
+ }
+
+ if (encryption_state >= SMB_ENCRYPTION_DESIRED) {
+ state->options.signing = SMB_SIGNING_REQUIRED;
+ }
+
+ make_nbt_name_client(&state->calling,
+ cli_credentials_get_workstation(credentials));
+
+ nbt_choose_called_name(state, &state->called,
+ host, NBT_NAME_SERVER);
+
+ state->unc = talloc_asprintf(state, "\\\\%s\\%s",
+ state->host, state->share);
+ if (tevent_req_nomem(state->unc, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ if (existing_conn != NULL) {
+ NTSTATUS status;
+
+ status = smb2_transport_raw_init(state, ev,
+ existing_conn,
+ &state->options,
+ &state->transport);
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
+
+ smb2_connect_session_start(req);
+ if (!tevent_req_is_in_progress(req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ return req;
+ }
+
+ creq = smbcli_sock_connect_send(state, NULL, state->ports,
+ state->host, state->resolve_ctx,
+ state->ev, state->socket_options,
+ &state->calling,
+ &state->called);
+ if (tevent_req_nomem(creq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ creq->async.fn = smb2_connect_socket_done;
+ creq->async.private_data = req;
+
+ return req;
+}
+
+static void smb2_connect_negprot_done(struct tevent_req *subreq);
+
+static void smb2_connect_socket_done(struct composite_context *creq)
+{
+ struct tevent_req *req =
+ talloc_get_type_abort(creq->async.private_data,
+ struct tevent_req);
+ struct smb2_connect_state *state =
+ tevent_req_data(req,
+ struct smb2_connect_state);
+ struct smbcli_socket *sock;
+ struct tevent_req *subreq;
+ NTSTATUS status;
+ uint32_t timeout_msec;
+ enum protocol_types min_protocol;
+
+ status = smbcli_sock_connect_recv(creq, state, &sock);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ state->transport = smb2_transport_init(sock, state, &state->options);
+ if (tevent_req_nomem(state->transport, req)) {
+ return;
+ }
+
+ timeout_msec = state->transport->options.request_timeout * 1000;
+ min_protocol = state->transport->options.min_protocol;
+ if (min_protocol < PROTOCOL_SMB2_02) {
+ min_protocol = PROTOCOL_SMB2_02;
+ }
+
+ subreq = smbXcli_negprot_send(state, state->ev,
+ state->transport->conn, timeout_msec,
+ min_protocol,
+ state->transport->options.max_protocol,
+ state->transport->options.max_credits,
+ NULL);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, smb2_connect_negprot_done, req);
+}
+
+static void smb2_connect_session_done(struct tevent_req *subreq);
+
+static void smb2_connect_negprot_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq,
+ struct tevent_req);
+ NTSTATUS status;
+
+ status = smbXcli_negprot_recv(subreq, NULL, NULL);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ smb2_connect_session_start(req);
+}
+
+static void smb2_connect_session_start(struct tevent_req *req)
+{
+ struct smb2_connect_state *state =
+ tevent_req_data(req,
+ struct smb2_connect_state);
+ struct smb2_transport *transport = state->transport;
+ struct tevent_req *subreq = NULL;
+
+ state->session = smb2_session_init(transport, state->gensec_settings, state);
+ if (tevent_req_nomem(state->session, req)) {
+ return;
+ }
+
+ if (state->options.only_negprot) {
+ state->tree = smb2_tree_init(state->session, state, true);
+ if (tevent_req_nomem(state->tree, req)) {
+ return;
+ }
+ tevent_req_done(req);
+ return;
+ }
+
+ subreq = smb2_session_setup_spnego_send(state, state->ev,
+ state->session,
+ state->credentials,
+ state->previous_session_id);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, smb2_connect_session_done, req);
+}
+
+static void smb2_connect_enc_start(struct tevent_req *req);
+static void smb2_connect_tcon_start(struct tevent_req *req);
+static void smb2_connect_tcon_done(struct tevent_req *subreq);
+
+static void smb2_connect_session_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct smb2_connect_state *state =
+ tevent_req_data(req,
+ struct smb2_connect_state);
+ NTSTATUS status;
+
+ status = smb2_session_setup_spnego_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (!NT_STATUS_IS_OK(status) &&
+ !cli_credentials_is_anonymous(state->credentials) &&
+ state->fallback_to_anonymous) {
+ struct cli_credentials *anon_creds = NULL;
+
+ /*
+ * The transport was moved to session,
+ * we need to revert that before removing
+ * the old broken session.
+ */
+ state->transport = talloc_move(state, &state->session->transport);
+ TALLOC_FREE(state->session);
+
+ anon_creds = cli_credentials_init_anon(state);
+ if (tevent_req_nomem(anon_creds, req)) {
+ return;
+ }
+ cli_credentials_set_workstation(anon_creds,
+ cli_credentials_get_workstation(state->credentials),
+ CRED_SPECIFIED);
+
+ /*
+ * retry with anonymous credentials
+ */
+ state->credentials = anon_creds;
+ smb2_connect_session_start(req);
+ return;
+ }
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ state->tree = smb2_tree_init(state->session, state, true);
+ if (tevent_req_nomem(state->tree, req)) {
+ return;
+ }
+
+ smb2_connect_enc_start(req);
+}
+
+static void smb2_connect_enc_start(struct tevent_req *req)
+{
+ struct smb2_connect_state *state =
+ tevent_req_data(req,
+ struct smb2_connect_state);
+ enum smb_encryption_setting encryption_state =
+ cli_credentials_get_smb_encryption(state->credentials);
+ NTSTATUS status;
+
+ if (encryption_state < SMB_ENCRYPTION_DESIRED) {
+ smb2_connect_tcon_start(req);
+ return;
+ }
+
+ status = smb2cli_session_encryption_on(state->session->smbXcli);
+ if (!NT_STATUS_IS_OK(status)) {
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)) {
+ if (encryption_state < SMB_ENCRYPTION_REQUIRED) {
+ smb2_connect_tcon_start(req);
+ return;
+ }
+
+ DBG_ERR("Encryption required and server doesn't support "
+ "SMB3 encryption - failing connect\n");
+ tevent_req_nterror(req, status);
+ return;
+ }
+
+ DBG_ERR("Encryption required and setup failed with error %s.\n",
+ nt_errstr(status));
+ tevent_req_nterror(req, NT_STATUS_PROTOCOL_NOT_SUPPORTED);
+ return;
+ }
+
+ smb2_connect_tcon_start(req);
+}
+
+static void smb2_connect_tcon_start(struct tevent_req *req)
+{
+ struct smb2_connect_state *state =
+ tevent_req_data(req,
+ struct smb2_connect_state);
+ struct tevent_req *subreq = NULL;
+ uint32_t timeout_msec;
+
+ timeout_msec = state->transport->options.request_timeout * 1000;
+
+ subreq = smb2cli_tcon_send(state, state->ev,
+ state->transport->conn,
+ timeout_msec,
+ state->session->smbXcli,
+ state->tree->smbXcli,
+ 0, /* flags */
+ state->unc);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, smb2_connect_tcon_done, req);
+}
+
+static void smb2_connect_tcon_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq,
+ struct tevent_req);
+ NTSTATUS status;
+
+ status = smb2cli_tcon_recv(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+NTSTATUS smb2_connect_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ struct smb2_tree **tree)
+{
+ struct smb2_connect_state *state =
+ tevent_req_data(req,
+ struct smb2_connect_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ tevent_req_received(req);
+ return status;
+ }
+
+ *tree = talloc_move(mem_ctx, &state->tree);
+
+ tevent_req_received(req);
+ return NT_STATUS_OK;
+}
+
+/*
+ sync version of smb2_connect
+*/
+NTSTATUS smb2_connect_ext(TALLOC_CTX *mem_ctx,
+ const char *host,
+ const char **ports,
+ const char *share,
+ struct resolve_context *resolve_ctx,
+ struct cli_credentials *credentials,
+ struct smbXcli_conn **existing_conn,
+ uint64_t previous_session_id,
+ struct smb2_tree **tree,
+ struct tevent_context *ev,
+ const struct smbcli_options *options,
+ const char *socket_options,
+ struct gensec_settings *gensec_settings)
+{
+ struct tevent_req *subreq;
+ NTSTATUS status;
+ bool ok;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ if (frame == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ subreq = smb2_connect_send(frame,
+ ev,
+ host,
+ ports,
+ share,
+ resolve_ctx,
+ credentials,
+ false, /* fallback_to_anonymous */
+ existing_conn,
+ previous_session_id,
+ options,
+ socket_options,
+ gensec_settings);
+ if (subreq == NULL) {
+ TALLOC_FREE(frame);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ok = tevent_req_poll(subreq, ev);
+ if (!ok) {
+ status = map_nt_error_from_unix_common(errno);
+ TALLOC_FREE(frame);
+ return status;
+ }
+
+ status = smb2_connect_recv(subreq, mem_ctx, tree);
+ TALLOC_FREE(subreq);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(frame);
+ return status;
+ }
+
+ TALLOC_FREE(frame);
+ return NT_STATUS_OK;
+}
+
+NTSTATUS smb2_connect(TALLOC_CTX *mem_ctx,
+ const char *host,
+ const char **ports,
+ const char *share,
+ struct resolve_context *resolve_ctx,
+ struct cli_credentials *credentials,
+ struct smb2_tree **tree,
+ struct tevent_context *ev,
+ const struct smbcli_options *options,
+ const char *socket_options,
+ struct gensec_settings *gensec_settings)
+{
+ NTSTATUS status;
+
+ status = smb2_connect_ext(mem_ctx, host, ports, share, resolve_ctx,
+ credentials,
+ NULL, /* existing_conn */
+ 0, /* previous_session_id */
+ tree, ev, options, socket_options,
+ gensec_settings);
+
+ return status;
+}
diff --git a/source4/libcli/smb2/create.c b/source4/libcli/smb2/create.c
new file mode 100644
index 0000000..c91b150
--- /dev/null
+++ b/source4/libcli/smb2/create.c
@@ -0,0 +1,446 @@
+/*
+ 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"
+
+/*
+ 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_open_v2) {
+ uint8_t data[32];
+ uint32_t flags = 0;
+ struct GUID_ndr_buf guid_buf = { .buf = {0}, };
+
+ SIVAL(data, 0, io->in.timeout);
+ if (io->in.persistent_open) {
+ flags = SMB2_DHANDLE_FLAG_PERSISTENT;
+ }
+ SIVAL(data, 4, flags);
+ SBVAL(data, 8, 0x0); /* reserved */
+ status = GUID_to_ndr_buf(&io->in.create_guid, &guid_buf);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(req);
+ return NULL;
+ }
+ memcpy(data+16, guid_buf.buf, sizeof(guid_buf.buf));
+
+ status = smb2_create_blob_add(req, &blobs,
+ SMB2_CREATE_TAG_DH2Q,
+ data_blob_const(data, 32));
+ 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.durable_handle_v2) {
+ uint8_t data[36];
+ struct GUID_ndr_buf guid_buf = { .buf = {0}, };
+ uint32_t flags = 0;
+
+ smb2_push_handle(data, io->in.durable_handle_v2);
+ status = GUID_to_ndr_buf(&io->in.create_guid, &guid_buf);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(req);
+ return NULL;
+ }
+ memcpy(data+16, guid_buf.buf, sizeof(guid_buf.buf));
+ if (io->in.persistent_open) {
+ flags = SMB2_DHANDLE_FLAG_PERSISTENT;
+ }
+ SIVAL(data, 32, flags);
+
+ status = smb2_create_blob_add(req, &blobs,
+ SMB2_CREATE_TAG_DH2C,
+ data_blob_const(data, 36));
+ 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, 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;
+ }
+ }
+
+ if (io->in.lease_request) {
+ uint8_t data[32];
+
+ if (!smb2_lease_push(io->in.lease_request, data,
+ sizeof(data))) {
+ TALLOC_FREE(req);
+ return NULL;
+ }
+
+ status = smb2_create_blob_add(
+ req, &blobs, SMB2_CREATE_TAG_RQLS,
+ data_blob_const(data, sizeof(data)));
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(req);
+ return NULL;
+ }
+ }
+
+ if (io->in.lease_request_v2) {
+ uint8_t data[52];
+
+ if (!smb2_lease_push(io->in.lease_request_v2, data,
+ sizeof(data))) {
+ TALLOC_FREE(req);
+ return NULL;
+ }
+
+ status = smb2_create_blob_add(
+ req, &blobs, SMB2_CREATE_TAG_RQLS,
+ data_blob_const(data, sizeof(data)));
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(req);
+ return NULL;
+ }
+ }
+
+ if (io->in.app_instance_id) {
+ uint8_t data[20];
+ struct GUID_ndr_buf guid_buf = { .buf = {0}, };
+
+ SSVAL(data, 0, 20); /* structure size */
+ SSVAL(data, 2, 0); /* reserved */
+
+ status = GUID_to_ndr_buf(io->in.app_instance_id, &guid_buf);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(req);
+ return NULL;
+ }
+ memcpy(data+4, guid_buf.buf, sizeof(guid_buf.buf));
+
+ status = smb2_create_blob_add(req, &blobs,
+ SMB2_CREATE_TAG_APP_INSTANCE_ID,
+ data_blob_const(data, 20));
+ 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;
+ }
+
+ if (((io->in.fname == NULL) || (strlen(io->in.fname) == 0)) &&
+ (blob.length == 0)) {
+ struct smb2_request_buffer *buf = &req->out;
+
+ status = smb2_grow_buffer(buf, 1);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(req);
+ return NULL;
+ }
+ buf->dynamic[0] = 0;
+ buf->dynamic += 1;
+ buf->body_size += 1;
+ buf->size += 1;
+ }
+
+ 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) {
+ if (io->out.blobs.blobs[i].data.length != 8) {
+ smb2_request_destroy(req);
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+ io->out.maximal_access_status =
+ IVAL(io->out.blobs.blobs[i].data.data, 0);
+ 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);
+ }
+ if (strcmp(io->out.blobs.blobs[i].tag, SMB2_CREATE_TAG_RQLS) == 0) {
+ struct smb2_lease *ls = NULL;
+ uint8_t *data;
+
+ ZERO_STRUCT(io->out.lease_response);
+ ZERO_STRUCT(io->out.lease_response_v2);
+
+ switch (io->out.blobs.blobs[i].data.length) {
+ case 32:
+ ls = &io->out.lease_response;
+ ls->lease_version = 1;
+ break;
+ case 52:
+ ls = &io->out.lease_response_v2;
+ ls->lease_version = 2;
+ break;
+ default:
+ smb2_request_destroy(req);
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ data = io->out.blobs.blobs[i].data.data;
+ memcpy(&ls->lease_key, data, 16);
+ ls->lease_state = IVAL(data, 16);
+ ls->lease_flags = IVAL(data, 20);
+ ls->lease_duration = BVAL(data, 24);
+
+ if (io->out.blobs.blobs[i].data.length == 52) {
+ memcpy(&ls->parent_lease_key, data+32, 16);
+ ls->lease_epoch = SVAL(data, 48);
+ }
+ }
+ if (strcmp(io->out.blobs.blobs[i].tag, SMB2_CREATE_TAG_DHNQ) == 0) {
+ if (io->out.blobs.blobs[i].data.length != 8) {
+ smb2_request_destroy(req);
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+ io->out.durable_open = true;
+ }
+ if (strcmp(io->out.blobs.blobs[i].tag, SMB2_CREATE_TAG_DH2Q) == 0) {
+ uint32_t flags;
+ uint8_t *data;
+
+ if (io->out.blobs.blobs[i].data.length != 8) {
+ smb2_request_destroy(req);
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ io->out.durable_open = false;
+ io->out.durable_open_v2 = true;
+
+ data = io->out.blobs.blobs[i].data.data;
+ io->out.timeout = IVAL(data, 0);
+ flags = IVAL(data, 4);
+ if ((flags & SMB2_DHANDLE_FLAG_PERSISTENT) != 0) {
+ io->out.persistent_open = true;
+ }
+ }
+ }
+
+ 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 0000000..23ac737
--- /dev/null
+++ b/source4/libcli/smb2/find.c
@@ -0,0 +1,181 @@
+/*
+ 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_QUERY_DIRECTORY, 0x20, true, 0);
+ if (req == NULL) return NULL;
+ req->credit_charge = (MAX(io->in.max_response_size, 1) - 1)/ 65536 + 1;
+
+ 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, unsigned int *count,
+ union smb_search_data **io)
+{
+ struct smb2_find f;
+ NTSTATUS status;
+ DATA_BLOB b;
+ enum smb_search_data_level smb_level;
+ unsigned int 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,
+ unsigned int *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 0000000..577d1ba
--- /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 0000000..5ffb988
--- /dev/null
+++ b/source4/libcli/smb2/getinfo.c
@@ -0,0 +1,239 @@
+/*
+ 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;
+ size_t max_payload;
+
+ req = smb2_request_init_tree(tree, SMB2_OP_GETINFO, 0x28, true,
+ io->in.input_buffer.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);
+ /*
+ * uint16_t input_buffer_offset
+ * uint16_t reserved
+ * uint32_t input_buffer_length
+ *
+ * We use smb2_push_o32s32_blob() which would
+ * expect uint32_t offset, uint32_t length.
+ *
+ * Everything is little endian, we can just
+ * overwrite the reserved field later.
+ */
+ 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.input_buffer);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(req);
+ return NULL;
+ }
+ SSVAL(req->out.body, 0x0C, io->in.reserved);
+
+ max_payload = MAX(io->in.output_buffer_length, io->in.input_buffer.length);
+ req->credit_charge = (MAX(max_payload, 1) - 1)/ 65536 + 1;
+
+ 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 info_class)
+{
+ if (info_class == SMB2_0_INFO_FILE &&
+ level == RAW_FILEINFO_SEC_DESC) {
+ return SMB2_0_INFO_SECURITY;
+ }
+ if ((level & 0xFF) == info_class) {
+ return level;
+ } else if (level > 1000) {
+ return ((level-1000)<<8) | info_class;
+ }
+ DEBUG(0,("Unable to map SMB2 info level 0x%04x of class %d\n",
+ level, info_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_0_INFO_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 = data_blob_null;
+ 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_0_INFO_FILESYSTEM);
+
+ 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 = {
+ .in = {0},
+ };
+ 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 0000000..fe74dfe
--- /dev/null
+++ b/source4/libcli/smb2/ioctl.c
@@ -0,0 +1,151 @@
+/*
+ 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"
+#include "librpc/gen_ndr/ioctl.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;
+ uint64_t max_payload_in;
+ uint64_t max_payload_out;
+ size_t max_payload;
+
+ 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.max_input_response);
+
+ 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_output_response);
+ SBVAL(req->out.body, 0x30, io->in.flags);
+
+ max_payload_in = io->in.out.length + io->in.in.length;
+ max_payload_in = MIN(max_payload_in, UINT32_MAX);
+ max_payload_out = io->in.max_input_response + io->in.max_output_response;
+ max_payload_out = MIN(max_payload_out, UINT32_MAX);
+
+ max_payload = MAX(max_payload_in, max_payload_out);
+ req->credit_charge = (MAX(max_payload, 1) - 1)/ 65536 + 1;
+
+ smb2_transport_send(req);
+
+ return req;
+}
+
+/*
+ * 3.3.4.4 Sending an Error Response
+ */
+static bool smb2_ioctl_is_failure(uint32_t ctl_code, NTSTATUS status,
+ size_t data_size)
+{
+ if (NT_STATUS_IS_OK(status)) {
+ return false;
+ }
+
+ if (NT_STATUS_EQUAL(status, STATUS_BUFFER_OVERFLOW)
+ && ((ctl_code == FSCTL_PIPE_TRANSCEIVE)
+ || (ctl_code == FSCTL_PIPE_PEEK)
+ || (ctl_code == FSCTL_DFS_GET_REFERRALS))) {
+ return false;
+ }
+
+ if (((ctl_code == FSCTL_SRV_COPYCHUNK)
+ || (ctl_code == FSCTL_SRV_COPYCHUNK_WRITE))
+ && (data_size == sizeof(struct srv_copychunk_rsp))) {
+ /*
+ * copychunk responses may come with copychunk data or error
+ * response data, independent of status.
+ */
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ 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_ioctl_is_failure(io->in.function, req->status,
+ req->in.bufinfo.data_size)) {
+ return smb2_request_destroy(req);
+ }
+
+ SMB2_CHECK_PACKET_RECV(req, 0x30, true);
+
+ io->out.reserved = 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.flags = IVAL(req->in.body, 0x28);
+ io->out.reserved2 = 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 0000000..71004aa
--- /dev/null
+++ b/source4/libcli/smb2/keepalive.c
@@ -0,0 +1,68 @@
+/*
+ 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_session *session)
+{
+ 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);
+
+ req->session = session;
+
+ 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, NULL);
+ return smb2_keepalive_recv(req);
+}
diff --git a/source4/libcli/smb2/lease_break.c b/source4/libcli/smb2/lease_break.c
new file mode 100644
index 0000000..c238f1d
--- /dev/null
+++ b/source4/libcli/smb2/lease_break.c
@@ -0,0 +1,81 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ SMB2 client oplock break handling
+
+ Copyright (C) Zachary Loafman 2009
+
+ 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 Lease Break Acknowledgement
+*/
+struct smb2_request *smb2_lease_break_ack_send(struct smb2_tree *tree,
+ struct smb2_lease_break_ack *io)
+{
+ struct smb2_request *req;
+
+ req = smb2_request_init_tree(tree, SMB2_OP_BREAK, 0x24, false, 0);
+ if (req == NULL) return NULL;
+
+ SIVAL(req->out.body, 0x02, io->in.reserved);
+ SIVAL(req->out.body, 0x04, io->in.lease.lease_flags);
+ memcpy(req->out.body+0x8, &io->in.lease.lease_key,
+ sizeof(struct smb2_lease_key));
+ SIVAL(req->out.body, 0x18, io->in.lease.lease_state);
+ SBVAL(req->out.body, 0x1C, io->in.lease.lease_duration);
+
+ smb2_transport_send(req);
+
+ return req;
+}
+
+
+/*
+ Receive a Lease Break Response
+*/
+NTSTATUS smb2_lease_break_ack_recv(struct smb2_request *req,
+ struct smb2_lease_break_ack *io)
+{
+ if (!smb2_request_receive(req) ||
+ !smb2_request_is_ok(req)) {
+ return smb2_request_destroy(req);
+ }
+
+ SMB2_CHECK_PACKET_RECV(req, 0x24, false);
+
+ io->out.reserved = IVAL(req->in.body, 0x02);
+ io->out.lease.lease_flags = IVAL(req->in.body, 0x04);
+ memcpy(&io->out.lease.lease_key, req->in.body+0x8,
+ sizeof(struct smb2_lease_key));
+ io->out.lease.lease_state = IVAL(req->in.body, 0x18);
+ io->out.lease.lease_duration = IVAL(req->in.body, 0x1C);
+
+ return smb2_request_destroy(req);
+}
+
+/*
+ sync flush request
+*/
+NTSTATUS smb2_lease_break_ack(struct smb2_tree *tree,
+ struct smb2_lease_break_ack *io)
+{
+ struct smb2_request *req = smb2_lease_break_ack_send(tree, io);
+ return smb2_lease_break_ack_recv(req, io);
+}
diff --git a/source4/libcli/smb2/lock.c b/source4/libcli/smb2/lock.c
new file mode 100644
index 0000000..f2a76d8
--- /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.lock_sequence);
+ 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 0000000..12cd553
--- /dev/null
+++ b/source4/libcli/smb2/logoff.c
@@ -0,0 +1,67 @@
+/*
+ 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;
+
+ 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/notify.c b/source4/libcli/smb2/notify.c
new file mode 100644
index 0000000..6786a70
--- /dev/null
+++ b/source4/libcli/smb2/notify.c
@@ -0,0 +1,116 @@
+/*
+ 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);
+
+ req->credit_charge = (MAX(io->in.buffer_size, 1) - 1)/ 65536 + 1;
+
+ 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 0000000..ca487a7
--- /dev/null
+++ b/source4/libcli/smb2/read.c
@@ -0,0 +1,89 @@
+/*
+ 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);
+
+ req->credit_charge = (MAX(io->in.length, 1) - 1)/ 65536 + 1;
+
+ 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 0000000..3024e01
--- /dev/null
+++ b/source4/libcli/smb2/request.c
@@ -0,0 +1,717 @@
+/*
+ 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"
+
+/* 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;
+ }
+}
+
+/*
+ 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;
+ uint32_t hdr_offset;
+ bool compound = false;
+
+ if (body_dynamic_present) {
+ if (body_dynamic_size == 0) {
+ body_dynamic_size = 1;
+ }
+ } else {
+ body_dynamic_size = 0;
+ }
+
+ req = talloc_zero(transport, struct smb2_request);
+ if (req == NULL) return NULL;
+
+ req->state = SMB2_REQUEST_INIT;
+ req->transport = transport;
+
+ hdr_offset = NBT_HDR_SIZE;
+
+ req->out.size = hdr_offset + SMB2_HDR_BODY + body_fixed_size;
+ req->out.allocated = req->out.size + body_dynamic_size;
+
+ req->out.buffer = talloc_realloc(req, req->out.buffer,
+ uint8_t, req->out.allocated);
+ if (req->out.buffer == NULL) {
+ talloc_free(req);
+ return NULL;
+ }
+
+ req->out.hdr = req->out.buffer + hdr_offset;
+ 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_CREDIT_CHARGE, 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, 0);
+ 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 && !compound) {
+ req->out.size += 1;
+ SCVAL(req->out.dynamic, 0, 0);
+ }
+
+ 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;
+
+ 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 (tevent_loop_once(req->transport->ev) != 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)) {
+ if (buf->dynamic != (buf->buffer + buf->size)) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/*
+ grow a SMB2 buffer by the specified amount
+*/
+NTSTATUS smb2_grow_buffer(struct smb2_request_buffer *buf, size_t increase)
+{
+ size_t hdr_ofs;
+ 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;
+
+ hdr_ofs = buf->hdr - buf->buffer;
+ 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 + hdr_ofs;
+ 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_INVALID_PARAMETER;
+ }
+ 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_INVALID_PARAMETER;
+ }
+ *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_INVALID_PARAMETER;
+ }
+
+ /* check if there're enough room for ofs and size */
+ if (smb2_oob(buf, ptr, 4)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ 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_INVALID_PARAMETER;
+ }
+
+ 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_INVALID_PARAMETER;
+ }
+
+ 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_INVALID_PARAMETER;
+ }
+
+ 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_INVALID_PARAMETER;
+ }
+ 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_INVALID_PARAMETER;
+ }
+ *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_INVALID_PARAMETER;
+ }
+ 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_INVALID_PARAMETER;
+ }
+ *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_INVALID_PARAMETER;
+ }
+ 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_INVALID_PARAMETER;
+ }
+ *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_INVALID_PARAMETER;
+ }
+ 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_INVALID_PARAMETER;
+ }
+ *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/ uint16_t ofs/blob triple from a data blob
+ the ptr points to the start of the offset/length pair
+*/
+NTSTATUS smb2_pull_s32o16_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_INVALID_PARAMETER;
+ }
+ size = IVAL(ptr, 0);
+ ofs = SVAL(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_INVALID_PARAMETER;
+ }
+ *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;
+ void *vstr;
+ size_t converted_size = 0;
+ bool ret;
+
+ 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;
+ }
+
+ ret = convert_string_talloc(mem_ctx, CH_UTF16, CH_UNIX,
+ blob.data, blob.length, &vstr, &converted_size);
+ data_blob_free(&blob);
+ (*str) = (char *)vstr;
+ if (!ret) {
+ 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;
+ bool ret;
+ void *ptr = NULL;
+
+ if (str == NULL) {
+ return smb2_push_o16s16_blob(buf, ofs, data_blob(NULL, 0));
+ }
+
+ if (*str == 0) {
+ blob.data = discard_const_p(uint8_t, str);
+ blob.length = 0;
+ return smb2_push_o16s16_blob(buf, ofs, blob);
+ }
+
+ ret = convert_string_talloc(buf->buffer, CH_UNIX, CH_UTF16,
+ str, strlen(str), &ptr, &blob.length);
+ if (!ret) {
+ return NT_STATUS_ILLEGAL_CHARACTER;
+ }
+ blob.data = (uint8_t *)ptr;
+
+ 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 0000000..e94512d
--- /dev/null
+++ b/source4/libcli/smb2/session.c
@@ -0,0 +1,477 @@
+/*
+ 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 "system/network.h"
+#include <tevent.h>
+#include "lib/util/tevent_ntstatus.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+#include "auth/gensec/gensec.h"
+#include "auth/credentials/credentials.h"
+#include "../libcli/smb/smbXcli_base.h"
+
+/**
+ initialise a smb2_session structure
+ */
+struct smb2_session *smb2_session_init(struct smb2_transport *transport,
+ struct gensec_settings *settings,
+ TALLOC_CTX *parent_ctx)
+{
+ struct smb2_session *session;
+ NTSTATUS status;
+
+ session = talloc_zero(parent_ctx, struct smb2_session);
+ if (!session) {
+ return NULL;
+ }
+ session->transport = talloc_steal(session, transport);
+
+ session->smbXcli = smbXcli_session_create(session, transport->conn);
+ if (session->smbXcli == NULL) {
+ talloc_free(session);
+ return NULL;
+ }
+
+ /* prepare a gensec context for later use */
+ status = gensec_client_start(session, &session->gensec,
+ settings);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(session);
+ return NULL;
+ }
+
+ gensec_want_feature(session->gensec, GENSEC_FEATURE_SESSION_KEY);
+
+ return session;
+}
+
+/*
+ * Note: that the caller needs to keep 'transport' around as
+ * long as the returned session is active!
+ */
+struct smb2_session *smb2_session_channel(struct smb2_transport *transport,
+ struct gensec_settings *settings,
+ TALLOC_CTX *parent_ctx,
+ struct smb2_session *base_session)
+{
+ struct smb2_session *session;
+ NTSTATUS status;
+
+ session = talloc_zero(parent_ctx, struct smb2_session);
+ if (!session) {
+ return NULL;
+ }
+ session->transport = transport;
+
+ status = smb2cli_session_create_channel(session,
+ base_session->smbXcli,
+ transport->conn,
+ &session->smbXcli);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(session);
+ return NULL;
+ }
+
+ session->needs_bind = true;
+
+ /* prepare a gensec context for later use */
+ status = gensec_client_start(session, &session->gensec,
+ settings);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(session);
+ return NULL;
+ }
+
+ gensec_want_feature(session->gensec, GENSEC_FEATURE_SESSION_KEY);
+
+ return session;
+}
+
+struct smb2_session_setup_spnego_state {
+ struct tevent_context *ev;
+ struct smb2_session *session;
+ struct cli_credentials *credentials;
+ uint64_t previous_session_id;
+ bool session_bind;
+ bool reauth;
+ NTSTATUS gensec_status;
+ NTSTATUS remote_status;
+ DATA_BLOB in_secblob;
+ DATA_BLOB out_secblob;
+ struct iovec *recv_iov;
+};
+
+static void smb2_session_setup_spnego_gensec_next(struct tevent_req *req);
+static void smb2_session_setup_spnego_gensec_done(struct tevent_req *subreq);
+static void smb2_session_setup_spnego_smb2_next(struct tevent_req *req);
+static void smb2_session_setup_spnego_smb2_done(struct tevent_req *subreq);
+static void smb2_session_setup_spnego_both_ready(struct tevent_req *req);
+
+/*
+ a composite function that does a full SPNEGO session setup
+ */
+struct tevent_req *smb2_session_setup_spnego_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct smb2_session *session,
+ struct cli_credentials *credentials,
+ uint64_t previous_session_id)
+{
+ struct smb2_transport *transport = session->transport;
+ struct tevent_req *req;
+ struct smb2_session_setup_spnego_state *state;
+ uint64_t current_session_id;
+ const char *chosen_oid;
+ NTSTATUS status;
+ const DATA_BLOB *server_gss_blob;
+ struct timeval endtime;
+ bool ok;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct smb2_session_setup_spnego_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->ev = ev;
+ state->session = session;
+ state->credentials = credentials;
+ state->previous_session_id = previous_session_id;
+ state->gensec_status = NT_STATUS_MORE_PROCESSING_REQUIRED;
+ state->remote_status = NT_STATUS_MORE_PROCESSING_REQUIRED;
+
+ endtime = timeval_current_ofs(transport->options.request_timeout, 0);
+
+ ok = tevent_req_set_endtime(req, ev, endtime);
+ if (!ok) {
+ return tevent_req_post(req, ev);
+ }
+
+ current_session_id = smb2cli_session_current_id(state->session->smbXcli);
+ if (state->session->needs_bind) {
+ state->session_bind = true;
+ } else if (current_session_id != 0) {
+ state->reauth = true;
+ }
+ server_gss_blob = smbXcli_conn_server_gss_blob(session->transport->conn);
+ if (server_gss_blob) {
+ state->out_secblob = *server_gss_blob;
+ }
+
+ status = gensec_set_credentials(session->gensec, credentials);
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
+
+ status = gensec_set_target_hostname(session->gensec,
+ smbXcli_conn_remote_name(session->transport->conn));
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
+
+ status = gensec_set_target_service(session->gensec, "cifs");
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
+
+ if (state->out_secblob.length > 0) {
+ chosen_oid = GENSEC_OID_SPNEGO;
+ status = gensec_start_mech_by_oid(session->gensec, chosen_oid);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Failed to start set GENSEC client mechanism %s: %s\n",
+ gensec_get_name_by_oid(session->gensec,
+ chosen_oid),
+ nt_errstr(status)));
+ state->out_secblob = data_blob_null;
+ chosen_oid = GENSEC_OID_NTLMSSP;
+ status = gensec_start_mech_by_oid(session->gensec,
+ chosen_oid);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Failed to start set (fallback) GENSEC client mechanism %s: %s\n",
+ gensec_get_name_by_oid(session->gensec,
+ chosen_oid),
+ nt_errstr(status)));
+ }
+ }
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
+ } else {
+ chosen_oid = GENSEC_OID_NTLMSSP;
+ status = gensec_start_mech_by_oid(session->gensec, chosen_oid);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Failed to start set GENSEC client mechanism %s: %s\n",
+ gensec_get_name_by_oid(session->gensec,
+ chosen_oid),
+ nt_errstr(status)));
+ }
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
+ }
+
+ smb2_session_setup_spnego_gensec_next(req);
+ if (!tevent_req_is_in_progress(req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ return req;
+}
+
+static void smb2_session_setup_spnego_gensec_next(struct tevent_req *req)
+{
+ struct smb2_session_setup_spnego_state *state =
+ tevent_req_data(req,
+ struct smb2_session_setup_spnego_state);
+ struct smb2_session *session = state->session;
+ struct tevent_req *subreq = NULL;
+
+ if (NT_STATUS_IS_OK(state->gensec_status)) {
+ tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
+ return;
+ }
+
+ subreq = gensec_update_send(state, state->ev,
+ session->gensec,
+ state->out_secblob);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq,
+ smb2_session_setup_spnego_gensec_done,
+ req);
+}
+
+static void smb2_session_setup_spnego_gensec_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct smb2_session_setup_spnego_state *state =
+ tevent_req_data(req,
+ struct smb2_session_setup_spnego_state);
+ NTSTATUS status;
+
+ status = gensec_update_recv(subreq, state,
+ &state->in_secblob);
+ state->gensec_status = status;
+ state->out_secblob = data_blob_null;
+ if (!NT_STATUS_IS_OK(status) &&
+ !NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ tevent_req_nterror(req, status);
+ return;
+ }
+
+ if (NT_STATUS_IS_OK(state->remote_status) &&
+ NT_STATUS_IS_OK(state->gensec_status)) {
+ smb2_session_setup_spnego_both_ready(req);
+ return;
+ }
+
+ smb2_session_setup_spnego_smb2_next(req);
+}
+
+static void smb2_session_setup_spnego_smb2_next(struct tevent_req *req)
+{
+ struct smb2_session_setup_spnego_state *state =
+ tevent_req_data(req,
+ struct smb2_session_setup_spnego_state);
+ struct smb2_session *session = state->session;
+ uint32_t timeout_msec;
+ uint8_t in_flags = 0;
+ struct tevent_req *subreq = NULL;
+
+ if (NT_STATUS_IS_OK(state->remote_status)) {
+ tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
+ return;
+ }
+
+ timeout_msec = session->transport->options.request_timeout * 1000;
+
+ if (state->session_bind) {
+ in_flags |= SMB2_SESSION_FLAG_BINDING;
+ }
+
+ subreq = smb2cli_session_setup_send(state, state->ev,
+ session->transport->conn,
+ timeout_msec,
+ session->smbXcli,
+ in_flags,
+ 0, /* in_capabilities */
+ 0, /* in_channel */
+ state->previous_session_id,
+ &state->in_secblob);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq,
+ smb2_session_setup_spnego_smb2_done,
+ req);
+}
+
+/*
+ handle continuations of the spnego session setup
+*/
+static void smb2_session_setup_spnego_smb2_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct smb2_session_setup_spnego_state *state =
+ tevent_req_data(req,
+ struct smb2_session_setup_spnego_state);
+ NTSTATUS status;
+
+ status = smb2cli_session_setup_recv(subreq, state,
+ &state->recv_iov,
+ &state->out_secblob);
+ state->remote_status = status;
+ state->in_secblob = data_blob_null;
+ if (!NT_STATUS_IS_OK(status) &&
+ !NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ tevent_req_nterror(req, status);
+ return;
+ }
+
+ if (NT_STATUS_IS_OK(state->remote_status) &&
+ NT_STATUS_IS_OK(state->gensec_status)) {
+ smb2_session_setup_spnego_both_ready(req);
+ return;
+ }
+
+ smb2_session_setup_spnego_gensec_next(req);
+}
+
+static void smb2_session_setup_spnego_both_ready(struct tevent_req *req)
+{
+ struct smb2_session_setup_spnego_state *state =
+ tevent_req_data(req,
+ struct smb2_session_setup_spnego_state);
+ struct smb2_session *session = state->session;
+ NTSTATUS status;
+ DATA_BLOB session_key;
+
+ if (state->out_secblob.length != 0) {
+ tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
+ return;
+ }
+
+ if (state->in_secblob.length != 0) {
+ tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
+ return;
+ }
+
+ if (state->reauth) {
+ tevent_req_done(req);
+ return;
+ }
+
+ if (cli_credentials_is_anonymous(state->credentials)) {
+ /*
+ * Windows server does not set the
+ * SMB2_SESSION_FLAG_IS_GUEST nor
+ * SMB2_SESSION_FLAG_IS_NULL flag.
+ *
+ * This fix makes sure we do not try
+ * to verify a signature on the final
+ * session setup response.
+ */
+ tevent_req_done(req);
+ return;
+ }
+
+ status = gensec_session_key(session->gensec, state,
+ &session_key);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ if (state->session_bind) {
+ status = smb2cli_session_set_channel_key(session->smbXcli,
+ session_key,
+ state->recv_iov);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ session->needs_bind = false;
+ } else {
+ status = smb2cli_session_set_session_key(session->smbXcli,
+ session_key,
+ state->recv_iov);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ }
+ tevent_req_done(req);
+ return;
+}
+
+/*
+ receive a composite session setup reply
+*/
+NTSTATUS smb2_session_setup_spnego_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
+
+/*
+ sync version of smb2_session_setup_spnego
+*/
+NTSTATUS smb2_session_setup_spnego(struct smb2_session *session,
+ struct cli_credentials *credentials,
+ uint64_t previous_session_id)
+{
+ struct tevent_req *subreq;
+ NTSTATUS status;
+ bool ok;
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct tevent_context *ev = session->transport->ev;
+
+ if (frame == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ subreq = smb2_session_setup_spnego_send(frame, ev,
+ session, credentials,
+ previous_session_id);
+ if (subreq == NULL) {
+ TALLOC_FREE(frame);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ok = tevent_req_poll(subreq, ev);
+ if (!ok) {
+ status = map_nt_error_from_unix_common(errno);
+ TALLOC_FREE(frame);
+ return status;
+ }
+
+ status = smb2_session_setup_spnego_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(frame);
+ return status;
+ }
+
+ TALLOC_FREE(frame);
+ return NT_STATUS_OK;
+}
diff --git a/source4/libcli/smb2/setinfo.c b/source4/libcli/smb2/setinfo.c
new file mode 100644
index 0000000..f8b50f6
--- /dev/null
+++ b/source4/libcli/smb2/setinfo.c
@@ -0,0 +1,123 @@
+/*
+ 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_0_INFO_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 0000000..c423b7c
--- /dev/null
+++ b/source4/libcli/smb2/signing.c
@@ -0,0 +1,139 @@
+/*
+ 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 <gnutls/gnutls.h>
+#include <gnutls/crypto.h>
+#include "lib/crypto/gnutls_helpers.h"
+
+/*
+ sign an outgoing message
+ */
+NTSTATUS smb2_sign_message(struct smb2_request_buffer *buf, DATA_BLOB session_key)
+{
+ uint8_t digest[gnutls_hash_get_len(GNUTLS_MAC_SHA256)];
+ uint64_t session_id;
+ size_t hdr_offset;
+ int rc;
+
+ if (buf->size < NBT_HDR_SIZE + SMB2_HDR_SIGNATURE + 16) {
+ /* can't sign non-SMB2 messages */
+ return NT_STATUS_OK;
+ }
+
+ hdr_offset = buf->hdr - buf->buffer;
+
+ 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);
+
+ rc = gnutls_hmac_fast(GNUTLS_MAC_SHA256,
+ session_key.data,
+ MIN(session_key.length, 16),
+ buf->hdr,
+ buf->size - hdr_offset,
+ digest);
+ if (rc < 0) {
+ return gnutls_error_to_ntstatus(rc, NT_STATUS_HMAC_NOT_SUPPORTED);
+ }
+
+ DEBUG(5,("signed SMB2 message of size %u\n", (unsigned)buf->size - NBT_HDR_SIZE));
+
+ memcpy(buf->hdr + SMB2_HDR_SIGNATURE, digest, 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;
+ uint8_t digest[gnutls_hash_get_len(GNUTLS_MAC_SHA256)];
+ uint8_t sig[16];
+ size_t hdr_offset;
+ int rc;
+
+ if (buf->size < NBT_HDR_SIZE + SMB2_HDR_SIGNATURE + 16) {
+ /* can't check non-SMB2 messages */
+ return NT_STATUS_OK;
+ }
+
+ hdr_offset = buf->hdr - buf->buffer;
+
+ 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);
+
+ rc = gnutls_hmac_fast(GNUTLS_MAC_SHA256,
+ session_key.data,
+ MIN(session_key.length, 16),
+ buf->hdr,
+ buf->size - hdr_offset,
+ digest);
+ if (rc < 0) {
+ return gnutls_error_to_ntstatus(rc, NT_STATUS_HMAC_NOT_SUPPORTED);
+ }
+
+ memcpy(buf->hdr + SMB2_HDR_SIGNATURE, digest, 16);
+
+ if (!mem_equal_const_time(digest, sig, 16)) {
+ 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, digest, 16);
+ ZERO_ARRAY(digest);
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ ZERO_ARRAY(digest);
+
+ return NT_STATUS_OK;
+}
diff --git a/source4/libcli/smb2/smb2.h b/source4/libcli/smb2/smb2.h
new file mode 100644
index 0000000..4aadab2
--- /dev/null
+++ b/source4/libcli/smb2/smb2.h
@@ -0,0 +1,204 @@
+/*
+ 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;
+struct smb2_lease_break;
+
+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;
+};
+
+/* this is the context for the smb2 transport layer */
+struct smb2_transport {
+ struct tevent_context *ev; /* TODO: remove this !!! */
+ struct smbXcli_conn *conn;
+
+ /* the details for coumpounded requests */
+ struct {
+ bool related;
+ struct tevent_req **reqs;
+ } compound;
+
+ /* 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_data;
+ unsigned int period;
+ struct tevent_timer *te;
+ } 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 {
+ /* a lease break request handler */
+ bool (*handler)(struct smb2_transport *transport,
+ const struct smb2_lease_break *lease_break,
+ void *private_data);
+ /* private data passed to the oplock handler */
+ void *private_data;
+ } lease;
+ struct tevent_req *break_subreq;
+
+ struct smbcli_options options;
+};
+
+
+/*
+ SMB2 LSA state
+*/
+struct smb2lsa_state {
+ struct dcerpc_binding_handle *binding_handle;
+ struct smb2_tree *ipc_tree;
+ struct policy_handle handle;
+};
+
+
+/*
+ SMB2 tree context
+*/
+struct smb2_tree {
+ struct smb2_session *session;
+ struct smbXcli_tcon *smbXcli;
+ struct smb2lsa_state *lsa;
+};
+
+/*
+ SMB2 session context
+*/
+struct smb2_session {
+ struct smb2_transport *transport;
+ struct gensec_security *gensec;
+ struct smbXcli_session *smbXcli;
+ bool needs_bind;
+};
+
+
+
+/*
+ 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 {
+ /* each request is in one of 3 possible states */
+ enum smb2_request_state state;
+
+ struct tevent_req *subreq;
+
+ struct smb2_transport *transport;
+ struct smb2_session *session;
+ struct smb2_tree *tree;
+
+ struct {
+ bool can_cancel;
+ } 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;
+ struct iovec *recv_iov;
+
+ uint16_t credit_charge;
+
+ /* 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
+#define SMB2_MIN_SIZE_NO_BODY 0x40
+
+/*
+ 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 0000000..b6c08c2
--- /dev/null
+++ b/source4/libcli/smb2/smb2_calls.h
@@ -0,0 +1,99 @@
+/*
+ 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;
+};
+
+/* 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;
+ /* uint16_t input_buffer_offset; */
+ uint16_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 input_buffer;
+ } 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 tevent_context;
+struct resolve_context;
+struct gensec_settings;
+#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 0000000..702e308
--- /dev/null
+++ b/source4/libcli/smb2/tcon.c
@@ -0,0 +1,52 @@
+/*
+ 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"
+#include "../libcli/smb/smbXcli_base.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);
+ }
+
+ tree->smbXcli = smbXcli_tcon_create(tree);
+ if (tree->smbXcli == NULL) {
+ talloc_free(tree);
+ return NULL;
+ }
+
+ return tree;
+}
diff --git a/source4/libcli/smb2/tdis.c b/source4/libcli/smb2/tdis.c
new file mode 100644
index 0000000..5adad9d
--- /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 0000000..292ca0f
--- /dev/null
+++ b/source4/libcli/smb2/transport.c
@@ -0,0 +1,557 @@
+/*
+ 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 "system/network.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/util/dlinklist.h"
+#include "../libcli/smb/smbXcli_base.h"
+#include "librpc/ndr/libndr.h"
+
+/*
+ destroy a transport
+ */
+static int transport_destructor(struct smb2_transport *transport)
+{
+ smb2_transport_dead(transport, NT_STATUS_LOCAL_DISCONNECT);
+ return 0;
+}
+
+/*
+ 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->ev = sock->event.ctx;
+ transport->options = *options;
+
+ if (transport->options.max_protocol == PROTOCOL_DEFAULT) {
+ transport->options.max_protocol = PROTOCOL_LATEST;
+ }
+
+ if (transport->options.max_protocol < PROTOCOL_SMB2_02) {
+ transport->options.max_protocol = PROTOCOL_LATEST;
+ }
+
+ TALLOC_FREE(sock->event.fde);
+ TALLOC_FREE(sock->event.te);
+
+ transport->conn = smbXcli_conn_create(transport,
+ sock->sock->fd,
+ sock->hostname,
+ options->signing,
+ 0, /* smb1_capabilities */
+ &options->client_guid,
+ options->smb2_capabilities,
+ &options->smb3_capabilities);
+ if (transport->conn == NULL) {
+ talloc_free(transport);
+ return NULL;
+ }
+ sock->sock->fd = -1;
+ TALLOC_FREE(sock);
+
+ talloc_set_destructor(transport, transport_destructor);
+
+ return transport;
+}
+
+/*
+ create a transport structure based on an established socket
+*/
+NTSTATUS smb2_transport_raw_init(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct smbXcli_conn **_conn,
+ const struct smbcli_options *options,
+ struct smb2_transport **_transport)
+{
+ struct smb2_transport *transport = NULL;
+ enum protocol_types protocol;
+
+ if (*_conn == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ protocol = smbXcli_conn_protocol(*_conn);
+ if (protocol < PROTOCOL_SMB2_02) {
+ return NT_STATUS_REVISION_MISMATCH;
+ }
+
+ transport = talloc_zero(mem_ctx, struct smb2_transport);
+ if (transport == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ transport->ev = ev;
+ transport->options = *options;
+ transport->conn = talloc_move(transport, _conn);
+
+ talloc_set_destructor(transport, transport_destructor);
+ *_transport = transport;
+ return NT_STATUS_OK;
+}
+
+/*
+ mark the transport as dead
+*/
+void smb2_transport_dead(struct smb2_transport *transport, NTSTATUS status)
+{
+ if (NT_STATUS_EQUAL(NT_STATUS_UNSUCCESSFUL, status)) {
+ status = NT_STATUS_UNEXPECTED_NETWORK_ERROR;
+ }
+ if (NT_STATUS_IS_OK(status)) {
+ status = NT_STATUS_LOCAL_DISCONNECT;
+ }
+
+ smbXcli_conn_disconnect(transport->conn, status);
+}
+
+static void smb2_request_done(struct tevent_req *subreq);
+static void smb2_transport_break_handler(struct tevent_req *subreq);
+
+/*
+ put a request into the send queue
+*/
+void smb2_transport_send(struct smb2_request *req)
+{
+ NTSTATUS status;
+ struct smb2_transport *transport = req->transport;
+ struct tevent_req **reqs = transport->compound.reqs;
+ size_t num_reqs = talloc_array_length(reqs);
+ size_t i;
+ uint16_t cmd = SVAL(req->out.hdr, SMB2_HDR_OPCODE);
+ uint32_t additional_flags = IVAL(req->out.hdr, SMB2_HDR_FLAGS);
+ uint32_t clear_flags = 0;
+ struct smbXcli_tcon *tcon = NULL;
+ struct smbXcli_session *session = NULL;
+ bool need_pending_break = false;
+ size_t hdr_ofs;
+ size_t pdu_len;
+ DATA_BLOB body = data_blob_null;
+ DATA_BLOB dyn = data_blob_null;
+ uint32_t timeout_msec = transport->options.request_timeout * 1000;
+
+ if (transport->oplock.handler) {
+ need_pending_break = true;
+ }
+
+ if (transport->lease.handler) {
+ need_pending_break = true;
+ }
+
+ if (transport->break_subreq) {
+ need_pending_break = false;
+ }
+
+ if (need_pending_break) {
+ struct tevent_req *subreq;
+
+ subreq = smb2cli_req_create(transport,
+ transport->ev,
+ transport->conn,
+ SMB2_OP_BREAK,
+ 0, /* additional_flags */
+ 0, /*clear_flags */
+ 0, /* timeout_msec */
+ NULL, /* tcon */
+ NULL, /* session */
+ NULL, /* body */
+ 0, /* body_fixed */
+ NULL, /* dyn */
+ 0, /* dyn_len */
+ 0); /* max_dyn_len */
+ if (subreq != NULL) {
+ smbXcli_req_set_pending(subreq);
+ tevent_req_set_callback(subreq,
+ smb2_transport_break_handler,
+ transport);
+ transport->break_subreq = subreq;
+ }
+ }
+
+ if (req->session) {
+ session = req->session->smbXcli;
+ }
+
+ if (req->tree) {
+ tcon = req->tree->smbXcli;
+ }
+
+ if (transport->compound.related) {
+ additional_flags |= SMB2_HDR_FLAG_CHAINED;
+ }
+
+ hdr_ofs = PTR_DIFF(req->out.hdr, req->out.buffer);
+ pdu_len = req->out.size - hdr_ofs;
+ body.data = req->out.body;
+ body.length = req->out.body_fixed;
+ dyn.data = req->out.body + req->out.body_fixed;
+ dyn.length = pdu_len - (SMB2_HDR_BODY + req->out.body_fixed);
+
+ req->subreq = smb2cli_req_create(req,
+ transport->ev,
+ transport->conn,
+ cmd,
+ additional_flags,
+ clear_flags,
+ timeout_msec,
+ tcon,
+ session,
+ body.data, body.length,
+ dyn.data, dyn.length,
+ 0); /* max_dyn_len */
+ if (req->subreq == NULL) {
+ req->state = SMB2_REQUEST_ERROR;
+ req->status = NT_STATUS_NO_MEMORY;
+ return;
+ }
+
+ if (!tevent_req_is_in_progress(req->subreq)) {
+ req->state = SMB2_REQUEST_ERROR;
+ req->status = NT_STATUS_INTERNAL_ERROR;/* TODO */
+ return;
+ }
+
+ tevent_req_set_callback(req->subreq, smb2_request_done, req);
+
+ smb2cli_req_set_notify_async(req->subreq);
+ if (req->credit_charge) {
+ smb2cli_req_set_credit_charge(req->subreq, req->credit_charge);
+ }
+
+ ZERO_STRUCT(req->out);
+ req->state = SMB2_REQUEST_RECV;
+
+ if (num_reqs > 0) {
+ for (i=0; i < num_reqs; i++) {
+ if (reqs[i] != NULL) {
+ continue;
+ }
+
+ reqs[i] = req->subreq;
+ i++;
+ break;
+ }
+
+ if (i < num_reqs) {
+ return;
+ }
+ } else {
+ reqs = &req->subreq;
+ num_reqs = 1;
+ }
+ status = smb2cli_req_compound_submit(reqs, num_reqs);
+
+ TALLOC_FREE(transport->compound.reqs);
+ transport->compound.related = false;
+
+ if (!NT_STATUS_IS_OK(status)) {
+ req->status = status;
+ req->state = SMB2_REQUEST_ERROR;
+ smbXcli_conn_disconnect(transport->conn, status);
+ }
+}
+
+static void smb2_request_done(struct tevent_req *subreq)
+{
+ struct smb2_request *req =
+ tevent_req_callback_data(subreq,
+ struct smb2_request);
+ ssize_t len;
+ size_t i;
+
+ req->recv_iov = NULL;
+
+ req->status = smb2cli_req_recv(req->subreq, req, &req->recv_iov, NULL, 0);
+ if (NT_STATUS_EQUAL(req->status, NT_STATUS_PENDING)) {
+ struct timeval endtime = smbXcli_req_endtime(subreq);
+ bool ok;
+
+ req->cancel.can_cancel = true;
+ if (timeval_is_zero(&endtime)) {
+ return;
+ }
+
+ ok = tevent_req_set_endtime(
+ subreq, req->transport->ev, endtime);
+ if (!ok) {
+ req->status = NT_STATUS_INTERNAL_ERROR;
+ req->state = SMB2_REQUEST_ERROR;
+ if (req->async.fn) {
+ req->async.fn(req);
+ }
+ return;
+ }
+ return;
+ }
+ TALLOC_FREE(req->subreq);
+ if (!NT_STATUS_IS_OK(req->status)) {
+ if (req->recv_iov == NULL) {
+ req->state = SMB2_REQUEST_ERROR;
+ if (req->async.fn) {
+ req->async.fn(req);
+ }
+ return;
+ }
+ }
+
+ len = req->recv_iov[0].iov_len;
+ for (i=1; i < 3; i++) {
+ uint8_t *p = req->recv_iov[i-1].iov_base;
+ uint8_t *c1 = req->recv_iov[i].iov_base;
+ uint8_t *c2 = p + req->recv_iov[i-1].iov_len;
+
+ len += req->recv_iov[i].iov_len;
+
+ if (req->recv_iov[i].iov_len == 0) {
+ continue;
+ }
+
+ if (c1 != c2) {
+ req->status = NT_STATUS_INTERNAL_ERROR;
+ req->state = SMB2_REQUEST_ERROR;
+ if (req->async.fn) {
+ req->async.fn(req);
+ }
+ return;
+ }
+ }
+
+ req->in.buffer = req->recv_iov[0].iov_base;
+ req->in.size = len;
+ req->in.allocated = req->in.size;
+
+ req->in.hdr = req->recv_iov[0].iov_base;
+ req->in.body = req->recv_iov[1].iov_base;
+ req->in.dynamic = req->recv_iov[2].iov_base;
+ req->in.body_fixed = req->recv_iov[1].iov_len;
+ req->in.body_size = req->in.body_fixed;
+ req->in.body_size += req->recv_iov[2].iov_len;
+
+ smb2_setup_bufinfo(req);
+
+ req->state = SMB2_REQUEST_DONE;
+ if (req->async.fn) {
+ req->async.fn(req);
+ }
+}
+
+static void smb2_transport_break_handler(struct tevent_req *subreq)
+{
+ struct smb2_transport *transport =
+ tevent_req_callback_data(subreq,
+ struct smb2_transport);
+ NTSTATUS status;
+ uint8_t *body;
+ uint16_t len = 0;
+ bool lease;
+ struct iovec *recv_iov = NULL;
+
+ transport->break_subreq = NULL;
+
+ status = smb2cli_req_recv(subreq, transport, &recv_iov, NULL, 0);
+ TALLOC_FREE(subreq);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(recv_iov);
+ smb2_transport_dead(transport, status);
+ return;
+ }
+
+ /*
+ * Setup the subreq to handle the
+ * next incoming SMB2 Break.
+ */
+ subreq = smb2cli_req_create(transport,
+ transport->ev,
+ transport->conn,
+ SMB2_OP_BREAK,
+ 0, /* additional_flags */
+ 0, /*clear_flags */
+ 0, /* timeout_msec */
+ NULL, /* tcon */
+ NULL, /* session */
+ NULL, /* body */
+ 0, /* body_fixed */
+ NULL, /* dyn */
+ 0, /* dyn_len */
+ 0); /* max_dyn_len */
+ if (subreq != NULL) {
+ smbXcli_req_set_pending(subreq);
+ tevent_req_set_callback(subreq,
+ smb2_transport_break_handler,
+ transport);
+ transport->break_subreq = subreq;
+ }
+
+ body = recv_iov[1].iov_base;
+
+ len = recv_iov[1].iov_len;
+ if (recv_iov[1].iov_len >= 2) {
+ len = CVAL(body, 0x00);
+ if (len != recv_iov[1].iov_len) {
+ len = recv_iov[1].iov_len;
+ }
+ }
+
+ if (len == 24) {
+ lease = false;
+ } else if (len == 44) {
+ lease = true;
+ } else {
+ DEBUG(1,("Discarding smb2 oplock reply of invalid size %u\n",
+ (unsigned)len));
+ TALLOC_FREE(recv_iov);
+ status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+ smb2_transport_dead(transport, status);
+ return;
+ }
+
+ if (!lease && transport->oplock.handler) {
+ struct smb2_handle h;
+ uint8_t level;
+
+ level = CVAL(body, 0x02);
+ smb2_pull_handle(body+0x08, &h);
+
+ TALLOC_FREE(recv_iov);
+
+ transport->oplock.handler(transport, &h, level,
+ transport->oplock.private_data);
+ } else if (lease && transport->lease.handler) {
+ struct smb2_lease_break lb;
+
+ ZERO_STRUCT(lb);
+ lb.new_epoch = SVAL(body, 0x2);
+ lb.break_flags = SVAL(body, 0x4);
+ memcpy(&lb.current_lease.lease_key, body+0x8,
+ sizeof(struct smb2_lease_key));
+ lb.current_lease.lease_state = SVAL(body, 0x18);
+ lb.new_lease_state = SVAL(body, 0x1C);
+ lb.break_reason = SVAL(body, 0x20);
+ lb.access_mask_hint = SVAL(body, 0x24);
+ lb.share_mask_hint = SVAL(body, 0x28);
+
+ TALLOC_FREE(recv_iov);
+
+ transport->lease.handler(transport, &lb,
+ transport->lease.private_data);
+ } else {
+ DEBUG(5,("Got SMB2 %s break with no handler\n",
+ lease ? "lease" : "oplock"));
+ }
+ TALLOC_FREE(recv_iov);
+}
+
+NTSTATUS smb2_transport_compound_start(struct smb2_transport *transport,
+ uint32_t num)
+{
+ TALLOC_FREE(transport->compound.reqs);
+ ZERO_STRUCT(transport->compound);
+
+ transport->compound.reqs = talloc_zero_array(transport,
+ struct tevent_req *,
+ num);
+ if (transport->compound.reqs == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ return NT_STATUS_OK;
+}
+
+void smb2_transport_compound_set_related(struct smb2_transport *transport,
+ bool related)
+{
+ transport->compound.related = related;
+}
+
+void smb2_transport_credits_ask_num(struct smb2_transport *transport,
+ uint16_t ask_num)
+{
+ smb2cli_conn_set_max_credits(transport->conn, ask_num);
+}
+
+static void idle_handler(struct tevent_context *ev,
+ struct tevent_timer *te, struct timeval t, void *private_data)
+{
+ struct smb2_transport *transport = talloc_get_type(private_data,
+ struct smb2_transport);
+ struct timeval next;
+
+ transport->idle.func(transport, transport->idle.private_data);
+
+ if (transport->idle.func == NULL) {
+ return;
+ }
+
+ if (!smbXcli_conn_is_connected(transport->conn)) {
+ return;
+ }
+
+ next = timeval_current_ofs_usec(transport->idle.period);
+ transport->idle.te = tevent_add_timer(transport->ev,
+ transport,
+ next,
+ idle_handler,
+ transport);
+}
+
+/*
+ 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_data)
+{
+ TALLOC_FREE(transport->idle.te);
+ ZERO_STRUCT(transport->idle);
+
+ if (idle_func == NULL) {
+ return;
+ }
+
+ if (!smbXcli_conn_is_connected(transport->conn)) {
+ return;
+ }
+
+ transport->idle.func = idle_func;
+ transport->idle.private_data = private_data;
+ transport->idle.period = period;
+
+ transport->idle.te = tevent_add_timer(transport->ev,
+ transport,
+ timeval_current_ofs_usec(period),
+ idle_handler,
+ transport);
+}
diff --git a/source4/libcli/smb2/util.c b/source4/libcli/smb2/util.c
new file mode 100644
index 0000000..f86a149
--- /dev/null
+++ b/source4/libcli/smb2/util.c
@@ -0,0 +1,363 @@
+/*
+ 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"
+#include "librpc/gen_ndr/ndr_security.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)
+{
+ struct smb2_create cr = {0};
+ struct smb2_handle h1 = {{0}};
+ union smb_setfileinfo setinfo;
+ NTSTATUS status;
+
+ cr = (struct smb2_create) {
+ .in.desired_access = SEC_FILE_WRITE_ATTRIBUTE,
+ .in.share_access = NTCREATEX_SHARE_ACCESS_MASK,
+ .in.create_disposition = FILE_OPEN,
+ .in.fname = name,
+ };
+ status = smb2_create(tree, tree, &cr);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ h1 = cr.out.file.handle;
+
+ setinfo = (union smb_setfileinfo) {
+ .basic_info.level = RAW_SFILEINFO_BASIC_INFORMATION,
+ .basic_info.in.file.handle = h1,
+ .basic_info.in.attrib = attrib,
+ };
+
+ status = smb2_setinfo_file(tree, &setinfo);
+ if (!NT_STATUS_IS_OK(status)) {
+ smb2_util_close(tree, h1);
+ return status;
+ }
+
+ smb2_util_close(tree, h1);
+ return NT_STATUS_OK;
+}
+
+
+/*
+ get file attribute with SMB2
+*/
+NTSTATUS smb2_util_getatr(struct smb2_tree *tree, const char *fname,
+ uint16_t *attr, size_t *size, time_t *t)
+{
+ union smb_fileinfo parms;
+ NTSTATUS status;
+ struct smb2_create create_io = {0};
+
+ create_io.in.desired_access = SEC_FILE_READ_ATTRIBUTE;
+ create_io.in.share_access = NTCREATEX_SHARE_ACCESS_NONE;
+ create_io.in.create_disposition = FILE_OPEN;
+ create_io.in.fname = fname;
+ status = smb2_create(tree, tree, &create_io);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ ZERO_STRUCT(parms);
+ parms.all_info2.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
+ parms.all_info2.in.file.handle = create_io.out.file.handle;
+ status = smb2_getinfo_file(tree, tree, &parms);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = smb2_util_close(tree, create_io.out.file.handle);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (size) {
+ *size = parms.all_info2.out.size;
+ }
+
+ if (t) {
+ *t = parms.all_info2.out.write_time;
+ }
+
+ if (attr) {
+ *attr = parms.all_info2.out.attrib;
+ }
+
+ return status;
+}
+
+
+/*
+ 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;
+ unsigned int count, i;
+ union smb_search_data *list;
+ TALLOC_CTX *tmp_ctx = talloc_new(tree);
+ struct smb2_find f;
+ struct smb2_create create_parm;
+ bool did_delete;
+
+ /* 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 */
+ 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_FILE_READ_DATA;
+ 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;
+ }
+
+
+ do {
+ did_delete = false;
+
+ 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 */
+ 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++;
+ did_delete = true;
+ }
+ }
+ } while (did_delete);
+
+ smb2_util_close(tree, create_parm.out.file.handle);
+
+ status = smb2_util_rmdir(tree, dname);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_CANNOT_DELETE)) {
+ /* it could be read-only */
+ smb2_util_setatr(tree, dname, FILE_ATTRIBUTE_NORMAL);
+ 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;
+}
+
+/*
+ check if two SMB2 file handles are the same
+*/
+bool smb2_util_handle_equal(const struct smb2_handle h1,
+ const struct smb2_handle h2)
+{
+ return (h1.data[0] == h2.data[0]) && (h1.data[1] == h2.data[1]);
+}
+
+bool smb2_util_handle_empty(const struct smb2_handle h)
+{
+ struct smb2_handle empty;
+
+ ZERO_STRUCT(empty);
+
+ return smb2_util_handle_equal(h, empty);
+}
+
+/****************************************************************************
+send a qpathinfo SMB_QUERY_FILE_ALT_NAME_INFO call
+****************************************************************************/
+NTSTATUS smb2_qpathinfo_alt_name(TALLOC_CTX *ctx, struct smb2_tree *tree,
+ const char *fname, const char **alt_name)
+{
+ union smb_fileinfo parms;
+ TALLOC_CTX *mem_ctx;
+ NTSTATUS status;
+ struct smb2_create create_io = {0};
+
+ mem_ctx = talloc_new(ctx);
+ if (!mem_ctx) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ create_io.in.desired_access = SEC_FILE_READ_ATTRIBUTE;
+ create_io.in.share_access = NTCREATEX_SHARE_ACCESS_NONE;
+ create_io.in.create_disposition = FILE_OPEN;
+ create_io.in.fname = fname;
+ status = smb2_create(tree, mem_ctx, &create_io);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(mem_ctx);
+ return status;
+ }
+
+ parms.alt_name_info.level = RAW_FILEINFO_SMB2_ALT_NAME_INFORMATION;
+ parms.alt_name_info.in.file.handle = create_io.out.file.handle;
+
+ status = smb2_getinfo_file(tree, mem_ctx, &parms);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(mem_ctx);
+ return status;
+ }
+
+ status = smb2_util_close(tree, create_io.out.file.handle);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(mem_ctx);
+ return status;
+ }
+
+ if (!parms.alt_name_info.out.fname.s) {
+ *alt_name = talloc_strdup(ctx, "");
+ } else {
+ *alt_name = talloc_strdup(ctx,
+ parms.alt_name_info.out.fname.s);
+ }
+
+ talloc_free(mem_ctx);
+
+ return NT_STATUS_OK;
+}
diff --git a/source4/libcli/smb2/write.c b/source4/libcli/smb2/write.c
new file mode 100644
index 0000000..62ffe2e
--- /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_ok(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);
+}
diff --git a/source4/libcli/smb2/wscript_build b/source4/libcli/smb2/wscript_build
new file mode 100644
index 0000000..51ac0f2
--- /dev/null
+++ b/source4/libcli/smb2/wscript_build
@@ -0,0 +1,10 @@
+#!/usr/bin/env python
+
+bld.SAMBA_SUBSYSTEM('LIBCLI_SMB2',
+ source='transport.c request.c session.c tcon.c create.c close.c connect.c getinfo.c write.c read.c setinfo.c find.c ioctl.c logoff.c tdis.c flush.c lock.c notify.c cancel.c keepalive.c break.c util.c signing.c lease_break.c',
+ autoproto='smb2_proto.h',
+ deps='tevent-util cli_smb_common GNUTLS_HELPERS',
+ public_deps='smbclient-raw gensec samba-credentials tevent',
+ private_headers='smb2.h',
+ )
+