wmi-1.3.16 from opsview.com
This commit is contained in:
@@ -0,0 +1,18 @@
|
||||
#######################
|
||||
# Start SUBSYSTEM SMB2_PROTOCOL
|
||||
[SUBSYSTEM::SMB2_PROTOCOL]
|
||||
PRIVATE_PROTO_HEADER = smb2_proto.h
|
||||
OBJ_FILES = \
|
||||
receive.o \
|
||||
negprot.o \
|
||||
sesssetup.o \
|
||||
tcon.o \
|
||||
fileio.o \
|
||||
fileinfo.o \
|
||||
find.o \
|
||||
keepalive.o
|
||||
PUBLIC_DEPENDENCIES = \
|
||||
ntvfs LIBPACKET LIBCLI_SMB2
|
||||
LDFLAGS = $(SUBSYSTEM_SMB_SERVER_OUTPUT)
|
||||
# End SUBSYSTEM SMB2_PROTOCOL
|
||||
#######################
|
||||
@@ -0,0 +1,368 @@
|
||||
/*
|
||||
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 2 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, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "libcli/smb2/smb2.h"
|
||||
#include "libcli/smb2/smb2_calls.h"
|
||||
#include "smb_server/smb_server.h"
|
||||
#include "smb_server/service_smb_proto.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));
|
||||
}
|
||||
|
||||
SMB2SRV_CHECK(smb2srv_setup_reply(req, 0x08, True, op->info->out.blob.length));
|
||||
|
||||
/* TODO: this is maybe a o16s32_blob */
|
||||
SMB2SRV_CHECK(smb2_push_o16s16_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;
|
||||
|
||||
io = talloc(op, union smb_fileinfo);
|
||||
NT_STATUS_HAVE_NO_MEMORY(io);
|
||||
|
||||
switch (op->info->in.level) {
|
||||
case RAW_FILEINFO_SMB2_ALL_EAS:
|
||||
io->all_eas.level = op->info->in.level;
|
||||
io->all_eas.in.file.ntvfs = op->info->in.file.ntvfs;
|
||||
io->all_eas.in.continue_flags = op->info->in.flags2;
|
||||
break;
|
||||
|
||||
case RAW_FILEINFO_SMB2_ALL_INFORMATION:
|
||||
io->all_info2.level = op->info->in.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);
|
||||
NTSTATUS status;
|
||||
|
||||
status = ndr_push_struct_blob(&op->info->out.blob, op->req,
|
||||
io->query_secdesc.out.sd,
|
||||
(ndr_push_flags_fn_t)ndr_push_security_descriptor);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
|
||||
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.flags;
|
||||
|
||||
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)
|
||||
{
|
||||
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_GETINFO_FILE:
|
||||
return smb2srv_getinfo_file(op, smb2_level);
|
||||
|
||||
case SMB2_GETINFO_FS:
|
||||
return smb2srv_getinfo_fs(op, smb2_level);
|
||||
|
||||
case SMB2_GETINFO_SECURITY:
|
||||
return smb2srv_getinfo_security(op, smb2_level);
|
||||
|
||||
case 0x04:
|
||||
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.level = SVAL(req->in.body, 0x02);
|
||||
info->in.max_response_size = IVAL(req->in.body, 0x04);
|
||||
info->in.unknown1 = IVAL(req->in.body, 0x08);
|
||||
info->in.unknown2 = IVAL(req->in.body, 0x0C);
|
||||
info->in.flags = IVAL(req->in.body, 0x10);
|
||||
info->in.flags2 = IVAL(req->in.body, 0x14);
|
||||
info->in.file.ntvfs = smb2srv_pull_handle(req, req->in.body, 0x18);
|
||||
|
||||
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(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;
|
||||
|
||||
status = smbsrv_pull_passthru_sfileinfo(io, io->generic.level, io,
|
||||
&op->info->in.blob,
|
||||
STR_UNICODE, NULL);
|
||||
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;
|
||||
NTSTATUS status;
|
||||
|
||||
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);
|
||||
|
||||
status = ndr_pull_struct_blob(&op->info->in.blob, io,
|
||||
io->set_secdesc.in.sd,
|
||||
(ndr_pull_flags_fn_t)ndr_pull_security_descriptor);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
|
||||
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_GETINFO_FILE:
|
||||
return smb2srv_setinfo_file(op, smb2_level);
|
||||
|
||||
case SMB2_GETINFO_FS:
|
||||
return smb2srv_setinfo_fs(op, smb2_level);
|
||||
|
||||
case SMB2_GETINFO_SECURITY:
|
||||
return smb2srv_setinfo_security(op, smb2_level);
|
||||
|
||||
case 0x04:
|
||||
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_s32o32_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));
|
||||
}
|
||||
@@ -0,0 +1,371 @@
|
||||
/*
|
||||
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 2 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, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "libcli/smb2/smb2.h"
|
||||
#include "libcli/smb2/smb2_calls.h"
|
||||
#include "smb_server/smb_server.h"
|
||||
#include "smb_server/service_smb_proto.h"
|
||||
#include "smb_server/smb2/smb2_server.h"
|
||||
#include "ntvfs/ntvfs.h"
|
||||
|
||||
static void smb2srv_create_send(struct ntvfs_request *ntvfs)
|
||||
{
|
||||
struct smb2srv_request *req;
|
||||
union smb_open *io;
|
||||
|
||||
SMB2SRV_CHECK_ASYNC_STATUS(io, union smb_open);
|
||||
SMB2SRV_CHECK(smb2srv_setup_reply(req, 0x58, True, io->smb2.out.blob.length));
|
||||
|
||||
SSVAL(req->out.body, 0x02, io->smb2.out.oplock_flags);
|
||||
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._pad);
|
||||
smb2srv_push_handle(req->out.body, 0x40,io->smb2.out.file.ntvfs);
|
||||
SMB2SRV_CHECK(smb2_push_o32s32_blob(&req->out, 0x50, io->smb2.out.blob));
|
||||
|
||||
smb2srv_send_reply(req);
|
||||
}
|
||||
|
||||
void smb2srv_create_recv(struct smb2srv_request *req)
|
||||
{
|
||||
union smb_open *io;
|
||||
DATA_BLOB blob;
|
||||
|
||||
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);
|
||||
|
||||
io->smb2.level = RAW_OPEN_SMB2;
|
||||
io->smb2.in.oplock_flags = SVAL(req->in.body, 0x02);
|
||||
io->smb2.in.impersonation = IVAL(req->in.body, 0x04);
|
||||
io->smb2.in.unknown3[0] = IVAL(req->in.body, 0x08);
|
||||
io->smb2.in.unknown3[1] = IVAL(req->in.body, 0x0C);
|
||||
io->smb2.in.unknown3[2] = IVAL(req->in.body, 0x10);
|
||||
io->smb2.in.unknown3[3] = IVAL(req->in.body, 0x14);
|
||||
io->smb2.in.access_mask = IVAL(req->in.body, 0x18);
|
||||
io->smb2.in.file_attr = IVAL(req->in.body, 0x1C);
|
||||
io->smb2.in.share_access = IVAL(req->in.body, 0x20);
|
||||
io->smb2.in.open_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));
|
||||
/* TODO: parse the blob */
|
||||
ZERO_STRUCT(io->smb2.in.eas);
|
||||
|
||||
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);
|
||||
|
||||
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, 0);
|
||||
|
||||
smb2srv_send_reply(req);
|
||||
}
|
||||
|
||||
void smb2srv_flush_recv(struct smb2srv_request *req)
|
||||
{
|
||||
union smb_flush *io;
|
||||
uint16_t _pad;
|
||||
|
||||
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;
|
||||
_pad = SVAL(req->in.body, 0x02);
|
||||
io->smb2.in.unknown = 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));
|
||||
|
||||
SMB2SRV_CHECK(smb2_push_o16s32_blob(&req->out, 0x02, io->smb2.out.data));
|
||||
SBVAL(req->out.body, 0x08, io->smb2.out.unknown1);
|
||||
|
||||
smb2srv_send_reply(req);
|
||||
}
|
||||
|
||||
void smb2srv_read_recv(struct smb2srv_request *req)
|
||||
{
|
||||
union smb_read *io;
|
||||
|
||||
SMB2SRV_CHECK_BODY_SIZE(req, 0x30, True);
|
||||
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.unknown1 = BVAL(req->in.body, 0x20);
|
||||
io->smb2.in.unknown2 = BVAL(req->in.body, 0x28);
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
void smb2srv_lock_recv(struct smb2srv_request *req)
|
||||
{
|
||||
smb2srv_send_error(req, NT_STATUS_NOT_IMPLEMENTED);
|
||||
}
|
||||
|
||||
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._pad);
|
||||
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.unknown2);
|
||||
SIVAL(req->out.body, 0x2C, io->smb2.out.unknown3);
|
||||
|
||||
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._pad = 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.unknown2 = 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_response_size = IVAL(req->in.body, 0x2C);
|
||||
io->smb2.in.flags = BVAL(req->in.body, 0x30);
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
void smb2srv_break_recv(struct smb2srv_request *req)
|
||||
{
|
||||
smb2srv_send_error(req, NT_STATUS_NOT_IMPLEMENTED);
|
||||
}
|
||||
@@ -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 2 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, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
/*
|
||||
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/service_smb_proto.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, union smb_search_data *file)
|
||||
{
|
||||
struct smb2srv_find_state *state = talloc_get_type(private, 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_NEW) {
|
||||
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);
|
||||
}
|
||||
|
||||
/* should not be reached */
|
||||
return NT_STATUS_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
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.unknown = 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);
|
||||
|
||||
SMB2SRV_CHECK_FILE_HANDLE(info->in.file.ntvfs);
|
||||
SMB2SRV_CALL_NTVFS_BACKEND(smb2srv_find_backend(state));
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
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 2 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, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "libcli/smb2/smb2.h"
|
||||
#include "smb_server/smb_server.h"
|
||||
#include "smb_server/service_smb_proto.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)
|
||||
{
|
||||
uint16_t _pad;
|
||||
|
||||
if (req->in.body_size < 0x04) {
|
||||
smb2srv_send_error(req, NT_STATUS_FOOBAR);
|
||||
return;
|
||||
}
|
||||
|
||||
_pad = SVAL(req->in.body, 0x02);
|
||||
|
||||
req->status = smb2srv_keepalive_backend(req);
|
||||
|
||||
if (req->control_flags & SMB2SRV_REQ_CTRL_FLAG_NOT_REPLY) {
|
||||
talloc_free(req);
|
||||
return;
|
||||
}
|
||||
smb2srv_keepalive_send(req);
|
||||
}
|
||||
@@ -0,0 +1,234 @@
|
||||
/*
|
||||
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 2 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, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "auth/credentials/credentials.h"
|
||||
#include "auth/gensec/gensec.h"
|
||||
#include "libcli/smb2/smb2.h"
|
||||
#include "libcli/smb2/smb2_calls.h"
|
||||
#include "smb_server/smb_server.h"
|
||||
#include "smb_server/service_smb_proto.h"
|
||||
#include "smb_server/smb2/smb2_server.h"
|
||||
#include "smbd/service_stream.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;
|
||||
|
||||
nt_status = gensec_server_start(req,
|
||||
req->smb_conn->connection->event.ctx,
|
||||
req->smb_conn->connection->msg_ctx,
|
||||
&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;
|
||||
}
|
||||
|
||||
server_credentials = cli_credentials_init(req);
|
||||
if (!server_credentials) {
|
||||
smbsrv_terminate_connection(req->smb_conn, "Failed to init server credentials\n");
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
cli_credentials_set_conf(server_credentials);
|
||||
nt_status = cli_credentials_set_machine_account(server_credentials);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
DEBUG(10, ("Failed to obtain server credentials, perhaps a standalone server?: %s\n", nt_errstr(nt_status)));
|
||||
talloc_free(server_credentials);
|
||||
server_credentials = NULL;
|
||||
}
|
||||
|
||||
req->smb_conn->negotiate.server_credentials = talloc_steal(req->smb_conn, server_credentials);
|
||||
|
||||
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;
|
||||
|
||||
req->smb_conn->negotiate.protocol = PROTOCOL_SMB2;
|
||||
|
||||
current_time = timeval_current(); /* TODO: handle timezone?! */
|
||||
boot_time = timeval_current(); /* TODO: fix me */
|
||||
|
||||
io->out._pad = 0;
|
||||
io->out.unknown2 = 0x06;
|
||||
ZERO_STRUCT(io->out.sessid);
|
||||
io->out.unknown3 = 0x0d;
|
||||
io->out.unknown4 = 0x00;
|
||||
io->out.unknown5 = 0x01;
|
||||
io->out.unknown6 = 0x01;
|
||||
io->out.unknown7 = 0x01;
|
||||
io->out.current_time = timeval_to_nttime(¤t_time);
|
||||
io->out.boot_time = timeval_to_nttime(&boot_time);
|
||||
status = smb2srv_negprot_secblob(req, &io->out.secblob);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
io->out.unknown9 = 0x204d4c20;
|
||||
|
||||
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._pad);
|
||||
SIVAL(req->out.body, 0x04, io->out.unknown2);
|
||||
memcpy(req->out.body+0x08, io->out.sessid, 16);
|
||||
SIVAL(req->out.body, 0x18, io->out.unknown3);
|
||||
SSVAL(req->out.body, 0x1C, io->out.unknown4);
|
||||
SIVAL(req->out.body, 0x1E, io->out.unknown5);
|
||||
SIVAL(req->out.body, 0x22, io->out.unknown6);
|
||||
SSVAL(req->out.body, 0x26, io->out.unknown7);
|
||||
push_nttime(req->out.body, 0x28, io->out.current_time);
|
||||
push_nttime(req->out.body, 0x30, io->out.boot_time);
|
||||
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;
|
||||
}
|
||||
|
||||
SIVAL(req->out.body, 0x3C, io->out.unknown9);
|
||||
|
||||
smb2srv_send_reply(req);
|
||||
}
|
||||
|
||||
void smb2srv_negprot_recv(struct smb2srv_request *req)
|
||||
{
|
||||
struct smb2_negprot *io;
|
||||
|
||||
if (req->in.body_size < 0x26) {
|
||||
smb2srv_send_error(req, NT_STATUS_FOOBAR);
|
||||
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.unknown1 = SVAL(req->in.body, 0x02);
|
||||
memcpy(io->in.unknown2, req->in.body + 0x04, 0x20);
|
||||
io->in.unknown3 = SVAL(req->in.body, 0x24);
|
||||
|
||||
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.001"
|
||||
*/
|
||||
void smb2srv_reply_smb_negprot(struct smbsrv_request *smb_req)
|
||||
{
|
||||
struct smb2srv_request *req;
|
||||
uint32_t body_fixed_size = 0x26;
|
||||
|
||||
/* create a fake SMB2 negprot request */
|
||||
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_size(req, 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;
|
||||
|
||||
SIVAL(req->in.hdr, 0, SMB2_MAGIC);
|
||||
SSVAL(req->in.hdr, SMB2_HDR_LENGTH, SMB2_HDR_BODY);
|
||||
SSVAL(req->in.hdr, SMB2_HDR_PAD1, 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_UNKNOWN1,0);
|
||||
SIVAL(req->in.hdr, SMB2_HDR_FLAGS, 0);
|
||||
SIVAL(req->in.hdr, SMB2_HDR_UNKNOWN2,0);
|
||||
SBVAL(req->in.hdr, SMB2_HDR_SEQNUM, 0);
|
||||
SIVAL(req->in.hdr, SMB2_HDR_PID, 0);
|
||||
SIVAL(req->in.hdr, SMB2_HDR_TID, 0);
|
||||
SBVAL(req->in.hdr, SMB2_HDR_UID, 0);
|
||||
memset(req->in.hdr+SMB2_HDR_SIG, 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, 0);
|
||||
|
||||
smb2srv_negprot_recv(req);
|
||||
return;
|
||||
nomem:
|
||||
smbsrv_terminate_connection(smb_req->smb_conn, nt_errstr(NT_STATUS_NO_MEMORY));
|
||||
talloc_free(req);
|
||||
return;
|
||||
}
|
||||
@@ -0,0 +1,450 @@
|
||||
/*
|
||||
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 2 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, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#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/service_smb_proto.h"
|
||||
#include "smb_server/smb2/smb2_server.h"
|
||||
#include "smbd/service_stream.h"
|
||||
#include "lib/stream/packet.h"
|
||||
#include "ntvfs/ntvfs.h"
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
static 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;
|
||||
|
||||
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 = 0x00000001;
|
||||
uint32_t pid = IVAL(req->in.hdr, SMB2_HDR_PID);
|
||||
uint32_t tid = IVAL(req->in.hdr, SMB2_HDR_TID);
|
||||
|
||||
if (req->pending_id) {
|
||||
flags |= 0x00000002;
|
||||
pid = req->pending_id;
|
||||
tid = 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_size(req, 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_PAD1, 0);
|
||||
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_UNKNOWN1,0x0001);
|
||||
SIVAL(req->out.hdr, SMB2_HDR_FLAGS, flags);
|
||||
SIVAL(req->out.hdr, SMB2_HDR_UNKNOWN2,0);
|
||||
SBVAL(req->out.hdr, SMB2_HDR_SEQNUM, req->seqnum);
|
||||
SIVAL(req->out.hdr, SMB2_HDR_PID, pid);
|
||||
SIVAL(req->out.hdr, SMB2_HDR_TID, tid);
|
||||
SBVAL(req->out.hdr, SMB2_HDR_UID, BVAL(req->in.hdr, SMB2_HDR_UID));
|
||||
memset(req->out.hdr+SMB2_HDR_SIG, 0, 16);
|
||||
|
||||
/* set the length of the fixed body part and +1 if there's a dynamic part also */
|
||||
SSVAL(req->out.body, 0, body_fixed_size + (body_dynamic_size?1:0));
|
||||
|
||||
/*
|
||||
* if we have a dynamic part, make sure the first byte
|
||||
* which is always be part of the packet is initialized
|
||||
*/
|
||||
if (body_dynamic_size) {
|
||||
req->out.size += 1;
|
||||
SCVAL(req->out.dynamic, 0, 0);
|
||||
}
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
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) {
|
||||
_smb2_setlen(req->out.buffer, req->out.size - NBT_HDR_SIZE);
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
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);
|
||||
|
||||
smb2srv_send_reply(req);
|
||||
}
|
||||
|
||||
static NTSTATUS smb2srv_reply(struct smb2srv_request *req)
|
||||
{
|
||||
uint16_t opcode;
|
||||
uint32_t tid;
|
||||
uint64_t uid;
|
||||
|
||||
opcode = SVAL(req->in.hdr, SMB2_HDR_OPCODE);
|
||||
req->seqnum = BVAL(req->in.hdr, SMB2_HDR_SEQNUM);
|
||||
tid = IVAL(req->in.hdr, SMB2_HDR_TID);
|
||||
uid = BVAL(req->in.hdr, SMB2_HDR_UID);
|
||||
|
||||
req->session = smbsrv_session_find(req->smb_conn, uid, req->request_time);
|
||||
req->tcon = smbsrv_smb2_tcon_find(req->session, tid, req->request_time);
|
||||
|
||||
errno = 0;
|
||||
|
||||
/* TODO: check the seqnum */
|
||||
|
||||
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_FIND:
|
||||
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_BLOB blob)
|
||||
{
|
||||
struct smbsrv_connection *smb_conn = talloc_get_type(private, 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;
|
||||
|
||||
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)) {
|
||||
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;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO: - make sure the length field is 64
|
||||
* - make sure it's a request
|
||||
*/
|
||||
|
||||
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)
|
||||
{
|
||||
int id;
|
||||
|
||||
if (req->pending_id) {
|
||||
return NT_STATUS_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
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, struct smb2srv_request *);
|
||||
req->pending_id = id;
|
||||
|
||||
talloc_set_destructor(req, smb2srv_request_deny_destructor);
|
||||
smb2srv_send_error(req, STATUS_PENDING);
|
||||
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 & 0x00000002)) {
|
||||
/* 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 = lp_max_xmit();
|
||||
|
||||
smb_conn->negotiate.zone_offset = get_time_zone(time(NULL));
|
||||
|
||||
smb_conn->config.security = SEC_USER;
|
||||
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;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,237 @@
|
||||
/*
|
||||
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 2 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, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "auth/credentials/credentials.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/service_smb_proto.h"
|
||||
#include "smb_server/smb2/smb2_server.h"
|
||||
#include "smbd/service_stream.h"
|
||||
|
||||
static void smb2srv_sesssetup_send(struct smb2srv_request *req, union smb_sesssetup *io)
|
||||
{
|
||||
uint16_t unknown1;
|
||||
|
||||
if (NT_STATUS_IS_OK(req->status)) {
|
||||
unknown1 = 0x0003;
|
||||
} else if (NT_STATUS_EQUAL(req->status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
|
||||
unknown1 = 0x0002;
|
||||
} else {
|
||||
smb2srv_send_error(req, req->status);
|
||||
return;
|
||||
}
|
||||
|
||||
SMB2SRV_CHECK(smb2srv_setup_reply(req, 0x08, True, io->smb2.out.secblob.length));
|
||||
|
||||
SSVAL(req->out.hdr, SMB2_HDR_UNKNOWN1, unknown1);
|
||||
SBVAL(req->out.hdr, SMB2_HDR_UID, io->smb2.out.uid);
|
||||
|
||||
SSVAL(req->out.body, 0x02, io->smb2.out._pad);
|
||||
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 gensec_update_request *greq, void *private_data)
|
||||
{
|
||||
struct smb2srv_sesssetup_callback_ctx *ctx = talloc_get_type(private_data,
|
||||
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;
|
||||
NTSTATUS status;
|
||||
|
||||
status = gensec_update_recv(greq, req, &io->smb2.out.secblob);
|
||||
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, &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;
|
||||
|
||||
done:
|
||||
io->smb2.out.uid = smb_sess->vuid;
|
||||
failed:
|
||||
req->status = auth_nt_status_squash(status);
|
||||
smb2srv_sesssetup_send(req, io);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
io->smb2.out._pad = 0;
|
||||
io->smb2.out.uid = 0;
|
||||
io->smb2.out.secblob = data_blob(NULL, 0);
|
||||
|
||||
vuid = BVAL(req->in.hdr, SMB2_HDR_UID);
|
||||
|
||||
/*
|
||||
* only when we got '0' we should allocate a new session
|
||||
*/
|
||||
if (vuid == 0) {
|
||||
struct gensec_security *gensec_ctx;
|
||||
|
||||
status = gensec_server_start(req,
|
||||
req->smb_conn->connection->event.ctx,
|
||||
req->smb_conn->connection->msg_ctx,
|
||||
&gensec_ctx);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DEBUG(1, ("Failed to start GENSEC server code: %s\n", nt_errstr(status)));
|
||||
goto failed;
|
||||
}
|
||||
|
||||
gensec_set_credentials(gensec_ctx, req->smb_conn->negotiate.server_credentials);
|
||||
|
||||
gensec_set_target_service(gensec_ctx, "cifs");
|
||||
|
||||
gensec_want_feature(gensec_ctx, GENSEC_FEATURE_SESSION_KEY);
|
||||
|
||||
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, 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->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;
|
||||
|
||||
gensec_update_send(smb_sess->gensec_ctx, io->smb2.in.secblob,
|
||||
smb2srv_sesssetup_callback, callback_ctx);
|
||||
return;
|
||||
nomem:
|
||||
status = NT_STATUS_NO_MEMORY;
|
||||
failed:
|
||||
talloc_free(smb_sess);
|
||||
req->status = auth_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._pad = SVAL(req->in.body, 0x02);
|
||||
io->smb2.in.unknown2 = IVAL(req->in.body, 0x04);
|
||||
io->smb2.in.unknown3 = IVAL(req->in.body, 0x08);
|
||||
SMB2SRV_CHECK(smb2_pull_o16s16_blob(&req->in, io, req->in.body+0x0C, &io->smb2.in.secblob));
|
||||
io->smb2.in.unknown4 = BVAL(req->in.body, 0x10);
|
||||
|
||||
smb2srv_sesssetup_backend(req, io);
|
||||
}
|
||||
|
||||
static NTSTATUS smb2srv_logoff_backend(struct smb2srv_request *req)
|
||||
{
|
||||
/* TODO: call ntvfs backends to close file of this session */
|
||||
talloc_free(req->session);
|
||||
req->session = NULL;
|
||||
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)
|
||||
{
|
||||
uint16_t _pad;
|
||||
|
||||
SMB2SRV_CHECK_BODY_SIZE(req, 0x04, False);
|
||||
|
||||
_pad = SVAL(req->in.body, 0x02);
|
||||
|
||||
req->status = smb2srv_logoff_backend(req);
|
||||
|
||||
if (req->control_flags & SMB2SRV_REQ_CTRL_FLAG_NOT_REPLY) {
|
||||
talloc_free(req);
|
||||
return;
|
||||
}
|
||||
smb2srv_logoff_send(req);
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
/*
|
||||
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 2 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, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
/* 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;
|
||||
|
||||
struct smb2_request_buffer in;
|
||||
struct smb2_request_buffer out;
|
||||
};
|
||||
|
||||
struct smbsrv_request;
|
||||
|
||||
#include "smb_server/smb2/smb2_proto.h"
|
||||
|
||||
/* useful way of catching wct 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 = SVAL(req->in.body, 0); \
|
||||
uint16_t want_size = ((dynamic)?(size)+1:(size)); \
|
||||
if (is_size < (size)) { \
|
||||
DEBUG(0,("%s: buffer too small 0x%x. Expected 0x%x\n", \
|
||||
__location__, (unsigned)is_size, (unsigned)want_size)); \
|
||||
smb2srv_send_error(req, NT_STATUS_FOOBAR); \
|
||||
return; \
|
||||
}\
|
||||
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_FOOBAR); \
|
||||
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); \
|
||||
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); \
|
||||
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)
|
||||
@@ -0,0 +1,388 @@
|
||||
/*
|
||||
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 2 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, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "libcli/smb2/smb2.h"
|
||||
#include "libcli/smb2/smb2_calls.h"
|
||||
#include "smb_server/smb_server.h"
|
||||
#include "smb_server/service_smb_proto.h"
|
||||
#include "smb_server/smb2/smb2_server.h"
|
||||
#include "librpc/gen_ndr/security.h"
|
||||
#include "smbd/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 *ntvfs, uint8_t level)
|
||||
{
|
||||
DEBUG(0,("TODO: we don't pass SMB2 oplock breaks to the Clients yet!\n"));
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
struct ntvfs_handle *smb2srv_pull_handle(struct smb2srv_request *req, const uint8_t *base, uint_t offset)
|
||||
{
|
||||
struct smbsrv_tcon *tcon;
|
||||
struct smbsrv_handle *handle;
|
||||
uint64_t hid;
|
||||
uint32_t tid;
|
||||
uint32_t pad;
|
||||
|
||||
hid = BVAL(base, offset);
|
||||
tid = IVAL(base, offset + 8);
|
||||
pad = IVAL(base, offset + 12);
|
||||
|
||||
if (pad != UINT32_MAX) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* if it's the wildcard handle, don't waste time to search it... */
|
||||
if (hid == UINT64_MAX && tid == UINT32_MAX) {
|
||||
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 if 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
|
||||
*/
|
||||
req->tcon = tcon;
|
||||
return handle->ntvfs;
|
||||
}
|
||||
|
||||
void smb2srv_push_handle(uint8_t *base, uint_t 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
|
||||
*/
|
||||
SBVAL(base, offset, handle->hid);
|
||||
SIVAL(base, offset + 8, handle->tcon->tid);
|
||||
SIVAL(base, offset + 12,UINT32_MAX);
|
||||
}
|
||||
|
||||
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;
|
||||
uint16_t type_smb2;
|
||||
uint32_t unknown2;
|
||||
const char *service = io->smb2.in.path;
|
||||
struct share_config *scfg;
|
||||
const char *sharetype;
|
||||
|
||||
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(scfg, SHARE_TYPE, "DISK");
|
||||
if (sharetype && strcmp(sharetype, "IPC") == 0) {
|
||||
type = NTVFS_IPC;
|
||||
type_smb2 = 0x0002;
|
||||
unknown2 = 0x00000030;
|
||||
} else if (sharetype && strcmp(sharetype, "PRINTER") == 0) {
|
||||
type = NTVFS_PRINT;
|
||||
type_smb2 = 0x0003;
|
||||
unknown2 = 0x00000000;
|
||||
} else {
|
||||
type = NTVFS_DISK;
|
||||
type_smb2 = 0x0001;
|
||||
unknown2 = 0x00000800;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
/* init ntvfs function pointers */
|
||||
status = ntvfs_init_connection(tcon, scfg, type,
|
||||
req->smb_conn->negotiate.protocol,
|
||||
req->smb_conn->connection->event.ctx,
|
||||
req->smb_conn->connection->msg_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_addr_callbacks(tcon->ntvfs, smbsrv_get_my_addr, smbsrv_get_peer_addr, req->smb_conn);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DEBUG(0,("smb2srv_tcon_backend: NTVFS failed to set the addr callbacks!\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,
|
||||
0, /* TODO: fill in PID */
|
||||
req->request_time,
|
||||
req, NULL, 0);
|
||||
if (!req->ntvfs) {
|
||||
status = NT_STATUS_NO_MEMORY;
|
||||
goto failed;
|
||||
}
|
||||
|
||||
/* Invoke NTVFS connection hook */
|
||||
status = ntvfs_connect(req->ntvfs, scfg->name);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DEBUG(0,("smb2srv_tcon_backend: NTVFS ntvfs_connect() failed!\n"));
|
||||
goto failed;
|
||||
}
|
||||
|
||||
io->smb2.out.unknown1 = type_smb2; /* 1 - DISK, 2 - Print, 3 - IPC */
|
||||
io->smb2.out.unknown2 = unknown2;
|
||||
io->smb2.out.unknown3 = 0x00000000;
|
||||
io->smb2.out.access_mask= SEC_RIGHTS_FILE_ALL;
|
||||
|
||||
io->smb2.out.tid = tcon->tid;
|
||||
|
||||
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)
|
||||
{
|
||||
uint16_t unknown1;
|
||||
|
||||
if (!NT_STATUS_IS_OK(req->status)) {
|
||||
smb2srv_send_error(req, req->status);
|
||||
return;
|
||||
}
|
||||
if (io->smb2.out.unknown1 == 0x0002) {
|
||||
/* if it's an IPC share vista returns 0x0005 */
|
||||
unknown1 = 0x0005;
|
||||
} else {
|
||||
unknown1 = 0x0001;
|
||||
}
|
||||
|
||||
SMB2SRV_CHECK(smb2srv_setup_reply(req, 0x10, False, 0));
|
||||
|
||||
SIVAL(req->out.hdr, SMB2_HDR_TID, io->smb2.out.tid);
|
||||
SSVAL(req->out.hdr, SMB2_HDR_UNKNOWN1,unknown1);
|
||||
|
||||
SSVAL(req->out.body, 0x02, io->smb2.out.unknown1);
|
||||
SIVAL(req->out.body, 0x04, io->smb2.out.unknown2);
|
||||
SIVAL(req->out.body, 0x08, io->smb2.out.unknown3);
|
||||
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.unknown1 = SVAL(req->in.body, 0x02);
|
||||
SMB2SRV_CHECK(smb2_pull_o16s16_string(&req->in, io, req->in.body+0x04, &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)
|
||||
{
|
||||
uint16_t _pad;
|
||||
|
||||
SMB2SRV_CHECK_BODY_SIZE(req, 0x04, False);
|
||||
|
||||
_pad = SVAL(req->in.body, 0x02);
|
||||
|
||||
req->status = smb2srv_tdis_backend(req);
|
||||
|
||||
if (req->control_flags & SMB2SRV_REQ_CTRL_FLAG_NOT_REPLY) {
|
||||
talloc_free(req);
|
||||
return;
|
||||
}
|
||||
smb2srv_tdis_send(req);
|
||||
}
|
||||
Reference in New Issue
Block a user