summaryrefslogtreecommitdiffstats
path: root/source4/smb_server/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/smb_server/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/smb_server/smb2/fileinfo.c377
-rw-r--r--source4/smb_server/smb2/fileio.c547
-rw-r--r--source4/smb_server/smb2/find.c167
-rw-r--r--source4/smb_server/smb2/keepalive.c71
-rw-r--r--source4/smb_server/smb2/negprot.c327
-rw-r--r--source4/smb_server/smb2/receive.c710
-rw-r--r--source4/smb_server/smb2/sesssetup.c326
-rw-r--r--source4/smb_server/smb2/smb2_server.h192
-rw-r--r--source4/smb_server/smb2/tcon.c446
-rw-r--r--source4/smb_server/smb2/wscript_build9
10 files changed, 3172 insertions, 0 deletions
diff --git a/source4/smb_server/smb2/fileinfo.c b/source4/smb_server/smb2/fileinfo.c
new file mode 100644
index 0000000..a90c41a
--- /dev/null
+++ b/source4/smb_server/smb2/fileinfo.c
@@ -0,0 +1,377 @@
+/*
+ Unix SMB2 implementation.
+
+ 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"
+#include "smb_server/smb_server.h"
+#include "smb_server/smb2/smb2_server.h"
+#include "ntvfs/ntvfs.h"
+#include "librpc/gen_ndr/ndr_security.h"
+
+struct smb2srv_getinfo_op {
+ struct smb2srv_request *req;
+ struct smb2_getinfo *info;
+ void *io_ptr;
+ NTSTATUS (*send_fn)(struct smb2srv_getinfo_op *op);
+};
+
+static void smb2srv_getinfo_send(struct ntvfs_request *ntvfs)
+{
+ struct smb2srv_getinfo_op *op;
+ struct smb2srv_request *req;
+
+ /*
+ * SMB2 uses NT_STATUS_INVALID_INFO_CLASS
+ * so we need to translated it here
+ */
+ if (NT_STATUS_EQUAL(NT_STATUS_INVALID_LEVEL, ntvfs->async_states->status)) {
+ ntvfs->async_states->status = NT_STATUS_INVALID_INFO_CLASS;
+ }
+
+ SMB2SRV_CHECK_ASYNC_STATUS(op, struct smb2srv_getinfo_op);
+
+ ZERO_STRUCT(op->info->out);
+ if (op->send_fn) {
+ SMB2SRV_CHECK(op->send_fn(op));
+ }
+
+ if (op->info->in.output_buffer_length < op->info->out.blob.length) {
+ smb2srv_send_error(req, NT_STATUS_INFO_LENGTH_MISMATCH);
+ return;
+ }
+
+ SMB2SRV_CHECK(smb2srv_setup_reply(req, 0x08, true, op->info->out.blob.length));
+
+ SMB2SRV_CHECK(smb2_push_o16s32_blob(&req->out, 0x02, op->info->out.blob));
+ SSVAL(req->out.body, 0x06, 0);
+
+ smb2srv_send_reply(req);
+}
+
+static NTSTATUS smb2srv_getinfo_file_send(struct smb2srv_getinfo_op *op)
+{
+ union smb_fileinfo *io = talloc_get_type(op->io_ptr, union smb_fileinfo);
+ NTSTATUS status;
+
+ status = smbsrv_push_passthru_fileinfo(op->req,
+ &op->info->out.blob,
+ io->generic.level, io,
+ STR_UNICODE);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS smb2srv_getinfo_file(struct smb2srv_getinfo_op *op, uint8_t smb2_level)
+{
+ union smb_fileinfo *io;
+ uint16_t level;
+
+ io = talloc(op, union smb_fileinfo);
+ NT_STATUS_HAVE_NO_MEMORY(io);
+
+ level = op->info->in.info_type | (op->info->in.info_class << 8);
+ switch (level) {
+ case RAW_FILEINFO_SMB2_ALL_EAS:
+ io->all_eas.level = level;
+ io->all_eas.in.file.ntvfs = op->info->in.file.ntvfs;
+ io->all_eas.in.continue_flags = op->info->in.getinfo_flags;
+ break;
+
+ case RAW_FILEINFO_SMB2_ALL_INFORMATION:
+ io->all_info2.level = level;
+ io->all_info2.in.file.ntvfs = op->info->in.file.ntvfs;
+ break;
+
+ default:
+ /* the rest directly maps to the passthru levels */
+ io->generic.level = smb2_level + 1000;
+ io->generic.in.file.ntvfs = op->info->in.file.ntvfs;
+ break;
+ }
+
+ op->io_ptr = io;
+ op->send_fn = smb2srv_getinfo_file_send;
+
+ return ntvfs_qfileinfo(op->req->ntvfs, io);
+}
+
+static NTSTATUS smb2srv_getinfo_fs_send(struct smb2srv_getinfo_op *op)
+{
+ union smb_fsinfo *io = talloc_get_type(op->io_ptr, union smb_fsinfo);
+ NTSTATUS status;
+
+ status = smbsrv_push_passthru_fsinfo(op->req,
+ &op->info->out.blob,
+ io->generic.level, io,
+ STR_UNICODE);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS smb2srv_getinfo_fs(struct smb2srv_getinfo_op *op, uint8_t smb2_level)
+{
+ union smb_fsinfo *io;
+
+ io = talloc(op, union smb_fsinfo);
+ NT_STATUS_HAVE_NO_MEMORY(io);
+
+ /* the rest directly maps to the passthru levels */
+ io->generic.level = smb2_level + 1000;
+
+ /* TODO: allow qfsinfo only the share root directory handle */
+
+ op->io_ptr = io;
+ op->send_fn = smb2srv_getinfo_fs_send;
+
+ return ntvfs_fsinfo(op->req->ntvfs, io);
+}
+
+static NTSTATUS smb2srv_getinfo_security_send(struct smb2srv_getinfo_op *op)
+{
+ union smb_fileinfo *io = talloc_get_type(op->io_ptr, union smb_fileinfo);
+ enum ndr_err_code ndr_err;
+
+ ndr_err = ndr_push_struct_blob(&op->info->out.blob, op->req,
+ io->query_secdesc.out.sd,
+ (ndr_push_flags_fn_t)ndr_push_security_descriptor);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return ndr_map_error2ntstatus(ndr_err);
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS smb2srv_getinfo_security(struct smb2srv_getinfo_op *op, uint8_t smb2_level)
+{
+ union smb_fileinfo *io;
+
+ switch (smb2_level) {
+ case 0x00:
+ io = talloc(op, union smb_fileinfo);
+ NT_STATUS_HAVE_NO_MEMORY(io);
+
+ io->query_secdesc.level = RAW_FILEINFO_SEC_DESC;
+ io->query_secdesc.in.file.ntvfs = op->info->in.file.ntvfs;
+ io->query_secdesc.in.secinfo_flags = op->info->in.additional_information;
+
+ op->io_ptr = io;
+ op->send_fn = smb2srv_getinfo_security_send;
+
+ return ntvfs_qfileinfo(op->req->ntvfs, io);
+ }
+
+ return NT_STATUS_INVALID_PARAMETER;
+}
+
+static NTSTATUS smb2srv_getinfo_backend(struct smb2srv_getinfo_op *op)
+{
+ switch (op->info->in.info_type) {
+ case SMB2_0_INFO_FILE:
+ return smb2srv_getinfo_file(op, op->info->in.info_class);
+
+ case SMB2_0_INFO_FILESYSTEM:
+ return smb2srv_getinfo_fs(op, op->info->in.info_class);
+
+ case SMB2_0_INFO_SECURITY:
+ return smb2srv_getinfo_security(op, op->info->in.info_class);
+
+ case SMB2_0_INFO_QUOTA:
+ return NT_STATUS_NOT_SUPPORTED;
+ }
+
+ return NT_STATUS_INVALID_PARAMETER;
+}
+
+void smb2srv_getinfo_recv(struct smb2srv_request *req)
+{
+ struct smb2_getinfo *info;
+ struct smb2srv_getinfo_op *op;
+
+ SMB2SRV_CHECK_BODY_SIZE(req, 0x28, true);
+ SMB2SRV_TALLOC_IO_PTR(info, struct smb2_getinfo);
+ /* this overwrites req->io_ptr !*/
+ SMB2SRV_TALLOC_IO_PTR(op, struct smb2srv_getinfo_op);
+ op->req = req;
+ op->info = info;
+ op->io_ptr = NULL;
+ op->send_fn = NULL;
+ SMB2SRV_SETUP_NTVFS_REQUEST(smb2srv_getinfo_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ info->in.info_type = CVAL(req->in.body, 0x02);
+ info->in.info_class = CVAL(req->in.body, 0x03);
+ info->in.output_buffer_length = IVAL(req->in.body, 0x04);
+ info->in.reserved = IVAL(req->in.body, 0x0C);
+ info->in.additional_information = IVAL(req->in.body, 0x10);
+ info->in.getinfo_flags = IVAL(req->in.body, 0x14);
+ info->in.file.ntvfs = smb2srv_pull_handle(req, req->in.body, 0x18);
+ SMB2SRV_CHECK(smb2_pull_o16As32_blob(&req->in, op,
+ req->in.body+0x08, &info->in.input_buffer));
+
+ SMB2SRV_CHECK_FILE_HANDLE(info->in.file.ntvfs);
+ SMB2SRV_CALL_NTVFS_BACKEND(smb2srv_getinfo_backend(op));
+}
+
+struct smb2srv_setinfo_op {
+ struct smb2srv_request *req;
+ struct smb2_setinfo *info;
+};
+
+static void smb2srv_setinfo_send(struct ntvfs_request *ntvfs)
+{
+ struct smb2srv_setinfo_op *op;
+ struct smb2srv_request *req;
+
+ /*
+ * SMB2 uses NT_STATUS_INVALID_INFO_CLASS
+ * so we need to translated it here
+ */
+ if (NT_STATUS_EQUAL(NT_STATUS_INVALID_LEVEL, ntvfs->async_states->status)) {
+ ntvfs->async_states->status = NT_STATUS_INVALID_INFO_CLASS;
+ }
+
+ SMB2SRV_CHECK_ASYNC_STATUS(op, struct smb2srv_setinfo_op);
+
+ SMB2SRV_CHECK(smb2srv_setup_reply(op->req, 0x02, false, 0));
+
+ smb2srv_send_reply(req);
+}
+
+static NTSTATUS smb2srv_setinfo_file(struct smb2srv_setinfo_op *op, uint8_t smb2_level)
+{
+ union smb_setfileinfo *io;
+ NTSTATUS status;
+
+ io = talloc(op, union smb_setfileinfo);
+ NT_STATUS_HAVE_NO_MEMORY(io);
+
+ /* the levels directly map to the passthru levels */
+ io->generic.level = smb2_level + 1000;
+ io->generic.in.file.ntvfs = op->info->in.file.ntvfs;
+
+ /* handle cases that don't map directly */
+ if (io->generic.level == RAW_SFILEINFO_RENAME_INFORMATION) {
+ io->generic.level = RAW_SFILEINFO_RENAME_INFORMATION_SMB2;
+ }
+
+ status = smbsrv_pull_passthru_sfileinfo(io, io->generic.level, io,
+ &op->info->in.blob,
+ STR_UNICODE, &op->req->in.bufinfo);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ return ntvfs_setfileinfo(op->req->ntvfs, io);
+}
+
+static NTSTATUS smb2srv_setinfo_fs(struct smb2srv_setinfo_op *op, uint8_t smb2_level)
+{
+ switch (smb2_level) {
+ case 0x02:
+ return NT_STATUS_NOT_IMPLEMENTED;
+
+ case 0x06:
+ return NT_STATUS_ACCESS_DENIED;
+
+ case 0x08:
+ return NT_STATUS_ACCESS_DENIED;
+
+ case 0x0A:
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ return NT_STATUS_INVALID_INFO_CLASS;
+}
+
+static NTSTATUS smb2srv_setinfo_security(struct smb2srv_setinfo_op *op, uint8_t smb2_level)
+{
+ union smb_setfileinfo *io;
+ enum ndr_err_code ndr_err;
+
+ switch (smb2_level) {
+ case 0x00:
+ io = talloc(op, union smb_setfileinfo);
+ NT_STATUS_HAVE_NO_MEMORY(io);
+
+ io->set_secdesc.level = RAW_SFILEINFO_SEC_DESC;
+ io->set_secdesc.in.file.ntvfs = op->info->in.file.ntvfs;
+ io->set_secdesc.in.secinfo_flags = op->info->in.flags;
+
+ io->set_secdesc.in.sd = talloc(io, struct security_descriptor);
+ NT_STATUS_HAVE_NO_MEMORY(io->set_secdesc.in.sd);
+
+ ndr_err = ndr_pull_struct_blob(&op->info->in.blob, io,
+ io->set_secdesc.in.sd,
+ (ndr_pull_flags_fn_t)ndr_pull_security_descriptor);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return ndr_map_error2ntstatus(ndr_err);
+ }
+
+ return ntvfs_setfileinfo(op->req->ntvfs, io);
+ }
+
+ return NT_STATUS_INVALID_INFO_CLASS;
+}
+
+static NTSTATUS smb2srv_setinfo_backend(struct smb2srv_setinfo_op *op)
+{
+ uint8_t smb2_class;
+ uint8_t smb2_level;
+
+ smb2_class = 0xFF & op->info->in.level;
+ smb2_level = 0xFF & (op->info->in.level>>8);
+
+ switch (smb2_class) {
+ case SMB2_0_INFO_FILE:
+ return smb2srv_setinfo_file(op, smb2_level);
+
+ case SMB2_0_INFO_FILESYSTEM:
+ return smb2srv_setinfo_fs(op, smb2_level);
+
+ case SMB2_0_INFO_SECURITY:
+ return smb2srv_setinfo_security(op, smb2_level);
+
+ case SMB2_0_INFO_QUOTA:
+ return NT_STATUS_NOT_SUPPORTED;
+ }
+
+ return NT_STATUS_INVALID_PARAMETER;
+}
+
+void smb2srv_setinfo_recv(struct smb2srv_request *req)
+{
+ struct smb2_setinfo *info;
+ struct smb2srv_setinfo_op *op;
+
+ SMB2SRV_CHECK_BODY_SIZE(req, 0x20, true);
+ SMB2SRV_TALLOC_IO_PTR(info, struct smb2_setinfo);
+ /* this overwrites req->io_ptr !*/
+ SMB2SRV_TALLOC_IO_PTR(op, struct smb2srv_setinfo_op);
+ op->req = req;
+ op->info = info;
+ SMB2SRV_SETUP_NTVFS_REQUEST(smb2srv_setinfo_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ info->in.level = SVAL(req->in.body, 0x02);
+ SMB2SRV_CHECK(smb2_pull_s32o16_blob(&req->in, info, req->in.body+0x04, &info->in.blob));
+ info->in.flags = IVAL(req->in.body, 0x0C);
+ info->in.file.ntvfs = smb2srv_pull_handle(req, req->in.body, 0x10);
+
+ SMB2SRV_CHECK_FILE_HANDLE(info->in.file.ntvfs);
+ SMB2SRV_CALL_NTVFS_BACKEND(smb2srv_setinfo_backend(op));
+}
diff --git a/source4/smb_server/smb2/fileio.c b/source4/smb_server/smb2/fileio.c
new file mode 100644
index 0000000..4153a42
--- /dev/null
+++ b/source4/smb_server/smb2/fileio.c
@@ -0,0 +1,547 @@
+/*
+ Unix SMB2 implementation.
+
+ 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/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+#include "smb_server/smb_server.h"
+#include "smb_server/smb2/smb2_server.h"
+#include "ntvfs/ntvfs.h"
+#include "libcli/raw/raw_proto.h"
+#include "librpc/gen_ndr/ndr_security.h"
+
+static void smb2srv_create_send(struct ntvfs_request *ntvfs)
+{
+ struct smb2srv_request *req;
+ union smb_open *io;
+ DATA_BLOB blob;
+
+ SMB2SRV_CHECK_ASYNC_STATUS(io, union smb_open);
+
+ /* setup the blobs we should give in the reply */
+ if (io->smb2.out.maximal_access != 0) {
+ uint32_t data[2];
+ SIVAL(data, 0, 0);
+ SIVAL(data, 4, io->smb2.out.maximal_access);
+ SMB2SRV_CHECK(smb2_create_blob_add(req, &io->smb2.out.blobs,
+ SMB2_CREATE_TAG_MXAC,
+ data_blob_const(data, 8)));
+ }
+
+ if (IVAL(io->smb2.out.on_disk_id, 0) != 0) {
+ SMB2SRV_CHECK(smb2_create_blob_add(req, &io->smb2.out.blobs,
+ SMB2_CREATE_TAG_QFID,
+ data_blob_const(io->smb2.out.on_disk_id, 32)));
+ }
+
+ SMB2SRV_CHECK(smb2_create_blob_push(req, &blob, io->smb2.out.blobs));
+ SMB2SRV_CHECK(smb2srv_setup_reply(req, 0x58, true, blob.length));
+
+ SCVAL(req->out.body, 0x02, io->smb2.out.oplock_level);
+ SCVAL(req->out.body, 0x03, io->smb2.out.reserved);
+ SIVAL(req->out.body, 0x04, io->smb2.out.create_action);
+ SBVAL(req->out.body, 0x08, io->smb2.out.create_time);
+ SBVAL(req->out.body, 0x10, io->smb2.out.access_time);
+ SBVAL(req->out.body, 0x18, io->smb2.out.write_time);
+ SBVAL(req->out.body, 0x20, io->smb2.out.change_time);
+ SBVAL(req->out.body, 0x28, io->smb2.out.alloc_size);
+ SBVAL(req->out.body, 0x30, io->smb2.out.size);
+ SIVAL(req->out.body, 0x38, io->smb2.out.file_attr);
+ SIVAL(req->out.body, 0x3C, io->smb2.out.reserved2);
+ smb2srv_push_handle(req->out.body, 0x40, io->smb2.out.file.ntvfs);
+ SMB2SRV_CHECK(smb2_push_o32s32_blob(&req->out, 0x50, blob));
+
+ /* also setup the chained file handle */
+ req->chained_file_handle = req->_chained_file_handle;
+ smb2srv_push_handle(req->chained_file_handle, 0, io->smb2.out.file.ntvfs);
+
+ smb2srv_send_reply(req);
+}
+
+void smb2srv_create_recv(struct smb2srv_request *req)
+{
+ union smb_open *io;
+ DATA_BLOB blob;
+ int i;
+
+ SMB2SRV_CHECK_BODY_SIZE(req, 0x38, true);
+ SMB2SRV_TALLOC_IO_PTR(io, union smb_open);
+ SMB2SRV_SETUP_NTVFS_REQUEST(smb2srv_create_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ ZERO_STRUCT(io->smb2.in);
+ io->smb2.level = RAW_OPEN_SMB2;
+ io->smb2.in.security_flags = CVAL(req->in.body, 0x02);
+ io->smb2.in.oplock_level = CVAL(req->in.body, 0x03);
+ io->smb2.in.impersonation_level = IVAL(req->in.body, 0x04);
+ io->smb2.in.create_flags = BVAL(req->in.body, 0x08);
+ io->smb2.in.reserved = BVAL(req->in.body, 0x10);
+ io->smb2.in.desired_access = IVAL(req->in.body, 0x18);
+ io->smb2.in.file_attributes = IVAL(req->in.body, 0x1C);
+ io->smb2.in.share_access = IVAL(req->in.body, 0x20);
+ io->smb2.in.create_disposition = IVAL(req->in.body, 0x24);
+ io->smb2.in.create_options = IVAL(req->in.body, 0x28);
+ SMB2SRV_CHECK(smb2_pull_o16s16_string(&req->in, io, req->in.body+0x2C, &io->smb2.in.fname));
+ SMB2SRV_CHECK(smb2_pull_o32s32_blob(&req->in, io, req->in.body+0x30, &blob));
+ SMB2SRV_CHECK(smb2_create_blob_parse(io, blob, &io->smb2.in.blobs));
+
+ /* interpret the parsed tags that a server needs to respond to */
+ for (i=0;i<io->smb2.in.blobs.num_blobs;i++) {
+ if (strcmp(io->smb2.in.blobs.blobs[i].tag, SMB2_CREATE_TAG_EXTA) == 0) {
+ SMB2SRV_CHECK(ea_pull_list_chained(&io->smb2.in.blobs.blobs[i].data, io,
+ &io->smb2.in.eas.num_eas,
+ &io->smb2.in.eas.eas));
+ }
+ if (strcmp(io->smb2.in.blobs.blobs[i].tag, SMB2_CREATE_TAG_SECD) == 0) {
+ enum ndr_err_code ndr_err;
+ io->smb2.in.sec_desc = talloc(io, struct security_descriptor);
+ if (io->smb2.in.sec_desc == NULL) {
+ smb2srv_send_error(req, NT_STATUS_NO_MEMORY);
+ return;
+ }
+ ndr_err = ndr_pull_struct_blob(&io->smb2.in.blobs.blobs[i].data, io,
+ io->smb2.in.sec_desc,
+ (ndr_pull_flags_fn_t)ndr_pull_security_descriptor);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ smb2srv_send_error(req, ndr_map_error2ntstatus(ndr_err));
+ return;
+ }
+ }
+ if (strcmp(io->smb2.in.blobs.blobs[i].tag, SMB2_CREATE_TAG_DHNQ) == 0) {
+ io->smb2.in.durable_open = true;
+ }
+ if (strcmp(io->smb2.in.blobs.blobs[i].tag, SMB2_CREATE_TAG_DHNC) == 0) {
+ if (io->smb2.in.blobs.blobs[i].data.length != 16) {
+ smb2srv_send_error(req, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+ io->smb2.in.durable_handle = talloc(io, struct smb2_handle);
+ if (io->smb2.in.durable_handle == NULL) {
+ smb2srv_send_error(req, NT_STATUS_NO_MEMORY);
+ return;
+ }
+ smb2_pull_handle(io->smb2.in.blobs.blobs[i].data.data, io->smb2.in.durable_handle);
+ }
+ if (strcmp(io->smb2.in.blobs.blobs[i].tag, SMB2_CREATE_TAG_ALSI) == 0) {
+ if (io->smb2.in.blobs.blobs[i].data.length != 8) {
+ smb2srv_send_error(req, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+ io->smb2.in.alloc_size = BVAL(io->smb2.in.blobs.blobs[i].data.data, 0);
+ }
+ if (strcmp(io->smb2.in.blobs.blobs[i].tag, SMB2_CREATE_TAG_MXAC) == 0) {
+ io->smb2.in.query_maximal_access = true;
+ }
+ if (strcmp(io->smb2.in.blobs.blobs[i].tag, SMB2_CREATE_TAG_TWRP) == 0) {
+ if (io->smb2.in.blobs.blobs[i].data.length != 8) {
+ smb2srv_send_error(req, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+ io->smb2.in.timewarp = BVAL(io->smb2.in.blobs.blobs[i].data.data, 0);
+ }
+ if (strcmp(io->smb2.in.blobs.blobs[i].tag, SMB2_CREATE_TAG_QFID) == 0) {
+ io->smb2.in.query_on_disk_id = true;
+ }
+ }
+
+ /* the VFS backend does not yet handle NULL filenames */
+ if (io->smb2.in.fname == NULL) {
+ io->smb2.in.fname = "";
+ }
+
+ SMB2SRV_CALL_NTVFS_BACKEND(ntvfs_open(req->ntvfs, io));
+}
+
+static void smb2srv_close_send(struct ntvfs_request *ntvfs)
+{
+ struct smb2srv_request *req;
+ union smb_close *io;
+
+ SMB2SRV_CHECK_ASYNC_STATUS(io, union smb_close);
+ SMB2SRV_CHECK(smb2srv_setup_reply(req, 0x3C, false, 0));
+
+ SSVAL(req->out.body, 0x02, io->smb2.out.flags);
+ SIVAL(req->out.body, 0x04, io->smb2.out._pad);
+ SBVAL(req->out.body, 0x08, io->smb2.out.create_time);
+ SBVAL(req->out.body, 0x10, io->smb2.out.access_time);
+ SBVAL(req->out.body, 0x18, io->smb2.out.write_time);
+ SBVAL(req->out.body, 0x20, io->smb2.out.change_time);
+ SBVAL(req->out.body, 0x28, io->smb2.out.alloc_size);
+ SBVAL(req->out.body, 0x30, io->smb2.out.size);
+ SIVAL(req->out.body, 0x38, io->smb2.out.file_attr);
+
+ /* also destroy the chained file handle */
+ req->chained_file_handle = NULL;
+ memset(req->_chained_file_handle, 0, sizeof(req->_chained_file_handle));
+
+ smb2srv_send_reply(req);
+}
+
+void smb2srv_close_recv(struct smb2srv_request *req)
+{
+ union smb_close *io;
+
+ SMB2SRV_CHECK_BODY_SIZE(req, 0x18, false);
+ SMB2SRV_TALLOC_IO_PTR(io, union smb_close);
+ SMB2SRV_SETUP_NTVFS_REQUEST(smb2srv_close_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ io->smb2.level = RAW_CLOSE_SMB2;
+ io->smb2.in.flags = SVAL(req->in.body, 0x02);
+ io->smb2.in._pad = IVAL(req->in.body, 0x04);
+ io->smb2.in.file.ntvfs = smb2srv_pull_handle(req, req->in.body, 0x08);
+
+ SMB2SRV_CHECK_FILE_HANDLE(io->smb2.in.file.ntvfs);
+ SMB2SRV_CALL_NTVFS_BACKEND(ntvfs_close(req->ntvfs, io));
+}
+
+static void smb2srv_flush_send(struct ntvfs_request *ntvfs)
+{
+ struct smb2srv_request *req;
+ union smb_flush *io;
+
+ SMB2SRV_CHECK_ASYNC_STATUS(io, union smb_flush);
+ SMB2SRV_CHECK(smb2srv_setup_reply(req, 0x04, false, 0));
+
+ SSVAL(req->out.body, 0x02, io->smb2.out.reserved);
+
+ smb2srv_send_reply(req);
+}
+
+void smb2srv_flush_recv(struct smb2srv_request *req)
+{
+ union smb_flush *io;
+
+ SMB2SRV_CHECK_BODY_SIZE(req, 0x18, false);
+ SMB2SRV_TALLOC_IO_PTR(io, union smb_flush);
+ SMB2SRV_SETUP_NTVFS_REQUEST(smb2srv_flush_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ io->smb2.level = RAW_FLUSH_SMB2;
+ io->smb2.in.reserved1 = SVAL(req->in.body, 0x02);
+ io->smb2.in.reserved2 = IVAL(req->in.body, 0x04);
+ io->smb2.in.file.ntvfs = smb2srv_pull_handle(req, req->in.body, 0x08);
+
+ SMB2SRV_CHECK_FILE_HANDLE(io->smb2.in.file.ntvfs);
+ SMB2SRV_CALL_NTVFS_BACKEND(ntvfs_flush(req->ntvfs, io));
+}
+
+static void smb2srv_read_send(struct ntvfs_request *ntvfs)
+{
+ struct smb2srv_request *req;
+ union smb_read *io;
+
+ SMB2SRV_CHECK_ASYNC_STATUS(io, union smb_read);
+ SMB2SRV_CHECK(smb2srv_setup_reply(req, 0x10, true, io->smb2.out.data.length));
+
+ /* TODO: avoid the memcpy */
+ SMB2SRV_CHECK(smb2_push_o16s32_blob(&req->out, 0x02, io->smb2.out.data));
+ SIVAL(req->out.body, 0x08, io->smb2.out.remaining);
+ SIVAL(req->out.body, 0x0C, io->smb2.out.reserved);
+
+ smb2srv_send_reply(req);
+}
+
+void smb2srv_read_recv(struct smb2srv_request *req)
+{
+ union smb_read *io;
+
+ SMB2SRV_CHECK_BODY_SIZE(req, 0x30, true);
+
+ /* MS-SMB2 2.2.19 read must have a single byte of zero */
+ if (req->in.body_size - req->in.body_fixed < 1) {
+ smb2srv_send_error(req, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+ SMB2SRV_TALLOC_IO_PTR(io, union smb_read);
+ SMB2SRV_SETUP_NTVFS_REQUEST(smb2srv_read_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ io->smb2.level = RAW_READ_SMB2;
+ io->smb2.in._pad = SVAL(req->in.body, 0x02);
+ io->smb2.in.length = IVAL(req->in.body, 0x04);
+ io->smb2.in.offset = BVAL(req->in.body, 0x08);
+ io->smb2.in.file.ntvfs = smb2srv_pull_handle(req, req->in.body, 0x10);
+ io->smb2.in.min_count = IVAL(req->in.body, 0x20);
+ io->smb2.in.channel = IVAL(req->in.body, 0x24);
+ io->smb2.in.remaining = IVAL(req->in.body, 0x28);
+ io->smb2.in.channel_offset = SVAL(req->in.body, 0x2C);
+ io->smb2.in.channel_length = SVAL(req->in.body, 0x2E);
+
+ SMB2SRV_CHECK_FILE_HANDLE(io->smb2.in.file.ntvfs);
+
+ /* preallocate the buffer for the backends */
+ io->smb2.out.data = data_blob_talloc(io, NULL, io->smb2.in.length);
+ if (io->smb2.out.data.length != io->smb2.in.length) {
+ SMB2SRV_CHECK(NT_STATUS_NO_MEMORY);
+ }
+
+ SMB2SRV_CALL_NTVFS_BACKEND(ntvfs_read(req->ntvfs, io));
+}
+
+static void smb2srv_write_send(struct ntvfs_request *ntvfs)
+{
+ struct smb2srv_request *req;
+ union smb_write *io;
+
+ SMB2SRV_CHECK_ASYNC_STATUS(io, union smb_write);
+ SMB2SRV_CHECK(smb2srv_setup_reply(req, 0x10, true, 0));
+
+ SSVAL(req->out.body, 0x02, io->smb2.out._pad);
+ SIVAL(req->out.body, 0x04, io->smb2.out.nwritten);
+ SBVAL(req->out.body, 0x08, io->smb2.out.unknown1);
+
+ smb2srv_send_reply(req);
+}
+
+void smb2srv_write_recv(struct smb2srv_request *req)
+{
+ union smb_write *io;
+
+ SMB2SRV_CHECK_BODY_SIZE(req, 0x30, true);
+ SMB2SRV_TALLOC_IO_PTR(io, union smb_write);
+ SMB2SRV_SETUP_NTVFS_REQUEST(smb2srv_write_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ /* TODO: avoid the memcpy */
+ io->smb2.level = RAW_WRITE_SMB2;
+ SMB2SRV_CHECK(smb2_pull_o16s32_blob(&req->in, io, req->in.body+0x02, &io->smb2.in.data));
+ io->smb2.in.offset = BVAL(req->in.body, 0x08);
+ io->smb2.in.file.ntvfs = smb2srv_pull_handle(req, req->in.body, 0x10);
+ io->smb2.in.unknown1 = BVAL(req->in.body, 0x20);
+ io->smb2.in.unknown2 = BVAL(req->in.body, 0x28);
+
+ SMB2SRV_CHECK_FILE_HANDLE(io->smb2.in.file.ntvfs);
+ SMB2SRV_CALL_NTVFS_BACKEND(ntvfs_write(req->ntvfs, io));
+}
+
+static void smb2srv_lock_send(struct ntvfs_request *ntvfs)
+{
+ struct smb2srv_request *req;
+ union smb_lock *io;
+
+ SMB2SRV_CHECK_ASYNC_STATUS_ERR(io, union smb_lock);
+ SMB2SRV_CHECK(smb2srv_setup_reply(req, 0x04, false, 0));
+
+ SSVAL(req->out.body, 0x02, io->smb2.out.reserved);
+
+ smb2srv_send_reply(req);
+}
+
+void smb2srv_lock_recv(struct smb2srv_request *req)
+{
+ union smb_lock *io;
+ int i;
+
+ SMB2SRV_CHECK_BODY_SIZE(req, 0x30, false);
+ SMB2SRV_TALLOC_IO_PTR(io, union smb_lock);
+ SMB2SRV_SETUP_NTVFS_REQUEST(smb2srv_lock_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ io->smb2.level = RAW_LOCK_SMB2;
+ io->smb2.in.lock_count = SVAL(req->in.body, 0x02);
+ io->smb2.in.lock_sequence = IVAL(req->in.body, 0x04);
+ io->smb2.in.file.ntvfs = smb2srv_pull_handle(req, req->in.body, 0x08);
+ if (req->in.body_size < 24 + 24*(uint64_t)io->smb2.in.lock_count) {
+ DEBUG(0,("%s: lock buffer too small\n", __location__));
+ smb2srv_send_error(req, NT_STATUS_FOOBAR);
+ return;
+ }
+ io->smb2.in.locks = talloc_array(io, struct smb2_lock_element,
+ io->smb2.in.lock_count);
+ if (io->smb2.in.locks == NULL) {
+ smb2srv_send_error(req, NT_STATUS_NO_MEMORY);
+ return;
+ }
+
+ for (i=0;i<io->smb2.in.lock_count;i++) {
+ io->smb2.in.locks[i].offset = BVAL(req->in.body, 24 + i*24);
+ io->smb2.in.locks[i].length = BVAL(req->in.body, 32 + i*24);
+ io->smb2.in.locks[i].flags = IVAL(req->in.body, 40 + i*24);
+ io->smb2.in.locks[i].reserved = IVAL(req->in.body, 44 + i*24);
+ }
+
+ SMB2SRV_CHECK_FILE_HANDLE(io->smb2.in.file.ntvfs);
+ SMB2SRV_CALL_NTVFS_BACKEND(ntvfs_lock(req->ntvfs, io));
+}
+
+static void smb2srv_ioctl_send(struct ntvfs_request *ntvfs)
+{
+ struct smb2srv_request *req;
+ union smb_ioctl *io;
+
+ SMB2SRV_CHECK_ASYNC_STATUS_ERR(io, union smb_ioctl);
+ SMB2SRV_CHECK(smb2srv_setup_reply(req, 0x30, true, 0));
+
+ SSVAL(req->out.body, 0x02, io->smb2.out.reserved);
+ SIVAL(req->out.body, 0x04, io->smb2.out.function);
+ if (io->smb2.level == RAW_IOCTL_SMB2_NO_HANDLE) {
+ struct smb2_handle h;
+ h.data[0] = UINT64_MAX;
+ h.data[1] = UINT64_MAX;
+ smb2_push_handle(req->out.body + 0x08, &h);
+ } else {
+ smb2srv_push_handle(req->out.body, 0x08,io->smb2.in.file.ntvfs);
+ }
+ SMB2SRV_CHECK(smb2_push_o32s32_blob(&req->out, 0x18, io->smb2.out.in));
+ SMB2SRV_CHECK(smb2_push_o32s32_blob(&req->out, 0x20, io->smb2.out.out));
+ SIVAL(req->out.body, 0x28, io->smb2.out.flags);
+ SIVAL(req->out.body, 0x2C, io->smb2.out.reserved2);
+
+ smb2srv_send_reply(req);
+}
+
+void smb2srv_ioctl_recv(struct smb2srv_request *req)
+{
+ union smb_ioctl *io;
+ struct smb2_handle h;
+
+ SMB2SRV_CHECK_BODY_SIZE(req, 0x38, true);
+ SMB2SRV_TALLOC_IO_PTR(io, union smb_ioctl);
+ SMB2SRV_SETUP_NTVFS_REQUEST(smb2srv_ioctl_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ /* TODO: avoid the memcpy */
+ io->smb2.in.reserved = SVAL(req->in.body, 0x02);
+ io->smb2.in.function = IVAL(req->in.body, 0x04);
+ /* file handle ... */
+ SMB2SRV_CHECK(smb2_pull_o32s32_blob(&req->in, io, req->in.body+0x18, &io->smb2.in.out));
+ io->smb2.in.max_input_response = IVAL(req->in.body, 0x20);
+ SMB2SRV_CHECK(smb2_pull_o32s32_blob(&req->in, io, req->in.body+0x24, &io->smb2.in.in));
+ io->smb2.in.max_output_response = IVAL(req->in.body, 0x2C);
+ io->smb2.in.flags = IVAL(req->in.body, 0x30);
+ io->smb2.in.reserved2 = IVAL(req->in.body, 0x34);
+
+ smb2_pull_handle(req->in.body + 0x08, &h);
+ if (h.data[0] == UINT64_MAX && h.data[1] == UINT64_MAX) {
+ io->smb2.level = RAW_IOCTL_SMB2_NO_HANDLE;
+ } else {
+ io->smb2.level = RAW_IOCTL_SMB2;
+ io->smb2.in.file.ntvfs = smb2srv_pull_handle(req, req->in.body, 0x08);
+ SMB2SRV_CHECK_FILE_HANDLE(io->smb2.in.file.ntvfs);
+ }
+
+ SMB2SRV_CALL_NTVFS_BACKEND(ntvfs_ioctl(req->ntvfs, io));
+}
+
+static void smb2srv_notify_send(struct ntvfs_request *ntvfs)
+{
+ struct smb2srv_request *req;
+ union smb_notify *io;
+ size_t size = 0;
+ int i;
+ uint8_t *p;
+ DATA_BLOB blob = data_blob(NULL, 0);
+
+ SMB2SRV_CHECK_ASYNC_STATUS(io, union smb_notify);
+ SMB2SRV_CHECK(smb2srv_setup_reply(req, 0x08, true, 0));
+
+#define MAX_BYTES_PER_CHAR 3
+
+ /* work out how big the reply buffer could be */
+ for (i=0;i<io->smb2.out.num_changes;i++) {
+ size += 12 + 3 + (1+strlen(io->smb2.out.changes[i].name.s)) * MAX_BYTES_PER_CHAR;
+ }
+
+ blob = data_blob_talloc(req, NULL, size);
+ if (size > 0 && !blob.data) {
+ SMB2SRV_CHECK(NT_STATUS_NO_MEMORY);
+ }
+
+ p = blob.data;
+
+ /* construct the changes buffer */
+ for (i=0;i<io->smb2.out.num_changes;i++) {
+ uint32_t ofs;
+ ssize_t len;
+
+ SIVAL(p, 4, io->smb2.out.changes[i].action);
+ len = push_string(p + 12, io->smb2.out.changes[i].name.s,
+ blob.length - (p+12 - blob.data), STR_UNICODE);
+ SIVAL(p, 8, len);
+
+ ofs = len + 12;
+
+ if (ofs & 3) {
+ int pad = 4 - (ofs & 3);
+ memset(p+ofs, 0, pad);
+ ofs += pad;
+ }
+
+ if (i == io->smb2.out.num_changes-1) {
+ SIVAL(p, 0, 0);
+ } else {
+ SIVAL(p, 0, ofs);
+ }
+
+ p += ofs;
+ }
+
+ blob.length = p - blob.data;
+
+ SMB2SRV_CHECK(smb2_push_o16s32_blob(&req->out, 0x02, blob));
+
+ smb2srv_send_reply(req);
+}
+
+void smb2srv_notify_recv(struct smb2srv_request *req)
+{
+ union smb_notify *io;
+
+ SMB2SRV_CHECK_BODY_SIZE(req, 0x20, false);
+ SMB2SRV_TALLOC_IO_PTR(io, union smb_notify);
+ SMB2SRV_SETUP_NTVFS_REQUEST(smb2srv_notify_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ io->smb2.level = RAW_NOTIFY_SMB2;
+ io->smb2.in.recursive = SVAL(req->in.body, 0x02);
+ io->smb2.in.buffer_size = IVAL(req->in.body, 0x04);
+ io->smb2.in.file.ntvfs = smb2srv_pull_handle(req, req->in.body, 0x08);
+ io->smb2.in.completion_filter = IVAL(req->in.body, 0x18);
+ io->smb2.in.unknown = BVAL(req->in.body, 0x1C);
+
+ SMB2SRV_CHECK_FILE_HANDLE(io->smb2.in.file.ntvfs);
+ SMB2SRV_CALL_NTVFS_BACKEND(ntvfs_notify(req->ntvfs, io));
+}
+
+static void smb2srv_break_send(struct ntvfs_request *ntvfs)
+{
+ struct smb2srv_request *req;
+ union smb_lock *io;
+
+ SMB2SRV_CHECK_ASYNC_STATUS_ERR(io, union smb_lock);
+ SMB2SRV_CHECK(smb2srv_setup_reply(req, 0x18, false, 0));
+
+ SCVAL(req->out.body, 0x02, io->smb2_break.out.oplock_level);
+ SCVAL(req->out.body, 0x03, io->smb2_break.out.reserved);
+ SIVAL(req->out.body, 0x04, io->smb2_break.out.reserved2);
+ smb2srv_push_handle(req->out.body, 0x08,io->smb2_break.out.file.ntvfs);
+
+ smb2srv_send_reply(req);
+}
+
+void smb2srv_break_recv(struct smb2srv_request *req)
+{
+ union smb_lock *io;
+
+ SMB2SRV_CHECK_BODY_SIZE(req, 0x18, false);
+ SMB2SRV_TALLOC_IO_PTR(io, union smb_lock);
+ SMB2SRV_SETUP_NTVFS_REQUEST(smb2srv_break_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ io->smb2_break.level = RAW_LOCK_SMB2_BREAK;
+ io->smb2_break.in.oplock_level = CVAL(req->in.body, 0x02);
+ io->smb2_break.in.reserved = CVAL(req->in.body, 0x03);
+ io->smb2_break.in.reserved2 = IVAL(req->in.body, 0x04);
+ io->smb2_break.in.file.ntvfs = smb2srv_pull_handle(req, req->in.body, 0x08);
+
+ SMB2SRV_CHECK_FILE_HANDLE(io->smb2_break.in.file.ntvfs);
+ SMB2SRV_CALL_NTVFS_BACKEND(ntvfs_lock(req->ntvfs, io));
+}
diff --git a/source4/smb_server/smb2/find.c b/source4/smb_server/smb2/find.c
new file mode 100644
index 0000000..17f09e1
--- /dev/null
+++ b/source4/smb_server/smb2/find.c
@@ -0,0 +1,167 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB2 Find
+ Copyright (C) Andrew Tridgell 2003
+ 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/>.
+*/
+/*
+ This file handles the parsing of transact2 requests
+*/
+
+#include "includes.h"
+#include "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+#include "smb_server/smb_server.h"
+#include "smb_server/smb2/smb2_server.h"
+#include "ntvfs/ntvfs.h"
+
+
+/* a structure to encapsulate the state information about an in-progress ffirst/fnext operation */
+struct smb2srv_find_state {
+ struct smb2srv_request *req;
+ struct smb2_find *info;
+ union smb_search_first *ff;
+ union smb_search_next *fn;
+ uint32_t last_entry_offset;
+};
+
+/* callback function for SMB2 Find */
+static bool smb2srv_find_callback(void *private_data, const union smb_search_data *file)
+{
+ struct smb2srv_find_state *state = talloc_get_type(private_data, struct smb2srv_find_state);
+ struct smb2_find *info = state->info;
+ uint32_t old_length;
+ NTSTATUS status;
+
+ old_length = info->out.blob.length;
+
+ status = smbsrv_push_passthru_search(state, &info->out.blob, info->data_level, file, STR_UNICODE);
+ if (!NT_STATUS_IS_OK(status) ||
+ info->out.blob.length > info->in.max_response_size) {
+ /* restore the old length and tell the backend to stop */
+ smbsrv_blob_grow_data(state, &info->out.blob, old_length);
+ return false;
+ }
+
+ state->last_entry_offset = old_length;
+
+ return true;
+}
+
+static void smb2srv_find_send(struct ntvfs_request *ntvfs)
+{
+ struct smb2srv_request *req;
+ struct smb2srv_find_state *state;
+
+ SMB2SRV_CHECK_ASYNC_STATUS(state, struct smb2srv_find_state);
+ SMB2SRV_CHECK(smb2srv_setup_reply(req, 0x08, true, state->info->out.blob.length));
+
+ if (state->info->out.blob.length > 0) {
+ SIVAL(state->info->out.blob.data + state->last_entry_offset, 0, 0);
+ }
+
+ SMB2SRV_CHECK(smb2_push_o16s32_blob(&req->out, 0x02, state->info->out.blob));
+
+ smb2srv_send_reply(req);
+}
+
+static NTSTATUS smb2srv_find_backend(struct smb2srv_find_state *state)
+{
+ struct smb2_find *info = state->info;
+
+ switch (info->in.level) {
+ case SMB2_FIND_DIRECTORY_INFO:
+ info->data_level = RAW_SEARCH_DATA_DIRECTORY_INFO;
+ break;
+
+ case SMB2_FIND_FULL_DIRECTORY_INFO:
+ info->data_level = RAW_SEARCH_DATA_FULL_DIRECTORY_INFO;
+ break;
+
+ case SMB2_FIND_BOTH_DIRECTORY_INFO:
+ info->data_level = RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO;
+ break;
+
+ case SMB2_FIND_NAME_INFO:
+ info->data_level = RAW_SEARCH_DATA_NAME_INFO;
+ break;
+
+ case SMB2_FIND_ID_BOTH_DIRECTORY_INFO:
+ info->data_level = RAW_SEARCH_DATA_ID_BOTH_DIRECTORY_INFO;
+ break;
+
+ case SMB2_FIND_ID_FULL_DIRECTORY_INFO:
+ info->data_level = RAW_SEARCH_DATA_ID_FULL_DIRECTORY_INFO;
+ break;
+
+ default:
+ return NT_STATUS_FOOBAR;
+ }
+
+ if (info->in.continue_flags & SMB2_CONTINUE_FLAG_REOPEN) {
+ state->ff = talloc(state, union smb_search_first);
+ NT_STATUS_HAVE_NO_MEMORY(state->ff);
+
+ state->ff->smb2 = *info;
+ state->info = &state->ff->smb2;
+ ZERO_STRUCT(state->ff->smb2.out);
+
+ return ntvfs_search_first(state->req->ntvfs, state->ff, state, smb2srv_find_callback);
+ } else {
+ state->fn = talloc(state, union smb_search_next);
+ NT_STATUS_HAVE_NO_MEMORY(state->fn);
+
+ state->fn->smb2 = *info;
+ state->info = &state->fn->smb2;
+ ZERO_STRUCT(state->fn->smb2.out);
+
+ return ntvfs_search_next(state->req->ntvfs, state->fn, state, smb2srv_find_callback);
+ }
+}
+
+void smb2srv_find_recv(struct smb2srv_request *req)
+{
+ struct smb2srv_find_state *state;
+ struct smb2_find *info;
+
+ SMB2SRV_CHECK_BODY_SIZE(req, 0x20, true);
+ SMB2SRV_TALLOC_IO_PTR(info, struct smb2_find);
+ /* this overwrites req->io_ptr !*/
+ SMB2SRV_TALLOC_IO_PTR(state, struct smb2srv_find_state);
+ state->req = req;
+ state->info = info;
+ state->ff = NULL;
+ state->fn = NULL;
+ state->last_entry_offset= 0;
+ SMB2SRV_SETUP_NTVFS_REQUEST(smb2srv_find_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ info->level = RAW_SEARCH_SMB2;
+ info->data_level = RAW_SEARCH_DATA_GENERIC;/* will be overwritten later */
+ info->in.level = CVAL(req->in.body, 0x02);
+ info->in.continue_flags = CVAL(req->in.body, 0x03);
+ info->in.file_index = IVAL(req->in.body, 0x04);
+ info->in.file.ntvfs = smb2srv_pull_handle(req, req->in.body, 0x08);
+ SMB2SRV_CHECK(smb2_pull_o16s16_string(&req->in, info, req->in.body+0x18, &info->in.pattern));
+ info->in.max_response_size = IVAL(req->in.body, 0x1C);
+
+ /* the VFS backend does not yet handle NULL patterns */
+ if (info->in.pattern == NULL) {
+ info->in.pattern = "";
+ }
+
+ SMB2SRV_CHECK_FILE_HANDLE(info->in.file.ntvfs);
+ SMB2SRV_CALL_NTVFS_BACKEND(smb2srv_find_backend(state));
+}
diff --git a/source4/smb_server/smb2/keepalive.c b/source4/smb_server/smb2/keepalive.c
new file mode 100644
index 0000000..cd53778
--- /dev/null
+++ b/source4/smb_server/smb2/keepalive.c
@@ -0,0 +1,71 @@
+/*
+ Unix SMB2 implementation.
+
+ 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/smb2/smb2.h"
+#include "smb_server/smb_server.h"
+#include "smb_server/smb2/smb2_server.h"
+
+static NTSTATUS smb2srv_keepalive_backend(struct smb2srv_request *req)
+{
+ /* TODO: maybe update some flags on the connection struct */
+ return NT_STATUS_OK;
+}
+
+static void smb2srv_keepalive_send(struct smb2srv_request *req)
+{
+ NTSTATUS status;
+
+ if (NT_STATUS_IS_ERR(req->status)) {
+ smb2srv_send_error(req, req->status);
+ return;
+ }
+
+ status = smb2srv_setup_reply(req, 0x04, false, 0);
+ if (!NT_STATUS_IS_OK(status)) {
+ smbsrv_terminate_connection(req->smb_conn, nt_errstr(status));
+ talloc_free(req);
+ return;
+ }
+
+ SSVAL(req->out.body, 0x02, 0);
+
+ smb2srv_send_reply(req);
+}
+
+void smb2srv_keepalive_recv(struct smb2srv_request *req)
+{
+ if (req->in.body_size != 0x04) {
+ smb2srv_send_error(req, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+
+ if (SVAL(req->in.body, 0x00) != 0x04) {
+ smb2srv_send_error(req, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+
+ req->status = smb2srv_keepalive_backend(req);
+
+ if (req->control_flags & SMB2SRV_REQ_CTRL_FLAG_NOT_REPLY) {
+ talloc_free(req);
+ return;
+ }
+ smb2srv_keepalive_send(req);
+}
diff --git a/source4/smb_server/smb2/negprot.c b/source4/smb_server/smb2/negprot.c
new file mode 100644
index 0000000..048d7b4
--- /dev/null
+++ b/source4/smb_server/smb2/negprot.c
@@ -0,0 +1,327 @@
+/*
+ Unix SMB2 implementation.
+
+ Copyright (C) Andrew Bartlett 2001-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 "auth/credentials/credentials.h"
+#include "auth/auth.h"
+#include "auth/gensec/gensec.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+#include "smb_server/smb_server.h"
+#include "smb_server/smb2/smb2_server.h"
+#include "samba/service_stream.h"
+#include "param/param.h"
+
+static NTSTATUS smb2srv_negprot_secblob(struct smb2srv_request *req, DATA_BLOB *_blob)
+{
+ struct gensec_security *gensec_security;
+ DATA_BLOB null_data_blob = data_blob(NULL, 0);
+ DATA_BLOB blob;
+ NTSTATUS nt_status;
+ struct cli_credentials *server_credentials;
+
+ server_credentials =
+ cli_credentials_init_server(req, req->smb_conn->lp_ctx);
+ if (server_credentials == NULL) {
+ DBG_DEBUG("Failed to obtain server credentials, "
+ "perhaps a standalone server?\n");
+ /*
+ * Create anon server credentials for for the
+ * spoolss.notify test.
+ */
+ server_credentials = cli_credentials_init_anon(req);
+ if (server_credentials == NULL) {
+ smbsrv_terminate_connection(req->smb_conn,
+ "Failed to init server credentials\n");
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ req->smb_conn->negotiate.server_credentials = talloc_steal(req->smb_conn, server_credentials);
+
+ nt_status = samba_server_gensec_start(req,
+ req->smb_conn->connection->event.ctx,
+ req->smb_conn->connection->msg_ctx,
+ req->smb_conn->lp_ctx,
+ server_credentials,
+ "cifs",
+ &gensec_security);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(0, ("Failed to start GENSEC: %s\n", nt_errstr(nt_status)));
+ smbsrv_terminate_connection(req->smb_conn, "Failed to start GENSEC\n");
+ return nt_status;
+ }
+
+ gensec_set_target_service(gensec_security, "cifs");
+
+ gensec_set_credentials(gensec_security, server_credentials);
+
+ nt_status = gensec_start_mech_by_oid(gensec_security, GENSEC_OID_SPNEGO);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(0, ("Failed to start SPNEGO: %s\n", nt_errstr(nt_status)));
+ smbsrv_terminate_connection(req->smb_conn, "Failed to start SPNEGO\n");
+ return nt_status;
+ }
+
+ nt_status = gensec_update(gensec_security, req,
+ null_data_blob, &blob);
+ if (!NT_STATUS_IS_OK(nt_status) && !NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ DEBUG(0, ("Failed to get SPNEGO to give us the first token: %s\n", nt_errstr(nt_status)));
+ smbsrv_terminate_connection(req->smb_conn, "Failed to start SPNEGO - no first token\n");
+ return nt_status;
+ }
+
+ *_blob = blob;
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS smb2srv_negprot_backend(struct smb2srv_request *req, struct smb2_negprot *io)
+{
+ NTSTATUS status;
+ struct timeval current_time;
+ struct timeval boot_time;
+ uint16_t i;
+ uint16_t dialect = 0;
+ enum smb_signing_setting signing_setting;
+ struct loadparm_context *lp_ctx = req->smb_conn->lp_ctx;
+
+ /* we only do one dialect for now */
+ if (io->in.dialect_count < 1) {
+ return NT_STATUS_NOT_SUPPORTED;
+ }
+ for (i=0; i < io->in.dialect_count; i++) {
+ dialect = io->in.dialects[i];
+ if (dialect == SMB2_DIALECT_REVISION_202) {
+ break;
+ }
+ }
+ if (dialect != SMB2_DIALECT_REVISION_202) {
+ DEBUG(0,("Got unexpected SMB2 dialect %u\n", dialect));
+ return NT_STATUS_NOT_SUPPORTED;
+ }
+
+ req->smb_conn->negotiate.protocol = PROTOCOL_SMB2_02;
+
+ current_time = timeval_current(); /* TODO: handle timezone?! */
+ boot_time = timeval_current(); /* TODO: fix me */
+
+ ZERO_STRUCT(io->out);
+
+ signing_setting = lpcfg_server_signing(lp_ctx);
+ if (signing_setting == SMB_SIGNING_DEFAULT) {
+ /*
+ * If we are a domain controller, SMB signing is
+ * really important, as it can prevent a number of
+ * attacks on communications between us and the
+ * clients
+ *
+ * However, it really sucks (no sendfile, CPU
+ * overhead) performance-wise when used on a
+ * file server, so disable it by default
+ * on non-DCs
+ */
+
+ if (lpcfg_server_role(lp_ctx) >= ROLE_ACTIVE_DIRECTORY_DC) {
+ signing_setting = SMB_SIGNING_REQUIRED;
+ } else {
+ signing_setting = SMB_SIGNING_OFF;
+ }
+ }
+
+ switch (signing_setting) {
+ case SMB_SIGNING_DEFAULT:
+ case SMB_SIGNING_IPC_DEFAULT:
+ smb_panic(__location__);
+ break;
+ case SMB_SIGNING_OFF:
+ io->out.security_mode = 0;
+ break;
+ case SMB_SIGNING_DESIRED:
+ case SMB_SIGNING_IF_REQUIRED:
+ io->out.security_mode = SMB2_NEGOTIATE_SIGNING_ENABLED;
+ break;
+ case SMB_SIGNING_REQUIRED:
+ io->out.security_mode = SMB2_NEGOTIATE_SIGNING_ENABLED | SMB2_NEGOTIATE_SIGNING_REQUIRED;
+ /* force signing on immediately */
+ req->smb_conn->smb2_signing_required = true;
+ break;
+ }
+ io->out.dialect_revision = dialect;
+ io->out.capabilities = 0;
+ io->out.max_transact_size = lpcfg_parm_ulong(req->smb_conn->lp_ctx, NULL,
+ "smb2", "max transaction size", 0x10000);
+ io->out.max_read_size = lpcfg_parm_ulong(req->smb_conn->lp_ctx, NULL,
+ "smb2", "max read size", 0x10000);
+ io->out.max_write_size = lpcfg_parm_ulong(req->smb_conn->lp_ctx, NULL,
+ "smb2", "max write size", 0x10000);
+ io->out.system_time = timeval_to_nttime(&current_time);
+ io->out.server_start_time = timeval_to_nttime(&boot_time);
+ io->out.reserved2 = 0;
+ status = smb2srv_negprot_secblob(req, &io->out.secblob);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ return NT_STATUS_OK;
+}
+
+static void smb2srv_negprot_send(struct smb2srv_request *req, struct smb2_negprot *io)
+{
+ NTSTATUS status;
+
+ if (NT_STATUS_IS_ERR(req->status)) {
+ smb2srv_send_error(req, req->status); /* TODO: is this correct? */
+ return;
+ }
+
+ status = smb2srv_setup_reply(req, 0x40, true, io->out.secblob.length);
+ if (!NT_STATUS_IS_OK(status)) {
+ smbsrv_terminate_connection(req->smb_conn, nt_errstr(status));
+ talloc_free(req);
+ return;
+ }
+
+ SSVAL(req->out.body, 0x02, io->out.security_mode);
+ SIVAL(req->out.body, 0x04, io->out.dialect_revision);
+ SIVAL(req->out.body, 0x06, io->out.reserved);
+ status = smbcli_push_guid(req->out.body, 0x08, &io->out.server_guid);
+ if (!NT_STATUS_IS_OK(status)) {
+ smbsrv_terminate_connection(req->smb_conn, nt_errstr(status));
+ talloc_free(req);
+ return;
+ }
+ SIVAL(req->out.body, 0x18, io->out.capabilities);
+ SIVAL(req->out.body, 0x1C, io->out.max_transact_size);
+ SIVAL(req->out.body, 0x20, io->out.max_read_size);
+ SIVAL(req->out.body, 0x24, io->out.max_write_size);
+ push_nttime(req->out.body, 0x28, io->out.system_time);
+ push_nttime(req->out.body, 0x30, io->out.server_start_time);
+ SIVAL(req->out.body, 0x3C, io->out.reserved2);
+ status = smb2_push_o16s16_blob(&req->out, 0x38, io->out.secblob);
+ if (!NT_STATUS_IS_OK(status)) {
+ smbsrv_terminate_connection(req->smb_conn, nt_errstr(status));
+ talloc_free(req);
+ return;
+ }
+
+ smb2srv_send_reply(req);
+}
+
+void smb2srv_negprot_recv(struct smb2srv_request *req)
+{
+ struct smb2_negprot *io;
+ int i;
+
+ if (req->in.body_size < 0x26) {
+ smbsrv_terminate_connection(req->smb_conn, "Bad body size in SMB2 negprot");
+ return;
+ }
+
+ io = talloc(req, struct smb2_negprot);
+ if (!io) {
+ smbsrv_terminate_connection(req->smb_conn, nt_errstr(NT_STATUS_NO_MEMORY));
+ talloc_free(req);
+ return;
+ }
+
+ io->in.dialect_count = SVAL(req->in.body, 0x02);
+ io->in.security_mode = SVAL(req->in.body, 0x04);
+ io->in.reserved = SVAL(req->in.body, 0x06);
+ io->in.capabilities = IVAL(req->in.body, 0x08);
+ req->status = smbcli_pull_guid(req->in.body, 0xC, &io->in.client_guid);
+ if (!NT_STATUS_IS_OK(req->status)) {
+ smbsrv_terminate_connection(req->smb_conn, "Bad GUID in SMB2 negprot");
+ talloc_free(req);
+ return;
+ }
+ io->in.start_time = smbcli_pull_nttime(req->in.body, 0x1C);
+
+ io->in.dialects = talloc_array(req, uint16_t, io->in.dialect_count);
+ if (io->in.dialects == NULL) {
+ smbsrv_terminate_connection(req->smb_conn, nt_errstr(NT_STATUS_NO_MEMORY));
+ talloc_free(req);
+ return;
+ }
+ for (i=0;i<io->in.dialect_count;i++) {
+ io->in.dialects[i] = SVAL(req->in.body, 0x24+i*2);
+ }
+
+ req->status = smb2srv_negprot_backend(req, io);
+
+ if (req->control_flags & SMB2SRV_REQ_CTRL_FLAG_NOT_REPLY) {
+ talloc_free(req);
+ return;
+ }
+ smb2srv_negprot_send(req, io);
+}
+
+/*
+ * reply to a SMB negprot request with dialect "SMB 2.002"
+ */
+void smb2srv_reply_smb_negprot(struct smbsrv_request *smb_req)
+{
+ struct smb2srv_request *req;
+ uint32_t body_fixed_size = 0x26;
+
+ req = talloc_zero(smb_req->smb_conn, struct smb2srv_request);
+ if (!req) goto nomem;
+ req->smb_conn = smb_req->smb_conn;
+ req->request_time = smb_req->request_time;
+ talloc_steal(req, smb_req);
+
+ req->in.size = NBT_HDR_SIZE+SMB2_HDR_BODY+body_fixed_size;
+ req->in.allocated = req->in.size;
+ req->in.buffer = talloc_array(req, uint8_t, req->in.allocated);
+ if (!req->in.buffer) goto nomem;
+ req->in.hdr = req->in.buffer + NBT_HDR_SIZE;
+ req->in.body = req->in.hdr + SMB2_HDR_BODY;
+ req->in.body_size = body_fixed_size;
+ req->in.dynamic = NULL;
+
+ smb2srv_setup_bufinfo(req);
+
+ SIVAL(req->in.hdr, 0, SMB2_MAGIC);
+ SSVAL(req->in.hdr, SMB2_HDR_LENGTH, SMB2_HDR_BODY);
+ SSVAL(req->in.hdr, SMB2_HDR_EPOCH, 0);
+ SIVAL(req->in.hdr, SMB2_HDR_STATUS, 0);
+ SSVAL(req->in.hdr, SMB2_HDR_OPCODE, SMB2_OP_NEGPROT);
+ SSVAL(req->in.hdr, SMB2_HDR_CREDIT, 0);
+ SIVAL(req->in.hdr, SMB2_HDR_FLAGS, 0);
+ SIVAL(req->in.hdr, SMB2_HDR_NEXT_COMMAND, 0);
+ SBVAL(req->in.hdr, SMB2_HDR_MESSAGE_ID, 0);
+ SIVAL(req->in.hdr, SMB2_HDR_PID, 0);
+ SIVAL(req->in.hdr, SMB2_HDR_TID, 0);
+ SBVAL(req->in.hdr, SMB2_HDR_SESSION_ID, 0);
+ memset(req->in.hdr+SMB2_HDR_SIGNATURE, 0, 16);
+
+ /* this seems to be a bug, they use 0x24 but the length is 0x26 */
+ SSVAL(req->in.body, 0x00, 0x24);
+
+ SSVAL(req->in.body, 0x02, 1);
+ memset(req->in.body+0x04, 0, 32);
+ SSVAL(req->in.body, 0x24, SMB2_DIALECT_REVISION_202);
+
+ smb2srv_negprot_recv(req);
+ return;
+nomem:
+ smbsrv_terminate_connection(smb_req->smb_conn, nt_errstr(NT_STATUS_NO_MEMORY));
+ talloc_free(req);
+ return;
+}
diff --git a/source4/smb_server/smb2/receive.c b/source4/smb_server/smb2/receive.c
new file mode 100644
index 0000000..c4109bf
--- /dev/null
+++ b/source4/smb_server/smb2/receive.c
@@ -0,0 +1,710 @@
+/*
+ Unix SMB2 implementation.
+
+ 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 "system/time.h"
+#include "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+#include "smb_server/smb_server.h"
+#include "smb_server/smb2/smb2_server.h"
+#include "samba/service_stream.h"
+#include "lib/stream/packet.h"
+#include "ntvfs/ntvfs.h"
+#include "param/param.h"
+#include "auth/auth.h"
+
+
+/* fill in the bufinfo */
+void smb2srv_setup_bufinfo(struct smb2srv_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;
+ }
+}
+
+static int smb2srv_request_destructor(struct smb2srv_request *req)
+{
+ DLIST_REMOVE(req->smb_conn->requests2.list, req);
+ if (req->pending_id) {
+ idr_remove(req->smb_conn->requests2.idtree_req, req->pending_id);
+ }
+ return 0;
+}
+
+static int smb2srv_request_deny_destructor(struct smb2srv_request *req)
+{
+ return -1;
+}
+
+struct smb2srv_request *smb2srv_init_request(struct smbsrv_connection *smb_conn)
+{
+ struct smb2srv_request *req;
+
+ req = talloc_zero(smb_conn, struct smb2srv_request);
+ if (!req) return NULL;
+
+ req->smb_conn = smb_conn;
+
+ req->chained_session_id = UINT64_MAX;
+ req->chained_tree_id = UINT32_MAX;
+
+ talloc_set_destructor(req, smb2srv_request_destructor);
+
+ return req;
+}
+
+NTSTATUS smb2srv_setup_reply(struct smb2srv_request *req, uint16_t body_fixed_size,
+ bool body_dynamic_present, uint32_t body_dynamic_size)
+{
+ uint32_t flags = IVAL(req->in.hdr, SMB2_HDR_FLAGS);
+ uint32_t pid = IVAL(req->in.hdr, SMB2_HDR_PID);
+ uint32_t tid = IVAL(req->in.hdr, SMB2_HDR_TID);
+ uint16_t credits = SVAL(req->in.hdr, SMB2_HDR_CREDIT);
+
+ if (credits == 0) {
+ credits = 1;
+ }
+
+ flags |= SMB2_HDR_FLAG_REDIRECT;
+
+ if (req->pending_id) {
+ flags |= SMB2_HDR_FLAG_ASYNC;
+ pid = req->pending_id;
+ tid = 0;
+ credits = 0;
+ }
+
+ if (body_dynamic_present) {
+ if (body_dynamic_size == 0) {
+ body_dynamic_size = 1;
+ }
+ } else {
+ body_dynamic_size = 0;
+ }
+
+ req->out.size = SMB2_HDR_BODY+NBT_HDR_SIZE+body_fixed_size;
+
+ req->out.allocated = req->out.size + body_dynamic_size;
+ req->out.buffer = talloc_array(req, uint8_t,
+ req->out.allocated);
+ NT_STATUS_HAVE_NO_MEMORY(req->out.buffer);
+
+ req->out.hdr = req->out.buffer + NBT_HDR_SIZE;
+ req->out.body = req->out.hdr + SMB2_HDR_BODY;
+ req->out.body_fixed = body_fixed_size;
+ req->out.body_size = body_fixed_size;
+ req->out.dynamic = (body_dynamic_size ? req->out.body + body_fixed_size : NULL);
+
+ SIVAL(req->out.hdr, 0, SMB2_MAGIC);
+ SSVAL(req->out.hdr, SMB2_HDR_LENGTH, SMB2_HDR_BODY);
+ SSVAL(req->out.hdr, SMB2_HDR_CREDIT_CHARGE,
+ SVAL(req->in.hdr, SMB2_HDR_CREDIT_CHARGE));
+ SIVAL(req->out.hdr, SMB2_HDR_STATUS, NT_STATUS_V(req->status));
+ SSVAL(req->out.hdr, SMB2_HDR_OPCODE, SVAL(req->in.hdr, SMB2_HDR_OPCODE));
+ SSVAL(req->out.hdr, SMB2_HDR_CREDIT, credits);
+ SIVAL(req->out.hdr, SMB2_HDR_FLAGS, flags);
+ SIVAL(req->out.hdr, SMB2_HDR_NEXT_COMMAND, 0);
+ SBVAL(req->out.hdr, SMB2_HDR_MESSAGE_ID, req->seqnum);
+ SIVAL(req->out.hdr, SMB2_HDR_PID, pid);
+ SIVAL(req->out.hdr, SMB2_HDR_TID, tid);
+ SBVAL(req->out.hdr, SMB2_HDR_SESSION_ID, BVAL(req->in.hdr, SMB2_HDR_SESSION_ID));
+ memcpy(req->out.hdr+SMB2_HDR_SIGNATURE,
+ req->in.hdr+SMB2_HDR_SIGNATURE, 16);
+
+ /* set the length of the fixed body part and +1 if there's a dynamic part also */
+ SSVAL(req->out.body, 0, body_fixed_size + (body_dynamic_size?1:0));
+
+ /*
+ * if we have a dynamic part, make sure the first byte
+ * which is always be part of the packet is initialized
+ */
+ if (body_dynamic_size) {
+ req->out.size += 1;
+ SCVAL(req->out.dynamic, 0, 0);
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS smb2srv_reply(struct smb2srv_request *req);
+
+static void smb2srv_chain_reply(struct smb2srv_request *p_req)
+{
+ NTSTATUS status;
+ struct smbsrv_connection *smb_conn = p_req->smb_conn;
+ struct smb2srv_request *req;
+ uint32_t chain_offset;
+ uint32_t protocol_version;
+ uint16_t buffer_code;
+ uint32_t dynamic_size;
+ uint32_t flags;
+ uint32_t last_hdr_offset;
+
+ last_hdr_offset = p_req->in.hdr - p_req->in.buffer;
+
+ chain_offset = p_req->chain_offset;
+ p_req->chain_offset = 0;
+
+ if (p_req->in.size < (last_hdr_offset + chain_offset + SMB2_MIN_SIZE_NO_BODY)) {
+ DEBUG(2,("Invalid SMB2 chained packet at offset 0x%X from last hdr 0x%X\n",
+ chain_offset, last_hdr_offset));
+ smbsrv_terminate_connection(smb_conn, "Invalid SMB2 chained packet");
+ return;
+ }
+
+ protocol_version = IVAL(p_req->in.buffer, last_hdr_offset + chain_offset);
+ if (protocol_version != SMB2_MAGIC) {
+ DEBUG(2,("Invalid SMB chained packet: protocol prefix: 0x%08X\n",
+ protocol_version));
+ smbsrv_terminate_connection(smb_conn, "NON-SMB2 chained packet");
+ return;
+ }
+
+ req = smb2srv_init_request(smb_conn);
+ if (!req) {
+ smbsrv_terminate_connection(smb_conn, "SMB2 chained packet - no memory");
+ return;
+ }
+
+ talloc_steal(req, p_req);
+
+ req->in.buffer = talloc_steal(req, p_req->in.buffer);
+ req->in.size = p_req->in.size;
+ req->request_time = p_req->request_time;
+ req->in.allocated = req->in.size;
+
+ req->in.hdr = req->in.buffer+ last_hdr_offset + chain_offset;
+ req->in.body = req->in.hdr + SMB2_HDR_BODY;
+ req->in.body_size = req->in.size - (last_hdr_offset+ chain_offset + SMB2_HDR_BODY);
+ req->in.dynamic = NULL;
+
+ req->seqnum = BVAL(req->in.hdr, SMB2_HDR_MESSAGE_ID);
+
+ if (req->in.body_size < 2) {
+ /* error handling for this is different for negprot to
+ other packet types */
+ uint16_t opcode = SVAL(req->in.hdr, SMB2_HDR_OPCODE);
+ if (opcode == SMB2_OP_NEGPROT) {
+ smbsrv_terminate_connection(smb_conn, "Bad body size in SMB2 negprot");
+ return;
+ } else {
+ smb2srv_send_error(req, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+ }
+
+ buffer_code = SVAL(req->in.body, 0);
+ req->in.body_fixed = (buffer_code & ~1);
+ dynamic_size = req->in.body_size - req->in.body_fixed;
+
+ if (dynamic_size != 0 && (buffer_code & 1)) {
+ req->in.dynamic = req->in.body + req->in.body_fixed;
+ if (smb2_oob(&req->in, req->in.dynamic, dynamic_size)) {
+ DEBUG(1,("SMB2 chained request invalid dynamic size 0x%x\n",
+ dynamic_size));
+ smb2srv_send_error(req, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+ }
+
+ smb2srv_setup_bufinfo(req);
+
+ flags = IVAL(req->in.hdr, SMB2_HDR_FLAGS);
+ if (flags & SMB2_HDR_FLAG_CHAINED) {
+ if (p_req->chained_file_handle) {
+ memcpy(req->_chained_file_handle,
+ p_req->_chained_file_handle,
+ sizeof(req->_chained_file_handle));
+ req->chained_file_handle = req->_chained_file_handle;
+ }
+ req->chained_session_id = p_req->chained_session_id;
+ req->chained_tree_id = p_req->chained_tree_id;
+ req->chain_status = p_req->chain_status;
+ }
+
+ /*
+ * TODO: - make sure the length field is 64
+ * - make sure it's a request
+ */
+
+ status = smb2srv_reply(req);
+ if (!NT_STATUS_IS_OK(status)) {
+ smbsrv_terminate_connection(smb_conn, nt_errstr(status));
+ return;
+ }
+}
+
+void smb2srv_send_reply(struct smb2srv_request *req)
+{
+ DATA_BLOB blob;
+ NTSTATUS status;
+
+ if (req->smb_conn->connection->event.fde == NULL) {
+ /* the socket has been destroyed - no point trying to send a reply! */
+ talloc_free(req);
+ return;
+ }
+
+ if (req->out.size > NBT_HDR_SIZE) {
+ _smb_setlen_tcp(req->out.buffer, req->out.size - NBT_HDR_SIZE);
+ }
+
+ /* if signing is active on the session then sign the packet */
+ if (req->is_signed) {
+ status = smb2_sign_message(&req->out,
+ req->session->session_info->session_key);
+ if (!NT_STATUS_IS_OK(status)) {
+ smbsrv_terminate_connection(req->smb_conn, nt_errstr(status));
+ return;
+ }
+ }
+
+
+ blob = data_blob_const(req->out.buffer, req->out.size);
+ status = packet_send(req->smb_conn->packet, blob);
+ if (!NT_STATUS_IS_OK(status)) {
+ smbsrv_terminate_connection(req->smb_conn, nt_errstr(status));
+ return;
+ }
+ if (req->chain_offset) {
+ smb2srv_chain_reply(req);
+ return;
+ }
+ talloc_free(req);
+}
+
+void smb2srv_send_error(struct smb2srv_request *req, NTSTATUS error)
+{
+ NTSTATUS status;
+
+ if (req->smb_conn->connection->event.fde == NULL) {
+ /* the socket has been destroyed - no point trying to send an error! */
+ talloc_free(req);
+ return;
+ }
+
+ status = smb2srv_setup_reply(req, 8, true, 0);
+ if (!NT_STATUS_IS_OK(status)) {
+ smbsrv_terminate_connection(req->smb_conn, nt_errstr(status));
+ talloc_free(req);
+ return;
+ }
+
+ SIVAL(req->out.hdr, SMB2_HDR_STATUS, NT_STATUS_V(error));
+
+ SSVAL(req->out.body, 0x02, 0);
+ SIVAL(req->out.body, 0x04, 0);
+
+ req->chain_status = NT_STATUS_INVALID_PARAMETER;
+
+ smb2srv_send_reply(req);
+}
+
+static NTSTATUS smb2srv_reply(struct smb2srv_request *req)
+{
+ uint16_t opcode;
+ uint32_t tid;
+ uint64_t uid;
+ uint32_t flags;
+
+ if (SVAL(req->in.hdr, SMB2_HDR_LENGTH) != SMB2_HDR_BODY) {
+ smbsrv_terminate_connection(req->smb_conn, "Invalid SMB2 header length");
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ opcode = SVAL(req->in.hdr, SMB2_HDR_OPCODE);
+ req->chain_offset = IVAL(req->in.hdr, SMB2_HDR_NEXT_COMMAND);
+ req->seqnum = BVAL(req->in.hdr, SMB2_HDR_MESSAGE_ID);
+ tid = IVAL(req->in.hdr, SMB2_HDR_TID);
+ uid = BVAL(req->in.hdr, SMB2_HDR_SESSION_ID);
+ flags = IVAL(req->in.hdr, SMB2_HDR_FLAGS);
+
+ if (opcode != SMB2_OP_CANCEL &&
+ req->smb_conn->highest_smb2_seqnum != 0 &&
+ req->seqnum <= req->smb_conn->highest_smb2_seqnum) {
+ smbsrv_terminate_connection(req->smb_conn, "Invalid SMB2 sequence number");
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ if (opcode != SMB2_OP_CANCEL) {
+ req->smb_conn->highest_smb2_seqnum = req->seqnum;
+ }
+
+ if (flags & SMB2_HDR_FLAG_CHAINED) {
+ uid = req->chained_session_id;
+ tid = req->chained_tree_id;
+ }
+
+ req->session = smbsrv_session_find(req->smb_conn, uid, req->request_time);
+ req->tcon = smbsrv_smb2_tcon_find(req->session, tid, req->request_time);
+
+ req->chained_session_id = uid;
+ req->chained_tree_id = tid;
+
+ errno = 0;
+
+ /* supporting signing is mandatory in SMB2, and is per-packet. So we
+ should check the signature on any incoming packet that is signed, and
+ should give a signed reply to any signed request */
+ if (flags & SMB2_HDR_FLAG_SIGNED) {
+ NTSTATUS status;
+
+ if (!req->session) goto nosession;
+
+ req->is_signed = true;
+ status = smb2_check_signature(&req->in,
+ req->session->session_info->session_key);
+ if (!NT_STATUS_IS_OK(status)) {
+ smb2srv_send_error(req, status);
+ return NT_STATUS_OK;
+ }
+ } else if (req->session && req->session->smb2_signing.active) {
+ /* we require signing and this request was not signed */
+ smb2srv_send_error(req, NT_STATUS_ACCESS_DENIED);
+ return NT_STATUS_OK;
+ }
+
+ if (!NT_STATUS_IS_OK(req->chain_status)) {
+ smb2srv_send_error(req, req->chain_status);
+ return NT_STATUS_OK;
+ }
+
+ switch (opcode) {
+ case SMB2_OP_NEGPROT:
+ smb2srv_negprot_recv(req);
+ return NT_STATUS_OK;
+ case SMB2_OP_SESSSETUP:
+ smb2srv_sesssetup_recv(req);
+ return NT_STATUS_OK;
+ case SMB2_OP_LOGOFF:
+ if (!req->session) goto nosession;
+ smb2srv_logoff_recv(req);
+ return NT_STATUS_OK;
+ case SMB2_OP_TCON:
+ if (!req->session) goto nosession;
+ smb2srv_tcon_recv(req);
+ return NT_STATUS_OK;
+ case SMB2_OP_TDIS:
+ if (!req->session) goto nosession;
+ if (!req->tcon) goto notcon;
+ smb2srv_tdis_recv(req);
+ return NT_STATUS_OK;
+ case SMB2_OP_CREATE:
+ if (!req->session) goto nosession;
+ if (!req->tcon) goto notcon;
+ smb2srv_create_recv(req);
+ return NT_STATUS_OK;
+ case SMB2_OP_CLOSE:
+ if (!req->session) goto nosession;
+ if (!req->tcon) goto notcon;
+ smb2srv_close_recv(req);
+ return NT_STATUS_OK;
+ case SMB2_OP_FLUSH:
+ if (!req->session) goto nosession;
+ if (!req->tcon) goto notcon;
+ smb2srv_flush_recv(req);
+ return NT_STATUS_OK;
+ case SMB2_OP_READ:
+ if (!req->session) goto nosession;
+ if (!req->tcon) goto notcon;
+ smb2srv_read_recv(req);
+ return NT_STATUS_OK;
+ case SMB2_OP_WRITE:
+ if (!req->session) goto nosession;
+ if (!req->tcon) goto notcon;
+ smb2srv_write_recv(req);
+ return NT_STATUS_OK;
+ case SMB2_OP_LOCK:
+ if (!req->session) goto nosession;
+ if (!req->tcon) goto notcon;
+ smb2srv_lock_recv(req);
+ return NT_STATUS_OK;
+ case SMB2_OP_IOCTL:
+ if (!req->session) goto nosession;
+ if (!req->tcon) goto notcon;
+ smb2srv_ioctl_recv(req);
+ return NT_STATUS_OK;
+ case SMB2_OP_CANCEL:
+ smb2srv_cancel_recv(req);
+ return NT_STATUS_OK;
+ case SMB2_OP_KEEPALIVE:
+ smb2srv_keepalive_recv(req);
+ return NT_STATUS_OK;
+ case SMB2_OP_QUERY_DIRECTORY:
+ if (!req->session) goto nosession;
+ if (!req->tcon) goto notcon;
+ smb2srv_find_recv(req);
+ return NT_STATUS_OK;
+ case SMB2_OP_NOTIFY:
+ if (!req->session) goto nosession;
+ if (!req->tcon) goto notcon;
+ smb2srv_notify_recv(req);
+ return NT_STATUS_OK;
+ case SMB2_OP_GETINFO:
+ if (!req->session) goto nosession;
+ if (!req->tcon) goto notcon;
+ smb2srv_getinfo_recv(req);
+ return NT_STATUS_OK;
+ case SMB2_OP_SETINFO:
+ if (!req->session) goto nosession;
+ if (!req->tcon) goto notcon;
+ smb2srv_setinfo_recv(req);
+ return NT_STATUS_OK;
+ case SMB2_OP_BREAK:
+ if (!req->session) goto nosession;
+ if (!req->tcon) goto notcon;
+ smb2srv_break_recv(req);
+ return NT_STATUS_OK;
+ }
+
+ DEBUG(1,("Invalid SMB2 opcode: 0x%04X\n", opcode));
+ smbsrv_terminate_connection(req->smb_conn, "Invalid SMB2 opcode");
+ return NT_STATUS_OK;
+
+nosession:
+ smb2srv_send_error(req, NT_STATUS_USER_SESSION_DELETED);
+ return NT_STATUS_OK;
+notcon:
+ smb2srv_send_error(req, NT_STATUS_NETWORK_NAME_DELETED);
+ return NT_STATUS_OK;
+}
+
+NTSTATUS smbsrv_recv_smb2_request(void *private_data, DATA_BLOB blob)
+{
+ struct smbsrv_connection *smb_conn = talloc_get_type(private_data, struct smbsrv_connection);
+ struct smb2srv_request *req;
+ struct timeval cur_time = timeval_current();
+ uint32_t protocol_version;
+ uint16_t buffer_code;
+ uint32_t dynamic_size;
+ uint32_t flags;
+
+ smb_conn->statistics.last_request_time = cur_time;
+
+ /* see if its a special NBT packet */
+ if (CVAL(blob.data,0) != 0) {
+ DEBUG(2,("Special NBT packet on SMB2 connection"));
+ smbsrv_terminate_connection(smb_conn, "Special NBT packet on SMB2 connection");
+ return NT_STATUS_OK;
+ }
+
+ if (blob.length < (NBT_HDR_SIZE + SMB2_MIN_SIZE_NO_BODY)) {
+ DEBUG(2,("Invalid SMB2 packet length count %ld\n", (long)blob.length));
+ smbsrv_terminate_connection(smb_conn, "Invalid SMB2 packet");
+ return NT_STATUS_OK;
+ }
+
+ protocol_version = IVAL(blob.data, NBT_HDR_SIZE);
+ if (protocol_version != SMB2_MAGIC) {
+ DEBUG(2,("Invalid SMB packet: protocol prefix: 0x%08X\n",
+ protocol_version));
+ smbsrv_terminate_connection(smb_conn, "NON-SMB2 packet");
+ return NT_STATUS_OK;
+ }
+
+ req = smb2srv_init_request(smb_conn);
+ NT_STATUS_HAVE_NO_MEMORY(req);
+
+ req->in.buffer = talloc_steal(req, blob.data);
+ req->in.size = blob.length;
+ req->request_time = cur_time;
+ req->in.allocated = req->in.size;
+
+ req->in.hdr = req->in.buffer+ NBT_HDR_SIZE;
+ req->in.body = req->in.hdr + SMB2_HDR_BODY;
+ req->in.body_size = req->in.size - (SMB2_HDR_BODY+NBT_HDR_SIZE);
+ req->in.dynamic = NULL;
+
+ req->seqnum = BVAL(req->in.hdr, SMB2_HDR_MESSAGE_ID);
+
+ if (req->in.body_size < 2) {
+ /* error handling for this is different for negprot to
+ other packet types */
+ uint16_t opcode = SVAL(req->in.hdr, SMB2_HDR_OPCODE);
+ if (opcode == SMB2_OP_NEGPROT) {
+ smbsrv_terminate_connection(req->smb_conn, "Bad body size in SMB2 negprot");
+ return NT_STATUS_OK;
+ } else {
+ smb2srv_send_error(req, NT_STATUS_INVALID_PARAMETER);
+ return NT_STATUS_OK;
+ }
+ }
+
+ buffer_code = SVAL(req->in.body, 0);
+ req->in.body_fixed = (buffer_code & ~1);
+ dynamic_size = req->in.body_size - req->in.body_fixed;
+
+ if (dynamic_size != 0 && (buffer_code & 1)) {
+ req->in.dynamic = req->in.body + req->in.body_fixed;
+ if (smb2_oob(&req->in, req->in.dynamic, dynamic_size)) {
+ DEBUG(1,("SMB2 request invalid dynamic size 0x%x\n",
+ dynamic_size));
+ smb2srv_send_error(req, NT_STATUS_INVALID_PARAMETER);
+ return NT_STATUS_OK;
+ }
+ }
+
+ smb2srv_setup_bufinfo(req);
+
+ /*
+ * TODO: - make sure the length field is 64
+ * - make sure it's a request
+ */
+
+ flags = IVAL(req->in.hdr, SMB2_HDR_FLAGS);
+ /* the first request should never have the related flag set */
+ if (flags & SMB2_HDR_FLAG_CHAINED) {
+ req->chain_status = NT_STATUS_INVALID_PARAMETER;
+ }
+
+ return smb2srv_reply(req);
+}
+
+static NTSTATUS smb2srv_init_pending(struct smbsrv_connection *smb_conn)
+{
+ smb_conn->requests2.idtree_req = idr_init(smb_conn);
+ NT_STATUS_HAVE_NO_MEMORY(smb_conn->requests2.idtree_req);
+ smb_conn->requests2.idtree_limit = 0x00FFFFFF & (UINT32_MAX - 1);
+ smb_conn->requests2.list = NULL;
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS smb2srv_queue_pending(struct smb2srv_request *req)
+{
+ NTSTATUS status;
+ bool signing_used = false;
+ int id;
+ uint16_t credits = SVAL(req->in.hdr, SMB2_HDR_CREDIT);
+
+ if (credits == 0) {
+ credits = 1;
+ }
+
+ if (req->pending_id) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ if (req->smb_conn->connection->event.fde == NULL) {
+ /* the socket has been destroyed - no point trying to send an error! */
+ return NT_STATUS_REMOTE_DISCONNECT;
+ }
+
+ id = idr_get_new_above(req->smb_conn->requests2.idtree_req, req,
+ 1, req->smb_conn->requests2.idtree_limit);
+ if (id == -1) {
+ return NT_STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ DLIST_ADD_END(req->smb_conn->requests2.list, req);
+ req->pending_id = id;
+
+ talloc_set_destructor(req, smb2srv_request_deny_destructor);
+
+ status = smb2srv_setup_reply(req, 8, true, 0);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ SIVAL(req->out.hdr, SMB2_HDR_STATUS, NT_STATUS_V(NT_STATUS_PENDING));
+ SSVAL(req->out.hdr, SMB2_HDR_CREDIT, credits);
+
+ SSVAL(req->out.body, 0x02, 0);
+ SIVAL(req->out.body, 0x04, 0);
+
+ /* if the real reply will be signed set the signed flags, but don't sign */
+ if (req->is_signed) {
+ SIVAL(req->out.hdr, SMB2_HDR_FLAGS, IVAL(req->out.hdr, SMB2_HDR_FLAGS) | SMB2_HDR_FLAG_SIGNED);
+ signing_used = req->is_signed;
+ req->is_signed = false;
+ }
+
+ smb2srv_send_reply(req);
+
+ req->is_signed = signing_used;
+
+ talloc_set_destructor(req, smb2srv_request_destructor);
+ return NT_STATUS_OK;
+}
+
+void smb2srv_cancel_recv(struct smb2srv_request *req)
+{
+ uint32_t pending_id;
+ uint32_t flags;
+ void *p;
+ struct smb2srv_request *r;
+
+ if (!req->session) goto done;
+
+ flags = IVAL(req->in.hdr, SMB2_HDR_FLAGS);
+ pending_id = IVAL(req->in.hdr, SMB2_HDR_PID);
+
+ if (!(flags & SMB2_HDR_FLAG_ASYNC)) {
+ /* TODO: what to do here? */
+ goto done;
+ }
+
+ p = idr_find(req->smb_conn->requests2.idtree_req, pending_id);
+ if (!p) goto done;
+
+ r = talloc_get_type(p, struct smb2srv_request);
+ if (!r) goto done;
+
+ if (!r->ntvfs) goto done;
+
+ ntvfs_cancel(r->ntvfs);
+
+done:
+ /* we never generate a reply for a SMB2 Cancel */
+ talloc_free(req);
+}
+
+/*
+ * init the SMB2 protocol related stuff
+ */
+NTSTATUS smbsrv_init_smb2_connection(struct smbsrv_connection *smb_conn)
+{
+ NTSTATUS status;
+
+ /* now initialise a few default values associated with this smb socket */
+ smb_conn->negotiate.max_send = 0xFFFF;
+
+ /* this is the size that w2k uses, and it appears to be important for
+ good performance */
+ smb_conn->negotiate.max_recv = lpcfg_max_xmit(smb_conn->lp_ctx);
+
+ smb_conn->negotiate.zone_offset = get_time_zone(time(NULL));
+
+ smb_conn->config.nt_status_support = true;
+
+ status = smbsrv_init_sessions(smb_conn, UINT64_MAX);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ status = smb2srv_init_pending(smb_conn);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ return NT_STATUS_OK;
+
+}
diff --git a/source4/smb_server/smb2/sesssetup.c b/source4/smb_server/smb2/sesssetup.c
new file mode 100644
index 0000000..a8c4560
--- /dev/null
+++ b/source4/smb_server/smb2/sesssetup.c
@@ -0,0 +1,326 @@
+/*
+ Unix SMB2 implementation.
+
+ Copyright (C) Andrew Bartlett 2001-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 <tevent.h>
+#include "auth/gensec/gensec.h"
+#include "auth/auth.h"
+#include "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+#include "smb_server/smb_server.h"
+#include "smb_server/smb2/smb2_server.h"
+#include "samba/service_stream.h"
+#include "lib/stream/packet.h"
+
+static void smb2srv_sesssetup_send(struct smb2srv_request *req, union smb_sesssetup *io)
+{
+ if (NT_STATUS_IS_OK(req->status)) {
+ /* nothing */
+ } else if (NT_STATUS_EQUAL(req->status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ /* nothing */
+ } else {
+ smb2srv_send_error(req, req->status);
+ return;
+ }
+
+ SMB2SRV_CHECK(smb2srv_setup_reply(req, 0x08, true, io->smb2.out.secblob.length));
+
+ SBVAL(req->out.hdr, SMB2_HDR_SESSION_ID, io->smb2.out.uid);
+
+ SSVAL(req->out.body, 0x02, io->smb2.out.session_flags);
+ SMB2SRV_CHECK(smb2_push_o16s16_blob(&req->out, 0x04, io->smb2.out.secblob));
+
+ smb2srv_send_reply(req);
+}
+
+struct smb2srv_sesssetup_callback_ctx {
+ struct smb2srv_request *req;
+ union smb_sesssetup *io;
+ struct smbsrv_session *smb_sess;
+};
+
+static void smb2srv_sesssetup_callback(struct tevent_req *subreq)
+{
+ struct smb2srv_sesssetup_callback_ctx *ctx = tevent_req_callback_data(subreq,
+ struct smb2srv_sesssetup_callback_ctx);
+ struct smb2srv_request *req = ctx->req;
+ union smb_sesssetup *io = ctx->io;
+ struct smbsrv_session *smb_sess = ctx->smb_sess;
+ struct auth_session_info *session_info = NULL;
+ enum security_user_level user_level;
+ NTSTATUS status;
+
+ packet_recv_enable(req->smb_conn->packet);
+
+ status = gensec_update_recv(subreq, req, &io->smb2.out.secblob);
+ TALLOC_FREE(subreq);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ goto done;
+ } else if (!NT_STATUS_IS_OK(status)) {
+ goto failed;
+ }
+
+ status = gensec_session_info(smb_sess->gensec_ctx, smb_sess, &session_info);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto failed;
+ }
+
+ /* Ensure this is marked as a 'real' vuid, not one
+ * simply valid for the session setup leg */
+ status = smbsrv_session_sesssetup_finished(smb_sess, session_info);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto failed;
+ }
+ req->session = smb_sess;
+
+ user_level = security_session_user_level(smb_sess->session_info, NULL);
+ if (user_level >= SECURITY_USER) {
+ if (smb_sess->smb2_signing.required) {
+ /* activate smb2 signing on the session */
+ smb_sess->smb2_signing.active = true;
+ }
+ /* we need to sign the session setup response */
+ req->is_signed = true;
+ }
+
+done:
+ io->smb2.out.uid = smb_sess->vuid;
+failed:
+ req->status = nt_status_squash(status);
+ smb2srv_sesssetup_send(req, io);
+ if (!NT_STATUS_IS_OK(status) && !
+ NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ talloc_free(smb_sess);
+ }
+}
+
+static void smb2srv_sesssetup_backend(struct smb2srv_request *req, union smb_sesssetup *io)
+{
+ NTSTATUS status;
+ struct smb2srv_sesssetup_callback_ctx *callback_ctx;
+ struct smbsrv_session *smb_sess = NULL;
+ uint64_t vuid;
+ struct tevent_req *subreq;
+
+ io->smb2.out.session_flags = 0;
+ io->smb2.out.uid = 0;
+ io->smb2.out.secblob = data_blob(NULL, 0);
+
+ vuid = BVAL(req->in.hdr, SMB2_HDR_SESSION_ID);
+
+ /*
+ * only when we got '0' we should allocate a new session
+ */
+ if (vuid == 0) {
+ struct gensec_security *gensec_ctx;
+ struct tsocket_address *remote_address, *local_address;
+
+ status = samba_server_gensec_start(req,
+ req->smb_conn->connection->event.ctx,
+ req->smb_conn->connection->msg_ctx,
+ req->smb_conn->lp_ctx,
+ req->smb_conn->negotiate.server_credentials,
+ "cifs",
+ &gensec_ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Failed to start GENSEC server code: %s\n", nt_errstr(status)));
+ goto failed;
+ }
+
+ gensec_want_feature(gensec_ctx, GENSEC_FEATURE_SESSION_KEY);
+ gensec_want_feature(gensec_ctx, GENSEC_FEATURE_SMB_TRANSPORT);
+
+ remote_address = socket_get_remote_addr(req->smb_conn->connection->socket,
+ req);
+ if (!remote_address) {
+ status = NT_STATUS_INTERNAL_ERROR;
+ DBG_ERR("Failed to obtain remote address");
+ goto failed;
+ }
+
+ status = gensec_set_remote_address(gensec_ctx,
+ remote_address);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_ERR("Failed to set remote address");
+ goto failed;
+ }
+
+ local_address = socket_get_local_addr(req->smb_conn->connection->socket,
+ req);
+ if (!local_address) {
+ status = NT_STATUS_INTERNAL_ERROR;
+ DBG_ERR("Failed to obtain local address");
+ goto failed;
+ }
+
+ status = gensec_set_local_address(gensec_ctx,
+ local_address);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_ERR("Failed to set local address");
+ goto failed;
+ }
+
+ status = gensec_set_target_service_description(gensec_ctx,
+ "SMB2");
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_ERR("Failed to set service description");
+ goto failed;
+ }
+
+ status = gensec_start_mech_by_oid(gensec_ctx, GENSEC_OID_SPNEGO);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Failed to start GENSEC SPNEGO server code: %s\n", nt_errstr(status)));
+ goto failed;
+ }
+
+ /* allocate a new session */
+ smb_sess = smbsrv_session_new(req->smb_conn, req->smb_conn, gensec_ctx);
+ if (!smb_sess) {
+ status = NT_STATUS_INSUFFICIENT_RESOURCES;
+ goto failed;
+ }
+ status = smbsrv_smb2_init_tcons(smb_sess);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto failed;
+ }
+ } else {
+ /* lookup an existing session */
+ smb_sess = smbsrv_session_find_sesssetup(req->smb_conn, vuid);
+ }
+
+ if (!smb_sess) {
+ status = NT_STATUS_USER_SESSION_DELETED;
+ goto failed;
+ }
+
+ if (smb_sess->session_info) {
+ /* see WSPP test suite - test 11 */
+ status = NT_STATUS_REQUEST_NOT_ACCEPTED;
+ goto failed;
+ }
+
+ if (!smb_sess->gensec_ctx) {
+ status = NT_STATUS_INTERNAL_ERROR;
+ DEBUG(1, ("Internal ERROR: no gensec_ctx on session: %s\n", nt_errstr(status)));
+ goto failed;
+ }
+
+ callback_ctx = talloc(req, struct smb2srv_sesssetup_callback_ctx);
+ if (!callback_ctx) goto nomem;
+ callback_ctx->req = req;
+ callback_ctx->io = io;
+ callback_ctx->smb_sess = smb_sess;
+
+ subreq = gensec_update_send(callback_ctx,
+ req->smb_conn->connection->event.ctx,
+ smb_sess->gensec_ctx,
+ io->smb2.in.secblob);
+ if (!subreq) goto nomem;
+ tevent_req_set_callback(subreq, smb2srv_sesssetup_callback, callback_ctx);
+
+ /* note that we ignore SMB2_NEGOTIATE_SIGNING_ENABLED from the client.
+ This is deliberate as windows does not set it even when it does
+ set SMB2_NEGOTIATE_SIGNING_REQUIRED */
+ if (io->smb2.in.security_mode & SMB2_NEGOTIATE_SIGNING_REQUIRED) {
+ smb_sess->smb2_signing.required = true;
+ }
+
+ /* disable receipt of more packets on this socket until we've
+ finished with the session setup. This avoids a problem with
+ crashes if we get EOF on the socket while processing a session
+ setup */
+ packet_recv_disable(req->smb_conn->packet);
+
+ return;
+nomem:
+ status = NT_STATUS_NO_MEMORY;
+failed:
+ talloc_free(smb_sess);
+ req->status = nt_status_squash(status);
+ smb2srv_sesssetup_send(req, io);
+}
+
+void smb2srv_sesssetup_recv(struct smb2srv_request *req)
+{
+ union smb_sesssetup *io;
+
+ SMB2SRV_CHECK_BODY_SIZE(req, 0x18, true);
+ SMB2SRV_TALLOC_IO_PTR(io, union smb_sesssetup);
+
+ io->smb2.level = RAW_SESSSETUP_SMB2;
+ io->smb2.in.vc_number = CVAL(req->in.body, 0x02);
+ io->smb2.in.security_mode = CVAL(req->in.body, 0x03);
+ io->smb2.in.capabilities = IVAL(req->in.body, 0x04);
+ io->smb2.in.channel = IVAL(req->in.body, 0x08);
+ io->smb2.in.previous_sessionid = BVAL(req->in.body, 0x10);
+ SMB2SRV_CHECK(smb2_pull_o16s16_blob(&req->in, io, req->in.body+0x0C, &io->smb2.in.secblob));
+
+ smb2srv_sesssetup_backend(req, io);
+}
+
+static int smb2srv_cleanup_session_destructor(struct smbsrv_session **session)
+{
+ /* TODO: call ntvfs backends to close file of this session */
+ DEBUG(0,("free session[%p]\n", *session));
+ talloc_free(*session);
+ return 0;
+}
+
+static NTSTATUS smb2srv_logoff_backend(struct smb2srv_request *req)
+{
+ struct smbsrv_session **session_ptr;
+
+ /* we need to destroy the session after sending the reply */
+ session_ptr = talloc(req, struct smbsrv_session *);
+ NT_STATUS_HAVE_NO_MEMORY(session_ptr);
+
+ *session_ptr = req->session;
+ talloc_set_destructor(session_ptr, smb2srv_cleanup_session_destructor);
+
+ return NT_STATUS_OK;
+}
+
+static void smb2srv_logoff_send(struct smb2srv_request *req)
+{
+ if (NT_STATUS_IS_ERR(req->status)) {
+ smb2srv_send_error(req, req->status);
+ return;
+ }
+
+ SMB2SRV_CHECK(smb2srv_setup_reply(req, 0x04, false, 0));
+
+ SSVAL(req->out.body, 0x02, 0);
+
+ smb2srv_send_reply(req);
+}
+
+void smb2srv_logoff_recv(struct smb2srv_request *req)
+{
+ SMB2SRV_CHECK_BODY_SIZE(req, 0x04, false);
+
+ req->status = smb2srv_logoff_backend(req);
+
+ if (req->control_flags & SMB2SRV_REQ_CTRL_FLAG_NOT_REPLY) {
+ talloc_free(req);
+ return;
+ }
+ smb2srv_logoff_send(req);
+}
diff --git a/source4/smb_server/smb2/smb2_server.h b/source4/smb_server/smb2/smb2_server.h
new file mode 100644
index 0000000..5fe12fe
--- /dev/null
+++ b/source4/smb_server/smb2/smb2_server.h
@@ -0,0 +1,192 @@
+/*
+ Unix SMB2 implementation.
+
+ 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/>.
+*/
+
+/* the context for a single SMB2 request. This is passed to any request-context
+ functions */
+struct smb2srv_request {
+ /* the smbsrv_connection needs a list of requests queued for send */
+ struct smb2srv_request *next, *prev;
+
+ /* the server_context contains all context specific to this SMB socket */
+ struct smbsrv_connection *smb_conn;
+
+ /* conn is only set for operations that have a valid TID */
+ struct smbsrv_tcon *tcon;
+
+ /* the session context is derived from the vuid */
+ struct smbsrv_session *session;
+
+#define SMB2SRV_REQ_CTRL_FLAG_NOT_REPLY (1<<0)
+ uint32_t control_flags;
+
+ /* the system time when the request arrived */
+ struct timeval request_time;
+
+ /* a pointer to the per request union smb_* io structure */
+ void *io_ptr;
+
+ /* the ntvfs_request */
+ struct ntvfs_request *ntvfs;
+
+ /* Now the SMB2 specific stuff */
+
+ /* the status the backend returned */
+ NTSTATUS status;
+
+ /* for matching request and reply */
+ uint64_t seqnum;
+
+ /* the id that can be used to cancel the request */
+ uint32_t pending_id;
+
+ /* the offset to the next SMB2 Header for chained requests */
+ uint32_t chain_offset;
+
+ /* the status we return for following chained requests */
+ NTSTATUS chain_status;
+
+ /* chained file handle */
+ uint8_t _chained_file_handle[16];
+ uint8_t *chained_file_handle;
+ uint64_t chained_session_id;
+ uint32_t chained_tree_id;
+
+ bool is_signed;
+
+ struct smb2_request_buffer in;
+ struct smb2_request_buffer out;
+};
+
+struct smbsrv_request;
+
+#include "smb_server/smb2/smb2_proto.h"
+
+/* useful way of catching field size errors with file and line number */
+#define SMB2SRV_CHECK_BODY_SIZE(req, size, dynamic) do { \
+ size_t is_size = req->in.body_size; \
+ uint16_t field_size; \
+ 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)); \
+ smb2srv_send_error(req, NT_STATUS_INVALID_PARAMETER); \
+ return; \
+ }\
+ field_size = SVAL(req->in.body, 0); \
+ 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)); \
+ smb2srv_send_error(req, NT_STATUS_INVALID_PARAMETER); \
+ return; \
+ } \
+} while (0)
+
+#define SMB2SRV_CHECK(cmd) do {\
+ NTSTATUS _status; \
+ _status = cmd; \
+ if (!NT_STATUS_IS_OK(_status)) { \
+ smb2srv_send_error(req, _status); \
+ return; \
+ } \
+} while (0)
+
+/* useful wrapper for talloc with NO_MEMORY reply */
+#define SMB2SRV_TALLOC_IO_PTR(ptr, type) do { \
+ ptr = talloc(req, type); \
+ if (!ptr) { \
+ smb2srv_send_error(req, NT_STATUS_NO_MEMORY); \
+ return; \
+ } \
+ req->io_ptr = ptr; \
+} while (0)
+
+#define SMB2SRV_SETUP_NTVFS_REQUEST(send_fn, state) do { \
+ req->ntvfs = ntvfs_request_create(req->tcon->ntvfs, req, \
+ req->session->session_info,\
+ 0, \
+ req->request_time, \
+ req, send_fn, state); \
+ if (!req->ntvfs) { \
+ smb2srv_send_error(req, NT_STATUS_NO_MEMORY); \
+ return; \
+ } \
+ (void)talloc_steal(req->tcon->ntvfs, req); \
+ req->ntvfs->frontend_data.private_data = req; \
+} while (0)
+
+#define SMB2SRV_CHECK_FILE_HANDLE(handle) do { \
+ if (!handle) { \
+ smb2srv_send_error(req, NT_STATUS_FILE_CLOSED); \
+ return; \
+ } \
+} while (0)
+
+/*
+ check if the backend wants to handle the request asynchronously.
+ if it wants it handled synchronously then call the send function
+ immediately
+*/
+#define SMB2SRV_CALL_NTVFS_BACKEND(cmd) do { \
+ req->ntvfs->async_states->status = cmd; \
+ if (req->ntvfs->async_states->state & NTVFS_ASYNC_STATE_ASYNC) { \
+ NTSTATUS _status; \
+ _status = smb2srv_queue_pending(req); \
+ if (!NT_STATUS_IS_OK(_status)) { \
+ ntvfs_cancel(req->ntvfs); \
+ } \
+ } else { \
+ req->ntvfs->async_states->send_fn(req->ntvfs); \
+ } \
+} while (0)
+
+/* check req->ntvfs->async_states->status and if not OK then send an error reply */
+#define SMB2SRV_CHECK_ASYNC_STATUS_ERR_SIMPLE do { \
+ req = talloc_get_type(ntvfs->async_states->private_data, struct smb2srv_request); \
+ if (ntvfs->async_states->state & NTVFS_ASYNC_STATE_CLOSE || NT_STATUS_EQUAL(ntvfs->async_states->status, NT_STATUS_NET_WRITE_FAULT)) { \
+ smbsrv_terminate_connection(req->smb_conn, get_friendly_nt_error_msg (ntvfs->async_states->status)); \
+ talloc_free(req); \
+ return; \
+ } \
+ req->status = ntvfs->async_states->status; \
+ if (NT_STATUS_IS_ERR(ntvfs->async_states->status)) { \
+ smb2srv_send_error(req, ntvfs->async_states->status); \
+ return; \
+ } \
+} while (0)
+#define SMB2SRV_CHECK_ASYNC_STATUS_ERR(ptr, type) do { \
+ SMB2SRV_CHECK_ASYNC_STATUS_ERR_SIMPLE; \
+ ptr = talloc_get_type(req->io_ptr, type); \
+} while (0)
+#define SMB2SRV_CHECK_ASYNC_STATUS_SIMPLE do { \
+ req = talloc_get_type(ntvfs->async_states->private_data, struct smb2srv_request); \
+ if (ntvfs->async_states->state & NTVFS_ASYNC_STATE_CLOSE || NT_STATUS_EQUAL(ntvfs->async_states->status, NT_STATUS_NET_WRITE_FAULT)) { \
+ smbsrv_terminate_connection(req->smb_conn, get_friendly_nt_error_msg (ntvfs->async_states->status)); \
+ talloc_free(req); \
+ return; \
+ } \
+ req->status = ntvfs->async_states->status; \
+ if (!NT_STATUS_IS_OK(ntvfs->async_states->status)) { \
+ smb2srv_send_error(req, ntvfs->async_states->status); \
+ return; \
+ } \
+} while (0)
+#define SMB2SRV_CHECK_ASYNC_STATUS(ptr, type) do { \
+ SMB2SRV_CHECK_ASYNC_STATUS_SIMPLE; \
+ ptr = talloc_get_type(req->io_ptr, type); \
+} while (0)
diff --git a/source4/smb_server/smb2/tcon.c b/source4/smb_server/smb2/tcon.c
new file mode 100644
index 0000000..0c56420
--- /dev/null
+++ b/source4/smb_server/smb2/tcon.c
@@ -0,0 +1,446 @@
+/*
+ Unix SMB2 implementation.
+
+ 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/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+#include "smb_server/smb_server.h"
+#include "smb_server/smb2/smb2_server.h"
+#include "samba/service_stream.h"
+#include "ntvfs/ntvfs.h"
+
+/*
+ send an oplock break request to a client
+*/
+static NTSTATUS smb2srv_send_oplock_break(void *p, struct ntvfs_handle *h, uint8_t level)
+{
+ struct smbsrv_handle *handle = talloc_get_type(h->frontend_data.private_data,
+ struct smbsrv_handle);
+ struct smb2srv_request *req;
+ NTSTATUS status;
+
+ /* setup a dummy request structure */
+ req = smb2srv_init_request(handle->tcon->smb_conn);
+ NT_STATUS_HAVE_NO_MEMORY(req);
+
+ req->in.buffer = talloc_array(req, uint8_t,
+ NBT_HDR_SIZE + SMB2_MIN_SIZE);
+ NT_STATUS_HAVE_NO_MEMORY(req->in.buffer);
+ req->in.size = NBT_HDR_SIZE + SMB2_MIN_SIZE;
+ req->in.allocated = req->in.size;
+
+ req->in.hdr = req->in.buffer+ NBT_HDR_SIZE;
+ req->in.body = req->in.hdr + SMB2_HDR_BODY;
+ req->in.body_size = req->in.size - (SMB2_HDR_BODY+NBT_HDR_SIZE);
+ req->in.dynamic = NULL;
+
+ req->seqnum = UINT64_MAX;
+
+ smb2srv_setup_bufinfo(req);
+
+ SIVAL(req->in.hdr, 0, SMB2_MAGIC);
+ SSVAL(req->in.hdr, SMB2_HDR_LENGTH, SMB2_HDR_BODY);
+ SSVAL(req->in.hdr, SMB2_HDR_EPOCH, 0);
+ SIVAL(req->in.hdr, SMB2_HDR_STATUS, 0);
+ SSVAL(req->in.hdr, SMB2_HDR_OPCODE, SMB2_OP_BREAK);
+ SSVAL(req->in.hdr, SMB2_HDR_CREDIT, 0);
+ SIVAL(req->in.hdr, SMB2_HDR_FLAGS, 0);
+ SIVAL(req->in.hdr, SMB2_HDR_NEXT_COMMAND, 0);
+ SBVAL(req->in.hdr, SMB2_HDR_MESSAGE_ID, 0);
+ SIVAL(req->in.hdr, SMB2_HDR_PID, 0);
+ SIVAL(req->in.hdr, SMB2_HDR_TID, 0);
+ SBVAL(req->in.hdr, SMB2_HDR_SESSION_ID, 0);
+ memset(req->in.hdr+SMB2_HDR_SIGNATURE, 0, 16);
+
+ SSVAL(req->in.body, 0, 2);
+
+ status = smb2srv_setup_reply(req, 0x18, false, 0);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ SSVAL(req->out.hdr, SMB2_HDR_CREDIT, 0x0000);
+
+ SSVAL(req->out.body, 0x02, 0x0001);
+ SIVAL(req->out.body, 0x04, 0x00000000);
+ smb2srv_push_handle(req->out.body, 0x08, h);
+
+ smb2srv_send_reply(req);
+
+ return NT_STATUS_OK;
+}
+
+struct ntvfs_handle *smb2srv_pull_handle(struct smb2srv_request *req, const uint8_t *base, unsigned int offset)
+{
+ struct smbsrv_tcon *tcon;
+ struct smbsrv_handle *handle;
+ uint32_t hid;
+ uint32_t tid;
+ uint64_t uid;
+
+ /*
+ * if there're chained requests used the cached handle
+ *
+ * TODO: check if this also correct when the given handle
+ * isn't all 0xFF.
+ */
+ if (req->chained_file_handle) {
+ base = req->chained_file_handle;
+ offset = 0;
+ }
+
+ hid = IVAL(base, offset);
+ tid = IVAL(base, offset + 4);
+ uid = BVAL(base, offset + 8);
+
+ /* if it's the wildcard handle, don't waste time to search it... */
+ if (hid == UINT32_MAX && tid == UINT32_MAX && uid == UINT64_MAX) {
+ return NULL;
+ }
+
+ /*
+ * if the (v)uid part doesn't match the given session the handle isn't
+ * valid
+ */
+ if (uid != req->session->vuid) {
+ return NULL;
+ }
+
+ /*
+ * the handle can belong to a different tcon
+ * as that TID in the SMB2 header says, but
+ * the request should succeed nevertheless!
+ *
+ * because of this we put the 32 bit TID into the
+ * 128 bit handle, so that we can extract the tcon from the
+ * handle
+ */
+ tcon = req->tcon;
+ if (tid != req->tcon->tid) {
+ tcon = smbsrv_smb2_tcon_find(req->session, tid, req->request_time);
+ if (!tcon) {
+ return NULL;
+ }
+ }
+
+ handle = smbsrv_smb2_handle_find(tcon, hid, req->request_time);
+ if (!handle) {
+ return NULL;
+ }
+
+ /*
+ * as the smb2srv_tcon is a child object of the smb2srv_session
+ * the handle belongs to the correct session!
+ *
+ * Note: no check is needed here for SMB2
+ */
+
+ /*
+ * as the handle may have overwritten the tcon
+ * we need to set it on the request so that the
+ * correct ntvfs context will be used for the ntvfs_*() request
+ *
+ * TODO: check if that's correct for chained requests as well!
+ */
+ req->tcon = tcon;
+ return handle->ntvfs;
+}
+
+void smb2srv_push_handle(uint8_t *base, unsigned int offset, struct ntvfs_handle *ntvfs)
+{
+ struct smbsrv_handle *handle = talloc_get_type(ntvfs->frontend_data.private_data,
+ struct smbsrv_handle);
+
+ /*
+ * the handle is 128 bit on the wire
+ */
+ SIVAL(base, offset, handle->hid);
+ SIVAL(base, offset + 4, handle->tcon->tid);
+ SBVAL(base, offset + 8, handle->session->vuid);
+}
+
+static NTSTATUS smb2srv_handle_create_new(void *private_data, struct ntvfs_request *ntvfs, struct ntvfs_handle **_h)
+{
+ struct smb2srv_request *req = talloc_get_type(ntvfs->frontend_data.private_data,
+ struct smb2srv_request);
+ struct smbsrv_handle *handle;
+ struct ntvfs_handle *h;
+
+ handle = smbsrv_handle_new(req->session, req->tcon, req, req->request_time);
+ if (!handle) return NT_STATUS_INSUFFICIENT_RESOURCES;
+
+ h = talloc_zero(handle, struct ntvfs_handle);
+ if (!h) goto nomem;
+
+ /*
+ * note: we don't set handle->ntvfs yet,
+ * this will be done by smbsrv_handle_make_valid()
+ * this makes sure the handle is invalid for clients
+ * until the ntvfs subsystem has made it valid
+ */
+ h->ctx = ntvfs->ctx;
+ h->session_info = ntvfs->session_info;
+ h->smbpid = ntvfs->smbpid;
+
+ h->frontend_data.private_data = handle;
+
+ *_h = h;
+ return NT_STATUS_OK;
+nomem:
+ talloc_free(handle);
+ return NT_STATUS_NO_MEMORY;
+}
+
+static NTSTATUS smb2srv_handle_make_valid(void *private_data, struct ntvfs_handle *h)
+{
+ struct smbsrv_tcon *tcon = talloc_get_type(private_data, struct smbsrv_tcon);
+ struct smbsrv_handle *handle = talloc_get_type(h->frontend_data.private_data,
+ struct smbsrv_handle);
+ /* this tells the frontend that the handle is valid */
+ handle->ntvfs = h;
+ /* this moves the smbsrv_request to the smbsrv_tcon memory context */
+ talloc_steal(tcon, handle);
+ return NT_STATUS_OK;
+}
+
+static void smb2srv_handle_destroy(void *private_data, struct ntvfs_handle *h)
+{
+ struct smbsrv_handle *handle = talloc_get_type(h->frontend_data.private_data,
+ struct smbsrv_handle);
+ talloc_free(handle);
+}
+
+static struct ntvfs_handle *smb2srv_handle_search_by_wire_key(void *private_data, struct ntvfs_request *ntvfs, const DATA_BLOB *key)
+{
+ return NULL;
+}
+
+static DATA_BLOB smb2srv_handle_get_wire_key(void *private_data, struct ntvfs_handle *handle, TALLOC_CTX *mem_ctx)
+{
+ return data_blob(NULL, 0);
+}
+
+static NTSTATUS smb2srv_tcon_backend(struct smb2srv_request *req, union smb_tcon *io)
+{
+ struct smbsrv_tcon *tcon;
+ NTSTATUS status;
+ enum ntvfs_type type;
+ const char *service = io->smb2.in.path;
+ struct share_config *scfg;
+ char *sharetype;
+ uint64_t ntvfs_caps = 0;
+
+ if (strncmp(service, "\\\\", 2) == 0) {
+ const char *p = strchr(service+2, '\\');
+ if (p) {
+ service = p + 1;
+ }
+ }
+
+ status = share_get_config(req, req->smb_conn->share_context, service, &scfg);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("smb2srv_tcon_backend: couldn't find service %s\n", service));
+ return NT_STATUS_BAD_NETWORK_NAME;
+ }
+
+ if (!socket_check_access(req->smb_conn->connection->socket,
+ scfg->name,
+ share_string_list_option(req, scfg, SHARE_HOSTS_ALLOW),
+ share_string_list_option(req, scfg, SHARE_HOSTS_DENY))) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ /* work out what sort of connection this is */
+ sharetype = share_string_option(req, scfg, SHARE_TYPE, "DISK");
+ if (sharetype && strcmp(sharetype, "IPC") == 0) {
+ type = NTVFS_IPC;
+ } else if (sharetype && strcmp(sharetype, "PRINTER") == 0) {
+ type = NTVFS_PRINT;
+ } else {
+ type = NTVFS_DISK;
+ }
+ TALLOC_FREE(sharetype);
+
+ tcon = smbsrv_smb2_tcon_new(req->session, scfg->name);
+ if (!tcon) {
+ DEBUG(0,("smb2srv_tcon_backend: Couldn't find free connection.\n"));
+ return NT_STATUS_INSUFFICIENT_RESOURCES;
+ }
+ req->tcon = tcon;
+
+ ntvfs_caps = NTVFS_CLIENT_CAP_LEVEL_II_OPLOCKS;
+
+ /* init ntvfs function pointers */
+ status = ntvfs_init_connection(tcon, scfg, type,
+ req->smb_conn->negotiate.protocol,
+ ntvfs_caps,
+ req->smb_conn->connection->event.ctx,
+ req->smb_conn->connection->msg_ctx,
+ req->smb_conn->lp_ctx,
+ req->smb_conn->connection->server_id,
+ &tcon->ntvfs);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("smb2srv_tcon_backend: ntvfs_init_connection failed for service %s\n",
+ scfg->name));
+ goto failed;
+ }
+
+ status = ntvfs_set_oplock_handler(tcon->ntvfs, smb2srv_send_oplock_break, tcon);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("smb2srv_tcon_backend: NTVFS failed to set the oplock handler!\n"));
+ goto failed;
+ }
+
+ status = ntvfs_set_addresses(tcon->ntvfs,
+ req->smb_conn->connection->local_address,
+ req->smb_conn->connection->remote_address);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("smb2srv_tcon_backend: NTVFS failed to set the address!\n"));
+ goto failed;
+ }
+
+ status = ntvfs_set_handle_callbacks(tcon->ntvfs,
+ smb2srv_handle_create_new,
+ smb2srv_handle_make_valid,
+ smb2srv_handle_destroy,
+ smb2srv_handle_search_by_wire_key,
+ smb2srv_handle_get_wire_key,
+ tcon);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("smb2srv_tcon_backend: NTVFS failed to set the handle callbacks!\n"));
+ goto failed;
+ }
+
+ req->ntvfs = ntvfs_request_create(req->tcon->ntvfs, req,
+ req->session->session_info,
+ SVAL(req->in.hdr, SMB2_HDR_PID),
+ req->request_time,
+ req, NULL, 0);
+ if (!req->ntvfs) {
+ status = NT_STATUS_NO_MEMORY;
+ goto failed;
+ }
+
+ io->smb2.out.share_type = (unsigned)type; /* 1 - DISK, 2 - Print, 3 - IPC */
+ io->smb2.out.reserved = 0;
+ io->smb2.out.flags = 0x00000000;
+ io->smb2.out.capabilities = 0;
+ io->smb2.out.access_mask = SEC_RIGHTS_FILE_ALL;
+
+ io->smb2.out.tid = tcon->tid;
+
+ /* Invoke NTVFS connection hook */
+ status = ntvfs_connect(req->ntvfs, io);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("smb2srv_tcon_backend: NTVFS ntvfs_connect() failed: %s!\n", nt_errstr(status)));
+ goto failed;
+ }
+
+ return NT_STATUS_OK;
+
+failed:
+ req->tcon = NULL;
+ talloc_free(tcon);
+ return status;
+}
+
+static void smb2srv_tcon_send(struct smb2srv_request *req, union smb_tcon *io)
+{
+ if (!NT_STATUS_IS_OK(req->status)) {
+ smb2srv_send_error(req, req->status);
+ return;
+ }
+
+ SMB2SRV_CHECK(smb2srv_setup_reply(req, 0x10, false, 0));
+
+ SIVAL(req->out.hdr, SMB2_HDR_TID, io->smb2.out.tid);
+
+ SCVAL(req->out.body, 0x02, io->smb2.out.share_type);
+ SCVAL(req->out.body, 0x03, io->smb2.out.reserved);
+ SIVAL(req->out.body, 0x04, io->smb2.out.flags);
+ SIVAL(req->out.body, 0x08, io->smb2.out.capabilities);
+ SIVAL(req->out.body, 0x0C, io->smb2.out.access_mask);
+
+ smb2srv_send_reply(req);
+}
+
+void smb2srv_tcon_recv(struct smb2srv_request *req)
+{
+ union smb_tcon *io;
+
+ SMB2SRV_CHECK_BODY_SIZE(req, 0x08, true);
+ SMB2SRV_TALLOC_IO_PTR(io, union smb_tcon);
+
+ io->smb2.level = RAW_TCON_SMB2;
+ io->smb2.in.reserved = SVAL(req->in.body, 0x02);
+ SMB2SRV_CHECK(smb2_pull_o16s16_string(&req->in, io, req->in.body+0x04, &io->smb2.in.path));
+
+ /* the VFS backend does not yet handle NULL paths */
+ if (io->smb2.in.path == NULL) {
+ io->smb2.in.path = "";
+ }
+
+ req->status = smb2srv_tcon_backend(req, io);
+
+ if (req->control_flags & SMB2SRV_REQ_CTRL_FLAG_NOT_REPLY) {
+ talloc_free(req);
+ return;
+ }
+ smb2srv_tcon_send(req, io);
+}
+
+static NTSTATUS smb2srv_tdis_backend(struct smb2srv_request *req)
+{
+ /* TODO: call ntvfs backends to close file of this tcon */
+ talloc_free(req->tcon);
+ req->tcon = NULL;
+ return NT_STATUS_OK;
+}
+
+static void smb2srv_tdis_send(struct smb2srv_request *req)
+{
+ NTSTATUS status;
+
+ if (NT_STATUS_IS_ERR(req->status)) {
+ smb2srv_send_error(req, req->status);
+ return;
+ }
+
+ status = smb2srv_setup_reply(req, 0x04, false, 0);
+ if (!NT_STATUS_IS_OK(status)) {
+ smbsrv_terminate_connection(req->smb_conn, nt_errstr(status));
+ talloc_free(req);
+ return;
+ }
+
+ SSVAL(req->out.body, 0x02, 0);
+
+ smb2srv_send_reply(req);
+}
+
+void smb2srv_tdis_recv(struct smb2srv_request *req)
+{
+ SMB2SRV_CHECK_BODY_SIZE(req, 0x04, false);
+
+ req->status = smb2srv_tdis_backend(req);
+
+ if (req->control_flags & SMB2SRV_REQ_CTRL_FLAG_NOT_REPLY) {
+ talloc_free(req);
+ return;
+ }
+ smb2srv_tdis_send(req);
+}
diff --git a/source4/smb_server/smb2/wscript_build b/source4/smb_server/smb2/wscript_build
new file mode 100644
index 0000000..7866ee9
--- /dev/null
+++ b/source4/smb_server/smb2/wscript_build
@@ -0,0 +1,9 @@
+#!/usr/bin/env python
+
+bld.SAMBA_SUBSYSTEM('SMB2_PROTOCOL',
+ source='receive.c negprot.c sesssetup.c tcon.c fileio.c fileinfo.c find.c keepalive.c',
+ autoproto='smb2_proto.h',
+ public_deps='ntvfs LIBPACKET LIBCLI_SMB2 samba_server_gensec NDR_DFSBLOBS',
+ enabled=bld.CONFIG_SET('WITH_NTVFS_FILESERVER')
+ )
+