wmi-1.3.16 from opsview.com

This commit is contained in:
Are Casilla
2019-02-16 00:16:52 +01:00
parent 163fdd3d1b
commit 17b3af2911
2146 changed files with 678824 additions and 0 deletions
+18
View File
@@ -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
#######################
+368
View File
@@ -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));
}
+371
View File
@@ -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);
}
+167
View File
@@ -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));
}
+72
View File
@@ -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);
}
+234
View File
@@ -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(&current_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;
}
+450
View File
@@ -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;
}
+237
View File
@@ -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);
}
+168
View File
@@ -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)
+388
View File
@@ -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);
}