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
+21
View File
@@ -0,0 +1,21 @@
#######################
# Start SUBSYSTEM SMB_PROTOCOL
[SUBSYSTEM::SMB_PROTOCOL]
PRIVATE_PROTO_HEADER = smb_proto.h
OBJ_FILES = \
receive.o \
negprot.o \
nttrans.o \
reply.o \
request.o \
search.o \
service.o \
sesssetup.o \
srvtime.o \
trans2.o \
signing.o
PUBLIC_DEPENDENCIES = \
ntvfs LIBPACKET
LDFLAGS = $(SUBSYSTEM_SMB_SERVER_OUTPUT)
# End SUBSYSTEM SMB_PROTOCOL
#######################
+521
View File
@@ -0,0 +1,521 @@
/*
Unix SMB/CIFS implementation.
negprot reply code
Copyright (C) Andrew Tridgell 1992-1998
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 "smb_server/smb_server.h"
#include "libcli/smb2/smb2.h"
#include "smb_server/smb2/smb2_server.h"
#include "smb_server/service_smb_proto.h"
#include "smbd/service_stream.h"
#include "lib/stream/packet.h"
/* initialise the auth_context for this server and return the cryptkey */
static NTSTATUS get_challenge(struct smbsrv_connection *smb_conn, uint8_t buff[8])
{
NTSTATUS nt_status;
const uint8_t *challenge;
/* muliple negprots are not premitted */
if (smb_conn->negotiate.auth_context) {
DEBUG(3,("get challenge: is this a secondary negprot? auth_context is non-NULL!\n"));
return NT_STATUS_FOOBAR;
}
DEBUG(10, ("get challenge: creating negprot_global_auth_context\n"));
nt_status = auth_context_create(smb_conn, lp_auth_methods(),
smb_conn->connection->event.ctx,
smb_conn->connection->msg_ctx,
&smb_conn->negotiate.auth_context);
if (!NT_STATUS_IS_OK(nt_status)) {
DEBUG(0, ("auth_context_create() returned %s", nt_errstr(nt_status)));
return nt_status;
}
nt_status = auth_get_challenge(smb_conn->negotiate.auth_context, &challenge);
if (!NT_STATUS_IS_OK(nt_status)) {
DEBUG(0, ("auth_get_challenge() returned %s", nt_errstr(nt_status)));
return nt_status;
}
memcpy(buff, challenge, 8);
return NT_STATUS_OK;
}
/****************************************************************************
Reply for the core protocol.
****************************************************************************/
static void reply_corep(struct smbsrv_request *req, uint16_t choice)
{
smbsrv_setup_reply(req, 1, 0);
SSVAL(req->out.vwv, VWV(0), choice);
req->smb_conn->negotiate.protocol = PROTOCOL_CORE;
if (req->smb_conn->signing.mandatory_signing) {
smbsrv_terminate_connection(req->smb_conn,
"CORE does not support SMB signing, and it is mandatory\n");
return;
}
smbsrv_send_reply(req);
}
/****************************************************************************
Reply for the coreplus protocol.
this is quite incomplete - we only fill in a small part of the reply, but as nobody uses
this any more it probably doesn't matter
****************************************************************************/
static void reply_coreplus(struct smbsrv_request *req, uint16_t choice)
{
uint16_t raw = (lp_readraw()?1:0) | (lp_writeraw()?2:0);
smbsrv_setup_reply(req, 13, 0);
/* Reply, SMBlockread, SMBwritelock supported. */
SCVAL(req->out.hdr,HDR_FLG,
CVAL(req->out.hdr, HDR_FLG) | FLAG_SUPPORT_LOCKREAD);
SSVAL(req->out.vwv, VWV(0), choice);
SSVAL(req->out.vwv, VWV(1), 0x1); /* user level security, don't encrypt */
/* tell redirector we support
readbraw and writebraw (possibly) */
SSVAL(req->out.vwv, VWV(5), raw);
req->smb_conn->negotiate.protocol = PROTOCOL_COREPLUS;
if (req->smb_conn->signing.mandatory_signing) {
smbsrv_terminate_connection(req->smb_conn,
"COREPLUS does not support SMB signing, and it is mandatory\n");
return;
}
smbsrv_send_reply(req);
}
/****************************************************************************
Reply for the lanman 1.0 protocol.
****************************************************************************/
static void reply_lanman1(struct smbsrv_request *req, uint16_t choice)
{
int raw = (lp_readraw()?1:0) | (lp_writeraw()?2:0);
int secword=0;
time_t t = req->request_time.tv_sec;
req->smb_conn->negotiate.encrypted_passwords = lp_encrypted_passwords();
if (lp_security() != SEC_SHARE)
secword |= NEGOTIATE_SECURITY_USER_LEVEL;
if (req->smb_conn->negotiate.encrypted_passwords)
secword |= NEGOTIATE_SECURITY_CHALLENGE_RESPONSE;
req->smb_conn->negotiate.protocol = PROTOCOL_LANMAN1;
smbsrv_setup_reply(req, 13, req->smb_conn->negotiate.encrypted_passwords ? 8 : 0);
/* SMBlockread, SMBwritelock supported. */
SCVAL(req->out.hdr,HDR_FLG,
CVAL(req->out.hdr, HDR_FLG) | FLAG_SUPPORT_LOCKREAD);
SSVAL(req->out.vwv, VWV(0), choice);
SSVAL(req->out.vwv, VWV(1), secword);
SSVAL(req->out.vwv, VWV(2), req->smb_conn->negotiate.max_recv);
SSVAL(req->out.vwv, VWV(3), lp_maxmux());
SSVAL(req->out.vwv, VWV(4), 1);
SSVAL(req->out.vwv, VWV(5), raw);
SIVAL(req->out.vwv, VWV(6), req->smb_conn->connection->server_id);
srv_push_dos_date(req->smb_conn, req->out.vwv, VWV(8), t);
SSVAL(req->out.vwv, VWV(10), req->smb_conn->negotiate.zone_offset/60);
SIVAL(req->out.vwv, VWV(11), 0); /* reserved */
/* Create a token value and add it to the outgoing packet. */
if (req->smb_conn->negotiate.encrypted_passwords) {
NTSTATUS nt_status;
SSVAL(req->out.vwv, VWV(11), 8);
nt_status = get_challenge(req->smb_conn, req->out.data);
if (!NT_STATUS_IS_OK(nt_status)) {
smbsrv_terminate_connection(req->smb_conn, "LANMAN1 get_challenge failed\n");
return;
}
}
if (req->smb_conn->signing.mandatory_signing) {
smbsrv_terminate_connection(req->smb_conn,
"LANMAN1 does not support SMB signing, and it is mandatory\n");
return;
}
smbsrv_send_reply(req);
}
/****************************************************************************
Reply for the lanman 2.0 protocol.
****************************************************************************/
static void reply_lanman2(struct smbsrv_request *req, uint16_t choice)
{
int raw = (lp_readraw()?1:0) | (lp_writeraw()?2:0);
int secword=0;
time_t t = req->request_time.tv_sec;
req->smb_conn->negotiate.encrypted_passwords = lp_encrypted_passwords();
if (lp_security() != SEC_SHARE)
secword |= NEGOTIATE_SECURITY_USER_LEVEL;
if (req->smb_conn->negotiate.encrypted_passwords)
secword |= NEGOTIATE_SECURITY_CHALLENGE_RESPONSE;
req->smb_conn->negotiate.protocol = PROTOCOL_LANMAN2;
smbsrv_setup_reply(req, 13, 0);
SSVAL(req->out.vwv, VWV(0), choice);
SSVAL(req->out.vwv, VWV(1), secword);
SSVAL(req->out.vwv, VWV(2), req->smb_conn->negotiate.max_recv);
SSVAL(req->out.vwv, VWV(3), lp_maxmux());
SSVAL(req->out.vwv, VWV(4), 1);
SSVAL(req->out.vwv, VWV(5), raw);
SIVAL(req->out.vwv, VWV(6), req->smb_conn->connection->server_id);
srv_push_dos_date(req->smb_conn, req->out.vwv, VWV(8), t);
SSVAL(req->out.vwv, VWV(10), req->smb_conn->negotiate.zone_offset/60);
SIVAL(req->out.vwv, VWV(11), 0);
/* Create a token value and add it to the outgoing packet. */
if (req->smb_conn->negotiate.encrypted_passwords) {
SSVAL(req->out.vwv, VWV(11), 8);
req_grow_data(req, 8);
get_challenge(req->smb_conn, req->out.data);
}
req_push_str(req, NULL, lp_workgroup(), -1, STR_TERMINATE);
if (req->smb_conn->signing.mandatory_signing) {
smbsrv_terminate_connection(req->smb_conn,
"LANMAN2 does not support SMB signing, and it is mandatory\n");
return;
}
smbsrv_send_reply(req);
}
static void reply_nt1_orig(struct smbsrv_request *req)
{
/* Create a token value and add it to the outgoing packet. */
if (req->smb_conn->negotiate.encrypted_passwords) {
req_grow_data(req, 8);
/* note that we do not send a challenge at all if
we are using plaintext */
get_challenge(req->smb_conn, req->out.ptr);
req->out.ptr += 8;
SCVAL(req->out.vwv+1, VWV(16), 8);
}
req_push_str(req, NULL, lp_workgroup(), -1, STR_UNICODE|STR_TERMINATE|STR_NOALIGN);
req_push_str(req, NULL, lp_netbios_name(), -1, STR_UNICODE|STR_TERMINATE|STR_NOALIGN);
DEBUG(3,("not using extended security (SPNEGO or NTLMSSP)\n"));
}
/****************************************************************************
Reply for the nt protocol.
****************************************************************************/
static void reply_nt1(struct smbsrv_request *req, uint16_t choice)
{
/* dual names + lock_and_read + nt SMBs + remote API calls */
int capabilities;
int secword=0;
time_t t = req->request_time.tv_sec;
NTTIME nttime;
BOOL negotiate_spnego = False;
char *large_test_path;
unix_to_nt_time(&nttime, t);
capabilities =
CAP_NT_FIND | CAP_LOCK_AND_READ |
CAP_LEVEL_II_OPLOCKS | CAP_NT_SMBS | CAP_RPC_REMOTE_APIS;
req->smb_conn->negotiate.encrypted_passwords = lp_encrypted_passwords();
/* do spnego in user level security if the client
supports it and we can do encrypted passwords */
if (req->smb_conn->negotiate.encrypted_passwords &&
(lp_security() != SEC_SHARE) &&
lp_use_spnego() &&
(req->flags2 & FLAGS2_EXTENDED_SECURITY)) {
negotiate_spnego = True;
capabilities |= CAP_EXTENDED_SECURITY;
}
if (lp_unix_extensions()) {
capabilities |= CAP_UNIX;
}
if (lp_large_readwrite()) {
capabilities |= CAP_LARGE_READX | CAP_LARGE_WRITEX | CAP_W2K_SMBS;
}
large_test_path = lock_path(req, "large_test.dat");
if (large_file_support(large_test_path)) {
capabilities |= CAP_LARGE_FILES;
}
if (lp_readraw() && lp_writeraw()) {
capabilities |= CAP_RAW_MODE;
}
/* allow for disabling unicode */
if (lp_unicode()) {
capabilities |= CAP_UNICODE;
}
if (lp_nt_status_support()) {
capabilities |= CAP_STATUS32;
}
if (lp_host_msdfs()) {
capabilities |= CAP_DFS;
}
if (lp_security() != SEC_SHARE) {
secword |= NEGOTIATE_SECURITY_USER_LEVEL;
}
if (req->smb_conn->negotiate.encrypted_passwords) {
secword |= NEGOTIATE_SECURITY_CHALLENGE_RESPONSE;
}
if (req->smb_conn->signing.allow_smb_signing) {
secword |= NEGOTIATE_SECURITY_SIGNATURES_ENABLED;
}
if (req->smb_conn->signing.mandatory_signing) {
secword |= NEGOTIATE_SECURITY_SIGNATURES_REQUIRED;
}
req->smb_conn->negotiate.protocol = PROTOCOL_NT1;
smbsrv_setup_reply(req, 17, 0);
SSVAL(req->out.vwv, VWV(0), choice);
SCVAL(req->out.vwv, VWV(1), secword);
/* notice the strange +1 on vwv here? That's because
this is the one and only SMB packet that is malformed in
the specification - all the command words after the secword
are offset by 1 byte */
SSVAL(req->out.vwv+1, VWV(1), lp_maxmux());
SSVAL(req->out.vwv+1, VWV(2), 1); /* num vcs */
SIVAL(req->out.vwv+1, VWV(3), req->smb_conn->negotiate.max_recv);
SIVAL(req->out.vwv+1, VWV(5), 0x10000); /* raw size. full 64k */
SIVAL(req->out.vwv+1, VWV(7), req->smb_conn->connection->server_id); /* session key */
SIVAL(req->out.vwv+1, VWV(9), capabilities);
push_nttime(req->out.vwv+1, VWV(11), nttime);
SSVALS(req->out.vwv+1,VWV(15), req->smb_conn->negotiate.zone_offset/60);
if (!negotiate_spnego) {
reply_nt1_orig(req);
} else {
struct cli_credentials *server_credentials;
struct gensec_security *gensec_security;
DATA_BLOB null_data_blob = data_blob(NULL, 0);
DATA_BLOB blob;
const char *oid;
NTSTATUS nt_status;
nt_status = gensec_server_start(req->smb_conn,
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;
}
if (req->smb_conn->negotiate.auth_context) {
smbsrv_terminate_connection(req->smb_conn, "reply_nt1: is this a secondary negprot? auth_context is non-NULL!\n");
return;
}
server_credentials
= cli_credentials_init(req);
if (!server_credentials) {
smbsrv_terminate_connection(req->smb_conn, "Failed to init server credentials\n");
return;
}
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);
oid = GENSEC_OID_SPNEGO;
nt_status = gensec_start_mech_by_oid(gensec_security, oid);
if (NT_STATUS_IS_OK(nt_status)) {
/* Get and push the proposed OID list into the packets */
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(1, ("Failed to get SPNEGO to give us the first token: %s\n", nt_errstr(nt_status)));
}
}
if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
DEBUG(3,("using SPNEGO\n"));
} else {
DEBUG(5, ("Failed to start SPNEGO, falling back to NTLMSSP only: %s\n", nt_errstr(nt_status)));
oid = GENSEC_OID_NTLMSSP;
nt_status = gensec_start_mech_by_oid(gensec_security, oid);
if (!NT_STATUS_IS_OK(nt_status)) {
DEBUG(0, ("Failed to start SPNEGO as well as NTLMSSP fallback: %s\n", nt_errstr(nt_status)));
reply_nt1_orig(req);
return;
}
/* NTLMSSP is a client-first exchange */
blob = data_blob(NULL, 0);
DEBUG(3,("using raw-NTLMSSP\n"));
}
req->smb_conn->negotiate.oid = oid;
req_grow_data(req, blob.length + 16);
/* a NOT very random guid, perhaps we should get it
* from the credentials (kitchen sink...) */
memset(req->out.ptr, '\0', 16);
req->out.ptr += 16;
memcpy(req->out.ptr, blob.data, blob.length);
SCVAL(req->out.vwv+1, VWV(16), blob.length + 16);
req->out.ptr += blob.length;
}
smbsrv_send_reply_nosign(req);
}
/****************************************************************************
Reply for the SMB2 2.001 protocol
****************************************************************************/
static void reply_smb2(struct smbsrv_request *req, uint16_t choice)
{
struct smbsrv_connection *smb_conn = req->smb_conn;
/* reply with a SMB2 packet */
packet_set_callback(smb_conn->packet, smbsrv_recv_smb2_request);
smb2srv_reply_smb_negprot(req);
req = NULL;
}
/* List of supported protocols, most desired first */
static const struct {
const char *proto_name;
const char *short_name;
void (*proto_reply_fn)(struct smbsrv_request *req, uint16_t choice);
int protocol_level;
} supported_protocols[] = {
{"SMB 2.001", "SMB2", reply_smb2, PROTOCOL_SMB2},
{"NT LANMAN 1.0", "NT1", reply_nt1, PROTOCOL_NT1},
{"NT LM 0.12", "NT1", reply_nt1, PROTOCOL_NT1},
{"LANMAN2.1", "LANMAN2", reply_lanman2, PROTOCOL_LANMAN2},
{"LM1.2X002", "LANMAN2", reply_lanman2, PROTOCOL_LANMAN2},
{"Samba", "LANMAN2", reply_lanman2, PROTOCOL_LANMAN2},
{"DOS LM1.2X002", "LANMAN2", reply_lanman2, PROTOCOL_LANMAN2},
{"Windows for Workgroups 3.1a", "LANMAN1", reply_lanman1, PROTOCOL_LANMAN1},
{"LANMAN1.0", "LANMAN1", reply_lanman1, PROTOCOL_LANMAN1},
{"MICROSOFT NETWORKS 3.0", "LANMAN1", reply_lanman1, PROTOCOL_LANMAN1},
{"MICROSOFT NETWORKS 1.03", "COREPLUS", reply_coreplus, PROTOCOL_COREPLUS},
{"PC NETWORK PROGRAM 1.0", "CORE", reply_corep, PROTOCOL_CORE},
{NULL,NULL,NULL,0},
};
/****************************************************************************
Reply to a negprot.
****************************************************************************/
void smbsrv_reply_negprot(struct smbsrv_request *req)
{
int protocol;
uint8_t *p;
uint32_t protos_count = 0;
char **protos = NULL;
if (req->smb_conn->negotiate.done_negprot) {
smbsrv_terminate_connection(req->smb_conn, "multiple negprot's are not permitted");
return;
}
req->smb_conn->negotiate.done_negprot = True;
p = req->in.data;
while (True) {
size_t len;
protos = talloc_realloc(req, protos, char *, protos_count + 1);
if (!protos) {
smbsrv_terminate_connection(req->smb_conn, nt_errstr(NT_STATUS_NO_MEMORY));
return;
}
protos[protos_count] = NULL;
len = req_pull_ascii4(req, (const char **)&protos[protos_count], p, STR_ASCII|STR_TERMINATE);
p += len;
if (len == 0 || !protos[protos_count]) break;
DEBUG(5,("Requested protocol [%d][%s]\n", protos_count, protos[protos_count]));
protos_count++;
}
/* Check for protocols, most desirable first */
for (protocol = 0; supported_protocols[protocol].proto_name; protocol++) {
int i;
if (supported_protocols[protocol].protocol_level > lp_srv_maxprotocol()) continue;
if (supported_protocols[protocol].protocol_level < lp_srv_minprotocol()) continue;
for (i = 0; i < protos_count; i++) {
if (strcmp(supported_protocols[protocol].proto_name, protos[i]) != 0) continue;
supported_protocols[protocol].proto_reply_fn(req, i);
DEBUG(3,("Selected protocol [%d][%s]\n",
i, supported_protocols[protocol].proto_name));
return;
}
}
smbsrv_terminate_connection(req->smb_conn, "No protocol supported !");
}
+649
View File
@@ -0,0 +1,649 @@
/*
Unix SMB/CIFS implementation.
NT transaction handling
Copyright (C) Andrew Tridgell 2003
Copyright (C) James J Myers 2003 <myersjj@samba.org>
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 "smb_server/smb_server.h"
#include "ntvfs/ntvfs.h"
#include "libcli/raw/libcliraw.h"
#include "librpc/gen_ndr/ndr_security.h"
/*
hold the state of a nttrans op while in progress. Needed to allow for async backend
functions.
*/
struct nttrans_op {
struct smb_nttrans *trans;
NTSTATUS (*send_fn)(struct nttrans_op *);
void *op_info;
};
/* setup a nttrans reply, given the data and params sizes */
static NTSTATUS nttrans_setup_reply(struct nttrans_op *op,
struct smb_nttrans *trans,
uint32_t param_size, uint32_t data_size,
uint8_t setup_count)
{
trans->out.setup_count = setup_count;
if (setup_count != 0) {
trans->out.setup = talloc_zero_array(op, uint16_t, setup_count);
NT_STATUS_HAVE_NO_MEMORY(trans->out.setup);
}
trans->out.params = data_blob_talloc(op, NULL, param_size);
if (param_size != 0) {
NT_STATUS_HAVE_NO_MEMORY(trans->out.params.data);
}
trans->out.data = data_blob_talloc(op, NULL, data_size);
if (data_size != 0) {
NT_STATUS_HAVE_NO_MEMORY(trans->out.data.data);
}
return NT_STATUS_OK;
}
/*
send a nttrans create reply
*/
static NTSTATUS nttrans_create_send(struct nttrans_op *op)
{
union smb_open *io = talloc_get_type(op->op_info, union smb_open);
uint8_t *params;
NTSTATUS status;
status = nttrans_setup_reply(op, op->trans, 69, 0, 0);
NT_STATUS_NOT_OK_RETURN(status);
params = op->trans->out.params.data;
SSVAL(params, 0, io->ntcreatex.out.oplock_level);
smbsrv_push_fnum(params, 2, io->ntcreatex.out.file.ntvfs);
SIVAL(params, 4, io->ntcreatex.out.create_action);
SIVAL(params, 8, 0); /* ea error offset */
push_nttime(params, 12, io->ntcreatex.out.create_time);
push_nttime(params, 20, io->ntcreatex.out.access_time);
push_nttime(params, 28, io->ntcreatex.out.write_time);
push_nttime(params, 36, io->ntcreatex.out.change_time);
SIVAL(params, 44, io->ntcreatex.out.attrib);
SBVAL(params, 48, io->ntcreatex.out.alloc_size);
SBVAL(params, 56, io->ntcreatex.out.size);
SSVAL(params, 64, io->ntcreatex.out.file_type);
SSVAL(params, 66, io->ntcreatex.out.ipc_state);
SCVAL(params, 68, io->ntcreatex.out.is_directory);
return NT_STATUS_OK;
}
/*
parse NTTRANS_CREATE request
*/
static NTSTATUS nttrans_create(struct smbsrv_request *req,
struct nttrans_op *op)
{
struct smb_nttrans *trans = op->trans;
union smb_open *io;
uint16_t fname_len;
uint32_t sd_length, ea_length;
NTSTATUS status;
uint8_t *params;
if (trans->in.params.length < 54) {
return NT_STATUS_INVALID_PARAMETER;
}
/* parse the request */
io = talloc(op, union smb_open);
NT_STATUS_HAVE_NO_MEMORY(io);
io->ntcreatex.level = RAW_OPEN_NTTRANS_CREATE;
params = trans->in.params.data;
io->ntcreatex.in.flags = IVAL(params, 0);
io->ntcreatex.in.root_fid = IVAL(params, 4);
io->ntcreatex.in.access_mask = IVAL(params, 8);
io->ntcreatex.in.alloc_size = BVAL(params, 12);
io->ntcreatex.in.file_attr = IVAL(params, 20);
io->ntcreatex.in.share_access = IVAL(params, 24);
io->ntcreatex.in.open_disposition = IVAL(params, 28);
io->ntcreatex.in.create_options = IVAL(params, 32);
sd_length = IVAL(params, 36);
ea_length = IVAL(params, 40);
fname_len = IVAL(params, 44);
io->ntcreatex.in.impersonation = IVAL(params, 48);
io->ntcreatex.in.security_flags = CVAL(params, 52);
io->ntcreatex.in.sec_desc = NULL;
io->ntcreatex.in.ea_list = NULL;
req_pull_string(req, &io->ntcreatex.in.fname,
params + 53,
MIN(fname_len+1, trans->in.params.length - 53),
STR_NO_RANGE_CHECK | STR_TERMINATE);
if (!io->ntcreatex.in.fname) {
return NT_STATUS_INVALID_PARAMETER;
}
if (sd_length > trans->in.data.length ||
ea_length > trans->in.data.length ||
(sd_length+ea_length) > trans->in.data.length) {
return NT_STATUS_INVALID_PARAMETER;
}
/* this call has an optional security descriptor */
if (sd_length != 0) {
DATA_BLOB blob;
blob.data = trans->in.data.data;
blob.length = sd_length;
io->ntcreatex.in.sec_desc = talloc(io, struct security_descriptor);
if (io->ntcreatex.in.sec_desc == NULL) {
return NT_STATUS_NO_MEMORY;
}
status = ndr_pull_struct_blob(&blob, io,
io->ntcreatex.in.sec_desc,
(ndr_pull_flags_fn_t)ndr_pull_security_descriptor);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
}
/* and an optional ea_list */
if (ea_length > 4) {
DATA_BLOB blob;
blob.data = trans->in.data.data + sd_length;
blob.length = ea_length;
io->ntcreatex.in.ea_list = talloc(io, struct smb_ea_list);
if (io->ntcreatex.in.ea_list == NULL) {
return NT_STATUS_NO_MEMORY;
}
status = ea_pull_list_chained(&blob, io,
&io->ntcreatex.in.ea_list->num_eas,
&io->ntcreatex.in.ea_list->eas);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
}
op->send_fn = nttrans_create_send;
op->op_info = io;
return ntvfs_open(req->ntvfs, io);
}
/*
send NTTRANS_QUERY_SEC_DESC reply
*/
static NTSTATUS nttrans_query_sec_desc_send(struct nttrans_op *op)
{
union smb_fileinfo *io = talloc_get_type(op->op_info, union smb_fileinfo);
uint8_t *params;
NTSTATUS status;
status = nttrans_setup_reply(op, op->trans, 4, 0, 0);
NT_STATUS_NOT_OK_RETURN(status);
params = op->trans->out.params.data;
status = ndr_push_struct_blob(&op->trans->out.data, op,
io->query_secdesc.out.sd,
(ndr_push_flags_fn_t)ndr_push_security_descriptor);
NT_STATUS_NOT_OK_RETURN(status);
SIVAL(params, 0, op->trans->out.data.length);
return NT_STATUS_OK;
}
/*
parse NTTRANS_QUERY_SEC_DESC request
*/
static NTSTATUS nttrans_query_sec_desc(struct smbsrv_request *req,
struct nttrans_op *op)
{
struct smb_nttrans *trans = op->trans;
union smb_fileinfo *io;
if (trans->in.params.length < 8) {
return NT_STATUS_INVALID_PARAMETER;
}
/* parse the request */
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 = smbsrv_pull_fnum(req, trans->in.params.data, 0);
io->query_secdesc.in.secinfo_flags = IVAL(trans->in.params.data, 4);
op->op_info = io;
op->send_fn = nttrans_query_sec_desc_send;
SMBSRV_CHECK_FILE_HANDLE_NTSTATUS(io->query_secdesc.in.file.ntvfs);
return ntvfs_qfileinfo(req->ntvfs, io);
}
/*
parse NTTRANS_SET_SEC_DESC request
*/
static NTSTATUS nttrans_set_sec_desc(struct smbsrv_request *req,
struct nttrans_op *op)
{
struct smb_nttrans *trans = op->trans;
union smb_setfileinfo *io;
NTSTATUS status;
if (trans->in.params.length < 8) {
return NT_STATUS_INVALID_PARAMETER;
}
/* parse the request */
io = talloc(req, union smb_setfileinfo);
NT_STATUS_HAVE_NO_MEMORY(io);
io->set_secdesc.level = RAW_SFILEINFO_SEC_DESC;
io->set_secdesc.in.file.ntvfs = smbsrv_pull_fnum(req, trans->in.params.data, 0);
io->set_secdesc.in.secinfo_flags = IVAL(trans->in.params.data, 4);
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(&trans->in.data, req,
io->set_secdesc.in.sd,
(ndr_pull_flags_fn_t)ndr_pull_security_descriptor);
NT_STATUS_NOT_OK_RETURN(status);
SMBSRV_CHECK_FILE_HANDLE_NTSTATUS(io->set_secdesc.in.file.ntvfs);
return ntvfs_setfileinfo(req->ntvfs, io);
}
/* parse NTTRANS_RENAME request
*/
static NTSTATUS nttrans_rename(struct smbsrv_request *req,
struct nttrans_op *op)
{
return NT_STATUS_NOT_IMPLEMENTED;
}
/*
parse NTTRANS_IOCTL send
*/
static NTSTATUS nttrans_ioctl_send(struct nttrans_op *op)
{
union smb_ioctl *info = talloc_get_type(op->op_info, union smb_ioctl);
NTSTATUS status;
/*
* we pass 0 as data_count here,
* because we reuse the DATA_BLOB from the smb_ioctl
* struct
*/
status = nttrans_setup_reply(op, op->trans, 0, 0, 1);
NT_STATUS_NOT_OK_RETURN(status);
op->trans->out.setup[0] = 0;
op->trans->out.data = info->ntioctl.out.blob;
return NT_STATUS_OK;
}
/*
parse NTTRANS_IOCTL request
*/
static NTSTATUS nttrans_ioctl(struct smbsrv_request *req,
struct nttrans_op *op)
{
struct smb_nttrans *trans = op->trans;
union smb_ioctl *nt;
/* should have at least 4 setup words */
if (trans->in.setup_count != 4) {
return NT_STATUS_INVALID_PARAMETER;
}
nt = talloc(op, union smb_ioctl);
NT_STATUS_HAVE_NO_MEMORY(nt);
nt->ntioctl.level = RAW_IOCTL_NTIOCTL;
nt->ntioctl.in.function = IVAL(trans->in.setup, 0);
nt->ntioctl.in.file.ntvfs = smbsrv_pull_fnum(req, (uint8_t *)trans->in.setup, 4);
nt->ntioctl.in.fsctl = CVAL(trans->in.setup, 6);
nt->ntioctl.in.filter = CVAL(trans->in.setup, 7);
nt->ntioctl.in.max_data = trans->in.max_data;
nt->ntioctl.in.blob = trans->in.data;
op->op_info = nt;
op->send_fn = nttrans_ioctl_send;
SMBSRV_CHECK_FILE_HANDLE_NTSTATUS(nt->ntioctl.in.file.ntvfs);
return ntvfs_ioctl(req->ntvfs, nt);
}
/*
send NTTRANS_NOTIFY_CHANGE reply
*/
static NTSTATUS nttrans_notify_change_send(struct nttrans_op *op)
{
union smb_notify *info = talloc_get_type(op->op_info, union smb_notify);
size_t size = 0;
int i;
NTSTATUS status;
uint8_t *p;
#define MAX_BYTES_PER_CHAR 3
/* work out how big the reply buffer could be */
for (i=0;i<info->nttrans.out.num_changes;i++) {
size += 12 + 3 + (1+strlen(info->nttrans.out.changes[i].name.s)) * MAX_BYTES_PER_CHAR;
}
status = nttrans_setup_reply(op, op->trans, size, 0, 0);
NT_STATUS_NOT_OK_RETURN(status);
p = op->trans->out.params.data;
/* construct the changes buffer */
for (i=0;i<info->nttrans.out.num_changes;i++) {
uint32_t ofs;
ssize_t len;
SIVAL(p, 4, info->nttrans.out.changes[i].action);
len = push_string(p + 12, info->nttrans.out.changes[i].name.s,
op->trans->out.params.length -
(p+12 - op->trans->out.params.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 == info->nttrans.out.num_changes-1) {
SIVAL(p, 0, 0);
} else {
SIVAL(p, 0, ofs);
}
p += ofs;
}
op->trans->out.params.length = p - op->trans->out.params.data;
return NT_STATUS_OK;
}
/*
parse NTTRANS_NOTIFY_CHANGE request
*/
static NTSTATUS nttrans_notify_change(struct smbsrv_request *req,
struct nttrans_op *op)
{
struct smb_nttrans *trans = op->trans;
union smb_notify *info;
/* should have at least 4 setup words */
if (trans->in.setup_count != 4) {
return NT_STATUS_INVALID_PARAMETER;
}
info = talloc(op, union smb_notify);
NT_STATUS_HAVE_NO_MEMORY(info);
info->nttrans.level = RAW_NOTIFY_NTTRANS;
info->nttrans.in.completion_filter = IVAL(trans->in.setup, 0);
info->nttrans.in.file.ntvfs = smbsrv_pull_fnum(req, (uint8_t *)trans->in.setup, 4);
info->nttrans.in.recursive = SVAL(trans->in.setup, 6);
info->nttrans.in.buffer_size = trans->in.max_param;
op->op_info = info;
op->send_fn = nttrans_notify_change_send;
SMBSRV_CHECK_FILE_HANDLE_NTSTATUS(info->nttrans.in.file.ntvfs);
return ntvfs_notify(req->ntvfs, info);
}
/*
backend for nttrans requests
*/
static NTSTATUS nttrans_backend(struct smbsrv_request *req,
struct nttrans_op *op)
{
/* the nttrans command is in function */
switch (op->trans->in.function) {
case NT_TRANSACT_CREATE:
return nttrans_create(req, op);
case NT_TRANSACT_IOCTL:
return nttrans_ioctl(req, op);
case NT_TRANSACT_RENAME:
return nttrans_rename(req, op);
case NT_TRANSACT_QUERY_SECURITY_DESC:
return nttrans_query_sec_desc(req, op);
case NT_TRANSACT_SET_SECURITY_DESC:
return nttrans_set_sec_desc(req, op);
case NT_TRANSACT_NOTIFY_CHANGE:
return nttrans_notify_change(req, op);
}
/* an unknown nttrans command */
return NT_STATUS_DOS(ERRSRV, ERRerror);
}
static void reply_nttrans_send(struct ntvfs_request *ntvfs)
{
struct smbsrv_request *req;
uint16_t params_left, data_left;
uint8_t *params, *data;
struct smb_nttrans *trans;
struct nttrans_op *op;
SMBSRV_CHECK_ASYNC_STATUS(op, struct nttrans_op);
trans = op->trans;
/* if this function needs work to form the nttrans reply buffer, then
call that now */
if (op->send_fn != NULL) {
NTSTATUS status;
status = op->send_fn(op);
if (!NT_STATUS_IS_OK(status)) {
smbsrv_send_error(req, status);
return;
}
}
smbsrv_setup_reply(req, 18 + trans->out.setup_count, 0);
/* note that we don't check the max_setup count (matching w2k3
behaviour) */
if (trans->out.params.length > trans->in.max_param) {
smbsrv_setup_error(req, NT_STATUS_BUFFER_TOO_SMALL);
trans->out.params.length = trans->in.max_param;
}
if (trans->out.data.length > trans->in.max_data) {
smbsrv_setup_error(req, NT_STATUS_BUFFER_TOO_SMALL);
trans->out.data.length = trans->in.max_data;
}
params_left = trans->out.params.length;
data_left = trans->out.data.length;
params = trans->out.params.data;
data = trans->out.data.data;
/* we need to divide up the reply into chunks that fit into
the negotiated buffer size */
do {
uint16_t this_data, this_param, max_bytes;
uint_t align1 = 1, align2 = (params_left ? 2 : 0);
struct smbsrv_request *this_req;
int i;
max_bytes = req_max_data(req) - (align1 + align2);
this_param = params_left;
if (this_param > max_bytes) {
this_param = max_bytes;
}
max_bytes -= this_param;
this_data = data_left;
if (this_data > max_bytes) {
this_data = max_bytes;
}
/* don't destroy unless this is the last chunk */
if (params_left - this_param != 0 ||
data_left - this_data != 0) {
this_req = smbsrv_setup_secondary_request(req);
} else {
this_req = req;
}
req_grow_data(req, this_param + this_data + (align1 + align2));
SSVAL(this_req->out.vwv, 0, 0); /* reserved */
SCVAL(this_req->out.vwv, 2, 0); /* reserved */
SIVAL(this_req->out.vwv, 3, trans->out.params.length);
SIVAL(this_req->out.vwv, 7, trans->out.data.length);
SIVAL(this_req->out.vwv, 11, this_param);
SIVAL(this_req->out.vwv, 15, align1 + PTR_DIFF(this_req->out.data, this_req->out.hdr));
SIVAL(this_req->out.vwv, 19, PTR_DIFF(params, trans->out.params.data));
SIVAL(this_req->out.vwv, 23, this_data);
SIVAL(this_req->out.vwv, 27, align1 + align2 +
PTR_DIFF(this_req->out.data + this_param, this_req->out.hdr));
SIVAL(this_req->out.vwv, 31, PTR_DIFF(data, trans->out.data.data));
SCVAL(this_req->out.vwv, 35, trans->out.setup_count);
for (i=0;i<trans->out.setup_count;i++) {
SSVAL(this_req->out.vwv, VWV(18+i), trans->out.setup[i]);
}
memset(this_req->out.data, 0, align1);
if (this_param != 0) {
memcpy(this_req->out.data + align1, params, this_param);
}
memset(this_req->out.data+this_param+align1, 0, align2);
if (this_data != 0) {
memcpy(this_req->out.data+this_param+align1+align2,
data, this_data);
}
params_left -= this_param;
data_left -= this_data;
params += this_param;
data += this_data;
smbsrv_send_reply(this_req);
} while (params_left != 0 || data_left != 0);
}
/****************************************************************************
Reply to an SMBnttrans request
****************************************************************************/
void smbsrv_reply_nttrans(struct smbsrv_request *req)
{
struct nttrans_op *op;
struct smb_nttrans *trans;
int i;
uint16_t param_ofs, data_ofs;
uint16_t param_count, data_count;
uint16_t param_total, data_total;
/* parse request */
if (req->in.wct < 19) {
smbsrv_send_error(req, NT_STATUS_FOOBAR);
return;
}
SMBSRV_TALLOC_IO_PTR(op, struct nttrans_op);
SMBSRV_SETUP_NTVFS_REQUEST(reply_nttrans_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
trans = talloc(op, struct smb_nttrans);
if (trans == NULL) {
smbsrv_send_error(req, NT_STATUS_NO_MEMORY);
return;
}
op->trans = trans;
op->op_info = NULL;
op->send_fn = NULL;
trans->in.max_setup = CVAL(req->in.vwv, 0);
param_total = IVAL(req->in.vwv, 3);
data_total = IVAL(req->in.vwv, 7);
trans->in.max_param = IVAL(req->in.vwv, 11);
trans->in.max_data = IVAL(req->in.vwv, 15);
param_count = IVAL(req->in.vwv, 19);
param_ofs = IVAL(req->in.vwv, 23);
data_count = IVAL(req->in.vwv, 27);
data_ofs = IVAL(req->in.vwv, 31);
trans->in.setup_count= CVAL(req->in.vwv, 35);
trans->in.function = SVAL(req->in.vwv, 36);
if (req->in.wct != 19 + trans->in.setup_count) {
smbsrv_send_error(req, NT_STATUS_DOS(ERRSRV, ERRerror));
return;
}
/* parse out the setup words */
trans->in.setup = talloc_array(req, uint16_t, trans->in.setup_count);
if (!trans->in.setup) {
smbsrv_send_error(req, NT_STATUS_NO_MEMORY);
return;
}
for (i=0;i<trans->in.setup_count;i++) {
trans->in.setup[i] = SVAL(req->in.vwv, VWV(19+i));
}
if (!req_pull_blob(req, req->in.hdr + param_ofs, param_count, &trans->in.params) ||
!req_pull_blob(req, req->in.hdr + data_ofs, data_count, &trans->in.data)) {
smbsrv_send_error(req, NT_STATUS_FOOBAR);
return;
}
/* is it a partial request? if so, then send a 'send more' message */
if (param_total > param_count ||
data_total > data_count) {
DEBUG(0,("REWRITE: not handling partial nttrans requests!\n"));
smbsrv_send_error(req, NT_STATUS_FOOBAR);
return;
}
ZERO_STRUCT(trans->out);
SMBSRV_CALL_NTVFS_BACKEND(nttrans_backend(req, op));
}
/****************************************************************************
Reply to an SMBnttranss request
****************************************************************************/
void smbsrv_reply_nttranss(struct smbsrv_request *req)
{
smbsrv_send_error(req, NT_STATUS_FOOBAR);
}
+671
View File
@@ -0,0 +1,671 @@
/*
Unix SMB/CIFS implementation.
process incoming packets - main loop
Copyright (C) Andrew Tridgell 1992-2005
Copyright (C) James J Myers 2003 <myersjj@samba.org>
Copyright (C) Stefan Metzmacher 2004-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 "smbd/service_stream.h"
#include "smb_server/smb_server.h"
#include "smb_server/service_smb_proto.h"
#include "ntvfs/ntvfs.h"
#include "system/filesys.h"
/*
send an oplock break request to a client
*/
NTSTATUS smbsrv_send_oplock_break(void *p, struct ntvfs_handle *ntvfs, uint8_t level)
{
struct smbsrv_tcon *tcon = talloc_get_type(p, struct smbsrv_tcon);
struct smbsrv_request *req;
req = smbsrv_init_request(tcon->smb_conn);
NT_STATUS_HAVE_NO_MEMORY(req);
smbsrv_setup_reply(req, 8, 0);
SCVAL(req->out.hdr,HDR_COM,SMBlockingX);
SSVAL(req->out.hdr,HDR_TID,tcon->tid);
SSVAL(req->out.hdr,HDR_PID,0xFFFF);
SSVAL(req->out.hdr,HDR_UID,0);
SSVAL(req->out.hdr,HDR_MID,0xFFFF);
SCVAL(req->out.hdr,HDR_FLG,0);
SSVAL(req->out.hdr,HDR_FLG2,0);
SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE);
SSVAL(req->out.vwv, VWV(1), 0);
smbsrv_push_fnum(req->out.vwv, VWV(2), ntvfs);
SCVAL(req->out.vwv, VWV(3), LOCKING_ANDX_OPLOCK_RELEASE);
SCVAL(req->out.vwv, VWV(3)+1, level);
SIVAL(req->out.vwv, VWV(4), 0);
SSVAL(req->out.vwv, VWV(6), 0);
SSVAL(req->out.vwv, VWV(7), 0);
smbsrv_send_reply(req);
return NT_STATUS_OK;
}
static void switch_message(int type, struct smbsrv_request *req);
/****************************************************************************
receive a SMB request header from the wire, forming a request_context
from the result
****************************************************************************/
NTSTATUS smbsrv_recv_smb_request(void *private, DATA_BLOB blob)
{
struct smbsrv_connection *smb_conn = talloc_get_type(private, struct smbsrv_connection);
struct smbsrv_request *req;
struct timeval cur_time = timeval_current();
uint8_t command;
smb_conn->statistics.last_request_time = cur_time;
/* see if its a special NBT packet */
if (CVAL(blob.data, 0) != 0) {
req = smbsrv_init_request(smb_conn);
NT_STATUS_HAVE_NO_MEMORY(req);
ZERO_STRUCT(req->in);
req->in.buffer = talloc_steal(req, blob.data);
req->in.size = blob.length;
req->request_time = cur_time;
smbsrv_reply_special(req);
return NT_STATUS_OK;
}
if ((NBT_HDR_SIZE + MIN_SMB_SIZE) > blob.length) {
DEBUG(2,("Invalid SMB packet: length %ld\n", (long)blob.length));
smbsrv_terminate_connection(smb_conn, "Invalid SMB packet");
return NT_STATUS_OK;
}
/* Make sure this is an SMB packet */
if (IVAL(blob.data, NBT_HDR_SIZE) != SMB_MAGIC) {
DEBUG(2,("Non-SMB packet of length %ld. Terminating connection\n",
(long)blob.length));
smbsrv_terminate_connection(smb_conn, "Non-SMB packet");
return NT_STATUS_OK;
}
req = smbsrv_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->chained_fnum = -1;
req->in.allocated = req->in.size;
req->in.hdr = req->in.buffer + NBT_HDR_SIZE;
req->in.vwv = req->in.hdr + HDR_VWV;
req->in.wct = CVAL(req->in.hdr, HDR_WCT);
if (req->in.vwv + VWV(req->in.wct) <= req->in.buffer + req->in.size) {
req->in.data = req->in.vwv + VWV(req->in.wct) + 2;
req->in.data_size = SVAL(req->in.vwv, VWV(req->in.wct));
/* the bcc length is only 16 bits, but some packets
(such as SMBwriteX) can be much larger than 64k. We
detect this by looking for a large non-chained NBT
packet (at least 64k bigger than what is
specified). If it is detected then the NBT size is
used instead of the bcc size */
if (req->in.data_size + 0x10000 <=
req->in.size - PTR_DIFF(req->in.data, req->in.buffer) &&
(req->in.wct < 1 || SVAL(req->in.vwv, VWV(0)) == SMB_CHAIN_NONE)) {
/* its an oversized packet! fun for all the family */
req->in.data_size = req->in.size - PTR_DIFF(req->in.data,req->in.buffer);
}
}
if (NBT_HDR_SIZE + MIN_SMB_SIZE + 2*req->in.wct > req->in.size) {
DEBUG(2,("Invalid SMB word count %d\n", req->in.wct));
smbsrv_terminate_connection(req->smb_conn, "Invalid SMB packet");
return NT_STATUS_OK;
}
if (NBT_HDR_SIZE + MIN_SMB_SIZE + 2*req->in.wct + req->in.data_size > req->in.size) {
DEBUG(2,("Invalid SMB buffer length count %d\n",
(int)req->in.data_size));
smbsrv_terminate_connection(req->smb_conn, "Invalid SMB packet");
return NT_STATUS_OK;
}
req->flags2 = SVAL(req->in.hdr, HDR_FLG2);
if (!smbsrv_signing_check_incoming(req)) {
smbsrv_send_error(req, NT_STATUS_ACCESS_DENIED);
return NT_STATUS_OK;
}
command = CVAL(req->in.hdr, HDR_COM);
switch_message(command, req);
return NT_STATUS_OK;
}
/*
These flags determine some of the permissions required to do an operation
*/
#define NEED_SESS (1<<0)
#define NEED_TCON (1<<1)
#define SIGNING_NO_REPLY (1<<2)
/*
define a list of possible SMB messages and their corresponding
functions. Any message that has a NULL function is unimplemented -
please feel free to contribute implementations!
*/
static const struct smb_message_struct
{
const char *name;
void (*fn)(struct smbsrv_request *);
int flags;
}
smb_messages[256] = {
/* 0x00 */ { "SMBmkdir", smbsrv_reply_mkdir, NEED_SESS|NEED_TCON },
/* 0x01 */ { "SMBrmdir", smbsrv_reply_rmdir, NEED_SESS|NEED_TCON },
/* 0x02 */ { "SMBopen", smbsrv_reply_open, NEED_SESS|NEED_TCON },
/* 0x03 */ { "SMBcreate", smbsrv_reply_mknew, NEED_SESS|NEED_TCON },
/* 0x04 */ { "SMBclose", smbsrv_reply_close, NEED_SESS|NEED_TCON },
/* 0x05 */ { "SMBflush", smbsrv_reply_flush, NEED_SESS|NEED_TCON },
/* 0x06 */ { "SMBunlink", smbsrv_reply_unlink, NEED_SESS|NEED_TCON },
/* 0x07 */ { "SMBmv", smbsrv_reply_mv, NEED_SESS|NEED_TCON },
/* 0x08 */ { "SMBgetatr", smbsrv_reply_getatr, NEED_SESS|NEED_TCON },
/* 0x09 */ { "SMBsetatr", smbsrv_reply_setatr, NEED_SESS|NEED_TCON },
/* 0x0a */ { "SMBread", smbsrv_reply_read, NEED_SESS|NEED_TCON },
/* 0x0b */ { "SMBwrite", smbsrv_reply_write, NEED_SESS|NEED_TCON },
/* 0x0c */ { "SMBlock", smbsrv_reply_lock, NEED_SESS|NEED_TCON },
/* 0x0d */ { "SMBunlock", smbsrv_reply_unlock, NEED_SESS|NEED_TCON },
/* 0x0e */ { "SMBctemp", smbsrv_reply_ctemp, NEED_SESS|NEED_TCON },
/* 0x0f */ { "SMBmknew", smbsrv_reply_mknew, NEED_SESS|NEED_TCON },
/* 0x10 */ { "SMBchkpth", smbsrv_reply_chkpth, NEED_SESS|NEED_TCON },
/* 0x11 */ { "SMBexit", smbsrv_reply_exit, NEED_SESS },
/* 0x12 */ { "SMBlseek", smbsrv_reply_lseek, NEED_SESS|NEED_TCON },
/* 0x13 */ { "SMBlockread", smbsrv_reply_lockread, NEED_SESS|NEED_TCON },
/* 0x14 */ { "SMBwriteunlock", smbsrv_reply_writeunlock, NEED_SESS|NEED_TCON },
/* 0x15 */ { NULL, NULL, 0 },
/* 0x16 */ { NULL, NULL, 0 },
/* 0x17 */ { NULL, NULL, 0 },
/* 0x18 */ { NULL, NULL, 0 },
/* 0x19 */ { NULL, NULL, 0 },
/* 0x1a */ { "SMBreadbraw", smbsrv_reply_readbraw, NEED_SESS|NEED_TCON },
/* 0x1b */ { "SMBreadBmpx", smbsrv_reply_readbmpx, NEED_SESS|NEED_TCON },
/* 0x1c */ { "SMBreadBs", NULL, 0 },
/* 0x1d */ { "SMBwritebraw", smbsrv_reply_writebraw, NEED_SESS|NEED_TCON },
/* 0x1e */ { "SMBwriteBmpx", smbsrv_reply_writebmpx, NEED_SESS|NEED_TCON },
/* 0x1f */ { "SMBwriteBs", smbsrv_reply_writebs, NEED_SESS|NEED_TCON },
/* 0x20 */ { "SMBwritec", NULL, 0 },
/* 0x21 */ { NULL, NULL, 0 },
/* 0x22 */ { "SMBsetattrE", smbsrv_reply_setattrE, NEED_SESS|NEED_TCON },
/* 0x23 */ { "SMBgetattrE", smbsrv_reply_getattrE, NEED_SESS|NEED_TCON },
/* 0x24 */ { "SMBlockingX", smbsrv_reply_lockingX, NEED_SESS|NEED_TCON },
/* 0x25 */ { "SMBtrans", smbsrv_reply_trans, NEED_SESS|NEED_TCON },
/* 0x26 */ { "SMBtranss", smbsrv_reply_transs, NEED_SESS|NEED_TCON },
/* 0x27 */ { "SMBioctl", smbsrv_reply_ioctl, NEED_SESS|NEED_TCON },
/* 0x28 */ { "SMBioctls", NULL, NEED_SESS|NEED_TCON },
/* 0x29 */ { "SMBcopy", smbsrv_reply_copy, NEED_SESS|NEED_TCON },
/* 0x2a */ { "SMBmove", NULL, NEED_SESS|NEED_TCON },
/* 0x2b */ { "SMBecho", smbsrv_reply_echo, 0 },
/* 0x2c */ { "SMBwriteclose", smbsrv_reply_writeclose, NEED_SESS|NEED_TCON },
/* 0x2d */ { "SMBopenX", smbsrv_reply_open_and_X, NEED_SESS|NEED_TCON },
/* 0x2e */ { "SMBreadX", smbsrv_reply_read_and_X, NEED_SESS|NEED_TCON },
/* 0x2f */ { "SMBwriteX", smbsrv_reply_write_and_X, NEED_SESS|NEED_TCON},
/* 0x30 */ { NULL, NULL, 0 },
/* 0x31 */ { NULL, NULL, 0 },
/* 0x32 */ { "SMBtrans2", smbsrv_reply_trans2, NEED_SESS|NEED_TCON },
/* 0x33 */ { "SMBtranss2", smbsrv_reply_transs2, NEED_SESS|NEED_TCON },
/* 0x34 */ { "SMBfindclose", smbsrv_reply_findclose, NEED_SESS|NEED_TCON },
/* 0x35 */ { "SMBfindnclose", smbsrv_reply_findnclose, NEED_SESS|NEED_TCON },
/* 0x36 */ { NULL, NULL, 0 },
/* 0x37 */ { NULL, NULL, 0 },
/* 0x38 */ { NULL, NULL, 0 },
/* 0x39 */ { NULL, NULL, 0 },
/* 0x3a */ { NULL, NULL, 0 },
/* 0x3b */ { NULL, NULL, 0 },
/* 0x3c */ { NULL, NULL, 0 },
/* 0x3d */ { NULL, NULL, 0 },
/* 0x3e */ { NULL, NULL, 0 },
/* 0x3f */ { NULL, NULL, 0 },
/* 0x40 */ { NULL, NULL, 0 },
/* 0x41 */ { NULL, NULL, 0 },
/* 0x42 */ { NULL, NULL, 0 },
/* 0x43 */ { NULL, NULL, 0 },
/* 0x44 */ { NULL, NULL, 0 },
/* 0x45 */ { NULL, NULL, 0 },
/* 0x46 */ { NULL, NULL, 0 },
/* 0x47 */ { NULL, NULL, 0 },
/* 0x48 */ { NULL, NULL, 0 },
/* 0x49 */ { NULL, NULL, 0 },
/* 0x4a */ { NULL, NULL, 0 },
/* 0x4b */ { NULL, NULL, 0 },
/* 0x4c */ { NULL, NULL, 0 },
/* 0x4d */ { NULL, NULL, 0 },
/* 0x4e */ { NULL, NULL, 0 },
/* 0x4f */ { NULL, NULL, 0 },
/* 0x50 */ { NULL, NULL, 0 },
/* 0x51 */ { NULL, NULL, 0 },
/* 0x52 */ { NULL, NULL, 0 },
/* 0x53 */ { NULL, NULL, 0 },
/* 0x54 */ { NULL, NULL, 0 },
/* 0x55 */ { NULL, NULL, 0 },
/* 0x56 */ { NULL, NULL, 0 },
/* 0x57 */ { NULL, NULL, 0 },
/* 0x58 */ { NULL, NULL, 0 },
/* 0x59 */ { NULL, NULL, 0 },
/* 0x5a */ { NULL, NULL, 0 },
/* 0x5b */ { NULL, NULL, 0 },
/* 0x5c */ { NULL, NULL, 0 },
/* 0x5d */ { NULL, NULL, 0 },
/* 0x5e */ { NULL, NULL, 0 },
/* 0x5f */ { NULL, NULL, 0 },
/* 0x60 */ { NULL, NULL, 0 },
/* 0x61 */ { NULL, NULL, 0 },
/* 0x62 */ { NULL, NULL, 0 },
/* 0x63 */ { NULL, NULL, 0 },
/* 0x64 */ { NULL, NULL, 0 },
/* 0x65 */ { NULL, NULL, 0 },
/* 0x66 */ { NULL, NULL, 0 },
/* 0x67 */ { NULL, NULL, 0 },
/* 0x68 */ { NULL, NULL, 0 },
/* 0x69 */ { NULL, NULL, 0 },
/* 0x6a */ { NULL, NULL, 0 },
/* 0x6b */ { NULL, NULL, 0 },
/* 0x6c */ { NULL, NULL, 0 },
/* 0x6d */ { NULL, NULL, 0 },
/* 0x6e */ { NULL, NULL, 0 },
/* 0x6f */ { NULL, NULL, 0 },
/* 0x70 */ { "SMBtcon", smbsrv_reply_tcon, NEED_SESS },
/* 0x71 */ { "SMBtdis", smbsrv_reply_tdis, NEED_TCON },
/* 0x72 */ { "SMBnegprot", smbsrv_reply_negprot, 0 },
/* 0x73 */ { "SMBsesssetupX", smbsrv_reply_sesssetup, 0 },
/* 0x74 */ { "SMBulogoffX", smbsrv_reply_ulogoffX, NEED_SESS }, /* ulogoff doesn't give a valid TID */
/* 0x75 */ { "SMBtconX", smbsrv_reply_tcon_and_X, NEED_SESS },
/* 0x76 */ { NULL, NULL, 0 },
/* 0x77 */ { NULL, NULL, 0 },
/* 0x78 */ { NULL, NULL, 0 },
/* 0x79 */ { NULL, NULL, 0 },
/* 0x7a */ { NULL, NULL, 0 },
/* 0x7b */ { NULL, NULL, 0 },
/* 0x7c */ { NULL, NULL, 0 },
/* 0x7d */ { NULL, NULL, 0 },
/* 0x7e */ { NULL, NULL, 0 },
/* 0x7f */ { NULL, NULL, 0 },
/* 0x80 */ { "SMBdskattr", smbsrv_reply_dskattr, NEED_SESS|NEED_TCON },
/* 0x81 */ { "SMBsearch", smbsrv_reply_search, NEED_SESS|NEED_TCON },
/* 0x82 */ { "SMBffirst", smbsrv_reply_search, NEED_SESS|NEED_TCON },
/* 0x83 */ { "SMBfunique", smbsrv_reply_search, NEED_SESS|NEED_TCON },
/* 0x84 */ { "SMBfclose", smbsrv_reply_fclose, NEED_SESS|NEED_TCON },
/* 0x85 */ { NULL, NULL, 0 },
/* 0x86 */ { NULL, NULL, 0 },
/* 0x87 */ { NULL, NULL, 0 },
/* 0x88 */ { NULL, NULL, 0 },
/* 0x89 */ { NULL, NULL, 0 },
/* 0x8a */ { NULL, NULL, 0 },
/* 0x8b */ { NULL, NULL, 0 },
/* 0x8c */ { NULL, NULL, 0 },
/* 0x8d */ { NULL, NULL, 0 },
/* 0x8e */ { NULL, NULL, 0 },
/* 0x8f */ { NULL, NULL, 0 },
/* 0x90 */ { NULL, NULL, 0 },
/* 0x91 */ { NULL, NULL, 0 },
/* 0x92 */ { NULL, NULL, 0 },
/* 0x93 */ { NULL, NULL, 0 },
/* 0x94 */ { NULL, NULL, 0 },
/* 0x95 */ { NULL, NULL, 0 },
/* 0x96 */ { NULL, NULL, 0 },
/* 0x97 */ { NULL, NULL, 0 },
/* 0x98 */ { NULL, NULL, 0 },
/* 0x99 */ { NULL, NULL, 0 },
/* 0x9a */ { NULL, NULL, 0 },
/* 0x9b */ { NULL, NULL, 0 },
/* 0x9c */ { NULL, NULL, 0 },
/* 0x9d */ { NULL, NULL, 0 },
/* 0x9e */ { NULL, NULL, 0 },
/* 0x9f */ { NULL, NULL, 0 },
/* 0xa0 */ { "SMBnttrans", smbsrv_reply_nttrans, NEED_SESS|NEED_TCON },
/* 0xa1 */ { "SMBnttranss", smbsrv_reply_nttranss, NEED_SESS|NEED_TCON },
/* 0xa2 */ { "SMBntcreateX", smbsrv_reply_ntcreate_and_X, NEED_SESS|NEED_TCON },
/* 0xa3 */ { NULL, NULL, 0 },
/* 0xa4 */ { "SMBntcancel", smbsrv_reply_ntcancel, NEED_SESS|NEED_TCON|SIGNING_NO_REPLY },
/* 0xa5 */ { "SMBntrename", smbsrv_reply_ntrename, NEED_SESS|NEED_TCON },
/* 0xa6 */ { NULL, NULL, 0 },
/* 0xa7 */ { NULL, NULL, 0 },
/* 0xa8 */ { NULL, NULL, 0 },
/* 0xa9 */ { NULL, NULL, 0 },
/* 0xaa */ { NULL, NULL, 0 },
/* 0xab */ { NULL, NULL, 0 },
/* 0xac */ { NULL, NULL, 0 },
/* 0xad */ { NULL, NULL, 0 },
/* 0xae */ { NULL, NULL, 0 },
/* 0xaf */ { NULL, NULL, 0 },
/* 0xb0 */ { NULL, NULL, 0 },
/* 0xb1 */ { NULL, NULL, 0 },
/* 0xb2 */ { NULL, NULL, 0 },
/* 0xb3 */ { NULL, NULL, 0 },
/* 0xb4 */ { NULL, NULL, 0 },
/* 0xb5 */ { NULL, NULL, 0 },
/* 0xb6 */ { NULL, NULL, 0 },
/* 0xb7 */ { NULL, NULL, 0 },
/* 0xb8 */ { NULL, NULL, 0 },
/* 0xb9 */ { NULL, NULL, 0 },
/* 0xba */ { NULL, NULL, 0 },
/* 0xbb */ { NULL, NULL, 0 },
/* 0xbc */ { NULL, NULL, 0 },
/* 0xbd */ { NULL, NULL, 0 },
/* 0xbe */ { NULL, NULL, 0 },
/* 0xbf */ { NULL, NULL, 0 },
/* 0xc0 */ { "SMBsplopen", smbsrv_reply_printopen, NEED_SESS|NEED_TCON },
/* 0xc1 */ { "SMBsplwr", smbsrv_reply_printwrite, NEED_SESS|NEED_TCON },
/* 0xc2 */ { "SMBsplclose", smbsrv_reply_printclose, NEED_SESS|NEED_TCON },
/* 0xc3 */ { "SMBsplretq", smbsrv_reply_printqueue, NEED_SESS|NEED_TCON },
/* 0xc4 */ { NULL, NULL, 0 },
/* 0xc5 */ { NULL, NULL, 0 },
/* 0xc6 */ { NULL, NULL, 0 },
/* 0xc7 */ { NULL, NULL, 0 },
/* 0xc8 */ { NULL, NULL, 0 },
/* 0xc9 */ { NULL, NULL, 0 },
/* 0xca */ { NULL, NULL, 0 },
/* 0xcb */ { NULL, NULL, 0 },
/* 0xcc */ { NULL, NULL, 0 },
/* 0xcd */ { NULL, NULL, 0 },
/* 0xce */ { NULL, NULL, 0 },
/* 0xcf */ { NULL, NULL, 0 },
/* 0xd0 */ { "SMBsends", NULL, 0 },
/* 0xd1 */ { "SMBsendb", NULL, 0 },
/* 0xd2 */ { "SMBfwdname", NULL, 0 },
/* 0xd3 */ { "SMBcancelf", NULL, 0 },
/* 0xd4 */ { "SMBgetmac", NULL, 0 },
/* 0xd5 */ { "SMBsendstrt", NULL, 0 },
/* 0xd6 */ { "SMBsendend", NULL, 0 },
/* 0xd7 */ { "SMBsendtxt", NULL, 0 },
/* 0xd8 */ { NULL, NULL, 0 },
/* 0xd9 */ { NULL, NULL, 0 },
/* 0xda */ { NULL, NULL, 0 },
/* 0xdb */ { NULL, NULL, 0 },
/* 0xdc */ { NULL, NULL, 0 },
/* 0xdd */ { NULL, NULL, 0 },
/* 0xde */ { NULL, NULL, 0 },
/* 0xdf */ { NULL, NULL, 0 },
/* 0xe0 */ { NULL, NULL, 0 },
/* 0xe1 */ { NULL, NULL, 0 },
/* 0xe2 */ { NULL, NULL, 0 },
/* 0xe3 */ { NULL, NULL, 0 },
/* 0xe4 */ { NULL, NULL, 0 },
/* 0xe5 */ { NULL, NULL, 0 },
/* 0xe6 */ { NULL, NULL, 0 },
/* 0xe7 */ { NULL, NULL, 0 },
/* 0xe8 */ { NULL, NULL, 0 },
/* 0xe9 */ { NULL, NULL, 0 },
/* 0xea */ { NULL, NULL, 0 },
/* 0xeb */ { NULL, NULL, 0 },
/* 0xec */ { NULL, NULL, 0 },
/* 0xed */ { NULL, NULL, 0 },
/* 0xee */ { NULL, NULL, 0 },
/* 0xef */ { NULL, NULL, 0 },
/* 0xf0 */ { NULL, NULL, 0 },
/* 0xf1 */ { NULL, NULL, 0 },
/* 0xf2 */ { NULL, NULL, 0 },
/* 0xf3 */ { NULL, NULL, 0 },
/* 0xf4 */ { NULL, NULL, 0 },
/* 0xf5 */ { NULL, NULL, 0 },
/* 0xf6 */ { NULL, NULL, 0 },
/* 0xf7 */ { NULL, NULL, 0 },
/* 0xf8 */ { NULL, NULL, 0 },
/* 0xf9 */ { NULL, NULL, 0 },
/* 0xfa */ { NULL, NULL, 0 },
/* 0xfb */ { NULL, NULL, 0 },
/* 0xfc */ { NULL, NULL, 0 },
/* 0xfd */ { NULL, NULL, 0 },
/* 0xfe */ { NULL, NULL, 0 },
/* 0xff */ { NULL, NULL, 0 }
};
/****************************************************************************
return a string containing the function name of a SMB command
****************************************************************************/
static const char *smb_fn_name(uint8_t type)
{
const char *unknown_name = "SMBunknown";
if (smb_messages[type].name == NULL)
return unknown_name;
return smb_messages[type].name;
}
/****************************************************************************
Do a switch on the message type and call the specific reply function for this
message. Unlike earlier versions of Samba the reply functions are responsible
for sending the reply themselves, rather than returning a size to this function
The reply functions may also choose to delay the processing by pushing the message
onto the message queue
****************************************************************************/
static void switch_message(int type, struct smbsrv_request *req)
{
int flags;
struct smbsrv_connection *smb_conn = req->smb_conn;
NTSTATUS status;
type &= 0xff;
errno = 0;
if (smb_messages[type].fn == NULL) {
DEBUG(0,("Unknown message type %d!\n",type));
smbsrv_reply_unknown(req);
return;
}
flags = smb_messages[type].flags;
req->tcon = smbsrv_smb_tcon_find(smb_conn, SVAL(req->in.hdr,HDR_TID), req->request_time);
if (!req->session) {
/* setup the user context for this request if it
hasn't already been initialised (to cope with SMB
chaining) */
/* In share mode security we must ignore the vuid. */
if (smb_conn->config.security == SEC_SHARE) {
if (req->tcon) {
req->session = req->tcon->sec_share.session;
}
} else {
req->session = smbsrv_session_find(req->smb_conn, SVAL(req->in.hdr,HDR_UID), req->request_time);
}
}
DEBUG(5,("switch message %s (task_id %d)\n",smb_fn_name(type), req->smb_conn->connection->server_id));
/* this must be called before we do any reply */
if (flags & SIGNING_NO_REPLY) {
smbsrv_signing_no_reply(req);
}
/* see if the vuid is valid */
if ((flags & NEED_SESS) && !req->session) {
status = NT_STATUS_DOS(ERRSRV, ERRbaduid);
/* amazingly, the error code depends on the command */
switch (type) {
case SMBntcreateX:
case SMBntcancel:
case SMBulogoffX:
break;
default:
if (req->smb_conn->config.nt_status_support &&
req->smb_conn->negotiate.client_caps & CAP_STATUS32) {
status = NT_STATUS_INVALID_HANDLE;
}
break;
}
/*
* TODO:
* don't know how to handle smb signing for this case
* so just skip the reply
*/
if ((flags & SIGNING_NO_REPLY) &&
(req->smb_conn->signing.signing_state != SMB_SIGNING_ENGINE_OFF)) {
DEBUG(1,("SKIP ERROR REPLY: %s %s because of unknown smb signing case\n",
smb_fn_name(type), nt_errstr(status)));
talloc_free(req);
return;
}
smbsrv_send_error(req, status);
return;
}
/* does this protocol need a valid tree connection? */
if ((flags & NEED_TCON) && !req->tcon) {
status = NT_STATUS_DOS(ERRSRV, ERRinvnid);
/* amazingly, the error code depends on the command */
switch (type) {
case SMBntcreateX:
case SMBntcancel:
case SMBtdis:
break;
default:
if (req->smb_conn->config.nt_status_support &&
req->smb_conn->negotiate.client_caps & CAP_STATUS32) {
status = NT_STATUS_INVALID_HANDLE;
}
break;
}
/*
* TODO:
* don't know how to handle smb signing for this case
* so just skip the reply
*/
if ((flags & SIGNING_NO_REPLY) &&
(req->smb_conn->signing.signing_state != SMB_SIGNING_ENGINE_OFF)) {
DEBUG(1,("SKIP ERROR REPLY: %s %s because of unknown smb signing case\n",
smb_fn_name(type), nt_errstr(status)));
talloc_free(req);
return;
}
smbsrv_send_error(req, status);
return;
}
smb_messages[type].fn(req);
}
/*
we call this when first first part of a possibly chained request has been completed
and we need to call the 2nd part, if any
*/
void smbsrv_chain_reply(struct smbsrv_request *req)
{
uint16_t chain_cmd, chain_offset;
uint8_t *vwv, *data;
uint16_t wct;
uint16_t data_size;
if (req->in.wct < 2 || req->out.wct < 2) {
smbsrv_send_error(req, NT_STATUS_DOS(ERRSRV, ERRerror));
return;
}
chain_cmd = CVAL(req->in.vwv, VWV(0));
chain_offset = SVAL(req->in.vwv, VWV(1));
if (chain_cmd == SMB_CHAIN_NONE) {
/* end of chain */
SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE);
SSVAL(req->out.vwv, VWV(1), 0);
smbsrv_send_reply(req);
return;
}
if (chain_offset + req->in.hdr >= req->in.buffer + req->in.size) {
goto error;
}
wct = CVAL(req->in.hdr, chain_offset);
vwv = req->in.hdr + chain_offset + 1;
if (vwv + VWV(wct) + 2 > req->in.buffer + req->in.size) {
goto error;
}
data_size = SVAL(vwv, VWV(wct));
data = vwv + VWV(wct) + 2;
if (data + data_size > req->in.buffer + req->in.size) {
goto error;
}
/* all seems legit */
req->in.vwv = vwv;
req->in.wct = wct;
req->in.data = data;
req->in.data_size = data_size;
req->in.ptr = data;
req->chain_count++;
SSVAL(req->out.vwv, VWV(0), chain_cmd);
SSVAL(req->out.vwv, VWV(1), req->out.size - NBT_HDR_SIZE);
/* cleanup somestuff for the next request */
talloc_free(req->ntvfs);
req->ntvfs = NULL;
talloc_free(req->io_ptr);
req->io_ptr = NULL;
switch_message(chain_cmd, req);
return;
error:
SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE);
SSVAL(req->out.vwv, VWV(1), 0);
smbsrv_send_error(req, NT_STATUS_DOS(ERRSRV, ERRerror));
}
/*
* init the SMB protocol related stuff
*/
NTSTATUS smbsrv_init_smb_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 = lp_security();
smb_conn->config.nt_status_support = lp_nt_status_support();
status = smbsrv_init_sessions(smb_conn, UINT16_MAX);
NT_STATUS_NOT_OK_RETURN(status);
status = smbsrv_smb_init_tcons(smb_conn);
NT_STATUS_NOT_OK_RETURN(status);
smbsrv_init_signing(smb_conn);
return NT_STATUS_OK;
}
File diff suppressed because it is too large Load Diff
+760
View File
@@ -0,0 +1,760 @@
/*
Unix SMB/CIFS implementation.
Copyright (C) Andrew Tridgell 2003
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 implements functions for manipulating the 'struct smbsrv_request' structure in smbd
*/
#include "includes.h"
#include "smb_server/smb_server.h"
#include "smb_server/service_smb_proto.h"
#include "smbd/service_stream.h"
#include "lib/stream/packet.h"
#include "ntvfs/ntvfs.h"
/* we over allocate the data buffer to prevent too many realloc calls */
#define REQ_OVER_ALLOCATION 0
static int smbsrv_request_destructor(struct smbsrv_request *req)
{
DLIST_REMOVE(req->smb_conn->requests, req);
return 0;
}
/****************************************************************************
construct a basic request packet, mostly used to construct async packets
such as change notify and oplock break requests
****************************************************************************/
struct smbsrv_request *smbsrv_init_request(struct smbsrv_connection *smb_conn)
{
struct smbsrv_request *req;
req = talloc_zero(smb_conn, struct smbsrv_request);
if (!req) {
return NULL;
}
/* setup the request context */
req->smb_conn = smb_conn;
talloc_set_destructor(req, smbsrv_request_destructor);
return req;
}
/*
setup a chained reply in req->out with the given word count and initial data buffer size.
*/
static void req_setup_chain_reply(struct smbsrv_request *req, uint_t wct, uint_t buflen)
{
uint32_t chain_base_size = req->out.size;
/* we need room for the wct value, the words, the buffer length and the buffer */
req->out.size += 1 + VWV(wct) + 2 + buflen;
/* over allocate by a small amount */
req->out.allocated = req->out.size + REQ_OVER_ALLOCATION;
req->out.buffer = talloc_realloc(req, req->out.buffer,
uint8_t, req->out.allocated);
if (!req->out.buffer) {
smbsrv_terminate_connection(req->smb_conn, "allocation failed");
return;
}
req->out.hdr = req->out.buffer + NBT_HDR_SIZE;
req->out.vwv = req->out.buffer + chain_base_size + 1;
req->out.wct = wct;
req->out.data = req->out.vwv + VWV(wct) + 2;
req->out.data_size = buflen;
req->out.ptr = req->out.data;
SCVAL(req->out.buffer, chain_base_size, wct);
SSVAL(req->out.vwv, VWV(wct), buflen);
}
/*
setup a reply in req->out with the given word count and initial data buffer size.
the caller will then fill in the command words and data before calling req_send_reply() to
send the reply on its way
*/
void smbsrv_setup_reply(struct smbsrv_request *req, uint_t wct, size_t buflen)
{
uint16_t flags2;
if (req->chain_count != 0) {
req_setup_chain_reply(req, wct, buflen);
return;
}
req->out.size = NBT_HDR_SIZE + MIN_SMB_SIZE + VWV(wct) + buflen;
/* over allocate by a small amount */
req->out.allocated = req->out.size + REQ_OVER_ALLOCATION;
req->out.buffer = talloc_size(req, req->out.allocated);
if (!req->out.buffer) {
smbsrv_terminate_connection(req->smb_conn, "allocation failed");
return;
}
flags2 = FLAGS2_LONG_PATH_COMPONENTS |
FLAGS2_EXTENDED_ATTRIBUTES |
FLAGS2_IS_LONG_NAME;
flags2 |= (req->flags2 & (FLAGS2_UNICODE_STRINGS|FLAGS2_EXTENDED_SECURITY));
if (req->smb_conn->negotiate.client_caps & CAP_STATUS32) {
flags2 |= FLAGS2_32_BIT_ERROR_CODES;
}
req->out.hdr = req->out.buffer + NBT_HDR_SIZE;
req->out.vwv = req->out.hdr + HDR_VWV;
req->out.wct = wct;
req->out.data = req->out.vwv + VWV(wct) + 2;
req->out.data_size = buflen;
req->out.ptr = req->out.data;
SIVAL(req->out.hdr, HDR_RCLS, 0);
SCVAL(req->out.hdr, HDR_WCT, wct);
SSVAL(req->out.vwv, VWV(wct), buflen);
memcpy(req->out.hdr, "\377SMB", 4);
SCVAL(req->out.hdr,HDR_FLG, FLAG_REPLY | FLAG_CASELESS_PATHNAMES);
SSVAL(req->out.hdr,HDR_FLG2, flags2);
SSVAL(req->out.hdr,HDR_PIDHIGH,0);
memset(req->out.hdr + HDR_SS_FIELD, 0, 10);
if (req->in.hdr) {
/* copy the cmd, tid, pid, uid and mid from the request */
SCVAL(req->out.hdr,HDR_COM,CVAL(req->in.hdr,HDR_COM));
SSVAL(req->out.hdr,HDR_TID,SVAL(req->in.hdr,HDR_TID));
SSVAL(req->out.hdr,HDR_PID,SVAL(req->in.hdr,HDR_PID));
SSVAL(req->out.hdr,HDR_UID,SVAL(req->in.hdr,HDR_UID));
SSVAL(req->out.hdr,HDR_MID,SVAL(req->in.hdr,HDR_MID));
} else {
SCVAL(req->out.hdr,HDR_COM,0);
SSVAL(req->out.hdr,HDR_TID,0);
SSVAL(req->out.hdr,HDR_PID,0);
SSVAL(req->out.hdr,HDR_UID,0);
SSVAL(req->out.hdr,HDR_MID,0);
}
}
/*
setup a copy of a request, used when the server needs to send
more than one reply for a single request packet
*/
struct smbsrv_request *smbsrv_setup_secondary_request(struct smbsrv_request *old_req)
{
struct smbsrv_request *req;
ptrdiff_t diff;
req = talloc_memdup(old_req, old_req, sizeof(struct smbsrv_request));
if (req == NULL) {
return NULL;
}
req->out.buffer = talloc_memdup(req, req->out.buffer, req->out.allocated);
if (req->out.buffer == NULL) {
talloc_free(req);
return NULL;
}
diff = req->out.buffer - old_req->out.buffer;
req->out.hdr += diff;
req->out.vwv += diff;
req->out.data += diff;
req->out.ptr += diff;
return req;
}
/*
work out the maximum data size we will allow for this reply, given
the negotiated max_xmit. The basic reply packet must be setup before
this call
note that this is deliberately a signed integer reply
*/
int req_max_data(struct smbsrv_request *req)
{
int ret;
ret = req->smb_conn->negotiate.max_send;
ret -= PTR_DIFF(req->out.data, req->out.hdr);
if (ret < 0) ret = 0;
return ret;
}
/*
grow the allocation of the data buffer portion of a reply
packet. Note that as this can reallocate the packet buffer this
invalidates any local pointers into the packet.
To cope with this req->out.ptr is supplied. This will be updated to
point at the same offset into the packet as before this call
*/
static void req_grow_allocation(struct smbsrv_request *req, uint_t new_size)
{
int delta;
uint8_t *buf2;
delta = new_size - req->out.data_size;
if (delta + req->out.size <= req->out.allocated) {
/* it fits in the preallocation */
return;
}
/* we need to realloc */
req->out.allocated = req->out.size + delta + REQ_OVER_ALLOCATION;
buf2 = talloc_realloc(req, req->out.buffer, uint8_t, req->out.allocated);
if (buf2 == NULL) {
smb_panic("out of memory in req_grow_allocation");
}
if (buf2 == req->out.buffer) {
/* the malloc library gave us the same pointer */
return;
}
/* update the pointers into the packet */
req->out.data = buf2 + PTR_DIFF(req->out.data, req->out.buffer);
req->out.ptr = buf2 + PTR_DIFF(req->out.ptr, req->out.buffer);
req->out.vwv = buf2 + PTR_DIFF(req->out.vwv, req->out.buffer);
req->out.hdr = buf2 + PTR_DIFF(req->out.hdr, req->out.buffer);
req->out.buffer = buf2;
}
/*
grow the data buffer portion of a reply packet. Note that as this
can reallocate the packet buffer this invalidates any local pointers
into the packet.
To cope with this req->out.ptr is supplied. This will be updated to
point at the same offset into the packet as before this call
*/
void req_grow_data(struct smbsrv_request *req, size_t new_size)
{
int delta;
if (!(req->control_flags & SMBSRV_REQ_CONTROL_LARGE) && new_size > req_max_data(req)) {
smb_panic("reply buffer too large!");
}
req_grow_allocation(req, new_size);
delta = new_size - req->out.data_size;
req->out.size += delta;
req->out.data_size += delta;
/* set the BCC to the new data size */
SSVAL(req->out.vwv, VWV(req->out.wct), new_size);
}
/*
send a reply and destroy the request buffer
note that this only looks at req->out.buffer and req->out.size, allowing manually
constructed packets to be sent
*/
void smbsrv_send_reply_nosign(struct smbsrv_request *req)
{
DATA_BLOB blob;
NTSTATUS status;
if (req->smb_conn->connection->event.fde == NULL) {
/* we are in the process of shutting down this connection */
talloc_free(req);
return;
}
if (req->out.size > NBT_HDR_SIZE) {
_smb_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);
}
/*
possibly sign a message then send a reply and destroy the request buffer
note that this only looks at req->out.buffer and req->out.size, allowing manually
constructed packets to be sent
*/
void smbsrv_send_reply(struct smbsrv_request *req)
{
if (req->smb_conn->connection->event.fde == NULL) {
/* we are in the process of shutting down this connection */
talloc_free(req);
return;
}
smbsrv_sign_packet(req);
smbsrv_send_reply_nosign(req);
}
/*
setup the header of a reply to include an NTSTATUS code
*/
void smbsrv_setup_error(struct smbsrv_request *req, NTSTATUS status)
{
if (!req->smb_conn->config.nt_status_support || !(req->smb_conn->negotiate.client_caps & CAP_STATUS32)) {
/* convert to DOS error codes */
uint8_t eclass;
uint32_t ecode;
ntstatus_to_dos(status, &eclass, &ecode);
SCVAL(req->out.hdr, HDR_RCLS, eclass);
SSVAL(req->out.hdr, HDR_ERR, ecode);
SSVAL(req->out.hdr, HDR_FLG2, SVAL(req->out.hdr, HDR_FLG2) & ~FLAGS2_32_BIT_ERROR_CODES);
return;
}
if (NT_STATUS_IS_DOS(status)) {
/* its a encoded DOS error, using the reserved range */
SSVAL(req->out.hdr, HDR_RCLS, NT_STATUS_DOS_CLASS(status));
SSVAL(req->out.hdr, HDR_ERR, NT_STATUS_DOS_CODE(status));
SSVAL(req->out.hdr, HDR_FLG2, SVAL(req->out.hdr, HDR_FLG2) & ~FLAGS2_32_BIT_ERROR_CODES);
} else {
SIVAL(req->out.hdr, HDR_RCLS, NT_STATUS_V(status));
SSVAL(req->out.hdr, HDR_FLG2, SVAL(req->out.hdr, HDR_FLG2) | FLAGS2_32_BIT_ERROR_CODES);
}
}
/*
construct and send an error packet, then destroy the request
auto-converts to DOS error format when appropriate
*/
void smbsrv_send_error(struct smbsrv_request *req, 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;
}
smbsrv_setup_reply(req, 0, 0);
/* error returns never have any data */
req_grow_data(req, 0);
smbsrv_setup_error(req, status);
smbsrv_send_reply(req);
}
/*
push a string into the data portion of the request packet, growing it if necessary
this gets quite tricky - please be very careful to cover all cases when modifying this
if dest is NULL, then put the string at the end of the data portion of the packet
if dest_len is -1 then no limit applies
*/
size_t req_push_str(struct smbsrv_request *req, uint8_t *dest, const char *str, int dest_len, size_t flags)
{
size_t len;
uint_t grow_size;
uint8_t *buf0;
const int max_bytes_per_char = 3;
if (!(flags & (STR_ASCII|STR_UNICODE))) {
flags |= (req->flags2 & FLAGS2_UNICODE_STRINGS) ? STR_UNICODE : STR_ASCII;
}
if (dest == NULL) {
dest = req->out.data + req->out.data_size;
}
if (dest_len != -1) {
len = dest_len;
} else {
len = (strlen(str)+2) * max_bytes_per_char;
}
grow_size = len + PTR_DIFF(dest, req->out.data);
buf0 = req->out.buffer;
req_grow_allocation(req, grow_size);
if (buf0 != req->out.buffer) {
dest = req->out.buffer + PTR_DIFF(dest, buf0);
}
len = push_string(dest, str, len, flags);
grow_size = len + PTR_DIFF(dest, req->out.data);
if (grow_size > req->out.data_size) {
req_grow_data(req, grow_size);
}
return len;
}
/*
append raw bytes into the data portion of the request packet
return the number of bytes added
*/
size_t req_append_bytes(struct smbsrv_request *req,
const uint8_t *bytes, size_t byte_len)
{
req_grow_allocation(req, byte_len + req->out.data_size);
memcpy(req->out.data + req->out.data_size, bytes, byte_len);
req_grow_data(req, byte_len + req->out.data_size);
return byte_len;
}
/*
append variable block (type 5 buffer) into the data portion of the request packet
return the number of bytes added
*/
size_t req_append_var_block(struct smbsrv_request *req,
const uint8_t *bytes, uint16_t byte_len)
{
req_grow_allocation(req, byte_len + 3 + req->out.data_size);
SCVAL(req->out.data + req->out.data_size, 0, 5);
SSVAL(req->out.data + req->out.data_size, 1, byte_len); /* add field length */
if (byte_len > 0) {
memcpy(req->out.data + req->out.data_size + 3, bytes, byte_len);
}
req_grow_data(req, byte_len + 3 + req->out.data_size);
return byte_len + 3;
}
/*
pull a UCS2 string from a request packet, returning a talloced unix string
the string length is limited by the 3 things:
- the data size in the request (end of packet)
- the passed 'byte_len' if it is not -1
- the end of string (null termination)
Note that 'byte_len' is the number of bytes in the packet
on failure zero is returned and *dest is set to NULL, otherwise the number
of bytes consumed in the packet is returned
*/
static size_t req_pull_ucs2(struct smbsrv_request *req, const char **dest, const uint8_t *src, int byte_len, uint_t flags)
{
int src_len, src_len2, alignment=0;
ssize_t ret;
char *dest2;
if (!(flags & STR_NOALIGN) && ucs2_align(req->in.buffer, src, flags)) {
src++;
alignment=1;
if (byte_len != -1) {
byte_len--;
}
}
if (flags & STR_NO_RANGE_CHECK) {
src_len = byte_len;
} else {
src_len = req->in.data_size - PTR_DIFF(src, req->in.data);
if (byte_len != -1 && src_len > byte_len) {
src_len = byte_len;
}
}
if (src_len < 0) {
*dest = NULL;
return 0;
}
src_len2 = utf16_len_n(src, src_len);
if (src_len2 == 0) {
*dest = talloc_strdup(req, "");
return src_len2 + alignment;
}
ret = convert_string_talloc(req, CH_UTF16, CH_UNIX, src, src_len2, (void **)&dest2);
if (ret == -1) {
*dest = NULL;
return 0;
}
*dest = dest2;
return src_len2 + alignment;
}
/*
pull a ascii string from a request packet, returning a talloced string
the string length is limited by the 3 things:
- the data size in the request (end of packet)
- the passed 'byte_len' if it is not -1
- the end of string (null termination)
Note that 'byte_len' is the number of bytes in the packet
on failure zero is returned and *dest is set to NULL, otherwise the number
of bytes consumed in the packet is returned
*/
static size_t req_pull_ascii(struct smbsrv_request *req, const char **dest, const uint8_t *src, int byte_len, uint_t flags)
{
int src_len, src_len2;
ssize_t ret;
char *dest2;
if (flags & STR_NO_RANGE_CHECK) {
src_len = byte_len;
} else {
src_len = req->in.data_size - PTR_DIFF(src, req->in.data);
if (src_len < 0) {
*dest = NULL;
return 0;
}
if (byte_len != -1 && src_len > byte_len) {
src_len = byte_len;
}
}
src_len2 = strnlen((const char *)src, src_len);
if (src_len2 <= src_len - 1) {
/* include the termination if we didn't reach the end of the packet */
src_len2++;
}
ret = convert_string_talloc(req, CH_DOS, CH_UNIX, src, src_len2, (void **)&dest2);
if (ret == -1) {
*dest = NULL;
return 0;
}
*dest = dest2;
return src_len2;
}
/*
pull a string from a request packet, returning a talloced string
the string length is limited by the 3 things:
- the data size in the request (end of packet)
- the passed 'byte_len' if it is not -1
- the end of string (null termination)
Note that 'byte_len' is the number of bytes in the packet
on failure zero is returned and *dest is set to NULL, otherwise the number
of bytes consumed in the packet is returned
*/
size_t req_pull_string(struct smbsrv_request *req, const char **dest, const uint8_t *src, int byte_len, uint_t flags)
{
if (!(flags & STR_ASCII) &&
(((flags & STR_UNICODE) || (req->flags2 & FLAGS2_UNICODE_STRINGS)))) {
return req_pull_ucs2(req, dest, src, byte_len, flags);
}
return req_pull_ascii(req, dest, src, byte_len, flags);
}
/*
pull a ASCII4 string buffer from a request packet, returning a talloced string
an ASCII4 buffer is a null terminated string that has a prefix
of the character 0x4. It tends to be used in older parts of the protocol.
on failure *dest is set to the zero length string. This seems to
match win2000 behaviour
*/
size_t req_pull_ascii4(struct smbsrv_request *req, const char **dest, const uint8_t *src, uint_t flags)
{
ssize_t ret;
if (PTR_DIFF(src, req->in.data) + 1 > req->in.data_size) {
/* win2000 treats this as the empty string! */
(*dest) = talloc_strdup(req, "");
return 0;
}
/* this consumes the 0x4 byte. We don't check whether the byte
is actually 0x4 or not. This matches win2000 server
behaviour */
src++;
ret = req_pull_string(req, dest, src, -1, flags);
if (ret == -1) {
(*dest) = talloc_strdup(req, "");
return 1;
}
return ret + 1;
}
/*
pull a DATA_BLOB from a request packet, returning a talloced blob
return False if any part is outside the data portion of the packet
*/
BOOL req_pull_blob(struct smbsrv_request *req, const uint8_t *src, int len, DATA_BLOB *blob)
{
if (len != 0 && req_data_oob(req, src, len)) {
return False;
}
(*blob) = data_blob_talloc(req, src, len);
return True;
}
/* check that a lump of data in a request is within the bounds of the data section of
the packet */
BOOL req_data_oob(struct smbsrv_request *req, const uint8_t *ptr, uint32_t count)
{
if (count == 0) {
return False;
}
/* be careful with wraparound! */
if (ptr < req->in.data ||
ptr >= req->in.data + req->in.data_size ||
count > req->in.data_size ||
ptr + count > req->in.data + req->in.data_size) {
return True;
}
return False;
}
/*
pull an open file handle from a packet, taking account of the chained_fnum
*/
static uint16_t req_fnum(struct smbsrv_request *req, const uint8_t *base, uint_t offset)
{
if (req->chained_fnum != -1) {
return req->chained_fnum;
}
return SVAL(base, offset);
}
struct ntvfs_handle *smbsrv_pull_fnum(struct smbsrv_request *req, const uint8_t *base, uint_t offset)
{
struct smbsrv_handle *handle;
uint16_t fnum = req_fnum(req, base, offset);
handle = smbsrv_smb_handle_find(req->tcon, fnum, req->request_time);
if (!handle) {
return NULL;
}
/*
* For SMB tcons and sessions can be mixed!
* But we need to make sure that file handles
* are only accessed by the opening session!
*
* So check if the handle is valid for the given session!
*/
if (handle->session != req->session) {
return NULL;
}
return handle->ntvfs;
}
void smbsrv_push_fnum(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);
SSVAL(base, offset, handle->hid);
}
NTSTATUS smbsrv_handle_create_new(void *private_data, struct ntvfs_request *ntvfs, struct ntvfs_handle **_h)
{
struct smbsrv_request *req = talloc_get_type(ntvfs->frontend_data.private_data,
struct smbsrv_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;
}
NTSTATUS smbsrv_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;
}
void smbsrv_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);
}
struct ntvfs_handle *smbsrv_handle_search_by_wire_key(void *private_data, struct ntvfs_request *ntvfs, const DATA_BLOB *key)
{
struct smbsrv_request *req = talloc_get_type(ntvfs->frontend_data.private_data,
struct smbsrv_request);
if (key->length != 2) return NULL;
return smbsrv_pull_fnum(req, key->data, 0);
}
DATA_BLOB smbsrv_handle_get_wire_key(void *private_data, struct ntvfs_handle *handle, TALLOC_CTX *mem_ctx)
{
uint8_t key[2];
smbsrv_push_fnum(key, 0, handle);
return data_blob_talloc(mem_ctx, key, sizeof(key));
}
+284
View File
@@ -0,0 +1,284 @@
/*
Unix SMB/CIFS implementation.
SMBsearch handling
Copyright (C) Andrew Tridgell 2003
Copyright (C) James J Myers 2003 <myersjj@samba.org>
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 "smb_server/smb_server.h"
#include "ntvfs/ntvfs.h"
/* a structure to encapsulate the state information about
* an in-progress search first/next operation */
struct search_state {
struct smbsrv_request *req;
union smb_search_data *file;
uint16_t last_entry_offset;
};
/*
fill a single entry in a search find reply
*/
static BOOL find_fill_info(struct smbsrv_request *req,
union smb_search_data *file)
{
uint8_t *p;
if (req->out.data_size + 43 > req_max_data(req)) {
return False;
}
req_grow_data(req, req->out.data_size + 43);
p = req->out.data + req->out.data_size - 43;
SCVAL(p, 0, file->search.id.reserved);
memcpy(p+1, file->search.id.name, 11);
SCVAL(p, 12, file->search.id.handle);
SIVAL(p, 13, file->search.id.server_cookie);
SIVAL(p, 17, file->search.id.client_cookie);
SCVAL(p, 21, file->search.attrib);
srv_push_dos_date(req->smb_conn, p, 22, file->search.write_time);
SIVAL(p, 26, file->search.size);
memset(p+30, ' ', 12);
memcpy(p+30, file->search.name, MIN(strlen(file->search.name)+1, 12));
SCVAL(p,42,0);
return True;
}
/* callback function for search first/next */
static BOOL find_callback(void *private, union smb_search_data *file)
{
struct search_state *state = (struct search_state *)private;
return find_fill_info(state->req, file);
}
/****************************************************************************
Reply to a search first (async reply)
****************************************************************************/
static void reply_search_first_send(struct ntvfs_request *ntvfs)
{
struct smbsrv_request *req;
union smb_search_first *sf;
SMBSRV_CHECK_ASYNC_STATUS(sf, union smb_search_first);
SSVAL(req->out.vwv, VWV(0), sf->search_first.out.count);
smbsrv_send_reply(req);
}
/****************************************************************************
Reply to a search next (async reply)
****************************************************************************/
static void reply_search_next_send(struct ntvfs_request *ntvfs)
{
struct smbsrv_request *req;
union smb_search_next *sn;
SMBSRV_CHECK_ASYNC_STATUS(sn, union smb_search_next);
SSVAL(req->out.vwv, VWV(0), sn->search_next.out.count);
smbsrv_send_reply(req);
}
/****************************************************************************
Reply to a search.
****************************************************************************/
void smbsrv_reply_search(struct smbsrv_request *req)
{
union smb_search_first *sf;
uint16_t resume_key_length;
struct search_state *state;
uint8_t *p;
enum smb_search_level level = RAW_SEARCH_SEARCH;
uint8_t op = CVAL(req->in.hdr,HDR_COM);
if (op == SMBffirst) {
level = RAW_SEARCH_FFIRST;
} else if (op == SMBfunique) {
level = RAW_SEARCH_FUNIQUE;
}
/* parse request */
if (req->in.wct != 2) {
smbsrv_send_error(req, NT_STATUS_INVALID_PARAMETER);
return;
}
SMBSRV_TALLOC_IO_PTR(sf, union smb_search_first);
p = req->in.data;
p += req_pull_ascii4(req, &sf->search_first.in.pattern,
p, STR_TERMINATE);
if (!sf->search_first.in.pattern) {
smbsrv_send_error(req, NT_STATUS_OBJECT_NAME_NOT_FOUND);
return;
}
if (req_data_oob(req, p, 3)) {
smbsrv_send_error(req, NT_STATUS_INVALID_PARAMETER);
return;
}
if (*p != 5) {
smbsrv_send_error(req, NT_STATUS_INVALID_PARAMETER);
return;
}
resume_key_length = SVAL(p, 1);
p += 3;
/* setup state for callback */
state = talloc(req, struct search_state);
if (!state) {
smbsrv_send_error(req, NT_STATUS_NO_MEMORY);
return;
}
state->req = req;
state->file = NULL;
state->last_entry_offset = 0;
/* construct reply */
smbsrv_setup_reply(req, 1, 0);
SSVAL(req->out.vwv, VWV(0), 0);
req_append_var_block(req, NULL, 0);
if (resume_key_length != 0) {
union smb_search_next *sn;
if (resume_key_length != 21 ||
req_data_oob(req, p, 21) ||
level == RAW_SEARCH_FUNIQUE) {
smbsrv_send_error(req, NT_STATUS_INVALID_PARAMETER);
return;
}
/* do a search next operation */
SMBSRV_TALLOC_IO_PTR(sn, union smb_search_next);
SMBSRV_SETUP_NTVFS_REQUEST(reply_search_next_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
sn->search_next.in.id.reserved = CVAL(p, 0);
memcpy(sn->search_next.in.id.name, p+1, 11);
sn->search_next.in.id.handle = CVAL(p, 12);
sn->search_next.in.id.server_cookie = IVAL(p, 13);
sn->search_next.in.id.client_cookie = IVAL(p, 17);
sn->search_next.level = level;
sn->search_next.data_level = RAW_SEARCH_DATA_SEARCH;
sn->search_next.in.max_count = SVAL(req->in.vwv, VWV(0));
sn->search_next.in.search_attrib = SVAL(req->in.vwv, VWV(1));
/* call backend */
SMBSRV_CALL_NTVFS_BACKEND(ntvfs_search_next(req->ntvfs, sn, state, find_callback));
} else {
SMBSRV_SETUP_NTVFS_REQUEST(reply_search_first_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
/* do a search first operation */
sf->search_first.level = level;
sf->search_first.data_level = RAW_SEARCH_DATA_SEARCH;
sf->search_first.in.search_attrib = SVAL(req->in.vwv, VWV(1));
sf->search_first.in.max_count = SVAL(req->in.vwv, VWV(0));
SMBSRV_CALL_NTVFS_BACKEND(ntvfs_search_first(req->ntvfs, sf, state, find_callback));
}
}
/****************************************************************************
Reply to a fclose (async reply)
****************************************************************************/
static void reply_fclose_send(struct ntvfs_request *ntvfs)
{
struct smbsrv_request *req;
SMBSRV_CHECK_ASYNC_STATUS_SIMPLE;
/* construct reply */
smbsrv_setup_reply(req, 1, 0);
SSVAL(req->out.vwv, VWV(0), 0);
smbsrv_send_reply(req);
}
/****************************************************************************
Reply to fclose (stop directory search).
****************************************************************************/
void smbsrv_reply_fclose(struct smbsrv_request *req)
{
union smb_search_close *sc;
uint16_t resume_key_length;
uint8_t *p;
const char *pattern;
/* parse request */
if (req->in.wct != 2) {
smbsrv_send_error(req, NT_STATUS_INVALID_PARAMETER);
return;
}
SMBSRV_TALLOC_IO_PTR(sc, union smb_search_close);
SMBSRV_SETUP_NTVFS_REQUEST(reply_fclose_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
p = req->in.data;
p += req_pull_ascii4(req, &pattern, p, STR_TERMINATE);
if (pattern && *pattern) {
smbsrv_send_error(req, NT_STATUS_INVALID_PARAMETER);
return;
}
if (req_data_oob(req, p, 3)) {
smbsrv_send_error(req, NT_STATUS_INVALID_PARAMETER);
return;
}
if (*p != 5) {
smbsrv_send_error(req, NT_STATUS_INVALID_PARAMETER);
return;
}
resume_key_length = SVAL(p, 1);
p += 3;
if (resume_key_length != 21) {
smbsrv_send_error(req, NT_STATUS_INVALID_PARAMETER);
return;
}
if (req_data_oob(req, p, 21)) {
smbsrv_send_error(req, NT_STATUS_INVALID_PARAMETER);
return;
}
sc->fclose.level = RAW_FINDCLOSE_FCLOSE;
sc->fclose.in.max_count = SVAL(req->in.vwv, VWV(0));
sc->fclose.in.search_attrib = SVAL(req->in.vwv, VWV(1));
sc->fclose.in.id.reserved = CVAL(p, 0);
memcpy(sc->fclose.in.id.name, p+1, 11);
sc->fclose.in.id.handle = CVAL(p, 12);
sc->fclose.in.id.server_cookie = IVAL(p, 13);
sc->fclose.in.id.client_cookie = IVAL(p, 17);
SMBSRV_CALL_NTVFS_BACKEND(ntvfs_search_close(req->ntvfs, sc));
}
+206
View File
@@ -0,0 +1,206 @@
/*
Unix SMB/CIFS implementation.
service (connection) handling
Copyright (C) Andrew Tridgell 1992-2003
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 "smb_server/smb_server.h"
#include "smbd/service_stream.h"
#include "ntvfs/ntvfs.h"
/****************************************************************************
Make a connection, given the snum to connect to, and the vuser of the
connecting user if appropriate.
****************************************************************************/
static NTSTATUS make_connection_scfg(struct smbsrv_request *req,
struct share_config *scfg,
enum ntvfs_type type,
DATA_BLOB password,
const char *dev)
{
struct smbsrv_tcon *tcon;
NTSTATUS status;
tcon = smbsrv_smb_tcon_new(req->smb_conn, scfg->name);
if (!tcon) {
DEBUG(0,("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, ("make_connection_scfg: connection failed for service %s\n",
scfg->name));
goto failed;
}
status = ntvfs_set_oplock_handler(tcon->ntvfs, smbsrv_send_oplock_break, tcon);
if (!NT_STATUS_IS_OK(status)) {
DEBUG(0,("make_connection: 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,("make_connection: NTVFS failed to set the addr callbacks!\n"));
goto failed;
}
status = ntvfs_set_handle_callbacks(tcon->ntvfs,
smbsrv_handle_create_new,
smbsrv_handle_make_valid,
smbsrv_handle_destroy,
smbsrv_handle_search_by_wire_key,
smbsrv_handle_get_wire_key,
tcon);
if (!NT_STATUS_IS_OK(status)) {
DEBUG(0,("make_connection: NTVFS failed to set the handle callbacks!\n"));
goto failed;
}
req->ntvfs = ntvfs_request_create(req->tcon->ntvfs, req,
req->session->session_info,
SVAL(req->in.hdr,HDR_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,("make_connection: NTVFS make connection failed!\n"));
goto failed;
}
return NT_STATUS_OK;
failed:
req->tcon = NULL;
talloc_free(tcon);
return status;
}
/****************************************************************************
Make a connection to a service.
*
* @param service
****************************************************************************/
static NTSTATUS make_connection(struct smbsrv_request *req,
const char *service, DATA_BLOB password,
const char *dev)
{
NTSTATUS status;
enum ntvfs_type type;
const char *type_str;
struct share_config *scfg;
const char *sharetype;
/* the service might be of the form \\SERVER\SHARE. Should we put
the server name we get from this somewhere? */
if (strncmp(service, "\\\\", 2) == 0) {
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,("make_connection: couldn't find service %s\n", service));
return NT_STATUS_BAD_NETWORK_NAME;
}
/* TODO: check the password, when it's share level security! */
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, "type", "DISK");
if (sharetype && strcmp(sharetype, "IPC") == 0) {
type = NTVFS_IPC;
type_str = "IPC";
} else if (sharetype && strcmp(sharetype, "PRINTER") == 0) {
type = NTVFS_PRINT;
type_str = "LPT:";
} else {
type = NTVFS_DISK;
type_str = "A:";
}
if (strcmp(dev, "?????") != 0 && strcasecmp(type_str, dev) != 0) {
/* the client gave us the wrong device type */
return NT_STATUS_BAD_DEVICE_TYPE;
}
return make_connection_scfg(req, scfg, type, password, dev);
}
/*
backend for tree connect call
*/
NTSTATUS smbsrv_tcon_backend(struct smbsrv_request *req, union smb_tcon *con)
{
NTSTATUS status;
if (con->generic.level == RAW_TCON_TCON) {
DATA_BLOB password;
password = data_blob(con->tcon.in.password, strlen(con->tcon.in.password) + 1);
status = make_connection(req, con->tcon.in.service, password, con->tcon.in.dev);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
con->tcon.out.max_xmit = req->smb_conn->negotiate.max_recv;
con->tcon.out.tid = req->tcon->tid;
return status;
}
status = make_connection(req, con->tconx.in.path, con->tconx.in.password,
con->tconx.in.device);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
con->tconx.out.tid = req->tcon->tid;
con->tconx.out.dev_type = talloc_strdup(req, req->tcon->ntvfs->dev_type);
con->tconx.out.fs_type = talloc_strdup(req, req->tcon->ntvfs->fs_type);
con->tconx.out.options = SMB_SUPPORT_SEARCH_BITS | (share_int_option(req->tcon->ntvfs->config, SHARE_CSC_POLICY, SHARE_CSC_POLICY_DEFAULT) << 2);
if (share_bool_option(req->tcon->ntvfs->config, SHARE_MSDFS_ROOT, SHARE_MSDFS_ROOT_DEFAULT) && lp_host_msdfs()) {
con->tconx.out.options |= SMB_SHARE_IN_DFS;
}
return status;
}
+457
View File
@@ -0,0 +1,457 @@
/*
Unix SMB/CIFS implementation.
handle SMBsessionsetup
Copyright (C) Andrew Tridgell 1998-2001
Copyright (C) Andrew Bartlett <abartlet@samba.org> 2001-2005
Copyright (C) Jim McDonough 2002
Copyright (C) Luke Howard 2003
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 "version.h"
#include "auth/credentials/credentials.h"
#include "auth/gensec/gensec.h"
#include "auth/auth.h"
#include "smb_server/smb_server.h"
#include "smbd/service_stream.h"
#include "librpc/gen_ndr/nbt.h"
/*
setup the OS, Lanman and domain portions of a session setup reply
*/
static void sesssetup_common_strings(struct smbsrv_request *req,
char **os, char **lanman, char **domain)
{
(*os) = talloc_asprintf(req, "Unix");
(*lanman) = talloc_asprintf(req, "Samba %s", SAMBA_VERSION_STRING);
(*domain) = talloc_asprintf(req, "%s", lp_workgroup());
}
static void smbsrv_sesssetup_backend_send(struct smbsrv_request *req,
union smb_sesssetup *sess,
NTSTATUS status)
{
if (NT_STATUS_IS_OK(status)) {
req->smb_conn->negotiate.done_sesssetup = True;
}
smbsrv_reply_sesssetup_send(req, sess, status);
}
static void sesssetup_old_send(struct auth_check_password_request *areq,
void *private_data)
{
struct smbsrv_request *req = talloc_get_type(private_data, struct smbsrv_request);
union smb_sesssetup *sess = talloc_get_type(req->io_ptr, union smb_sesssetup);
struct auth_serversupplied_info *server_info = NULL;
struct auth_session_info *session_info;
struct smbsrv_session *smb_sess;
NTSTATUS status;
status = auth_check_password_recv(areq, req, &server_info);
if (!NT_STATUS_IS_OK(status)) goto failed;
/* This references server_info into session_info */
status = auth_generate_session_info(req, server_info, &session_info);
if (!NT_STATUS_IS_OK(status)) goto failed;
/* allocate a new session */
smb_sess = smbsrv_session_new(req->smb_conn, NULL);
if (!smb_sess) {
status = NT_STATUS_INSUFFICIENT_RESOURCES;
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;
/* To correctly process any AndX packet (like a tree connect)
* we need to fill in the session on the request here */
req->session = smb_sess;
sess->old.out.vuid = smb_sess->vuid;
failed:
status = auth_nt_status_squash(status);
smbsrv_sesssetup_backend_send(req, sess, status);
}
/*
handler for old style session setup
*/
static void sesssetup_old(struct smbsrv_request *req, union smb_sesssetup *sess)
{
struct auth_usersupplied_info *user_info = NULL;
struct socket_address *remote_address;
const char *remote_machine = NULL;
sess->old.out.vuid = 0;
sess->old.out.action = 0;
sesssetup_common_strings(req,
&sess->old.out.os,
&sess->old.out.lanman,
&sess->old.out.domain);
if (!req->smb_conn->negotiate.done_sesssetup) {
req->smb_conn->negotiate.max_send = sess->old.in.bufsize;
}
if (req->smb_conn->negotiate.calling_name) {
remote_machine = req->smb_conn->negotiate.calling_name->name;
}
remote_address = socket_get_peer_addr(req->smb_conn->connection->socket, req);
if (!remote_address) goto nomem;
if (!remote_machine) {
remote_machine = remote_address->addr;
}
user_info = talloc(req, struct auth_usersupplied_info);
if (!user_info) goto nomem;
user_info->mapped_state = False;
user_info->logon_parameters = 0;
user_info->flags = 0;
user_info->client.account_name = sess->old.in.user;
user_info->client.domain_name = sess->old.in.domain;
user_info->workstation_name = remote_machine;
user_info->remote_host = talloc_steal(user_info, remote_address);
user_info->password_state = AUTH_PASSWORD_RESPONSE;
user_info->password.response.lanman = sess->old.in.password;
user_info->password.response.lanman.data = talloc_steal(user_info, sess->old.in.password.data);
user_info->password.response.nt = data_blob(NULL, 0);
auth_check_password_send(req->smb_conn->negotiate.auth_context, user_info,
sesssetup_old_send, req);
return;
nomem:
smbsrv_sesssetup_backend_send(req, sess, NT_STATUS_NO_MEMORY);
}
static void sesssetup_nt1_send(struct auth_check_password_request *areq,
void *private_data)
{
struct smbsrv_request *req = talloc_get_type(private_data, struct smbsrv_request);
union smb_sesssetup *sess = talloc_get_type(req->io_ptr, union smb_sesssetup);
struct auth_serversupplied_info *server_info = NULL;
struct auth_session_info *session_info;
struct smbsrv_session *smb_sess;
NTSTATUS status;
status = auth_check_password_recv(areq, req, &server_info);
if (!NT_STATUS_IS_OK(status)) goto failed;
/* This references server_info into session_info */
status = auth_generate_session_info(req, server_info, &session_info);
if (!NT_STATUS_IS_OK(status)) goto failed;
/* allocate a new session */
smb_sess = smbsrv_session_new(req->smb_conn, NULL);
if (!smb_sess) {
status = NT_STATUS_INSUFFICIENT_RESOURCES;
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;
/* To correctly process any AndX packet (like a tree connect)
* we need to fill in the session on the request here */
req->session = smb_sess;
sess->nt1.out.vuid = smb_sess->vuid;
if (!session_info->server_info->authenticated) {
/* don't try signing as anonymous */
goto done;
}
if (!smbsrv_setup_signing(req->smb_conn, &session_info->session_key, &sess->nt1.in.password2)) {
/* Already signing, or disabled */
goto done;
}
/* Force check of the request packet, now we know the session key */
smbsrv_signing_check_incoming(req);
/* TODO: why don't we check the result here? */
/* Unfortunetly win2k3 as a client doesn't sign the request
* packet here, so we have to force signing to start again */
smbsrv_signing_restart(req->smb_conn, &session_info->session_key, &sess->nt1.in.password2);
done:
status = NT_STATUS_OK;
failed:
status = auth_nt_status_squash(status);
smbsrv_sesssetup_backend_send(req, sess, status);
}
/*
handler for NT1 style session setup
*/
static void sesssetup_nt1(struct smbsrv_request *req, union smb_sesssetup *sess)
{
NTSTATUS status;
struct auth_context *auth_context;
struct auth_usersupplied_info *user_info = NULL;
struct socket_address *remote_address;
const char *remote_machine = NULL;
sess->nt1.out.vuid = 0;
sess->nt1.out.action = 0;
sesssetup_common_strings(req,
&sess->nt1.out.os,
&sess->nt1.out.lanman,
&sess->nt1.out.domain);
if (!req->smb_conn->negotiate.done_sesssetup) {
req->smb_conn->negotiate.max_send = sess->nt1.in.bufsize;
req->smb_conn->negotiate.client_caps = sess->nt1.in.capabilities;
}
if (req->smb_conn->negotiate.oid) {
if (sess->nt1.in.user && *sess->nt1.in.user) {
/* We can't accept a normal login, because we
* don't have a challenge */
status = NT_STATUS_LOGON_FAILURE;
goto failed;
}
/* TODO: should we use just "anonymous" here? */
status = auth_context_create(req, lp_auth_methods(),
req->smb_conn->connection->event.ctx,
req->smb_conn->connection->msg_ctx,
&auth_context);
if (!NT_STATUS_IS_OK(status)) goto failed;
} else {
auth_context = req->smb_conn->negotiate.auth_context;
}
if (req->smb_conn->negotiate.calling_name) {
remote_machine = req->smb_conn->negotiate.calling_name->name;
}
remote_address = socket_get_peer_addr(req->smb_conn->connection->socket, req);
if (!remote_address) goto nomem;
if (!remote_machine) {
remote_machine = remote_address->addr;
}
user_info = talloc(req, struct auth_usersupplied_info);
if (!user_info) goto nomem;
user_info->mapped_state = False;
user_info->logon_parameters = 0;
user_info->flags = 0;
user_info->client.account_name = sess->nt1.in.user;
user_info->client.domain_name = sess->nt1.in.domain;
user_info->workstation_name = remote_machine;
user_info->remote_host = talloc_steal(user_info, remote_address);
user_info->password_state = AUTH_PASSWORD_RESPONSE;
user_info->password.response.lanman = sess->nt1.in.password1;
user_info->password.response.lanman.data = talloc_steal(user_info, sess->nt1.in.password1.data);
user_info->password.response.nt = sess->nt1.in.password2;
user_info->password.response.nt.data = talloc_steal(user_info, sess->nt1.in.password2.data);
auth_check_password_send(auth_context, user_info,
sesssetup_nt1_send, req);
return;
nomem:
status = NT_STATUS_NO_MEMORY;
failed:
status = auth_nt_status_squash(status);
smbsrv_sesssetup_backend_send(req, sess, status);
}
struct sesssetup_spnego_state {
struct smbsrv_request *req;
union smb_sesssetup *sess;
struct smbsrv_session *smb_sess;
};
static void sesssetup_spnego_send(struct gensec_update_request *greq, void *private_data)
{
struct sesssetup_spnego_state *s = talloc_get_type(private_data,
struct sesssetup_spnego_state);
struct smbsrv_request *req = s->req;
union smb_sesssetup *sess = s->sess;
struct smbsrv_session *smb_sess = s->smb_sess;
struct auth_session_info *session_info = NULL;
NTSTATUS status;
NTSTATUS skey_status;
DATA_BLOB session_key;
status = gensec_update_recv(greq, req, &sess->spnego.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;
skey_status = gensec_session_key(smb_sess->gensec_ctx, &session_key);
if (NT_STATUS_IS_OK(skey_status) &&
session_info->server_info->authenticated &&
smbsrv_setup_signing(req->smb_conn, &session_key, NULL)) {
/* Force check of the request packet, now we know the session key */
smbsrv_signing_check_incoming(req);
smbsrv_signing_restart(req->smb_conn, &session_key, NULL);
}
/* 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:
sess->spnego.out.vuid = smb_sess->vuid;
failed:
status = auth_nt_status_squash(status);
smbsrv_sesssetup_backend_send(req, sess, status);
}
/*
handler for SPNEGO style session setup
*/
static void sesssetup_spnego(struct smbsrv_request *req, union smb_sesssetup *sess)
{
NTSTATUS status;
struct smbsrv_session *smb_sess = NULL;
struct sesssetup_spnego_state *s = NULL;
uint16_t vuid;
sess->spnego.out.vuid = 0;
sess->spnego.out.action = 0;
sesssetup_common_strings(req,
&sess->spnego.out.os,
&sess->spnego.out.lanman,
&sess->spnego.out.workgroup);
if (!req->smb_conn->negotiate.done_sesssetup) {
req->smb_conn->negotiate.max_send = sess->spnego.in.bufsize;
req->smb_conn->negotiate.client_caps = sess->spnego.in.capabilities;
}
vuid = SVAL(req->in.hdr,HDR_UID);
/* lookup an existing session */
smb_sess = smbsrv_session_find_sesssetup(req->smb_conn, vuid);
if (!smb_sess) {
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, req->smb_conn->negotiate.oid);
if (!NT_STATUS_IS_OK(status)) {
DEBUG(1, ("Failed to start GENSEC %s server code: %s\n",
gensec_get_name_by_oid(req->smb_conn->negotiate.oid), 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;
}
}
if (!smb_sess) {
status = NT_STATUS_ACCESS_DENIED;
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;
}
s = talloc(req, struct sesssetup_spnego_state);
if (!s) goto nomem;
s->req = req;
s->sess = sess;
s->smb_sess = smb_sess;
gensec_update_send(smb_sess->gensec_ctx, sess->spnego.in.secblob,
sesssetup_spnego_send, s);
return;
nomem:
status = NT_STATUS_NO_MEMORY;
failed:
talloc_free(smb_sess);
status = auth_nt_status_squash(status);
smbsrv_sesssetup_backend_send(req, sess, status);
}
/*
backend for sessionsetup call - this takes all 3 variants of the call
*/
void smbsrv_sesssetup_backend(struct smbsrv_request *req,
union smb_sesssetup *sess)
{
switch (sess->old.level) {
case RAW_SESSSETUP_OLD:
sesssetup_old(req, sess);
return;
case RAW_SESSSETUP_NT1:
sesssetup_nt1(req, sess);
return;
case RAW_SESSSETUP_SPNEGO:
sesssetup_spnego(req, sess);
return;
case RAW_SESSSETUP_SMB2:
break;
}
smbsrv_sesssetup_backend_send(req, sess, NT_STATUS_INVALID_LEVEL);
}
+183
View File
@@ -0,0 +1,183 @@
/*
Unix SMB/CIFS implementation.
Copyright (C) Andrew Tridgell 2004
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 "smb_server/smb_server.h"
#include "libcli/raw/libcliraw.h"
/*
sign an outgoing packet
*/
void smbsrv_sign_packet(struct smbsrv_request *req)
{
#if 0
/* enable this when packet signing is preventing you working out why valgrind
says that data is uninitialised */
file_save("pkt.dat", req->out.buffer, req->out.size);
#endif
switch (req->smb_conn->signing.signing_state) {
case SMB_SIGNING_ENGINE_OFF:
break;
case SMB_SIGNING_ENGINE_BSRSPYL:
/* mark the packet as signed - BEFORE we sign it...*/
mark_packet_signed(&req->out);
/* I wonder what BSRSPYL stands for - but this is what MS
actually sends! */
memcpy((req->out.hdr + HDR_SS_FIELD), "BSRSPYL ", 8);
break;
case SMB_SIGNING_ENGINE_ON:
sign_outgoing_message(&req->out,
&req->smb_conn->signing.mac_key,
req->seq_num+1);
break;
}
return;
}
/*
setup the signing key for a connection. Called after authentication succeeds
in a session setup
*/
BOOL smbsrv_setup_signing(struct smbsrv_connection *smb_conn,
DATA_BLOB *session_key,
DATA_BLOB *response)
{
if (!set_smb_signing_common(&smb_conn->signing)) {
return False;
}
return smbcli_simple_set_signing(smb_conn,
&smb_conn->signing, session_key, response);
}
void smbsrv_signing_restart(struct smbsrv_connection *smb_conn,
DATA_BLOB *session_key,
DATA_BLOB *response)
{
if (!smb_conn->signing.seen_valid) {
DEBUG(5, ("Client did not send a valid signature on "
"SPNEGO session setup - ignored, expect good next time\n"));
/* force things back on (most clients do not sign this packet)... */
smbsrv_setup_signing(smb_conn, session_key, response);
smb_conn->signing.next_seq_num = 2;
if (smb_conn->signing.mandatory_signing) {
DEBUG(5, ("Configured for mandatory signing, 'good packet seen' forced on\n"));
/* if this is mandatory, then
* pretend we have seen a
* valid packet, so we don't
* turn it off */
smb_conn->signing.seen_valid = True;
}
}
}
BOOL smbsrv_init_signing(struct smbsrv_connection *smb_conn)
{
smb_conn->signing.mac_key = data_blob(NULL, 0);
if (!smbcli_set_signing_off(&smb_conn->signing)) {
return False;
}
switch (lp_server_signing()) {
case SMB_SIGNING_OFF:
smb_conn->signing.allow_smb_signing = False;
break;
case SMB_SIGNING_SUPPORTED:
smb_conn->signing.allow_smb_signing = True;
break;
case SMB_SIGNING_REQUIRED:
smb_conn->signing.allow_smb_signing = True;
smb_conn->signing.mandatory_signing = True;
break;
case SMB_SIGNING_AUTO:
if (lp_domain_logons()) {
smb_conn->signing.allow_smb_signing = True;
} else {
smb_conn->signing.allow_smb_signing = False;
}
break;
}
return True;
}
/*
allocate a sequence number to a request
*/
static void req_signing_alloc_seq_num(struct smbsrv_request *req)
{
req->seq_num = req->smb_conn->signing.next_seq_num;
if (req->smb_conn->signing.signing_state != SMB_SIGNING_ENGINE_OFF) {
req->smb_conn->signing.next_seq_num += 2;
}
}
/*
called for requests that do not produce a reply of their own
*/
void smbsrv_signing_no_reply(struct smbsrv_request *req)
{
if (req->smb_conn->signing.signing_state != SMB_SIGNING_ENGINE_OFF) {
req->smb_conn->signing.next_seq_num--;
}
}
/***********************************************************
SMB signing - Simple implementation - check a MAC sent by client
************************************************************/
/**
* Check a packet supplied by the server.
* @return False if we had an established signing connection
* which had a back checksum, True otherwise
*/
BOOL smbsrv_signing_check_incoming(struct smbsrv_request *req)
{
BOOL good;
req_signing_alloc_seq_num(req);
switch (req->smb_conn->signing.signing_state)
{
case SMB_SIGNING_ENGINE_OFF:
return True;
case SMB_SIGNING_ENGINE_BSRSPYL:
case SMB_SIGNING_ENGINE_ON:
{
if (req->in.size < (HDR_SS_FIELD + 8)) {
return False;
} else {
good = check_signed_incoming_message(&req->in,
&req->smb_conn->signing.mac_key,
req->seq_num);
return signing_good(&req->smb_conn->signing,
req->seq_num+1, good);
}
}
}
return False;
}
+83
View File
@@ -0,0 +1,83 @@
/*
Unix SMB/CIFS implementation.
server side time handling
Copyright (C) Andrew Tridgell 2004
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 "smb_server/smb_server.h"
/*******************************************************************
put a dos date into a buffer (time/date format)
This takes GMT time and puts local time for zone_offset in the buffer
********************************************************************/
void srv_push_dos_date(struct smbsrv_connection *smb_server,
uint8_t *buf, int offset, time_t unixdate)
{
push_dos_date(buf, offset, unixdate, smb_server->negotiate.zone_offset);
}
/*******************************************************************
put a dos date into a buffer (date/time format)
This takes GMT time and puts local time in the buffer
********************************************************************/
void srv_push_dos_date2(struct smbsrv_connection *smb_server,
uint8_t *buf, int offset, time_t unixdate)
{
push_dos_date2(buf, offset, unixdate, smb_server->negotiate.zone_offset);
}
/*******************************************************************
put a dos 32 bit "unix like" date into a buffer. This routine takes
GMT and converts it to LOCAL time in zone_offset before putting it
********************************************************************/
void srv_push_dos_date3(struct smbsrv_connection *smb_server,
uint8_t *buf, int offset, time_t unixdate)
{
push_dos_date3(buf, offset, unixdate, smb_server->negotiate.zone_offset);
}
/*******************************************************************
convert a dos date
********************************************************************/
time_t srv_pull_dos_date(struct smbsrv_connection *smb_server,
const uint8_t *date_ptr)
{
return pull_dos_date(date_ptr, smb_server->negotiate.zone_offset);
}
/*******************************************************************
like srv_pull_dos_date() but the words are reversed
********************************************************************/
time_t srv_pull_dos_date2(struct smbsrv_connection *smb_server,
const uint8_t *date_ptr)
{
return pull_dos_date2(date_ptr, smb_server->negotiate.zone_offset);
}
/*******************************************************************
create a unix GMT date from a dos date in 32 bit "unix like" format
these arrive in server zone, with corresponding DST
******************************************************************/
time_t srv_pull_dos_date3(struct smbsrv_connection *smb_server,
const uint8_t *date_ptr)
{
return pull_dos_date3(date_ptr, smb_server->negotiate.zone_offset);
}
File diff suppressed because it is too large Load Diff