From 6d34ab056f70d85c3207696fac4fbb9e7f437b14 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Fri, 18 Nov 2005 13:30:18 +0000 Subject: r11786: move all SMB protocol specific stuff to smb_server/smb/ metze (This used to be commit 5fea278cb65076cea71bb6c921e51c4feffc37d7) --- source4/smb_server/config.mk | 15 +- source4/smb_server/negprot.c | 472 ------- source4/smb_server/nttrans.c | 518 -------- source4/smb_server/receive.c | 657 ---------- source4/smb_server/reply.c | 2461 ------------------------------------ source4/smb_server/request.c | 675 ---------- source4/smb_server/search.c | 285 ----- source4/smb_server/service.c | 194 --- source4/smb_server/sesssetup.c | 370 ------ source4/smb_server/signing.c | 182 --- source4/smb_server/smb/negprot.c | 472 +++++++ source4/smb_server/smb/nttrans.c | 518 ++++++++ source4/smb_server/smb/receive.c | 657 ++++++++++ source4/smb_server/smb/reply.c | 2461 ++++++++++++++++++++++++++++++++++++ source4/smb_server/smb/request.c | 675 ++++++++++ source4/smb_server/smb/search.c | 285 +++++ source4/smb_server/smb/service.c | 194 +++ source4/smb_server/smb/sesssetup.c | 370 ++++++ source4/smb_server/smb/signing.c | 182 +++ source4/smb_server/smb/srvtime.c | 83 ++ source4/smb_server/smb/trans2.c | 1779 ++++++++++++++++++++++++++ source4/smb_server/srvtime.c | 83 -- source4/smb_server/trans2.c | 1779 -------------------------- 23 files changed, 7679 insertions(+), 7688 deletions(-) delete mode 100644 source4/smb_server/negprot.c delete mode 100644 source4/smb_server/nttrans.c delete mode 100644 source4/smb_server/receive.c delete mode 100644 source4/smb_server/reply.c delete mode 100644 source4/smb_server/request.c delete mode 100644 source4/smb_server/search.c delete mode 100644 source4/smb_server/service.c delete mode 100644 source4/smb_server/sesssetup.c delete mode 100644 source4/smb_server/signing.c create mode 100644 source4/smb_server/smb/negprot.c create mode 100644 source4/smb_server/smb/nttrans.c create mode 100644 source4/smb_server/smb/receive.c create mode 100644 source4/smb_server/smb/reply.c create mode 100644 source4/smb_server/smb/request.c create mode 100644 source4/smb_server/smb/search.c create mode 100644 source4/smb_server/smb/service.c create mode 100644 source4/smb_server/smb/sesssetup.c create mode 100644 source4/smb_server/smb/signing.c create mode 100644 source4/smb_server/smb/srvtime.c create mode 100644 source4/smb_server/smb/trans2.c delete mode 100644 source4/smb_server/srvtime.c delete mode 100644 source4/smb_server/trans2.c diff --git a/source4/smb_server/config.mk b/source4/smb_server/config.mk index cc2d77d7a2..1dd2632890 100644 --- a/source4/smb_server/config.mk +++ b/source4/smb_server/config.mk @@ -7,20 +7,11 @@ INIT_OBJ_FILES = \ smb_server.o ADD_OBJ_FILES = \ tcon.o \ - negprot.o \ - nttrans.o \ session.o \ - receive.o \ - reply.o \ - request.o \ - search.o \ - service.o \ - sesssetup.o \ - srvtime.o \ - trans2.o \ - signing.o \ management.o REQUIRED_SUBSYSTEMS = \ - NTVFS LIBPACKET + LIBPACKET SMB_PROTOCOL # End SUBSYSTEM SMB ####################### + +include smb/config.mk diff --git a/source4/smb_server/negprot.c b/source4/smb_server/negprot.c deleted file mode 100644 index a9cc05e251..0000000000 --- a/source4/smb_server/negprot.c +++ /dev/null @@ -1,472 +0,0 @@ -/* - 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/auth.h" -#include "smb_server/smb_server.h" -#include "smbd/service_stream.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->negotiate.auth_context, - smb_conn->connection->event.ctx); - 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) -{ - req_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; - } - - req_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); - - req_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; - } - - req_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; - - req_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; - } - - req_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; - - req_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; - } - - req_send_reply(req); -} - -/**************************************************************************** - 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; - - 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; - } - - 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; - - req_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) { - /* 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 SPNEGO\n")); - } else { - struct cli_credentials *server_credentials; - struct gensec_security *gensec_security; - DATA_BLOB null_data_blob = data_blob(NULL, 0); - DATA_BLOB blob; - NTSTATUS nt_status = gensec_server_start(req->smb_conn, - &gensec_security, - req->smb_conn->connection->event.ctx); - - 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); - - nt_status = gensec_start_mech_by_oid(gensec_security, GENSEC_OID_SPNEGO); - - if (!NT_STATUS_IS_OK(nt_status)) { - DEBUG(0, ("Failed to start SPNEGO: %s\n", nt_errstr(nt_status))); - smbsrv_terminate_connection(req->smb_conn, "Failed to start SPNEGO\n"); - return; - } - - nt_status = gensec_update(gensec_security, req, null_data_blob, &blob); - - if (!NT_STATUS_IS_OK(nt_status) && !NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { - DEBUG(0, ("Failed to get SPNEGO to give us the first token: %s\n", nt_errstr(nt_status))); - smbsrv_terminate_connection(req->smb_conn, "Failed to start SPNEGO - no first token\n"); - return; - } - - req->smb_conn->negotiate.spnego_negotiated = True; - - req_grow_data(req, blob.length + 16); - /* a NOT very random guid */ - 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; - DEBUG(3,("using SPNEGO\n")); - } - - req_send_reply_nosign(req); -} - - -/* 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[] = { - {"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}, - {"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 reply_negprot(struct smbsrv_request *req) -{ - int Index=0; - int choice = -1; - int protocol; - uint8_t *p; - - 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 + 1; - - while (p < req->in.data + req->in.data_size) { - Index++; - DEBUG(3,("Requested protocol [%s]\n",(const char *)p)); - p += strlen((const char *)p) + 2; - } - - /* Check for protocols, most desirable first */ - for (protocol = 0; supported_protocols[protocol].proto_name; protocol++) { - p = req->in.data+1; - Index = 0; - if ((supported_protocols[protocol].protocol_level <= lp_maxprotocol()) && - (supported_protocols[protocol].protocol_level >= lp_minprotocol())) - while (p < (req->in.data + req->in.data_size)) { - if (strequal((const char *)p,supported_protocols[protocol].proto_name)) - choice = Index; - Index++; - p += strlen((const char *)p) + 2; - } - if(choice != -1) - break; - } - - if(choice != -1) { - sub_set_remote_proto(supported_protocols[protocol].short_name); - supported_protocols[protocol].proto_reply_fn(req, choice); - DEBUG(3,("Selected protocol %s\n",supported_protocols[protocol].proto_name)); - } else { - DEBUG(0,("No protocol supported !\n")); - } - - DEBUG(5,("negprot index=%d\n", choice)); -} diff --git a/source4/smb_server/nttrans.c b/source4/smb_server/nttrans.c deleted file mode 100644 index 215b378283..0000000000 --- a/source4/smb_server/nttrans.c +++ /dev/null @@ -1,518 +0,0 @@ -/* - Unix SMB/CIFS implementation. - NT transaction handling - Copyright (C) Andrew Tridgell 2003 - Copyright (C) James J Myers 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 handles the parsing of transact2 requests -*/ - -#include "includes.h" -#include "smb_server/smb_server.h" -#include "librpc/gen_ndr/ndr_security.h" - - - -#define CHECK_MIN_BLOB_SIZE(blob, size) do { \ - if ((blob)->length < (size)) { \ - return NT_STATUS_INFO_LENGTH_MISMATCH; \ - }} while (0) - - -/* setup a nttrans reply, given the data and params sizes */ -static void nttrans_setup_reply(struct smbsrv_request *req, - struct smb_nttrans *trans, - uint16_t param_size, uint16_t data_size, - uint16_t setup_count) -{ - trans->out.setup_count = setup_count; - if (setup_count != 0) { - trans->out.setup = talloc_zero_array(req, uint16_t, setup_count); - } - trans->out.params = data_blob_talloc(req, NULL, param_size); - trans->out.data = data_blob_talloc(req, NULL, data_size); -} - - -/* - parse NTTRANS_CREATE request - */ -static NTSTATUS nttrans_create(struct smbsrv_request *req, - struct smb_nttrans *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(req, union smb_open); - if (io == NULL) { - return NT_STATUS_NO_MEMORY; - } - - 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 + 54, - trans->in.params.length - 54, - 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; - } - } - - /* call the backend - notice that we do it sync for now, until we support - async nttrans requests */ - status = ntvfs_openfile(req, io); - if (!NT_STATUS_IS_OK(status)) { - return status; - } - - trans->out.setup_count = 0; - trans->out.setup = NULL; - trans->out.params = data_blob_talloc(req, NULL, 69); - trans->out.data = data_blob(NULL, 0); - - params = trans->out.params.data; - if (params == NULL) { - return NT_STATUS_NO_MEMORY; - } - - SSVAL(params, 0, io->ntcreatex.out.oplock_level); - SSVAL(params, 2, io->ntcreatex.out.fnum); - 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_QUERY_SEC_DESC request - */ -static NTSTATUS nttrans_query_sec_desc(struct smbsrv_request *req, - struct smb_nttrans *trans) -{ - union smb_fileinfo *io; - NTSTATUS status; - - if (trans->in.params.length < 8) { - return NT_STATUS_INVALID_PARAMETER; - } - - /* parse the request */ - io = talloc(req, union smb_fileinfo); - if (io == NULL) { - return NT_STATUS_NO_MEMORY; - } - - io->query_secdesc.level = RAW_FILEINFO_SEC_DESC; - io->query_secdesc.in.fnum = SVAL(trans->in.params.data, 0); - io->query_secdesc.secinfo_flags = IVAL(trans->in.params.data, 4); - - /* call the backend - notice that we do it sync for now, until we support - async nttrans requests */ - status = ntvfs_qfileinfo(req, io); - if (!NT_STATUS_IS_OK(status)) { - return status; - } - - trans->out.setup_count = 0; - trans->out.setup = NULL; - trans->out.params = data_blob_talloc(req, NULL, 4); - trans->out.data = data_blob(NULL, 0); - - status = ndr_push_struct_blob(&trans->out.data, req, - io->query_secdesc.out.sd, - (ndr_push_flags_fn_t)ndr_push_security_descriptor); - if (!NT_STATUS_IS_OK(status)) { - return status; - } - - SIVAL(trans->out.params.data, 0, trans->out.data.length); - - return NT_STATUS_OK; -} - - -/* - parse NTTRANS_SET_SEC_DESC request - */ -static NTSTATUS nttrans_set_sec_desc(struct smbsrv_request *req, - struct smb_nttrans *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); - if (io == NULL) { - return NT_STATUS_NO_MEMORY; - } - - io->set_secdesc.level = RAW_SFILEINFO_SEC_DESC; - io->set_secdesc.file.fnum = SVAL(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); - if (io->set_secdesc.in.sd == NULL) { - return NT_STATUS_NO_MEMORY; - } - - status = ndr_pull_struct_blob(&trans->in.data, req, - io->set_secdesc.in.sd, - (ndr_pull_flags_fn_t)ndr_pull_security_descriptor); - if (!NT_STATUS_IS_OK(status)) { - return status; - } - - /* call the backend - notice that we do it sync for now, until we support - async nttrans requests */ - status = ntvfs_setfileinfo(req, io); - if (!NT_STATUS_IS_OK(status)) { - return status; - } - - trans->out.setup_count = 0; - trans->out.setup = NULL; - trans->out.params = data_blob(NULL, 0); - trans->out.data = data_blob(NULL, 0); - - return NT_STATUS_OK; -} - - -/* parse NTTRANS_RENAME request - */ -static NTSTATUS nttrans_rename(struct smbsrv_request *req, - struct smb_nttrans *trans) -{ - return NT_STATUS_FOOBAR; -} - -/* - parse NTTRANS_IOCTL request - */ -static NTSTATUS nttrans_ioctl(struct smbsrv_request *req, - struct smb_nttrans *trans) -{ - union smb_ioctl nt; - uint32_t function; - uint16_t fnum; - uint8_t filter; - BOOL fsctl; - DATA_BLOB *blob; - - /* should have at least 4 setup words */ - if (trans->in.setup_count != 4) { - return NT_STATUS_INVALID_PARAMETER; - } - - function = IVAL(trans->in.setup, 0); - fnum = SVAL(trans->in.setup, 4); - fsctl = CVAL(trans->in.setup, 6); - filter = CVAL(trans->in.setup, 7); - - blob = &trans->in.data; - - nt.ntioctl.level = RAW_IOCTL_NTIOCTL; - nt.ntioctl.in.fnum = fnum; - nt.ntioctl.in.function = function; - nt.ntioctl.in.fsctl = fsctl; - nt.ntioctl.in.filter = filter; - - nttrans_setup_reply(req, trans, 0, 0, 1); - trans->out.setup[0] = 0; - - return ntvfs_ioctl(req, &nt); -} - -/* - backend for nttrans requests -*/ -static NTSTATUS nttrans_backend(struct smbsrv_request *req, - struct smb_nttrans *trans) -{ - /* the nttrans command is in function */ - switch (trans->in.function) { - case NT_TRANSACT_CREATE: - return nttrans_create(req, trans); - case NT_TRANSACT_IOCTL: - return nttrans_ioctl(req, trans); - case NT_TRANSACT_RENAME: - return nttrans_rename(req, trans); - case NT_TRANSACT_QUERY_SECURITY_DESC: - return nttrans_query_sec_desc(req, trans); - case NT_TRANSACT_SET_SECURITY_DESC: - return nttrans_set_sec_desc(req, trans); - } - - /* an unknown nttrans command */ - return NT_STATUS_FOOBAR; -} - - -/**************************************************************************** - Reply to an SMBnttrans request -****************************************************************************/ -void reply_nttrans(struct smbsrv_request *req) -{ - struct smb_nttrans trans; - int i; - uint16_t param_ofs, data_ofs; - uint16_t param_count, data_count; - uint16_t params_left, data_left; - uint16_t param_total, data_total; - uint8_t *params, *data; - NTSTATUS status; - - /* parse request */ - if (req->in.wct < 19) { - req_reply_error(req, NT_STATUS_FOOBAR); - return; - } - - 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) { - req_reply_dos_error(req, ERRSRV, ERRerror); - return; - } - - /* parse out the setup words */ - trans.in.setup = talloc_array(req, uint16_t, trans.in.setup_count); - if (!trans.in.setup) { - req_reply_error(req, NT_STATUS_NO_MEMORY); - return; - } - for (i=0;iin.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)) { - req_reply_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")); - return; - } - - /* its a full request, give it to the backend */ - status = nttrans_backend(req, &trans); - - if (NT_STATUS_IS_ERR(status)) { - req_reply_error(req, status); - return; - } - -#if 0 - /* w2k3 does not check the max_setup count */ - if (trans.out.setup_count > trans.in.max_setup) { - req_reply_error(req, NT_STATUS_BUFFER_TOO_SMALL); - return; - } -#endif - if (trans.out.params.length > trans.in.max_param) { - status = NT_STATUS_BUFFER_TOO_SMALL; - trans.out.params.length = trans.in.max_param; - } - if (trans.out.data.length > trans.in.max_data) { - status = 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; - - req_setup_reply(req, 18 + trans.out.setup_count, 0); - - if (!NT_STATUS_IS_OK(status)) { - req_setup_error(req, status); - } - - /* 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; - - 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 = req_setup_secondary(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;iout.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; - - req_send_reply(this_req); - } while (params_left != 0 || data_left != 0); -} - - -/**************************************************************************** - Reply to an SMBnttranss request -****************************************************************************/ -void reply_nttranss(struct smbsrv_request *req) -{ - req_reply_error(req, NT_STATUS_FOOBAR); -} diff --git a/source4/smb_server/receive.c b/source4/smb_server/receive.c deleted file mode 100644 index 0ee87698c8..0000000000 --- a/source4/smb_server/receive.c +++ /dev/null @@ -1,657 +0,0 @@ -/* - Unix SMB/CIFS implementation. - process incoming packets - main loop - Copyright (C) Andrew Tridgell 1992-2005 - Copyright (C) James J Myers 2003 - 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 "lib/events/events.h" -#include "system/time.h" -#include "dlinklist.h" -#include "smbd/service_stream.h" -#include "smb_server/smb_server.h" -#include "lib/messaging/irpc.h" -#include "lib/stream/packet.h" - - -/* - send an oplock break request to a client -*/ -BOOL req_send_oplock_break(struct smbsrv_tcon *tcon, uint16_t fnum, uint8_t level) -{ - struct smbsrv_request *req; - - req = init_smb_request(tcon->smb_conn); - - req_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); - SSVAL(req->out.vwv, VWV(2), fnum); - 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); - - req_send_reply(req); - return True; -} - -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; - uint8_t command; - - /* see if its a special NBT packet */ - if (CVAL(blob.data, 0) != 0) { - req = init_smb_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 = timeval_current(); - - reply_special(req); - return NT_STATUS_OK; - } - - if ((NBT_HDR_SIZE + MIN_SMB_SIZE) > blob.length) { - DEBUG(2,("Invalid SMB packet: length %d\n", 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 %d. Terminating connection\n", - blob.length)); - smbsrv_terminate_connection(smb_conn, "Non-SMB packet"); - return NT_STATUS_OK; - } - - req = init_smb_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 = timeval_current(); - 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", req->in.data_size)); - smbsrv_terminate_connection(req->smb_conn, "Invalid SMB packet"); - return NT_STATUS_OK; - } - - req->flags = CVAL(req->in.hdr, HDR_FLG); - req->flags2 = SVAL(req->in.hdr, HDR_FLG2); - req->smbpid = SVAL(req->in.hdr, HDR_PID); - - if (!req_signing_check_incoming(req)) { - req_reply_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 AS_USER (1<<0) -#define SIGNING_NO_REPLY (1<<1) - -/* - 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",reply_mkdir,AS_USER}, -/* 0x01 */ { "SMBrmdir",reply_rmdir,AS_USER}, -/* 0x02 */ { "SMBopen",reply_open,AS_USER}, -/* 0x03 */ { "SMBcreate",reply_mknew,AS_USER}, -/* 0x04 */ { "SMBclose",reply_close,AS_USER}, -/* 0x05 */ { "SMBflush",reply_flush,AS_USER}, -/* 0x06 */ { "SMBunlink",reply_unlink,AS_USER}, -/* 0x07 */ { "SMBmv",reply_mv,AS_USER}, -/* 0x08 */ { "SMBgetatr",reply_getatr,AS_USER}, -/* 0x09 */ { "SMBsetatr",reply_setatr,AS_USER}, -/* 0x0a */ { "SMBread",reply_read,AS_USER}, -/* 0x0b */ { "SMBwrite",reply_write,AS_USER}, -/* 0x0c */ { "SMBlock",reply_lock,AS_USER}, -/* 0x0d */ { "SMBunlock",reply_unlock,AS_USER}, -/* 0x0e */ { "SMBctemp",reply_ctemp,AS_USER }, -/* 0x0f */ { "SMBmknew",reply_mknew,AS_USER}, -/* 0x10 */ { "SMBchkpth",reply_chkpth,AS_USER}, -/* 0x11 */ { "SMBexit",reply_exit,0}, -/* 0x12 */ { "SMBlseek",reply_lseek,AS_USER}, -/* 0x13 */ { "SMBlockread",reply_lockread,AS_USER}, -/* 0x14 */ { "SMBwriteunlock",reply_writeunlock,AS_USER}, -/* 0x15 */ { NULL, NULL, 0 }, -/* 0x16 */ { NULL, NULL, 0 }, -/* 0x17 */ { NULL, NULL, 0 }, -/* 0x18 */ { NULL, NULL, 0 }, -/* 0x19 */ { NULL, NULL, 0 }, -/* 0x1a */ { "SMBreadbraw",reply_readbraw,AS_USER}, -/* 0x1b */ { "SMBreadBmpx",reply_readbmpx,AS_USER}, -/* 0x1c */ { "SMBreadBs",NULL,0 }, -/* 0x1d */ { "SMBwritebraw",reply_writebraw,AS_USER}, -/* 0x1e */ { "SMBwriteBmpx",reply_writebmpx,AS_USER}, -/* 0x1f */ { "SMBwriteBs",reply_writebs,AS_USER}, -/* 0x20 */ { "SMBwritec",NULL,0}, -/* 0x21 */ { NULL, NULL, 0 }, -/* 0x22 */ { "SMBsetattrE",reply_setattrE,AS_USER}, -/* 0x23 */ { "SMBgetattrE",reply_getattrE,AS_USER}, -/* 0x24 */ { "SMBlockingX",reply_lockingX,AS_USER}, -/* 0x25 */ { "SMBtrans",reply_trans,AS_USER}, -/* 0x26 */ { "SMBtranss",reply_transs,AS_USER}, -/* 0x27 */ { "SMBioctl",reply_ioctl,AS_USER}, -/* 0x28 */ { "SMBioctls",NULL,AS_USER}, -/* 0x29 */ { "SMBcopy",reply_copy,AS_USER}, -/* 0x2a */ { "SMBmove",NULL,AS_USER}, -/* 0x2b */ { "SMBecho",reply_echo,0}, -/* 0x2c */ { "SMBwriteclose",reply_writeclose,AS_USER}, -/* 0x2d */ { "SMBopenX",reply_open_and_X,AS_USER}, -/* 0x2e */ { "SMBreadX",reply_read_and_X,AS_USER}, -/* 0x2f */ { "SMBwriteX",reply_write_and_X,AS_USER}, -/* 0x30 */ { NULL, NULL, 0 }, -/* 0x31 */ { NULL, NULL, 0 }, -/* 0x32 */ { "SMBtrans2", reply_trans2, AS_USER}, -/* 0x33 */ { "SMBtranss2", reply_transs2, AS_USER}, -/* 0x34 */ { "SMBfindclose", reply_findclose,AS_USER}, -/* 0x35 */ { "SMBfindnclose", reply_findnclose, AS_USER}, -/* 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",reply_tcon,0}, -/* 0x71 */ { "SMBtdis",reply_tdis,0}, -/* 0x72 */ { "SMBnegprot",reply_negprot,0}, -/* 0x73 */ { "SMBsesssetupX",reply_sesssetup,0}, -/* 0x74 */ { "SMBulogoffX", reply_ulogoffX, 0}, /* ulogoff doesn't give a valid TID */ -/* 0x75 */ { "SMBtconX",reply_tcon_and_X,0}, -/* 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",reply_dskattr,AS_USER}, -/* 0x81 */ { "SMBsearch",reply_search,AS_USER}, -/* 0x82 */ { "SMBffirst",reply_search,AS_USER}, -/* 0x83 */ { "SMBfunique",reply_search,AS_USER}, -/* 0x84 */ { "SMBfclose",reply_fclose,AS_USER}, -/* 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", reply_nttrans, AS_USER}, -/* 0xa1 */ { "SMBnttranss", reply_nttranss, AS_USER}, -/* 0xa2 */ { "SMBntcreateX", reply_ntcreate_and_X, AS_USER}, -/* 0xa3 */ { NULL, NULL, 0 }, -/* 0xa4 */ { "SMBntcancel", reply_ntcancel, AS_USER|SIGNING_NO_REPLY}, -/* 0xa5 */ { "SMBntrename", reply_ntrename, AS_USER}, -/* 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",reply_printopen,AS_USER }, -/* 0xc1 */ { "SMBsplwr",reply_printwrite,AS_USER}, -/* 0xc2 */ { "SMBsplclose",reply_printclose,AS_USER}, -/* 0xc3 */ { "SMBsplretq",reply_printqueue,AS_USER}, -/* 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",reply_sends,0}, -/* 0xd1 */ { "SMBsendb",NULL,0}, -/* 0xd2 */ { "SMBfwdname",NULL,0}, -/* 0xd3 */ { "SMBcancelf",NULL,0}, -/* 0xd4 */ { "SMBgetmac",NULL,0}, -/* 0xd5 */ { "SMBsendstrt",reply_sendstrt,0}, -/* 0xd6 */ { "SMBsendend",reply_sendend,0}, -/* 0xd7 */ { "SMBsendtxt",reply_sendtxt,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)); - reply_unknown(req); - return; - } - - flags = smb_messages[type].flags; - - req->tcon = smbsrv_tcon_find(smb_conn, SVAL(req->in.hdr,HDR_TID)); - - 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)); - } - } - - DEBUG(3,("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) { - req_signing_no_reply(req); - } - - /* see if the vuid is valid */ - if ((flags & AS_USER) && !req->session) { - /* amazingly, the error code depends on the command */ - switch (type) { - case SMBntcreateX: - case SMBntcancel: - status = NT_STATUS_DOS(ERRSRV, ERRbaduid); - break; - default: - 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))); - req_destroy(req); - return; - } - req_reply_error(req, status); - return; - } - - /* does this protocol need a valid tree connection? */ - if ((flags & AS_USER) && !req->tcon) { - /* amazingly, the error code depends on the command */ - switch (type) { - case SMBntcreateX: - case SMBntcancel: - status = NT_STATUS_DOS(ERRSRV, ERRinvnid); - break; - default: - 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))); - req_destroy(req); - return; - } - req_reply_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 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) { - req_reply_dos_error(req, 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); - req_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); - - /* the current request in the chain might have used an async reply, - but that doesn't mean the next element needs to */ - ZERO_STRUCTP(req->async_states); - - switch_message(chain_cmd, req); - return; - -error: - SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE); - SSVAL(req->out.vwv, VWV(1), 0); - req_reply_dos_error(req, 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_init_tcons(smb_conn, UINT16_MAX); - NT_STATUS_NOT_OK_RETURN(status); - - srv_init_signing(smb_conn); - - return NT_STATUS_OK; -} diff --git a/source4/smb_server/reply.c b/source4/smb_server/reply.c deleted file mode 100644 index eb7b5a1082..0000000000 --- a/source4/smb_server/reply.c +++ /dev/null @@ -1,2461 +0,0 @@ -/* - Unix SMB/CIFS implementation. - Main SMB reply routines - Copyright (C) Andrew Tridgell 1992-2003 - Copyright (C) James J Myers 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 handles most of the reply_ calls that the server - makes to handle specific protocols -*/ - -#include "includes.h" -#include "smb_server/smb_server.h" -#include "libcli/nbt/libnbt.h" - - -/* useful way of catching wct errors with file and line number */ -#define REQ_CHECK_WCT(req, wcount) do { \ - if ((req)->in.wct != (wcount)) { \ - DEBUG(1,("Unexpected WCT %d at %s(%d) - expected %d\n", \ - (req)->in.wct, __FILE__, __LINE__, wcount)); \ - req_reply_dos_error(req, ERRSRV, ERRerror); \ - return; \ - }} while (0) - -/* check req->async_states->status and if not OK then send an error reply */ -#define CHECK_ASYNC_STATUS do { \ - if (!NT_STATUS_IS_OK(req->async_states->status)) { \ - req_reply_error(req, req->async_states->status); \ - return; \ - }} while (0) - -/* useful wrapper for talloc with NO_MEMORY reply */ -#define REQ_TALLOC(ptr, size) do { \ - ptr = talloc_size(req, size); \ - if (!ptr) { \ - req_reply_error(req, NT_STATUS_NO_MEMORY); \ - return; \ - }} while (0) - -/* - check if the backend wants to handle the request asynchronously. - if it wants it handled synchronously then call the send function - immediately -*/ -#define REQ_ASYNC_TAIL do { \ - if (!(req->async_states->state & NTVFS_ASYNC_STATE_ASYNC)) { \ - req->async_states->send_fn(req); \ - }} while (0) - -/* zero out some reserved fields in a reply */ -#define REQ_VWV_RESERVED(start, count) memset(req->out.vwv + VWV(start), 0, (count)*2) - -/**************************************************************************** - Reply to a simple request (async send) -****************************************************************************/ -static void reply_simple_send(struct smbsrv_request *req) -{ - CHECK_ASYNC_STATUS; - - req_setup_reply(req, 0, 0); - req_send_reply(req); -} - - -/**************************************************************************** - Reply to a tcon. -****************************************************************************/ -void reply_tcon(struct smbsrv_request *req) -{ - union smb_tcon con; - NTSTATUS status; - uint8_t *p; - - /* parse request */ - REQ_CHECK_WCT(req, 0); - - con.tcon.level = RAW_TCON_TCON; - - p = req->in.data; - p += req_pull_ascii4(req, &con.tcon.in.service, p, STR_TERMINATE); - p += req_pull_ascii4(req, &con.tcon.in.password, p, STR_TERMINATE); - p += req_pull_ascii4(req, &con.tcon.in.dev, p, STR_TERMINATE); - - if (!con.tcon.in.service || !con.tcon.in.password || !con.tcon.in.dev) { - req_reply_error(req, NT_STATUS_INVALID_PARAMETER); - return; - } - - /* call backend */ - status = tcon_backend(req, &con); - - if (!NT_STATUS_IS_OK(status)) { - req_reply_error(req, status); - return; - } - - /* construct reply */ - req_setup_reply(req, 2, 0); - - SSVAL(req->out.vwv, VWV(0), con.tcon.out.max_xmit); - SSVAL(req->out.vwv, VWV(1), con.tcon.out.tid); - SSVAL(req->out.hdr, HDR_TID, req->tcon->tid); - - req_send_reply(req); -} - - -/**************************************************************************** - Reply to a tcon and X. -****************************************************************************/ -void reply_tcon_and_X(struct smbsrv_request *req) -{ - NTSTATUS status; - union smb_tcon con; - uint8_t *p; - uint16_t passlen; - - con.tconx.level = RAW_TCON_TCONX; - - /* parse request */ - REQ_CHECK_WCT(req, 4); - - con.tconx.in.flags = SVAL(req->in.vwv, VWV(2)); - passlen = SVAL(req->in.vwv, VWV(3)); - - p = req->in.data; - - if (!req_pull_blob(req, p, passlen, &con.tconx.in.password)) { - req_reply_error(req, NT_STATUS_ILL_FORMED_PASSWORD); - return; - } - p += passlen; - - p += req_pull_string(req, &con.tconx.in.path, p, -1, STR_TERMINATE); - p += req_pull_string(req, &con.tconx.in.device, p, -1, STR_ASCII); - - if (!con.tconx.in.path || !con.tconx.in.device) { - req_reply_error(req, NT_STATUS_BAD_DEVICE_TYPE); - return; - } - - /* call backend */ - status = tcon_backend(req, &con); - - if (!NT_STATUS_IS_OK(status)) { - req_reply_error(req, status); - return; - } - - /* construct reply - two variants */ - if (req->smb_conn->negotiate.protocol < PROTOCOL_NT1) { - req_setup_reply(req, 2, 0); - - SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE); - SSVAL(req->out.vwv, VWV(1), 0); - - req_push_str(req, NULL, con.tconx.out.dev_type, -1, STR_TERMINATE|STR_ASCII); - } else { - req_setup_reply(req, 3, 0); - - SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE); - SSVAL(req->out.vwv, VWV(1), 0); - SSVAL(req->out.vwv, VWV(2), con.tconx.out.options); - - req_push_str(req, NULL, con.tconx.out.dev_type, -1, STR_TERMINATE|STR_ASCII); - req_push_str(req, NULL, con.tconx.out.fs_type, -1, STR_TERMINATE); - } - - /* set the incoming and outgoing tid to the just created one */ - SSVAL(req->in.hdr, HDR_TID, con.tconx.out.tid); - SSVAL(req->out.hdr,HDR_TID, con.tconx.out.tid); - - chain_reply(req); -} - - -/**************************************************************************** - Reply to an unknown request -****************************************************************************/ -void reply_unknown(struct smbsrv_request *req) -{ - int type; - - type = CVAL(req->in.hdr, HDR_COM); - - DEBUG(0,("unknown command type %d (0x%X)\n", type, type)); - - req_reply_dos_error(req, ERRSRV, ERRunknownsmb); -} - - -/**************************************************************************** - Reply to an ioctl (async reply) -****************************************************************************/ -static void reply_ioctl_send(struct smbsrv_request *req) -{ - union smb_ioctl *io = req->async_states->private_data; - - CHECK_ASYNC_STATUS; - - /* the +1 is for nicer alignment */ - req_setup_reply(req, 8, io->ioctl.out.blob.length+1); - SSVAL(req->out.vwv, VWV(1), io->ioctl.out.blob.length); - SSVAL(req->out.vwv, VWV(5), io->ioctl.out.blob.length); - SSVAL(req->out.vwv, VWV(6), PTR_DIFF(req->out.data, req->out.hdr) + 1); - - memcpy(req->out.data+1, io->ioctl.out.blob.data, io->ioctl.out.blob.length); - - req_send_reply(req); -} - -/**************************************************************************** - Reply to an ioctl. -****************************************************************************/ -void reply_ioctl(struct smbsrv_request *req) -{ - union smb_ioctl *io; - - /* parse request */ - REQ_CHECK_WCT(req, 3); - REQ_TALLOC(io, sizeof(*io)); - - io->ioctl.level = RAW_IOCTL_IOCTL; - io->ioctl.in.fnum = req_fnum(req, req->in.vwv, VWV(0)); - io->ioctl.in.request = IVAL(req->in.vwv, VWV(1)); - - req->async_states->state |= NTVFS_ASYNC_STATE_MAY_ASYNC; - req->async_states->send_fn = reply_ioctl_send; - req->async_states->private_data = io; - - /* call backend */ - req->async_states->status = ntvfs_ioctl(req, io); - - REQ_ASYNC_TAIL; -} - - -/**************************************************************************** - Reply to a chkpth. -****************************************************************************/ -void reply_chkpth(struct smbsrv_request *req) -{ - struct smb_chkpath *io; - - REQ_TALLOC(io, sizeof(*io)); - - req_pull_ascii4(req, &io->in.path, req->in.data, STR_TERMINATE); - - req->async_states->state |= NTVFS_ASYNC_STATE_MAY_ASYNC; - req->async_states->send_fn = reply_simple_send; - - req->async_states->status = ntvfs_chkpath(req, io); - - REQ_ASYNC_TAIL; -} - -/**************************************************************************** - Reply to a getatr (async reply) -****************************************************************************/ -static void reply_getatr_send(struct smbsrv_request *req) -{ - union smb_fileinfo *st = req->async_states->private_data; - - CHECK_ASYNC_STATUS; - - /* construct reply */ - req_setup_reply(req, 10, 0); - - SSVAL(req->out.vwv, VWV(0), st->getattr.out.attrib); - srv_push_dos_date3(req->smb_conn, req->out.vwv, VWV(1), st->getattr.out.write_time); - SIVAL(req->out.vwv, VWV(3), st->getattr.out.size); - - REQ_VWV_RESERVED(5, 5); - - req_send_reply(req); -} - - -/**************************************************************************** - Reply to a getatr. -****************************************************************************/ -void reply_getatr(struct smbsrv_request *req) -{ - union smb_fileinfo *st; - - REQ_TALLOC(st, sizeof(*st)); - - st->getattr.level = RAW_FILEINFO_GETATTR; - - /* parse request */ - req_pull_ascii4(req, &st->getattr.in.fname, req->in.data, STR_TERMINATE); - if (!st->getattr.in.fname) { - req_reply_error(req, NT_STATUS_OBJECT_NAME_NOT_FOUND); - return; - } - - req->async_states->state |= NTVFS_ASYNC_STATE_MAY_ASYNC; - req->async_states->send_fn = reply_getatr_send; - req->async_states->private_data = st; - - /* call backend */ - req->async_states->status = ntvfs_qpathinfo(req, st); - - REQ_ASYNC_TAIL; -} - - -/**************************************************************************** - Reply to a setatr. -****************************************************************************/ -void reply_setatr(struct smbsrv_request *req) -{ - union smb_setfileinfo *st; - - /* parse request */ - REQ_CHECK_WCT(req, 8); - REQ_TALLOC(st, sizeof(*st)); - - st->setattr.level = RAW_SFILEINFO_SETATTR; - st->setattr.in.attrib = SVAL(req->in.vwv, VWV(0)); - st->setattr.in.write_time = srv_pull_dos_date3(req->smb_conn, req->in.vwv + VWV(1)); - - req_pull_ascii4(req, &st->setattr.file.fname, req->in.data, STR_TERMINATE); - - if (!st->setattr.file.fname) { - req_reply_error(req, NT_STATUS_OBJECT_NAME_NOT_FOUND); - return; - } - - req->async_states->state |= NTVFS_ASYNC_STATE_MAY_ASYNC; - req->async_states->send_fn = reply_simple_send; - - /* call backend */ - req->async_states->status = ntvfs_setpathinfo(req, st); - - REQ_ASYNC_TAIL; -} - - -/**************************************************************************** - Reply to a dskattr (async reply) -****************************************************************************/ -static void reply_dskattr_send(struct smbsrv_request *req) -{ - union smb_fsinfo *fs = req->async_states->private_data; - - CHECK_ASYNC_STATUS; - - /* construct reply */ - req_setup_reply(req, 5, 0); - - SSVAL(req->out.vwv, VWV(0), fs->dskattr.out.units_total); - SSVAL(req->out.vwv, VWV(1), fs->dskattr.out.blocks_per_unit); - SSVAL(req->out.vwv, VWV(2), fs->dskattr.out.block_size); - SSVAL(req->out.vwv, VWV(3), fs->dskattr.out.units_free); - - REQ_VWV_RESERVED(4, 1); - - req_send_reply(req); -} - - -/**************************************************************************** - Reply to a dskattr. -****************************************************************************/ -void reply_dskattr(struct smbsrv_request *req) -{ - union smb_fsinfo *fs; - - REQ_TALLOC(fs, sizeof(*fs)); - - fs->dskattr.level = RAW_QFS_DSKATTR; - - req->async_states->state |= NTVFS_ASYNC_STATE_MAY_ASYNC; - req->async_states->send_fn = reply_dskattr_send; - req->async_states->private_data = fs; - - /* call backend */ - req->async_states->status = ntvfs_fsinfo(req, fs); - - REQ_ASYNC_TAIL; -} - - - -/**************************************************************************** - Reply to an open (async reply) -****************************************************************************/ -static void reply_open_send(struct smbsrv_request *req) -{ - union smb_open *oi = req->async_states->private_data; - - CHECK_ASYNC_STATUS; - - /* construct reply */ - req_setup_reply(req, 7, 0); - - SSVAL(req->out.vwv, VWV(0), oi->openold.out.fnum); - SSVAL(req->out.vwv, VWV(1), oi->openold.out.attrib); - srv_push_dos_date3(req->smb_conn, req->out.vwv, VWV(2), oi->openold.out.write_time); - SIVAL(req->out.vwv, VWV(4), oi->openold.out.size); - SSVAL(req->out.vwv, VWV(6), oi->openold.out.rmode); - - req_send_reply(req); -} - -/**************************************************************************** - Reply to an open. -****************************************************************************/ -void reply_open(struct smbsrv_request *req) -{ - union smb_open *oi; - - /* parse request */ - REQ_CHECK_WCT(req, 2); - REQ_TALLOC(oi, sizeof(*oi)); - - oi->openold.level = RAW_OPEN_OPEN; - oi->openold.in.open_mode = SVAL(req->in.vwv, VWV(0)); - oi->openold.in.search_attrs = SVAL(req->in.vwv, VWV(1)); - - req_pull_ascii4(req, &oi->openold.in.fname, req->in.data, STR_TERMINATE); - - if (!oi->openold.in.fname) { - req_reply_error(req, NT_STATUS_OBJECT_NAME_NOT_FOUND); - return; - } - - req->async_states->state |= NTVFS_ASYNC_STATE_MAY_ASYNC; - req->async_states->send_fn = reply_open_send; - req->async_states->private_data = oi; - - /* call backend */ - req->async_states->status = ntvfs_openfile(req, oi); - - REQ_ASYNC_TAIL; -} - - -/**************************************************************************** - Reply to an open and X (async reply) -****************************************************************************/ -static void reply_open_and_X_send(struct smbsrv_request *req) -{ - union smb_open *oi = req->async_states->private_data; - - CHECK_ASYNC_STATUS; - - /* build the reply */ - if (oi->openx.in.flags & OPENX_FLAGS_EXTENDED_RETURN) { - req_setup_reply(req, 19, 0); - } else { - req_setup_reply(req, 15, 0); - } - - SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE); - SSVAL(req->out.vwv, VWV(1), 0); - SSVAL(req->out.vwv, VWV(2), oi->openx.out.fnum); - SSVAL(req->out.vwv, VWV(3), oi->openx.out.attrib); - srv_push_dos_date3(req->smb_conn, req->out.vwv, VWV(4), oi->openx.out.write_time); - SIVAL(req->out.vwv, VWV(6), oi->openx.out.size); - SSVAL(req->out.vwv, VWV(8), oi->openx.out.access); - SSVAL(req->out.vwv, VWV(9), oi->openx.out.ftype); - SSVAL(req->out.vwv, VWV(10),oi->openx.out.devstate); - SSVAL(req->out.vwv, VWV(11),oi->openx.out.action); - SIVAL(req->out.vwv, VWV(12),oi->openx.out.unique_fid); - SSVAL(req->out.vwv, VWV(14),0); /* reserved */ - if (oi->openx.in.flags & OPENX_FLAGS_EXTENDED_RETURN) { - SIVAL(req->out.vwv, VWV(15),oi->openx.out.access_mask); - REQ_VWV_RESERVED(17, 2); - } - - req->chained_fnum = oi->openx.out.fnum; - - chain_reply(req); -} - - -/**************************************************************************** - Reply to an open and X. -****************************************************************************/ -void reply_open_and_X(struct smbsrv_request *req) -{ - union smb_open *oi; - - /* parse the request */ - REQ_CHECK_WCT(req, 15); - REQ_TALLOC(oi, sizeof(*oi)); - - oi->openx.level = RAW_OPEN_OPENX; - oi->openx.in.flags = SVAL(req->in.vwv, VWV(2)); - oi->openx.in.open_mode = SVAL(req->in.vwv, VWV(3)); - oi->openx.in.search_attrs = SVAL(req->in.vwv, VWV(4)); - oi->openx.in.file_attrs = SVAL(req->in.vwv, VWV(5)); - oi->openx.in.write_time = srv_pull_dos_date3(req->smb_conn, req->in.vwv + VWV(6)); - oi->openx.in.open_func = SVAL(req->in.vwv, VWV(8)); - oi->openx.in.size = IVAL(req->in.vwv, VWV(9)); - oi->openx.in.timeout = IVAL(req->in.vwv, VWV(11)); - - req_pull_ascii4(req, &oi->openx.in.fname, req->in.data, STR_TERMINATE); - - if (!oi->openx.in.fname) { - req_reply_error(req, NT_STATUS_OBJECT_NAME_NOT_FOUND); - return; - } - - req->async_states->state |= NTVFS_ASYNC_STATE_MAY_ASYNC; - req->async_states->send_fn = reply_open_and_X_send; - req->async_states->private_data = oi; - - /* call the backend */ - req->async_states->status = ntvfs_openfile(req, oi); - - REQ_ASYNC_TAIL; -} - - -/**************************************************************************** - Reply to a mknew or a create. -****************************************************************************/ -static void reply_mknew_send(struct smbsrv_request *req) -{ - union smb_open *oi = req->async_states->private_data; - - CHECK_ASYNC_STATUS; - - /* build the reply */ - req_setup_reply(req, 1, 0); - - SSVAL(req->out.vwv, VWV(0), oi->mknew.out.fnum); - - req_send_reply(req); -} - - -/**************************************************************************** - Reply to a mknew or a create. -****************************************************************************/ -void reply_mknew(struct smbsrv_request *req) -{ - union smb_open *oi; - - /* parse the request */ - REQ_CHECK_WCT(req, 3); - REQ_TALLOC(oi, sizeof(*oi)); - - if (CVAL(req->in.hdr, HDR_COM) == SMBmknew) { - oi->mknew.level = RAW_OPEN_MKNEW; - } else { - oi->mknew.level = RAW_OPEN_CREATE; - } - oi->mknew.in.attrib = SVAL(req->in.vwv, VWV(0)); - oi->mknew.in.write_time = srv_pull_dos_date3(req->smb_conn, req->in.vwv + VWV(1)); - - req_pull_ascii4(req, &oi->mknew.in.fname, req->in.data, STR_TERMINATE); - - if (!oi->mknew.in.fname) { - req_reply_error(req, NT_STATUS_OBJECT_NAME_NOT_FOUND); - return; - } - - req->async_states->state |= NTVFS_ASYNC_STATE_MAY_ASYNC; - req->async_states->send_fn = reply_mknew_send; - req->async_states->private_data = oi; - - /* call the backend */ - req->async_states->status = ntvfs_openfile(req, oi); - - REQ_ASYNC_TAIL; -} - -/**************************************************************************** - Reply to a create temporary file (async reply) -****************************************************************************/ -static void reply_ctemp_send(struct smbsrv_request *req) -{ - union smb_open *oi = req->async_states->private_data; - - CHECK_ASYNC_STATUS; - - /* build the reply */ - req_setup_reply(req, 1, 0); - - SSVAL(req->out.vwv, VWV(0), oi->ctemp.out.fnum); - - /* the returned filename is relative to the directory */ - req_push_str(req, NULL, oi->ctemp.out.name, -1, STR_TERMINATE | STR_ASCII); - - req_send_reply(req); -} - -/**************************************************************************** - Reply to a create temporary file. -****************************************************************************/ -void reply_ctemp(struct smbsrv_request *req) -{ - union smb_open *oi; - - /* parse the request */ - REQ_CHECK_WCT(req, 3); - REQ_TALLOC(oi, sizeof(*oi)); - - oi->ctemp.level = RAW_OPEN_CTEMP; - oi->ctemp.in.attrib = SVAL(req->in.vwv, VWV(0)); - oi->ctemp.in.write_time = srv_pull_dos_date3(req->smb_conn, req->in.vwv + VWV(1)); - - /* the filename is actually a directory name, the server provides a filename - in that directory */ - req_pull_ascii4(req, &oi->ctemp.in.directory, req->in.data, STR_TERMINATE); - - if (!oi->ctemp.in.directory) { - req_reply_error(req, NT_STATUS_OBJECT_NAME_NOT_FOUND); - return; - } - - req->async_states->state |= NTVFS_ASYNC_STATE_MAY_ASYNC; - req->async_states->send_fn = reply_ctemp_send; - req->async_states->private_data = oi; - - /* call the backend */ - req->async_states->status = ntvfs_openfile(req, oi); - - REQ_ASYNC_TAIL; -} - - -/**************************************************************************** - Reply to a unlink -****************************************************************************/ -void reply_unlink(struct smbsrv_request *req) -{ - struct smb_unlink *unl; - - /* parse the request */ - REQ_CHECK_WCT(req, 1); - REQ_TALLOC(unl, sizeof(*unl)); - - unl->in.attrib = SVAL(req->in.vwv, VWV(0)); - - req_pull_ascii4(req, &unl->in.pattern, req->in.data, STR_TERMINATE); - - req->async_states->state |= NTVFS_ASYNC_STATE_MAY_ASYNC; - req->async_states->send_fn = reply_simple_send; - - /* call backend */ - req->async_states->status = ntvfs_unlink(req, unl); - - REQ_ASYNC_TAIL; -} - - -/**************************************************************************** - Reply to a readbraw (core+ protocol). - this is a strange packet because it doesn't use a standard SMB header in the reply, - only the 4 byte NBT header - This command must be replied to synchronously -****************************************************************************/ -void reply_readbraw(struct smbsrv_request *req) -{ - NTSTATUS status; - union smb_read io; - - io.readbraw.level = RAW_READ_READBRAW; - - /* there are two variants, one with 10 and one with 8 command words */ - if (req->in.wct < 8) { - goto failed; - } - - io.readbraw.in.fnum = req_fnum(req, req->in.vwv, VWV(0)); - io.readbraw.in.offset = IVAL(req->in.vwv, VWV(1)); - io.readbraw.in.maxcnt = SVAL(req->in.vwv, VWV(3)); - io.readbraw.in.mincnt = SVAL(req->in.vwv, VWV(4)); - io.readbraw.in.timeout = IVAL(req->in.vwv, VWV(5)); - - /* the 64 bit variant */ - if (req->in.wct == 10) { - uint32_t offset_high = IVAL(req->in.vwv, VWV(8)); - io.readbraw.in.offset |= (((off_t)offset_high) << 32); - } - - /* before calling the backend we setup the raw buffer. This - * saves a copy later */ - req->out.size = io.readbraw.in.maxcnt + NBT_HDR_SIZE; - req->out.buffer = talloc_size(req, req->out.size); - if (req->out.buffer == NULL) { - goto failed; - } - SIVAL(req->out.buffer, 0, 0); /* init NBT header */ - - /* tell the backend where to put the data */ - io.readbraw.out.data = req->out.buffer + NBT_HDR_SIZE; - - /* call the backend */ - status = ntvfs_read(req, &io); - - if (!NT_STATUS_IS_OK(status)) { - goto failed; - } - - req->out.size = io.readbraw.out.nread + NBT_HDR_SIZE; - - req_send_reply_nosign(req); - return; - -failed: - /* any failure in readbraw is equivalent to reading zero bytes */ - req->out.size = 4; - req->out.buffer = talloc_size(req, req->out.size); - SIVAL(req->out.buffer, 0, 0); /* init NBT header */ - - req_send_reply_nosign(req); -} - - -/**************************************************************************** - Reply to a lockread (async reply) -****************************************************************************/ -static void reply_lockread_send(struct smbsrv_request *req) -{ - union smb_read *io = req->async_states->private_data; - - CHECK_ASYNC_STATUS; - - /* trim packet */ - io->lockread.out.nread = MIN(io->lockread.out.nread, - req_max_data(req) - 3); - req_grow_data(req, 3 + io->lockread.out.nread); - - /* construct reply */ - SSVAL(req->out.vwv, VWV(0), io->lockread.out.nread); - REQ_VWV_RESERVED(1, 4); - - SCVAL(req->out.data, 0, SMB_DATA_BLOCK); - SSVAL(req->out.data, 1, io->lockread.out.nread); - - req_send_reply(req); -} - - -/**************************************************************************** - Reply to a lockread (core+ protocol). - note that the lock is a write lock, not a read lock! -****************************************************************************/ -void reply_lockread(struct smbsrv_request *req) -{ - union smb_read *io; - - /* parse request */ - REQ_CHECK_WCT(req, 5); - REQ_TALLOC(io, sizeof(*io)); - - io->lockread.level = RAW_READ_LOCKREAD; - io->lockread.in.fnum = req_fnum(req, req->in.vwv, VWV(0)); - io->lockread.in.count = SVAL(req->in.vwv, VWV(1)); - io->lockread.in.offset = IVAL(req->in.vwv, VWV(2)); - io->lockread.in.remaining = SVAL(req->in.vwv, VWV(4)); - - /* setup the reply packet assuming the maximum possible read */ - req_setup_reply(req, 5, 3 + io->lockread.in.count); - - /* tell the backend where to put the data */ - io->lockread.out.data = req->out.data + 3; - - req->async_states->state |= NTVFS_ASYNC_STATE_MAY_ASYNC; - req->async_states->send_fn = reply_lockread_send; - req->async_states->private_data = io; - - /* call backend */ - req->async_states->status = ntvfs_read(req, io); - - REQ_ASYNC_TAIL; -} - - - -/**************************************************************************** - Reply to a read (async reply) -****************************************************************************/ -static void reply_read_send(struct smbsrv_request *req) -{ - union smb_read *io = req->async_states->private_data; - - CHECK_ASYNC_STATUS; - - /* trim packet */ - io->read.out.nread = MIN(io->read.out.nread, - req_max_data(req) - 3); - req_grow_data(req, 3 + io->read.out.nread); - - /* construct reply */ - SSVAL(req->out.vwv, VWV(0), io->read.out.nread); - REQ_VWV_RESERVED(1, 4); - - SCVAL(req->out.data, 0, SMB_DATA_BLOCK); - SSVAL(req->out.data, 1, io->read.out.nread); - - req_send_reply(req); -} - -/**************************************************************************** - Reply to a read. -****************************************************************************/ -void reply_read(struct smbsrv_request *req) -{ - union smb_read *io; - - /* parse request */ - REQ_CHECK_WCT(req, 5); - REQ_TALLOC(io, sizeof(*io)); - - io->read.level = RAW_READ_READ; - io->read.in.fnum = req_fnum(req, req->in.vwv, VWV(0)); - io->read.in.count = SVAL(req->in.vwv, VWV(1)); - io->read.in.offset = IVAL(req->in.vwv, VWV(2)); - io->read.in.remaining = SVAL(req->in.vwv, VWV(4)); - - /* setup the reply packet assuming the maximum possible read */ - req_setup_reply(req, 5, 3 + io->read.in.count); - - /* tell the backend where to put the data */ - io->read.out.data = req->out.data + 3; - - req->async_states->state |= NTVFS_ASYNC_STATE_MAY_ASYNC; - req->async_states->send_fn = reply_read_send; - req->async_states->private_data = io; - - /* call backend */ - req->async_states->status = ntvfs_read(req, io); - - REQ_ASYNC_TAIL; -} - - - -/**************************************************************************** - Reply to a read and X (async reply) -****************************************************************************/ -static void reply_read_and_X_send(struct smbsrv_request *req) -{ - union smb_read *io = req->async_states->private_data; - - CHECK_ASYNC_STATUS; - - /* readx reply packets can be over-sized */ - req->control_flags |= REQ_CONTROL_LARGE; - if (io->readx.in.maxcnt != 0xFFFF && - io->readx.in.mincnt != 0xFFFF) { - req_grow_data(req, 1 + io->readx.out.nread); - SCVAL(req->out.data, 0, 0); /* padding */ - } else { - req_grow_data(req, io->readx.out.nread); - } - - /* construct reply */ - SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE); - SSVAL(req->out.vwv, VWV(1), 0); - SSVAL(req->out.vwv, VWV(2), io->readx.out.remaining); - SSVAL(req->out.vwv, VWV(3), io->readx.out.compaction_mode); - REQ_VWV_RESERVED(4, 1); - SSVAL(req->out.vwv, VWV(5), io->readx.out.nread); - SSVAL(req->out.vwv, VWV(6), PTR_DIFF(io->readx.out.data, req->out.hdr)); - REQ_VWV_RESERVED(7, 5); - - chain_reply(req); -} - -/**************************************************************************** - Reply to a read and X. -****************************************************************************/ -void reply_read_and_X(struct smbsrv_request *req) -{ - union smb_read *io; - - /* parse request */ - if (req->in.wct != 12) { - REQ_CHECK_WCT(req, 10); - } - - REQ_TALLOC(io, sizeof(*io)); - - io->readx.level = RAW_READ_READX; - io->readx.in.fnum = req_fnum(req, req->in.vwv, VWV(2)); - io->readx.in.offset = IVAL(req->in.vwv, VWV(3)); - io->readx.in.maxcnt = SVAL(req->in.vwv, VWV(5)); - io->readx.in.mincnt = SVAL(req->in.vwv, VWV(6)); - io->readx.in.remaining = SVAL(req->in.vwv, VWV(9)); - - if (req->smb_conn->negotiate.client_caps & CAP_LARGE_READX) { - uint32_t high_part = IVAL(req->in.vwv, VWV(7)); - if (high_part == 1) { - io->readx.in.maxcnt |= high_part << 16; - } - } - - /* the 64 bit variant */ - if (req->in.wct == 12) { - uint32_t offset_high = IVAL(req->in.vwv, VWV(10)); - io->readx.in.offset |= (((uint64_t)offset_high) << 32); - } - - /* setup the reply packet assuming the maximum possible read */ - req_setup_reply(req, 12, 1 + io->readx.in.maxcnt); - - /* tell the backend where to put the data. Notice the pad byte. */ - if (io->readx.in.maxcnt != 0xFFFF && - io->readx.in.mincnt != 0xFFFF) { - io->readx.out.data = req->out.data + 1; - } else { - io->readx.out.data = req->out.data; - } - - req->async_states->state |= NTVFS_ASYNC_STATE_MAY_ASYNC; - req->async_states->send_fn = reply_read_and_X_send; - req->async_states->private_data = io; - - /* call backend */ - req->async_states->status = ntvfs_read(req, io); - - REQ_ASYNC_TAIL; -} - - -/**************************************************************************** - Reply to a writebraw (core+ or LANMAN1.0 protocol). -****************************************************************************/ -void reply_writebraw(struct smbsrv_request *req) -{ - req_reply_dos_error(req, ERRSRV, ERRuseSTD); -} - - -/**************************************************************************** - Reply to a writeunlock (async reply) -****************************************************************************/ -static void reply_writeunlock_send(struct smbsrv_request *req) -{ - union smb_write *io = req->async_states->private_data; - - CHECK_ASYNC_STATUS; - - /* construct reply */ - req_setup_reply(req, 1, 0); - - SSVAL(req->out.vwv, VWV(0), io->writeunlock.out.nwritten); - - req_send_reply(req); -} - -/**************************************************************************** - Reply to a writeunlock (core+). -****************************************************************************/ -void reply_writeunlock(struct smbsrv_request *req) -{ - union smb_write *io; - - REQ_CHECK_WCT(req, 5); - REQ_TALLOC(io, sizeof(*io)); - - io->writeunlock.level = RAW_WRITE_WRITEUNLOCK; - io->writeunlock.in.fnum = req_fnum(req, req->in.vwv, VWV(0)); - io->writeunlock.in.count = SVAL(req->in.vwv, VWV(1)); - io->writeunlock.in.offset = IVAL(req->in.vwv, VWV(2)); - io->writeunlock.in.remaining = SVAL(req->in.vwv, VWV(4)); - io->writeunlock.in.data = req->in.data + 3; - - /* make sure they gave us the data they promised */ - if (io->writeunlock.in.count+3 > req->in.data_size) { - req_reply_error(req, NT_STATUS_FOOBAR); - return; - } - - /* make sure the data block is big enough */ - if (SVAL(req->in.data, 1) < io->writeunlock.in.count) { - req_reply_error(req, NT_STATUS_FOOBAR); - return; - } - - req->async_states->state |= NTVFS_ASYNC_STATE_MAY_ASYNC; - req->async_states->send_fn = reply_writeunlock_send; - req->async_states->private_data = io; - - /* call backend */ - req->async_states->status = ntvfs_write(req, io); - - REQ_ASYNC_TAIL; -} - - - -/**************************************************************************** - Reply to a write (async reply) -****************************************************************************/ -static void reply_write_send(struct smbsrv_request *req) -{ - union smb_write *io = req->async_states->private_data; - - CHECK_ASYNC_STATUS; - - /* construct reply */ - req_setup_reply(req, 1, 0); - - SSVAL(req->out.vwv, VWV(0), io->write.out.nwritten); - - req_send_reply(req); -} - -/**************************************************************************** - Reply to a write -****************************************************************************/ -void reply_write(struct smbsrv_request *req) -{ - union smb_write *io; - - REQ_CHECK_WCT(req, 5); - REQ_TALLOC(io, sizeof(*io)); - - io->write.level = RAW_WRITE_WRITE; - io->write.in.fnum = req_fnum(req, req->in.vwv, VWV(0)); - io->write.in.count = SVAL(req->in.vwv, VWV(1)); - io->write.in.offset = IVAL(req->in.vwv, VWV(2)); - io->write.in.remaining = SVAL(req->in.vwv, VWV(4)); - io->write.in.data = req->in.data + 3; - - /* make sure they gave us the data they promised */ - if (req_data_oob(req, io->write.in.data, io->write.in.count)) { - req_reply_error(req, NT_STATUS_FOOBAR); - return; - } - - /* make sure the data block is big enough */ - if (SVAL(req->in.data, 1) < io->write.in.count) { - req_reply_error(req, NT_STATUS_FOOBAR); - return; - } - - req->async_states->state |= NTVFS_ASYNC_STATE_MAY_ASYNC; - req->async_states->send_fn = reply_write_send; - req->async_states->private_data = io; - - /* call backend */ - req->async_states->status = ntvfs_write(req, io); - - REQ_ASYNC_TAIL; -} - - -/**************************************************************************** - Reply to a write and X (async reply) -****************************************************************************/ -static void reply_write_and_X_send(struct smbsrv_request *req) -{ - union smb_write *io = req->async_states->private_data; - - CHECK_ASYNC_STATUS; - - /* construct reply */ - req_setup_reply(req, 6, 0); - - SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE); - SSVAL(req->out.vwv, VWV(1), 0); - SSVAL(req->out.vwv, VWV(2), io->writex.out.nwritten & 0xFFFF); - SSVAL(req->out.vwv, VWV(3), io->writex.out.remaining); - SSVAL(req->out.vwv, VWV(4), io->writex.out.nwritten >> 16); - REQ_VWV_RESERVED(5, 1); - - chain_reply(req); -} - -/**************************************************************************** - Reply to a write and X. -****************************************************************************/ -void reply_write_and_X(struct smbsrv_request *req) -{ - union smb_write *io; - - if (req->in.wct != 14) { - REQ_CHECK_WCT(req, 12); - } - - REQ_TALLOC(io, sizeof(*io)); - - io->writex.level = RAW_WRITE_WRITEX; - io->writex.in.fnum = req_fnum(req, req->in.vwv, VWV(2)); - io->writex.in.offset = IVAL(req->in.vwv, VWV(3)); - io->writex.in.wmode = SVAL(req->in.vwv, VWV(7)); - io->writex.in.remaining = SVAL(req->in.vwv, VWV(8)); - io->writex.in.count = SVAL(req->in.vwv, VWV(10)); - io->writex.in.data = req->in.hdr + SVAL(req->in.vwv, VWV(11)); - - if (req->in.wct == 14) { - uint32_t offset_high = IVAL(req->in.vwv, VWV(12)); - uint16_t count_high = SVAL(req->in.vwv, VWV(9)); - io->writex.in.offset |= (((uint64_t)offset_high) << 32); - io->writex.in.count |= ((uint32_t)count_high) << 16; - } - - /* make sure the data is in bounds */ - if (req_data_oob(req, io->writex.in.data, io->writex.in.count)) { - req_reply_error(req, NT_STATUS_FOOBAR); - return; - } - - req->async_states->state |= NTVFS_ASYNC_STATE_MAY_ASYNC; - req->async_states->send_fn = reply_write_and_X_send; - req->async_states->private_data = io; - - /* call backend */ - req->async_states->status = ntvfs_write(req, io); - - REQ_ASYNC_TAIL; -} - - -/**************************************************************************** - Reply to a lseek (async reply) -****************************************************************************/ -static void reply_lseek_send(struct smbsrv_request *req) -{ - struct smb_seek *io = req->async_states->private_data; - - CHECK_ASYNC_STATUS; - - /* construct reply */ - req_setup_reply(req, 2, 0); - - SIVALS(req->out.vwv, VWV(0), io->out.offset); - - req_send_reply(req); -} - -/**************************************************************************** - Reply to a lseek. -****************************************************************************/ -void reply_lseek(struct smbsrv_request *req) -{ - struct smb_seek *io; - - REQ_CHECK_WCT(req, 4); - REQ_TALLOC(io, sizeof(*io)); - - io->in.fnum = req_fnum(req, req->in.vwv, VWV(0)); - io->in.mode = SVAL(req->in.vwv, VWV(1)); - io->in.offset = IVALS(req->in.vwv, VWV(2)); - - req->async_states->state |= NTVFS_ASYNC_STATE_MAY_ASYNC; - req->async_states->send_fn = reply_lseek_send; - req->async_states->private_data = io; - - /* call backend */ - req->async_states->status = ntvfs_seek(req, io); - - REQ_ASYNC_TAIL; -} - -/**************************************************************************** - Reply to a flush. -****************************************************************************/ -void reply_flush(struct smbsrv_request *req) -{ - struct smb_flush *io; - - /* parse request */ - REQ_CHECK_WCT(req, 1); - REQ_TALLOC(io, sizeof(*io)); - - io->in.fnum = req_fnum(req, req->in.vwv, VWV(0)); - - req->async_states->state |= NTVFS_ASYNC_STATE_MAY_ASYNC; - req->async_states->send_fn = reply_simple_send; - - /* call backend */ - req->async_states->status = ntvfs_flush(req, io); - - REQ_ASYNC_TAIL; -} - - -/**************************************************************************** - Reply to a exit. This closes all files open by a smbpid -****************************************************************************/ -void reply_exit(struct smbsrv_request *req) -{ - NTSTATUS status; - struct smbsrv_tcon *tcon; - REQ_CHECK_WCT(req, 0); - - for (tcon=req->smb_conn->tcons.list;tcon;tcon=tcon->next) { - req->tcon = tcon; - status = ntvfs_exit(req); - req->tcon = NULL; - if (!NT_STATUS_IS_OK(status)) { - req_reply_error(req, status); - return; - } - } - - req_setup_reply(req, 0, 0); - req_send_reply(req); -} - - -/**************************************************************************** - Reply to a close - - Note that this has to deal with closing a directory opened by NT SMB's. -****************************************************************************/ -void reply_close(struct smbsrv_request *req) -{ - union smb_close *io; - - /* parse request */ - REQ_CHECK_WCT(req, 3); - REQ_TALLOC(io, sizeof(*io)); - - io->close.level = RAW_CLOSE_CLOSE; - io->close.in.fnum = req_fnum(req, req->in.vwv, VWV(0)); - io->close.in.write_time = srv_pull_dos_date3(req->smb_conn, req->in.vwv + VWV(1)); - - req->async_states->state |= NTVFS_ASYNC_STATE_MAY_ASYNC; - req->async_states->send_fn = reply_simple_send; - - /* call backend */ - req->async_states->status = ntvfs_close(req, io); - - REQ_ASYNC_TAIL; -} - - - -/**************************************************************************** - Reply to a writeclose (async reply) -****************************************************************************/ -static void reply_writeclose_send(struct smbsrv_request *req) -{ - union smb_write *io = req->async_states->private_data; - - CHECK_ASYNC_STATUS; - - /* construct reply */ - req_setup_reply(req, 1, 0); - - SSVAL(req->out.vwv, VWV(0), io->write.out.nwritten); - - req_send_reply(req); -} - -/**************************************************************************** - Reply to a writeclose (Core+ protocol). -****************************************************************************/ -void reply_writeclose(struct smbsrv_request *req) -{ - union smb_write *io; - - /* this one is pretty weird - the wct can be 6 or 12 */ - if (req->in.wct != 12) { - REQ_CHECK_WCT(req, 6); - } - - REQ_TALLOC(io, sizeof(*io)); - - io->writeclose.level = RAW_WRITE_WRITECLOSE; - io->writeclose.in.fnum = req_fnum(req, req->in.vwv, VWV(0)); - io->writeclose.in.count = SVAL(req->in.vwv, VWV(1)); - io->writeclose.in.offset = IVAL(req->in.vwv, VWV(2)); - io->writeclose.in.mtime = srv_pull_dos_date3(req->smb_conn, req->in.vwv + VWV(4)); - io->writeclose.in.data = req->in.data + 1; - - /* make sure they gave us the data they promised */ - if (req_data_oob(req, io->writeclose.in.data, io->writeclose.in.count)) { - req_reply_error(req, NT_STATUS_FOOBAR); - return; - } - - req->async_states->state |= NTVFS_ASYNC_STATE_MAY_ASYNC; - req->async_states->send_fn = reply_writeclose_send; - req->async_states->private_data = io; - - /* call backend */ - req->async_states->status = ntvfs_write(req, io); - - REQ_ASYNC_TAIL; -} - -/**************************************************************************** - Reply to a lock. -****************************************************************************/ -void reply_lock(struct smbsrv_request *req) -{ - union smb_lock *lck; - - /* parse request */ - REQ_CHECK_WCT(req, 5); - REQ_TALLOC(lck, sizeof(*lck)); - - lck->lock.level = RAW_LOCK_LOCK; - lck->lock.in.fnum = req_fnum(req, req->in.vwv, VWV(0)); - lck->lock.in.count = IVAL(req->in.vwv, VWV(1)); - lck->lock.in.offset = IVAL(req->in.vwv, VWV(3)); - - req->async_states->state |= NTVFS_ASYNC_STATE_MAY_ASYNC; - req->async_states->send_fn = reply_simple_send; - - /* call backend */ - req->async_states->status = ntvfs_lock(req, lck); - - REQ_ASYNC_TAIL; -} - - -/**************************************************************************** - Reply to a unlock. -****************************************************************************/ -void reply_unlock(struct smbsrv_request *req) -{ - union smb_lock *lck; - - /* parse request */ - REQ_CHECK_WCT(req, 5); - REQ_TALLOC(lck, sizeof(*lck)); - - lck->unlock.level = RAW_LOCK_UNLOCK; - lck->unlock.in.fnum = req_fnum(req, req->in.vwv, VWV(0)); - lck->unlock.in.count = IVAL(req->in.vwv, VWV(1)); - lck->unlock.in.offset = IVAL(req->in.vwv, VWV(3)); - - req->async_states->state |= NTVFS_ASYNC_STATE_MAY_ASYNC; - req->async_states->send_fn = reply_simple_send; - - /* call backend */ - req->async_states->status = ntvfs_lock(req, lck); - - REQ_ASYNC_TAIL; -} - - -/**************************************************************************** - Reply to a tdis. -****************************************************************************/ -void reply_tdis(struct smbsrv_request *req) -{ - REQ_CHECK_WCT(req, 0); - - if (req->tcon == NULL) { - req_reply_error(req, NT_STATUS_INVALID_HANDLE); - return; - } - - talloc_free(req->tcon); - - /* construct reply */ - req_setup_reply(req, 0, 0); - - req_send_reply(req); -} - - -/**************************************************************************** - Reply to a echo. This is one of the few calls that is handled directly (the - backends don't see it at all) -****************************************************************************/ -void reply_echo(struct smbsrv_request *req) -{ - uint16_t count; - int i; - - REQ_CHECK_WCT(req, 0); - - count = SVAL(req->in.vwv, VWV(0)); - - req_setup_reply(req, 1, req->in.data_size); - - memcpy(req->out.data, req->in.data, req->in.data_size); - - for (i=1; i <= count;i++) { - struct smbsrv_request *this_req; - - if (i != count) { - this_req = req_setup_secondary(req); - } else { - this_req = req; - } - - SSVAL(this_req->out.vwv, VWV(0), i); - req_send_reply(this_req); - } -} - - - -/**************************************************************************** - Reply to a printopen (async reply) -****************************************************************************/ -static void reply_printopen_send(struct smbsrv_request *req) -{ - union smb_open *oi = req->async_states->private_data; - - CHECK_ASYNC_STATUS; - - /* construct reply */ - req_setup_reply(req, 1, 0); - - SSVAL(req->out.vwv, VWV(0), oi->openold.out.fnum); - - req_send_reply(req); -} - -/**************************************************************************** - Reply to a printopen. -****************************************************************************/ -void reply_printopen(struct smbsrv_request *req) -{ - union smb_open *oi; - - /* parse request */ - REQ_CHECK_WCT(req, 2); - REQ_TALLOC(oi, sizeof(*oi)); - - oi->splopen.level = RAW_OPEN_SPLOPEN; - oi->splopen.in.setup_length = SVAL(req->in.vwv, VWV(0)); - oi->splopen.in.mode = SVAL(req->in.vwv, VWV(1)); - - req_pull_ascii4(req, &oi->splopen.in.ident, req->in.data, STR_TERMINATE); - - req->async_states->state |= NTVFS_ASYNC_STATE_MAY_ASYNC; - req->async_states->send_fn = reply_printopen_send; - req->async_states->private_data = oi; - - /* call backend */ - req->async_states->status = ntvfs_openfile(req, oi); - - REQ_ASYNC_TAIL; -} - -/**************************************************************************** - Reply to a printclose. -****************************************************************************/ -void reply_printclose(struct smbsrv_request *req) -{ - union smb_close *io; - - /* parse request */ - REQ_CHECK_WCT(req, 3); - REQ_TALLOC(io, sizeof(*io)); - - io->splclose.level = RAW_CLOSE_SPLCLOSE; - io->splclose.in.fnum = req_fnum(req, req->in.vwv, VWV(0)); - - req->async_states->state |= NTVFS_ASYNC_STATE_MAY_ASYNC; - req->async_states->send_fn = reply_simple_send; - - /* call backend */ - req->async_states->status = ntvfs_close(req, io); - - REQ_ASYNC_TAIL; -} - -/**************************************************************************** - Reply to a printqueue. -****************************************************************************/ -void reply_printqueue_send(struct smbsrv_request *req) -{ - union smb_lpq *lpq = req->async_states->private_data; - int i, maxcount; - const uint_t el_size = 28; - - CHECK_ASYNC_STATUS; - - /* construct reply */ - req_setup_reply(req, 2, 0); - - /* truncate the returned list to fit in the negotiated buffer size */ - maxcount = (req_max_data(req) - 3) / el_size; - if (maxcount < lpq->retq.out.count) { - lpq->retq.out.count = maxcount; - } - - /* setup enough space in the reply */ - req_grow_data(req, 3 + el_size*lpq->retq.out.count); - - /* and fill it in */ - SSVAL(req->out.vwv, VWV(0), lpq->retq.out.count); - SSVAL(req->out.vwv, VWV(1), lpq->retq.out.restart_idx); - - SCVAL(req->out.data, 0, SMB_DATA_BLOCK); - SSVAL(req->out.data, 1, el_size*lpq->retq.out.count); - - req->out.ptr = req->out.data + 3; - - for (i=0;iretq.out.count;i++) { - srv_push_dos_date2(req->smb_conn, req->out.ptr, 0 , lpq->retq.out.queue[i].time); - SCVAL(req->out.ptr, 4, lpq->retq.out.queue[i].status); - SSVAL(req->out.ptr, 5, lpq->retq.out.queue[i].job); - SIVAL(req->out.ptr, 7, lpq->retq.out.queue[i].size); - SCVAL(req->out.ptr, 11, 0); /* reserved */ - req_push_str(req, req->out.ptr+12, lpq->retq.out.queue[i].user, 16, STR_ASCII); - req->out.ptr += el_size; - } - - req_send_reply(req); -} - -/**************************************************************************** - Reply to a printqueue. -****************************************************************************/ -void reply_printqueue(struct smbsrv_request *req) -{ - union smb_lpq *lpq; - - /* parse request */ - REQ_CHECK_WCT(req, 2); - REQ_TALLOC(lpq, sizeof(*lpq)); - - lpq->retq.level = RAW_LPQ_RETQ; - lpq->retq.in.maxcount = SVAL(req->in.vwv, VWV(0)); - lpq->retq.in.startidx = SVAL(req->in.vwv, VWV(1)); - - req->async_states->state |= NTVFS_ASYNC_STATE_MAY_ASYNC; - req->async_states->send_fn = reply_printqueue_send; - req->async_states->private_data = lpq; - - /* call backend */ - req->async_states->status = ntvfs_lpq(req, lpq); - - REQ_ASYNC_TAIL; -} - - -/**************************************************************************** - Reply to a printwrite. -****************************************************************************/ -void reply_printwrite(struct smbsrv_request *req) -{ - union smb_write *io; - - /* parse request */ - REQ_CHECK_WCT(req, 1); - REQ_TALLOC(io, sizeof(*io)); - - io->splwrite.level = RAW_WRITE_SPLWRITE; - - if (req->in.data_size < 3) { - req_reply_error(req, NT_STATUS_FOOBAR); - return; - } - - io->splwrite.in.fnum = req_fnum(req, req->in.vwv, VWV(0)); - io->splwrite.in.count = SVAL(req->in.data, 1); - io->splwrite.in.data = req->in.data + 3; - - /* make sure they gave us the data they promised */ - if (req_data_oob(req, io->splwrite.in.data, io->splwrite.in.count)) { - req_reply_error(req, NT_STATUS_FOOBAR); - return; - } - - req->async_states->state |= NTVFS_ASYNC_STATE_MAY_ASYNC; - req->async_states->send_fn = reply_simple_send; - - /* call backend */ - req->async_states->status = ntvfs_write(req, io); - - REQ_ASYNC_TAIL; -} - - -/**************************************************************************** - Reply to a mkdir. -****************************************************************************/ -void reply_mkdir(struct smbsrv_request *req) -{ - union smb_mkdir *io; - - /* parse the request */ - REQ_CHECK_WCT(req, 0); - REQ_TALLOC(io, sizeof(*io)); - - io->generic.level = RAW_MKDIR_MKDIR; - req_pull_ascii4(req, &io->mkdir.in.path, req->in.data, STR_TERMINATE); - - req->async_states->state |= NTVFS_ASYNC_STATE_MAY_ASYNC; - req->async_states->send_fn = reply_simple_send; - - /* call backend */ - req->async_states->status = ntvfs_mkdir(req, io); - - REQ_ASYNC_TAIL; -} - - -/**************************************************************************** - Reply to a rmdir. -****************************************************************************/ -void reply_rmdir(struct smbsrv_request *req) -{ - struct smb_rmdir *io; - - /* parse the request */ - REQ_CHECK_WCT(req, 0); - REQ_TALLOC(io, sizeof(*io)); - - req_pull_ascii4(req, &io->in.path, req->in.data, STR_TERMINATE); - - req->async_states->state |= NTVFS_ASYNC_STATE_MAY_ASYNC; - req->async_states->send_fn = reply_simple_send; - - /* call backend */ - req->async_states->status = ntvfs_rmdir(req, io); - - REQ_ASYNC_TAIL; -} - - -/**************************************************************************** - Reply to a mv. -****************************************************************************/ -void reply_mv(struct smbsrv_request *req) -{ - union smb_rename *io; - uint8_t *p; - - /* parse the request */ - REQ_CHECK_WCT(req, 1); - REQ_TALLOC(io, sizeof(*io)); - - io->generic.level = RAW_RENAME_RENAME; - io->rename.in.attrib = SVAL(req->in.vwv, VWV(0)); - - p = req->in.data; - p += req_pull_ascii4(req, &io->rename.in.pattern1, p, STR_TERMINATE); - p += req_pull_ascii4(req, &io->rename.in.pattern2, p, STR_TERMINATE); - - if (!io->rename.in.pattern1 || !io->rename.in.pattern2) { - req_reply_error(req, NT_STATUS_FOOBAR); - return; - } - - req->async_states->state |= NTVFS_ASYNC_STATE_MAY_ASYNC; - req->async_states->send_fn = reply_simple_send; - - /* call backend */ - req->async_states->status = ntvfs_rename(req, io); - - REQ_ASYNC_TAIL; -} - - -/**************************************************************************** - Reply to an NT rename. -****************************************************************************/ -void reply_ntrename(struct smbsrv_request *req) -{ - union smb_rename *io; - uint8_t *p; - - /* parse the request */ - REQ_CHECK_WCT(req, 4); - REQ_TALLOC(io, sizeof(*io)); - - io->generic.level = RAW_RENAME_NTRENAME; - io->ntrename.in.attrib = SVAL(req->in.vwv, VWV(0)); - io->ntrename.in.flags = SVAL(req->in.vwv, VWV(1)); - io->ntrename.in.cluster_size = IVAL(req->in.vwv, VWV(2)); - - p = req->in.data; - p += req_pull_ascii4(req, &io->ntrename.in.old_name, p, STR_TERMINATE); - p += req_pull_ascii4(req, &io->ntrename.in.new_name, p, STR_TERMINATE); - - if (!io->ntrename.in.old_name || !io->ntrename.in.new_name) { - req_reply_error(req, NT_STATUS_FOOBAR); - return; - } - - req->async_states->state |= NTVFS_ASYNC_STATE_MAY_ASYNC; - req->async_states->send_fn = reply_simple_send; - - /* call backend */ - req->async_states->status = ntvfs_rename(req, io); - - REQ_ASYNC_TAIL; -} - -/**************************************************************************** - Reply to a file copy (async reply) -****************************************************************************/ -static void reply_copy_send(struct smbsrv_request *req) -{ - struct smb_copy *cp = req->async_states->private_data; - - CHECK_ASYNC_STATUS; - - /* build the reply */ - req_setup_reply(req, 1, 0); - - SSVAL(req->out.vwv, VWV(0), cp->out.count); - - req_send_reply(req); -} - -/**************************************************************************** - Reply to a file copy. -****************************************************************************/ -void reply_copy(struct smbsrv_request *req) -{ - struct smb_copy *cp; - uint8_t *p; - - /* parse request */ - REQ_CHECK_WCT(req, 3); - REQ_TALLOC(cp, sizeof(*cp)); - - cp->in.tid2 = SVAL(req->in.vwv, VWV(0)); - cp->in.ofun = SVAL(req->in.vwv, VWV(1)); - cp->in.flags = SVAL(req->in.vwv, VWV(2)); - - p = req->in.data; - p += req_pull_ascii4(req, &cp->in.path1, p, STR_TERMINATE); - p += req_pull_ascii4(req, &cp->in.path2, p, STR_TERMINATE); - - if (!cp->in.path1 || !cp->in.path2) { - req_reply_error(req, NT_STATUS_FOOBAR); - return; - } - - req->async_states->state |= NTVFS_ASYNC_STATE_MAY_ASYNC; - req->async_states->send_fn = reply_copy_send; - req->async_states->private_data = cp; - - /* call backend */ - req->async_states->status = ntvfs_copy(req, cp); - - REQ_ASYNC_TAIL; -} - -/**************************************************************************** - Reply to a lockingX request (async send) -****************************************************************************/ -static void reply_lockingX_send(struct smbsrv_request *req) -{ - union smb_lock *lck = req->async_states->private_data; - - CHECK_ASYNC_STATUS; - - /* if it was an oplock break ack then we only send a reply if - there was an error */ - if (lck->lockx.in.ulock_cnt + lck->lockx.in.lock_cnt == 0) { - req_destroy(req); - return; - } - - /* construct reply */ - req_setup_reply(req, 2, 0); - - SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE); - SSVAL(req->out.vwv, VWV(1), 0); - - chain_reply(req); -} - - -/**************************************************************************** - Reply to a lockingX request. -****************************************************************************/ -void reply_lockingX(struct smbsrv_request *req) -{ - union smb_lock *lck; - uint_t total_locks, i; - uint_t lck_size; - uint8_t *p; - - /* parse request */ - REQ_CHECK_WCT(req, 8); - REQ_TALLOC(lck, sizeof(*lck)); - - lck->lockx.level = RAW_LOCK_LOCKX; - lck->lockx.in.fnum = req_fnum(req, req->in.vwv, VWV(2)); - lck->lockx.in.mode = SVAL(req->in.vwv, VWV(3)); - lck->lockx.in.timeout = IVAL(req->in.vwv, VWV(4)); - lck->lockx.in.ulock_cnt = SVAL(req->in.vwv, VWV(6)); - lck->lockx.in.lock_cnt = SVAL(req->in.vwv, VWV(7)); - - total_locks = lck->lockx.in.ulock_cnt + lck->lockx.in.lock_cnt; - - /* there are two variants, one with 64 bit offsets and counts */ - if (lck->lockx.in.mode & LOCKING_ANDX_LARGE_FILES) { - lck_size = 20; - } else { - lck_size = 10; - } - - /* make sure we got the promised data */ - if (req_data_oob(req, req->in.data, total_locks * lck_size)) { - req_reply_error(req, NT_STATUS_FOOBAR); - return; - } - - /* allocate the locks array */ - if (total_locks) { - REQ_TALLOC(lck->lockx.in.locks, total_locks * sizeof(lck->lockx.in.locks[0])); - } - - p = req->in.data; - - /* construct the locks array */ - for (i=0;ilockx.in.locks[i].pid = SVAL(p, 0); - - if (lck->lockx.in.mode & LOCKING_ANDX_LARGE_FILES) { - ofs_high = IVAL(p, 4); - lck->lockx.in.locks[i].offset = IVAL(p, 8); - count_high = IVAL(p, 12); - lck->lockx.in.locks[i].count = IVAL(p, 16); - } else { - lck->lockx.in.locks[i].offset = IVAL(p, 2); - lck->lockx.in.locks[i].count = IVAL(p, 6); - } - if (ofs_high != 0 || count_high != 0) { - lck->lockx.in.locks[i].count |= ((uint64_t)count_high) << 32; - lck->lockx.in.locks[i].offset |= ((uint64_t)ofs_high) << 32; - } - p += lck_size; - } - - req->async_states->state |= NTVFS_ASYNC_STATE_MAY_ASYNC; - req->async_states->send_fn = reply_lockingX_send; - req->async_states->private_data = lck; - - /* call backend */ - req->async_states->status = ntvfs_lock(req, lck); - - REQ_ASYNC_TAIL; -} - -/**************************************************************************** - Reply to a SMBreadbmpx (read block multiplex) request. -****************************************************************************/ -void reply_readbmpx(struct smbsrv_request *req) -{ - /* tell the client to not use a multiplexed read - its too broken to use */ - req_reply_dos_error(req, ERRSRV, ERRuseSTD); -} - - -/**************************************************************************** - Reply to a SMBsetattrE. -****************************************************************************/ -void reply_setattrE(struct smbsrv_request *req) -{ - union smb_setfileinfo *info; - - /* parse request */ - REQ_CHECK_WCT(req, 7); - REQ_TALLOC(info, sizeof(*info)); - - info->setattre.level = RAW_SFILEINFO_SETATTRE; - info->setattre.file.fnum = req_fnum(req, req->in.vwv, VWV(0)); - info->setattre.in.create_time = srv_pull_dos_date2(req->smb_conn, req->in.vwv + VWV(1)); - info->setattre.in.access_time = srv_pull_dos_date2(req->smb_conn, req->in.vwv + VWV(3)); - info->setattre.in.write_time = srv_pull_dos_date2(req->smb_conn, req->in.vwv + VWV(5)); - - req->async_states->state |= NTVFS_ASYNC_STATE_MAY_ASYNC; - req->async_states->send_fn = reply_simple_send; - - /* call backend */ - req->async_states->status = ntvfs_setfileinfo(req, info); - - REQ_ASYNC_TAIL; -} - - -/**************************************************************************** - Reply to a SMBwritebmpx (write block multiplex primary) request. -****************************************************************************/ -void reply_writebmpx(struct smbsrv_request *req) -{ - req_reply_dos_error(req, ERRSRV, ERRuseSTD); -} - - -/**************************************************************************** - Reply to a SMBwritebs (write block multiplex secondary) request. -****************************************************************************/ -void reply_writebs(struct smbsrv_request *req) -{ - req_reply_dos_error(req, ERRSRV, ERRuseSTD); -} - - - -/**************************************************************************** - Reply to a SMBgetattrE (async reply) -****************************************************************************/ -static void reply_getattrE_send(struct smbsrv_request *req) -{ - union smb_fileinfo *info = req->async_states->private_data; - - CHECK_ASYNC_STATUS; - - /* setup reply */ - req_setup_reply(req, 11, 0); - - srv_push_dos_date2(req->smb_conn, req->out.vwv, VWV(0), info->getattre.out.create_time); - srv_push_dos_date2(req->smb_conn, req->out.vwv, VWV(2), info->getattre.out.access_time); - srv_push_dos_date2(req->smb_conn, req->out.vwv, VWV(4), info->getattre.out.write_time); - SIVAL(req->out.vwv, VWV(6), info->getattre.out.size); - SIVAL(req->out.vwv, VWV(8), info->getattre.out.alloc_size); - SSVAL(req->out.vwv, VWV(10), info->getattre.out.attrib); - - req_send_reply(req); -} - -/**************************************************************************** - Reply to a SMBgetattrE. -****************************************************************************/ -void reply_getattrE(struct smbsrv_request *req) -{ - union smb_fileinfo *info; - - /* parse request */ - REQ_CHECK_WCT(req, 1); - REQ_TALLOC(info, sizeof(*info)); - - info->getattr.level = RAW_FILEINFO_GETATTRE; - info->getattr.in.fnum = req_fnum(req, req->in.vwv, VWV(0)); - - req->async_states->state |= NTVFS_ASYNC_STATE_MAY_ASYNC; - req->async_states->send_fn = reply_getattrE_send; - req->async_states->private_data = info; - - /* call backend */ - req->async_states->status = ntvfs_qfileinfo(req, info); - - REQ_ASYNC_TAIL; -} - - -/**************************************************************************** -reply to an old style session setup command -****************************************************************************/ -static void reply_sesssetup_old(struct smbsrv_request *req) -{ - NTSTATUS status; - union smb_sesssetup sess; - uint8_t *p; - uint16_t passlen; - - sess.old.level = RAW_SESSSETUP_OLD; - - /* parse request */ - sess.old.in.bufsize = SVAL(req->in.vwv, VWV(2)); - sess.old.in.mpx_max = SVAL(req->in.vwv, VWV(3)); - sess.old.in.vc_num = SVAL(req->in.vwv, VWV(4)); - sess.old.in.sesskey = IVAL(req->in.vwv, VWV(5)); - passlen = SVAL(req->in.vwv, VWV(7)); - - /* check the request isn't malformed */ - if (req_data_oob(req, req->in.data, passlen)) { - req_reply_error(req, NT_STATUS_FOOBAR); - return; - } - - p = req->in.data; - if (!req_pull_blob(req, p, passlen, &sess.old.in.password)) { - req_reply_error(req, NT_STATUS_FOOBAR); - return; - } - p += passlen; - - p += req_pull_string(req, &sess.old.in.user, p, -1, STR_TERMINATE); - p += req_pull_string(req, &sess.old.in.domain, p, -1, STR_TERMINATE); - p += req_pull_string(req, &sess.old.in.os, p, -1, STR_TERMINATE); - p += req_pull_string(req, &sess.old.in.lanman, p, -1, STR_TERMINATE); - - /* call the generic handler */ - status = sesssetup_backend(req, &sess); - - if (!NT_STATUS_IS_OK(status)) { - req_reply_error(req, status); - return; - } - - /* construct reply */ - req_setup_reply(req, 3, 0); - - SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE); - SSVAL(req->out.vwv, VWV(1), 0); - SSVAL(req->out.vwv, VWV(2), sess.old.out.action); - - SSVAL(req->out.hdr, HDR_UID, sess.old.out.vuid); - - chain_reply(req); -} - - -/**************************************************************************** -reply to an NT1 style session setup command -****************************************************************************/ -static void reply_sesssetup_nt1(struct smbsrv_request *req) -{ - NTSTATUS status; - union smb_sesssetup sess; - uint8_t *p; - uint16_t passlen1, passlen2; - - sess.nt1.level = RAW_SESSSETUP_NT1; - - /* parse request */ - sess.nt1.in.bufsize = SVAL(req->in.vwv, VWV(2)); - sess.nt1.in.mpx_max = SVAL(req->in.vwv, VWV(3)); - sess.nt1.in.vc_num = SVAL(req->in.vwv, VWV(4)); - sess.nt1.in.sesskey = IVAL(req->in.vwv, VWV(5)); - passlen1 = SVAL(req->in.vwv, VWV(7)); - passlen2 = SVAL(req->in.vwv, VWV(8)); - sess.nt1.in.capabilities = IVAL(req->in.vwv, VWV(11)); - - /* check the request isn't malformed */ - if (req_data_oob(req, req->in.data, passlen1) || - req_data_oob(req, req->in.data + passlen1, passlen2)) { - req_reply_error(req, NT_STATUS_FOOBAR); - return; - } - - p = req->in.data; - if (!req_pull_blob(req, p, passlen1, &sess.nt1.in.password1)) { - req_reply_error(req, NT_STATUS_FOOBAR); - return; - } - p += passlen1; - if (!req_pull_blob(req, p, passlen2, &sess.nt1.in.password2)) { - req_reply_error(req, NT_STATUS_FOOBAR); - return; - } - p += passlen2; - - p += req_pull_string(req, &sess.nt1.in.user, p, -1, STR_TERMINATE); - p += req_pull_string(req, &sess.nt1.in.domain, p, -1, STR_TERMINATE); - p += req_pull_string(req, &sess.nt1.in.os, p, -1, STR_TERMINATE); - p += req_pull_string(req, &sess.nt1.in.lanman, p, -1, STR_TERMINATE); - - /* call the generic handler */ - status = sesssetup_backend(req, &sess); - - if (!NT_STATUS_IS_OK(status)) { - req_reply_error(req, status); - return; - } - - /* construct reply */ - req_setup_reply(req, 3, 0); - - SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE); - SSVAL(req->out.vwv, VWV(1), 0); - SSVAL(req->out.vwv, VWV(2), sess.nt1.out.action); - - SSVAL(req->out.hdr, HDR_UID, sess.nt1.out.vuid); - - req_push_str(req, NULL, sess.nt1.out.os, -1, STR_TERMINATE); - req_push_str(req, NULL, sess.nt1.out.lanman, -1, STR_TERMINATE); - req_push_str(req, NULL, sess.nt1.out.domain, -1, STR_TERMINATE); - - chain_reply(req); -} - - -/**************************************************************************** -reply to an SPNEGO style session setup command -****************************************************************************/ -static void reply_sesssetup_spnego(struct smbsrv_request *req) -{ - NTSTATUS status; - union smb_sesssetup sess; - uint8_t *p; - uint16_t blob_len; - - sess.spnego.level = RAW_SESSSETUP_SPNEGO; - - /* parse request */ - sess.spnego.in.bufsize = SVAL(req->in.vwv, VWV(2)); - sess.spnego.in.mpx_max = SVAL(req->in.vwv, VWV(3)); - sess.spnego.in.vc_num = SVAL(req->in.vwv, VWV(4)); - sess.spnego.in.sesskey = IVAL(req->in.vwv, VWV(5)); - blob_len = SVAL(req->in.vwv, VWV(7)); - sess.spnego.in.capabilities = IVAL(req->in.vwv, VWV(10)); - - p = req->in.data; - if (!req_pull_blob(req, p, blob_len, &sess.spnego.in.secblob)) { - req_reply_error(req, NT_STATUS_FOOBAR); - return; - } - p += blob_len; - - p += req_pull_string(req, &sess.spnego.in.os, p, -1, STR_TERMINATE); - p += req_pull_string(req, &sess.spnego.in.lanman, p, -1, STR_TERMINATE); - p += req_pull_string(req, &sess.spnego.in.workgroup, p, -1, STR_TERMINATE); - - /* call the generic handler */ - status = sesssetup_backend(req, &sess); - - if (!NT_STATUS_IS_OK(status) && - !NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { - req_reply_error(req, status); - return; - } - - /* construct reply */ - req_setup_reply(req, 4, sess.spnego.out.secblob.length); - - if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { - req_setup_error(req, status); - } - - SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE); - SSVAL(req->out.vwv, VWV(1), 0); - SSVAL(req->out.vwv, VWV(2), sess.spnego.out.action); - SSVAL(req->out.vwv, VWV(3), sess.spnego.out.secblob.length); - - SSVAL(req->out.hdr, HDR_UID, sess.spnego.out.vuid); - - memcpy(req->out.data, sess.spnego.out.secblob.data, sess.spnego.out.secblob.length); - req_push_str(req, NULL, sess.spnego.out.os, -1, STR_TERMINATE); - req_push_str(req, NULL, sess.spnego.out.lanman, -1, STR_TERMINATE); - req_push_str(req, NULL, sess.spnego.out.workgroup, -1, STR_TERMINATE); - - chain_reply(req); -} - - -/**************************************************************************** -reply to a session setup command -****************************************************************************/ -void reply_sesssetup(struct smbsrv_request *req) -{ - switch (req->in.wct) { - case 10: - /* a pre-NT1 call */ - reply_sesssetup_old(req); - return; - case 13: - /* a NT1 call */ - reply_sesssetup_nt1(req); - return; - case 12: - /* a SPNEGO call */ - reply_sesssetup_spnego(req); - return; - } - - /* unsupported variant */ - req_reply_error(req, NT_STATUS_FOOBAR); -} - -/**************************************************************************** - Reply to a SMBulogoffX. -****************************************************************************/ -void reply_ulogoffX(struct smbsrv_request *req) -{ - struct smbsrv_tcon *tcon; - NTSTATUS status; - - if (!req->session) { - req_reply_error(req, NT_STATUS_DOS(ERRSRV, ERRbaduid)); - return; - } - - /* in user level security we are supposed to close any files - open by this user on all open tree connects */ - for (tcon=req->smb_conn->tcons.list;tcon;tcon=tcon->next) { - req->tcon = tcon; - status = ntvfs_logoff(req); - req->tcon = NULL; - if (!NT_STATUS_IS_OK(status)) { - req_reply_error(req, status); - return; - } - } - - talloc_free(req->session); - req->session = NULL; /* it is now invalid, don't use on - any chained packets */ - - req_setup_reply(req, 2, 0); - - SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE); - SSVAL(req->out.vwv, VWV(1), 0); - - chain_reply(req); -} - - -/**************************************************************************** - Reply to an SMBfindclose request -****************************************************************************/ -void reply_findclose(struct smbsrv_request *req) -{ - NTSTATUS status; - union smb_search_close io; - - io.findclose.level = RAW_FINDCLOSE_FINDCLOSE; - - /* parse request */ - REQ_CHECK_WCT(req, 1); - - io.findclose.in.handle = SVAL(req->in.vwv, VWV(0)); - - /* call backend */ - status = ntvfs_search_close(req, &io); - - if (!NT_STATUS_IS_OK(status)) { - req_reply_error(req, status); - return; - } - - /* construct reply */ - req_setup_reply(req, 0, 0); - - req_send_reply(req); -} - -/**************************************************************************** - Reply to an SMBfindnclose request -****************************************************************************/ -void reply_findnclose(struct smbsrv_request *req) -{ - req_reply_error(req, NT_STATUS_FOOBAR); -} - - -/**************************************************************************** - Reply to an SMBntcreateX request (async send) -****************************************************************************/ -static void reply_ntcreate_and_X_send(struct smbsrv_request *req) -{ - union smb_open *io = req->async_states->private_data; - - CHECK_ASYNC_STATUS; - - /* construct reply */ - req_setup_reply(req, 34, 0); - - SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE); - SSVAL(req->out.vwv, VWV(1), 0); - SCVAL(req->out.vwv, VWV(2), io->ntcreatex.out.oplock_level); - - /* the rest of the parameters are not aligned! */ - SSVAL(req->out.vwv, 5, io->ntcreatex.out.fnum); - SIVAL(req->out.vwv, 7, io->ntcreatex.out.create_action); - push_nttime(req->out.vwv, 11, io->ntcreatex.out.create_time); - push_nttime(req->out.vwv, 19, io->ntcreatex.out.access_time); - push_nttime(req->out.vwv, 27, io->ntcreatex.out.write_time); - push_nttime(req->out.vwv, 35, io->ntcreatex.out.change_time); - SIVAL(req->out.vwv, 43, io->ntcreatex.out.attrib); - SBVAL(req->out.vwv, 47, io->ntcreatex.out.alloc_size); - SBVAL(req->out.vwv, 55, io->ntcreatex.out.size); - SSVAL(req->out.vwv, 63, io->ntcreatex.out.file_type); - SSVAL(req->out.vwv, 65, io->ntcreatex.out.ipc_state); - SCVAL(req->out.vwv, 67, io->ntcreatex.out.is_directory); - - req->chained_fnum = io->ntcreatex.out.fnum; - - chain_reply(req); -} - -/**************************************************************************** - Reply to an SMBntcreateX request -****************************************************************************/ -void reply_ntcreate_and_X(struct smbsrv_request *req) -{ - union smb_open *io; - uint16_t fname_len; - - /* parse the request */ - REQ_CHECK_WCT(req, 24); - REQ_TALLOC(io, sizeof(*io)); - - io->ntcreatex.level = RAW_OPEN_NTCREATEX; - - /* notice that the word parameters are not word aligned, so we don't use VWV() */ - fname_len = SVAL(req->in.vwv, 5); - io->ntcreatex.in.flags = IVAL(req->in.vwv, 7); - io->ntcreatex.in.root_fid = IVAL(req->in.vwv, 11); - io->ntcreatex.in.access_mask = IVAL(req->in.vwv, 15); - io->ntcreatex.in.alloc_size = BVAL(req->in.vwv, 19); - io->ntcreatex.in.file_attr = IVAL(req->in.vwv, 27); - io->ntcreatex.in.share_access = IVAL(req->in.vwv, 31); - io->ntcreatex.in.open_disposition = IVAL(req->in.vwv, 35); - io->ntcreatex.in.create_options = IVAL(req->in.vwv, 39); - io->ntcreatex.in.impersonation = IVAL(req->in.vwv, 43); - io->ntcreatex.in.security_flags = CVAL(req->in.vwv, 47); - io->ntcreatex.in.ea_list = NULL; - io->ntcreatex.in.sec_desc = NULL; - - /* we need a neater way to handle this alignment */ - if ((req->flags2 & FLAGS2_UNICODE_STRINGS) && - ucs2_align(req->in.buffer, req->in.data, STR_TERMINATE|STR_UNICODE)) { - fname_len++; - } - - req_pull_string(req, &io->ntcreatex.in.fname, req->in.data, fname_len, STR_TERMINATE); - if (!io->ntcreatex.in.fname) { - req_reply_error(req, NT_STATUS_FOOBAR); - return; - } - - req->async_states->state |= NTVFS_ASYNC_STATE_MAY_ASYNC; - req->async_states->send_fn = reply_ntcreate_and_X_send; - req->async_states->private_data = io; - - /* call the backend */ - req->async_states->status = ntvfs_openfile(req, io); - - REQ_ASYNC_TAIL; -} - - -/**************************************************************************** - Reply to an SMBntcancel request -****************************************************************************/ -void reply_ntcancel(struct smbsrv_request *req) -{ - /* NOTE: this request does not generate a reply */ - ntvfs_cancel(req); - req_destroy(req); -} - -/**************************************************************************** - Reply to an SMBsends request -****************************************************************************/ -void reply_sends(struct smbsrv_request *req) -{ - req_reply_error(req, NT_STATUS_FOOBAR); -} - -/**************************************************************************** - Reply to an SMBsendstrt request -****************************************************************************/ -void reply_sendstrt(struct smbsrv_request *req) -{ - req_reply_error(req, NT_STATUS_FOOBAR); -} - -/**************************************************************************** - Reply to an SMBsendend request -****************************************************************************/ -void reply_sendend(struct smbsrv_request *req) -{ - req_reply_error(req, NT_STATUS_FOOBAR); -} - -/**************************************************************************** - Reply to an SMBsendtxt request -****************************************************************************/ -void reply_sendtxt(struct smbsrv_request *req) -{ - req_reply_error(req, NT_STATUS_FOOBAR); -} - - -/* - parse the called/calling names from session request -*/ -static NTSTATUS parse_session_request(struct smbsrv_request *req) -{ - DATA_BLOB blob; - NTSTATUS status; - - blob.data = req->in.buffer + 4; - blob.length = ascii_len_n((const char *)blob.data, req->in.size - PTR_DIFF(blob.data, req->in.buffer)); - if (blob.length == 0) return NT_STATUS_BAD_NETWORK_NAME; - - req->smb_conn->negotiate.called_name = talloc(req->smb_conn, struct nbt_name); - req->smb_conn->negotiate.calling_name = talloc(req->smb_conn, struct nbt_name); - if (req->smb_conn->negotiate.called_name == NULL || - req->smb_conn->negotiate.calling_name == NULL) { - return NT_STATUS_NO_MEMORY; - } - - status = nbt_name_from_blob(req->smb_conn, &blob, - req->smb_conn->negotiate.called_name); - NT_STATUS_NOT_OK_RETURN(status); - - blob.data += blob.length; - blob.length = ascii_len_n((const char *)blob.data, req->in.size - PTR_DIFF(blob.data, req->in.buffer)); - if (blob.length == 0) return NT_STATUS_BAD_NETWORK_NAME; - - status = nbt_name_from_blob(req->smb_conn, &blob, - req->smb_conn->negotiate.calling_name); - NT_STATUS_NOT_OK_RETURN(status); - - req->smb_conn->negotiate.done_nbt_session = True; - - return NT_STATUS_OK; -} - - - -/**************************************************************************** - Reply to a special message - a SMB packet with non zero NBT message type -****************************************************************************/ -void reply_special(struct smbsrv_request *req) -{ - uint8_t msg_type; - uint8_t *buf = talloc_zero_array(req, uint8_t, 4); - - msg_type = CVAL(req->in.buffer,0); - - SIVAL(buf, 0, 0); - - switch (msg_type) { - case 0x81: /* session request */ - if (req->smb_conn->negotiate.done_nbt_session) { - DEBUG(0,("Warning: ignoring secondary session request\n")); - return; - } - - SCVAL(buf,0,0x82); - SCVAL(buf,3,0); - - /* we don't check the status - samba always accepts session - requests for any name */ - parse_session_request(req); - - req->out.buffer = buf; - req->out.size = 4; - req_send_reply_nosign(req); - return; - - case 0x89: /* session keepalive request - (some old clients produce this?) */ - SCVAL(buf, 0, SMBkeepalive); - SCVAL(buf, 3, 0); - req->out.buffer = buf; - req->out.size = 4; - req_send_reply_nosign(req); - return; - - case SMBkeepalive: - /* session keepalive - swallow it */ - req_destroy(req); - return; - } - - DEBUG(0,("Unexpected NBT session packet (%d)\n", msg_type)); - req_destroy(req); -} diff --git a/source4/smb_server/request.c b/source4/smb_server/request.c deleted file mode 100644 index 5491089c8e..0000000000 --- a/source4/smb_server/request.c +++ /dev/null @@ -1,675 +0,0 @@ -/* - 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 "lib/events/events.h" -#include "dlinklist.h" -#include "smb_server/smb_server.h" -#include "smbd/service_stream.h" -#include "lib/stream/packet.h" - - -/* we over allocate the data buffer to prevent too many realloc calls */ -#define REQ_OVER_ALLOCATION 0 - -/* destroy a request structure */ -void req_destroy(struct smbsrv_request *req) -{ - /* ahh, its so nice to destroy a complex structure in such a - * simple way! */ - talloc_free(req); -} - -/**************************************************************************** -construct a basic request packet, mostly used to construct async packets -such as change notify and oplock break requests -****************************************************************************/ -struct smbsrv_request *init_smb_request(struct smbsrv_connection *smb_conn) -{ - struct smbsrv_request *req; - - req = talloc(smb_conn, struct smbsrv_request); - if (!req) { - return NULL; - } - - ZERO_STRUCTP(req); - - /* setup the request context */ - req->smb_conn = smb_conn; - - req->async_states = talloc(req, struct ntvfs_async_state); - if (!req->async_states) { - talloc_free(req); - return NULL; - } - req->async_states->state = 0; - - 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 req_setup_reply(struct smbsrv_request *req, uint_t wct, uint_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 + wct*2 + 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 { - 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 *req_setup_secondary(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, uint_t new_size) -{ - int delta; - - if (!(req->control_flags & 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 req_send_reply_nosign(struct smbsrv_request *req) -{ - DATA_BLOB blob; - NTSTATUS status; - - 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)); - } - req_destroy(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 req_send_reply(struct smbsrv_request *req) -{ - req_sign_packet(req); - - req_send_reply_nosign(req); -} - - - -/* - construct and send an error packet with a forced DOS error code - this is needed to match win2000 behaviour for some parts of the protocol -*/ -void req_reply_dos_error(struct smbsrv_request *req, uint8_t eclass, uint16_t ecode) -{ - /* if the basic packet hasn't been setup yet then do it now */ - if (req->out.buffer == NULL) { - req_setup_reply(req, 0, 0); - } - - 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); - req_send_reply(req); -} - -/* - setup the header of a reply to include an NTSTATUS code -*/ -void req_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 req_reply_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; - } - req_setup_reply(req, 0, 0); - - /* error returns never have any data */ - req_grow_data(req, 0); - - req_setup_error(req, status); - req_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, uint_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 NULL 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 -*/ -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); -} diff --git a/source4/smb_server/search.c b/source4/smb_server/search.c deleted file mode 100644 index 3ec66a0f33..0000000000 --- a/source4/smb_server/search.c +++ /dev/null @@ -1,285 +0,0 @@ -/* - Unix SMB/CIFS implementation. - SMBsearch handling - Copyright (C) Andrew Tridgell 2003 - Copyright (C) James J Myers 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 handles the parsing of transact2 requests -*/ - -#include "includes.h" -#include "smb_server/smb_server.h" - - -/* check req->async.status and if not OK then send an error reply */ -#define CHECK_ASYNC_STATUS do { \ - if (!NT_STATUS_IS_OK(req->async_states->status)) { \ - req_reply_error(req, req->async_states->status); \ - return; \ - }} while (0) - -/* - check if the backend wants to handle the request asynchronously. - if it wants it handled synchronously then call the send function - immediately -*/ -#define REQ_ASYNC_TAIL do { \ - if (!(req->async_states->state & NTVFS_ASYNC_STATE_ASYNC)) { \ - req->async_states->send_fn(req); \ - }} while (0) - -/* useful wrapper for talloc with NO_MEMORY reply */ -#define REQ_TALLOC(ptr) do { \ - ptr = talloc_size(req, sizeof(*(ptr))); \ - if (!ptr) { \ - req_reply_error(req, NT_STATUS_NO_MEMORY); \ - return; \ - }} while (0) - -#define CHECK_MIN_BLOB_SIZE(blob, size) do { \ - if ((blob)->length < (size)) { \ - return NT_STATUS_INFO_LENGTH_MISMATCH; \ - }} while (0) - -/* 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. -****************************************************************************/ -void reply_search(struct smbsrv_request *req) -{ - union smb_search_first *sf; - union smb_search_next *sn; - uint16_t resume_key_length; - struct search_state state; - uint8_t *p; - NTSTATUS status; - 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; - } - - REQ_TALLOC(sf); - - /* parse request */ - if (req->in.wct != 2) { - req_reply_error(req, NT_STATUS_INVALID_PARAMETER); - return; - } - - p = req->in.data; - p += req_pull_ascii4(req, &sf->search_first.in.pattern, - p, STR_TERMINATE); - if (!sf->search_first.in.pattern) { - req_reply_error(req, NT_STATUS_OBJECT_NAME_NOT_FOUND); - return; - } - - if (req_data_oob(req, p, 3)) { - req_reply_error(req, NT_STATUS_INVALID_PARAMETER); - return; - } - if (*p != 5) { - req_reply_error(req, NT_STATUS_INVALID_PARAMETER); - return; - } - resume_key_length = SVAL(p, 1); - p += 3; - - /* setup state for callback */ - state.req = req; - state.file = NULL; - state.last_entry_offset = 0; - - /* construct reply */ - req_setup_reply(req, 1, 0); - req_append_var_block(req, NULL, 0); - - if (resume_key_length != 0) { - if (resume_key_length != 21 || - req_data_oob(req, p, 21) || - level == RAW_SEARCH_FUNIQUE) { - req_reply_error(req, NT_STATUS_INVALID_PARAMETER); - return; - } - - /* do a search next operation */ - REQ_TALLOC(sn); - - 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.in.max_count = SVAL(req->in.vwv, VWV(0)); - sn->search_next.in.search_attrib = SVAL(req->in.vwv, VWV(1)); - - /* call backend */ - status = ntvfs_search_next(req, sn, &state, find_callback); - SSVAL(req->out.vwv, VWV(0), sn->search_next.out.count); - } else { - /* do a search first operation */ - sf->search_first.level = level; - sf->search_first.in.search_attrib = SVAL(req->in.vwv, VWV(1)); - sf->search_first.in.max_count = SVAL(req->in.vwv, VWV(0)); - - /* call backend */ - status = ntvfs_search_first(req, sf, &state, find_callback); - SSVAL(req->out.vwv, VWV(0), sf->search_first.out.count); - } - - if (!NT_STATUS_IS_OK(status)) { - req_reply_error(req, status); - return; - } - - req_send_reply(req); -} - - -/**************************************************************************** - Reply to a fclose (async reply) -****************************************************************************/ -static void reply_fclose_send(struct smbsrv_request *req) -{ - CHECK_ASYNC_STATUS; - - /* construct reply */ - req_setup_reply(req, 1, 0); - - SSVAL(req->out.vwv, VWV(0), 0); - - req_send_reply(req); -} - - -/**************************************************************************** - Reply to fclose (stop directory search). -****************************************************************************/ -void reply_fclose(struct smbsrv_request *req) -{ - union smb_search_close *sc; - uint16_t resume_key_length; - uint8_t *p; - const char *pattern; - - REQ_TALLOC(sc); - - /* parse request */ - if (req->in.wct != 2) { - req_reply_error(req, NT_STATUS_INVALID_PARAMETER); - return; - } - - p = req->in.data; - p += req_pull_ascii4(req, &pattern, p, STR_TERMINATE); - if (pattern && *pattern) { - req_reply_error(req, NT_STATUS_INVALID_PARAMETER); - return; - } - - if (req_data_oob(req, p, 3)) { - req_reply_error(req, NT_STATUS_INVALID_PARAMETER); - return; - } - if (*p != 5) { - req_reply_error(req, NT_STATUS_INVALID_PARAMETER); - return; - } - resume_key_length = SVAL(p, 1); - p += 3; - - if (resume_key_length != 21) { - req_reply_error(req, NT_STATUS_INVALID_PARAMETER); - return; - } - - if (req_data_oob(req, p, 21)) { - req_reply_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); - - /* do a search close operation */ - req->async_states->state |= NTVFS_ASYNC_STATE_MAY_ASYNC; - req->async_states->send_fn = reply_fclose_send; - req->async_states->private_data = sc; - - /* call backend */ - req->async_states->status = ntvfs_search_close(req, sc); - - REQ_ASYNC_TAIL; -} diff --git a/source4/smb_server/service.c b/source4/smb_server/service.c deleted file mode 100644 index 213cf6726b..0000000000 --- a/source4/smb_server/service.c +++ /dev/null @@ -1,194 +0,0 @@ -/* - 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" - - -/** - * Find a service entry. service is always in dos codepage. - * - * @param service is modified (to canonical form??) - **/ -static int find_service(const char *service) -{ - int iService; - - iService = lp_servicenumber(service); - - if (iService >= 0 && !lp_snum_ok(iService)) { - DEBUG(0,("Invalid snum %d for %s\n",iService, service)); - iService = -1; - } - - if (iService == -1) { - DEBUG(3,("find_service() failed to find service %s\n", service)); - } - - return iService; -} - - -/**************************************************************************** - Make a connection, given the snum to connect to, and the vuser of the - connecting user if appropriate. -****************************************************************************/ -static NTSTATUS make_connection_snum(struct smbsrv_request *req, - int snum, enum ntvfs_type type, - DATA_BLOB password, - const char *dev) -{ - struct smbsrv_tcon *tcon; - NTSTATUS status; - - if (!socket_check_access(req->smb_conn->connection->socket, - lp_servicename(snum), - lp_hostsallow(snum), - lp_hostsdeny(snum))) { - return NT_STATUS_ACCESS_DENIED; - } - - tcon = smbsrv_tcon_new(req->smb_conn); - if (!tcon) { - DEBUG(0,("Couldn't find free connection.\n")); - return NT_STATUS_INSUFFICIENT_RESOURCES; - } - req->tcon = tcon; - - tcon->service = snum; - - /* init ntvfs function pointers */ - status = ntvfs_init_connection(req, type); - if (!NT_STATUS_IS_OK(status)) { - DEBUG(0, ("ntvfs_init_connection failed for service %s\n", - lp_servicename(tcon->service))); - req->tcon = NULL; - talloc_free(tcon); - return status; - } - - /* Invoke NTVFS connection hook */ - status = ntvfs_connect(req, lp_servicename(snum)); - if (!NT_STATUS_IS_OK(status)) { - DEBUG(0,("make_connection: NTVFS make connection failed!\n")); - req->tcon = NULL; - talloc_free(tcon); - return status; - } - - return NT_STATUS_OK; -} - -/**************************************************************************** - 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) -{ - int snum; - enum ntvfs_type type; - const char *type_str; - - /* TODO: check the password, when it's share level security! */ - - /* 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; - } - } - - snum = find_service(service); - - if (snum == -1) { - DEBUG(0,("couldn't find service %s\n", service)); - return NT_STATUS_BAD_NETWORK_NAME; - } - - /* work out what sort of connection this is */ - if (strcmp(lp_fstype(snum), "IPC") == 0) { - type = NTVFS_IPC; - type_str = "IPC"; - } else if (lp_print_ok(snum)) { - 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_snum(req, snum, type, password, dev); -} - -/* - backend for tree connect call -*/ -NTSTATUS tcon_backend(struct smbsrv_request *req, union smb_tcon *con) -{ - NTSTATUS status; - - /* can only do bare tcon in share level security */ - if (!req->session && lp_security() != SEC_SHARE) { - return NT_STATUS_ACCESS_DENIED; - } - - 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->dev_type); - con->tconx.out.fs_type = talloc_strdup(req, req->tcon->fs_type); - con->tconx.out.options = SMB_SUPPORT_SEARCH_BITS | (lp_csc_policy(req->tcon->service) << 2); - if (lp_msdfs_root(req->tcon->service) && lp_host_msdfs()) { - con->tconx.out.options |= SMB_SHARE_IN_DFS; - } - - return status; -} diff --git a/source4/smb_server/sesssetup.c b/source4/smb_server/sesssetup.c deleted file mode 100644 index 3f09346243..0000000000 --- a/source4/smb_server/sesssetup.c +++ /dev/null @@ -1,370 +0,0 @@ -/* - Unix SMB/CIFS implementation. - handle SMBsessionsetup - Copyright (C) Andrew Tridgell 1998-2001 - Copyright (C) Andrew Bartlett 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/auth.h" -#include "smb_server/smb_server.h" -#include "smbd/service_stream.h" -#include "libcli/nbt/libnbt.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()); -} - - -/* - handler for old style session setup -*/ -static NTSTATUS sesssetup_old(struct smbsrv_request *req, union smb_sesssetup *sess) -{ - NTSTATUS status; - struct auth_usersupplied_info *user_info = NULL; - struct auth_serversupplied_info *server_info = NULL; - struct auth_session_info *session_info; - struct smbsrv_session *smb_sess; - const char *remote_machine = NULL; - - sess->old.out.vuid = 0; - sess->old.out.action = 0; - - 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; - } - - if (!remote_machine) { - remote_machine = socket_get_peer_addr(req->smb_conn->connection->socket, req); - } - - user_info = talloc(req, struct auth_usersupplied_info); - NT_STATUS_HAVE_NO_MEMORY(user_info); - - 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 = socket_get_peer_addr(req->smb_conn->connection->socket, user_info); - - 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); - - status = auth_check_password(req->smb_conn->negotiate.auth_context, - req, user_info, &server_info); - if (!NT_STATUS_IS_OK(status)) { - return auth_nt_status_squash(status); - } - - /* This references server_info into session_info */ - status = auth_generate_session_info(req, server_info, &session_info); - if (!NT_STATUS_IS_OK(status)) { - return auth_nt_status_squash(status); - } - - /* allocate a new session */ - smb_sess = smbsrv_session_new(req->smb_conn, NULL); - if (!smb_sess) { - return NT_STATUS_ACCESS_DENIED; - } - - /* 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)) { - return auth_nt_status_squash(status); - } - - /* 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; - - sesssetup_common_strings(req, - &sess->old.out.os, - &sess->old.out.lanman, - &sess->old.out.domain); - - return NT_STATUS_OK; -} - - -/* - handler for NT1 style session setup -*/ -static NTSTATUS 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 auth_serversupplied_info *server_info = NULL; - struct auth_session_info *session_info; - struct smbsrv_session *smb_sess; - const char *remote_machine = NULL; - - sess->nt1.out.vuid = 0; - sess->nt1.out.action = 0; - - 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.spnego_negotiated) { - if (sess->nt1.in.user && *sess->nt1.in.user) { - /* We can't accept a normal login, because we - * don't have a challenge */ - return NT_STATUS_LOGON_FAILURE; - } - - /* TODO: should we use just "anonymous" here? */ - status = auth_context_create(req, lp_auth_methods(), - &auth_context, - req->smb_conn->connection->event.ctx); - NT_STATUS_NOT_OK_RETURN(status); - } 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; - } - - if (!remote_machine) { - remote_machine = socket_get_peer_addr(req->smb_conn->connection->socket, req); - } - - user_info = talloc(req, struct auth_usersupplied_info); - NT_STATUS_HAVE_NO_MEMORY(user_info); - - 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 = socket_get_peer_addr(req->smb_conn->connection->socket, user_info); - - 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); - - status = auth_check_password(auth_context, req, user_info, &server_info); - if (!NT_STATUS_IS_OK(status)) { - return auth_nt_status_squash(status); - } - - /* This references server_info into session_info */ - status = auth_generate_session_info(req, server_info, &session_info); - if (!NT_STATUS_IS_OK(status)) { - return auth_nt_status_squash(status); - } - - /* allocate a new session */ - smb_sess = smbsrv_session_new(req->smb_conn, NULL); - if (!smb_sess) { - return NT_STATUS_ACCESS_DENIED; - } - - /* 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)) { - return auth_nt_status_squash(status); - } - - /* 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; - - sesssetup_common_strings(req, - &sess->nt1.out.os, - &sess->nt1.out.lanman, - &sess->nt1.out.domain); - - if (!session_info->server_info->authenticated) { - return NT_STATUS_OK; - } - - if (!srv_setup_signing(req->smb_conn, &session_info->session_key, &sess->nt1.in.password2)) { - /* Already signing, or disabled */ - return NT_STATUS_OK; - } - - /* Force check of the request packet, now we know the session key */ - req_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 */ - - srv_signing_restart(req->smb_conn, &session_info->session_key, &sess->nt1.in.password2); - - return NT_STATUS_OK; -} - - -/* - handler for SPNEGO style session setup -*/ -static NTSTATUS sesssetup_spnego(struct smbsrv_request *req, union smb_sesssetup *sess) -{ - NTSTATUS status = NT_STATUS_ACCESS_DENIED; - struct smbsrv_session *smb_sess; - struct gensec_security *gensec_ctx; - struct auth_session_info *session_info = 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->nt1.in.bufsize; - req->smb_conn->negotiate.client_caps = sess->nt1.in.capabilities; - } - - vuid = SVAL(req->in.hdr,HDR_UID); - smb_sess = smbsrv_session_find_sesssetup(req->smb_conn, vuid); - if (smb_sess) { - gensec_ctx = smb_sess->gensec_ctx; - } else { - status = gensec_server_start(req, &gensec_ctx, - req->smb_conn->connection->event.ctx); - if (!NT_STATUS_IS_OK(status)) { - DEBUG(1, ("Failed to start GENSEC server code: %s\n", nt_errstr(status))); - return status; - } - - gensec_set_credentials(gensec_ctx, req->smb_conn->negotiate.server_credentials); - - gensec_set_target_service(gensec_ctx, "cifs"); - - gensec_want_feature(gensec_ctx, GENSEC_FEATURE_SESSION_KEY); - - status = gensec_start_mech_by_oid(gensec_ctx, GENSEC_OID_SPNEGO); - if (!NT_STATUS_IS_OK(status)) { - DEBUG(1, ("Failed to start GENSEC SPNEGO server code: %s\n", nt_errstr(status))); - return status; - } - - smb_sess = smbsrv_session_new(req->smb_conn, gensec_ctx); - if (!smb_sess) { - return NT_STATUS_ACCESS_DENIED; - } - } - - status = gensec_update(gensec_ctx, req, sess->spnego.in.secblob, &sess->spnego.out.secblob); - if (NT_STATUS_IS_OK(status)) { - DATA_BLOB session_key; - - status = gensec_session_info(gensec_ctx, &session_info); - if (!NT_STATUS_IS_OK(status)) { - talloc_free(smb_sess); - return status; - } - - status = gensec_session_key(gensec_ctx, - &session_key); -/* TODO: what if getting the session key failed? */ - if (NT_STATUS_IS_OK(status) - && session_info->server_info->authenticated - && srv_setup_signing(req->smb_conn, &session_key, NULL)) { - /* Force check of the request packet, now we know the session key */ - req_signing_check_incoming(req); - - srv_signing_restart(req->smb_conn, &session_key, NULL); - } - - } else if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { - } else { - status = auth_nt_status_squash(status); - - /* This invalidates the VUID of the failed login */ - talloc_free(smb_sess); - return status; - } - - if (NT_STATUS_IS_OK(status)) { - /* 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)) { - return auth_nt_status_squash(status); - } - req->session = smb_sess; - } - sess->spnego.out.vuid = smb_sess->vuid; - - return status; -} - -/* - backend for sessionsetup call - this takes all 3 variants of the call -*/ -NTSTATUS sesssetup_backend(struct smbsrv_request *req, - union smb_sesssetup *sess) -{ - NTSTATUS status = NT_STATUS_INVALID_LEVEL; - - switch (sess->old.level) { - case RAW_SESSSETUP_OLD: - status = sesssetup_old(req, sess); - break; - case RAW_SESSSETUP_NT1: - status = sesssetup_nt1(req, sess); - break; - case RAW_SESSSETUP_SPNEGO: - status = sesssetup_spnego(req, sess); - break; - } - - if (NT_STATUS_IS_OK(status)) { - req->smb_conn->negotiate.done_sesssetup = True; - } - - return status; -} - - diff --git a/source4/smb_server/signing.c b/source4/smb_server/signing.c deleted file mode 100644 index b461056397..0000000000 --- a/source4/smb_server/signing.c +++ /dev/null @@ -1,182 +0,0 @@ -/* - 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" - - -/* - sign an outgoing packet -*/ -void req_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 srv_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 srv_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)... */ - srv_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 srv_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 req_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 req_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; -} diff --git a/source4/smb_server/smb/negprot.c b/source4/smb_server/smb/negprot.c new file mode 100644 index 0000000000..a9cc05e251 --- /dev/null +++ b/source4/smb_server/smb/negprot.c @@ -0,0 +1,472 @@ +/* + 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/auth.h" +#include "smb_server/smb_server.h" +#include "smbd/service_stream.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->negotiate.auth_context, + smb_conn->connection->event.ctx); + 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) +{ + req_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; + } + + req_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); + + req_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; + } + + req_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; + + req_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; + } + + req_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; + + req_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; + } + + req_send_reply(req); +} + +/**************************************************************************** + 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; + + 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; + } + + 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; + + req_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) { + /* 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 SPNEGO\n")); + } else { + struct cli_credentials *server_credentials; + struct gensec_security *gensec_security; + DATA_BLOB null_data_blob = data_blob(NULL, 0); + DATA_BLOB blob; + NTSTATUS nt_status = gensec_server_start(req->smb_conn, + &gensec_security, + req->smb_conn->connection->event.ctx); + + 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); + + nt_status = gensec_start_mech_by_oid(gensec_security, GENSEC_OID_SPNEGO); + + if (!NT_STATUS_IS_OK(nt_status)) { + DEBUG(0, ("Failed to start SPNEGO: %s\n", nt_errstr(nt_status))); + smbsrv_terminate_connection(req->smb_conn, "Failed to start SPNEGO\n"); + return; + } + + nt_status = gensec_update(gensec_security, req, null_data_blob, &blob); + + if (!NT_STATUS_IS_OK(nt_status) && !NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { + DEBUG(0, ("Failed to get SPNEGO to give us the first token: %s\n", nt_errstr(nt_status))); + smbsrv_terminate_connection(req->smb_conn, "Failed to start SPNEGO - no first token\n"); + return; + } + + req->smb_conn->negotiate.spnego_negotiated = True; + + req_grow_data(req, blob.length + 16); + /* a NOT very random guid */ + 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; + DEBUG(3,("using SPNEGO\n")); + } + + req_send_reply_nosign(req); +} + + +/* 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[] = { + {"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}, + {"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 reply_negprot(struct smbsrv_request *req) +{ + int Index=0; + int choice = -1; + int protocol; + uint8_t *p; + + 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 + 1; + + while (p < req->in.data + req->in.data_size) { + Index++; + DEBUG(3,("Requested protocol [%s]\n",(const char *)p)); + p += strlen((const char *)p) + 2; + } + + /* Check for protocols, most desirable first */ + for (protocol = 0; supported_protocols[protocol].proto_name; protocol++) { + p = req->in.data+1; + Index = 0; + if ((supported_protocols[protocol].protocol_level <= lp_maxprotocol()) && + (supported_protocols[protocol].protocol_level >= lp_minprotocol())) + while (p < (req->in.data + req->in.data_size)) { + if (strequal((const char *)p,supported_protocols[protocol].proto_name)) + choice = Index; + Index++; + p += strlen((const char *)p) + 2; + } + if(choice != -1) + break; + } + + if(choice != -1) { + sub_set_remote_proto(supported_protocols[protocol].short_name); + supported_protocols[protocol].proto_reply_fn(req, choice); + DEBUG(3,("Selected protocol %s\n",supported_protocols[protocol].proto_name)); + } else { + DEBUG(0,("No protocol supported !\n")); + } + + DEBUG(5,("negprot index=%d\n", choice)); +} diff --git a/source4/smb_server/smb/nttrans.c b/source4/smb_server/smb/nttrans.c new file mode 100644 index 0000000000..215b378283 --- /dev/null +++ b/source4/smb_server/smb/nttrans.c @@ -0,0 +1,518 @@ +/* + Unix SMB/CIFS implementation. + NT transaction handling + Copyright (C) Andrew Tridgell 2003 + Copyright (C) James J Myers 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 handles the parsing of transact2 requests +*/ + +#include "includes.h" +#include "smb_server/smb_server.h" +#include "librpc/gen_ndr/ndr_security.h" + + + +#define CHECK_MIN_BLOB_SIZE(blob, size) do { \ + if ((blob)->length < (size)) { \ + return NT_STATUS_INFO_LENGTH_MISMATCH; \ + }} while (0) + + +/* setup a nttrans reply, given the data and params sizes */ +static void nttrans_setup_reply(struct smbsrv_request *req, + struct smb_nttrans *trans, + uint16_t param_size, uint16_t data_size, + uint16_t setup_count) +{ + trans->out.setup_count = setup_count; + if (setup_count != 0) { + trans->out.setup = talloc_zero_array(req, uint16_t, setup_count); + } + trans->out.params = data_blob_talloc(req, NULL, param_size); + trans->out.data = data_blob_talloc(req, NULL, data_size); +} + + +/* + parse NTTRANS_CREATE request + */ +static NTSTATUS nttrans_create(struct smbsrv_request *req, + struct smb_nttrans *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(req, union smb_open); + if (io == NULL) { + return NT_STATUS_NO_MEMORY; + } + + 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 + 54, + trans->in.params.length - 54, + 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; + } + } + + /* call the backend - notice that we do it sync for now, until we support + async nttrans requests */ + status = ntvfs_openfile(req, io); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + trans->out.setup_count = 0; + trans->out.setup = NULL; + trans->out.params = data_blob_talloc(req, NULL, 69); + trans->out.data = data_blob(NULL, 0); + + params = trans->out.params.data; + if (params == NULL) { + return NT_STATUS_NO_MEMORY; + } + + SSVAL(params, 0, io->ntcreatex.out.oplock_level); + SSVAL(params, 2, io->ntcreatex.out.fnum); + 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_QUERY_SEC_DESC request + */ +static NTSTATUS nttrans_query_sec_desc(struct smbsrv_request *req, + struct smb_nttrans *trans) +{ + union smb_fileinfo *io; + NTSTATUS status; + + if (trans->in.params.length < 8) { + return NT_STATUS_INVALID_PARAMETER; + } + + /* parse the request */ + io = talloc(req, union smb_fileinfo); + if (io == NULL) { + return NT_STATUS_NO_MEMORY; + } + + io->query_secdesc.level = RAW_FILEINFO_SEC_DESC; + io->query_secdesc.in.fnum = SVAL(trans->in.params.data, 0); + io->query_secdesc.secinfo_flags = IVAL(trans->in.params.data, 4); + + /* call the backend - notice that we do it sync for now, until we support + async nttrans requests */ + status = ntvfs_qfileinfo(req, io); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + trans->out.setup_count = 0; + trans->out.setup = NULL; + trans->out.params = data_blob_talloc(req, NULL, 4); + trans->out.data = data_blob(NULL, 0); + + status = ndr_push_struct_blob(&trans->out.data, req, + io->query_secdesc.out.sd, + (ndr_push_flags_fn_t)ndr_push_security_descriptor); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + SIVAL(trans->out.params.data, 0, trans->out.data.length); + + return NT_STATUS_OK; +} + + +/* + parse NTTRANS_SET_SEC_DESC request + */ +static NTSTATUS nttrans_set_sec_desc(struct smbsrv_request *req, + struct smb_nttrans *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); + if (io == NULL) { + return NT_STATUS_NO_MEMORY; + } + + io->set_secdesc.level = RAW_SFILEINFO_SEC_DESC; + io->set_secdesc.file.fnum = SVAL(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); + if (io->set_secdesc.in.sd == NULL) { + return NT_STATUS_NO_MEMORY; + } + + status = ndr_pull_struct_blob(&trans->in.data, req, + io->set_secdesc.in.sd, + (ndr_pull_flags_fn_t)ndr_pull_security_descriptor); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + /* call the backend - notice that we do it sync for now, until we support + async nttrans requests */ + status = ntvfs_setfileinfo(req, io); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + trans->out.setup_count = 0; + trans->out.setup = NULL; + trans->out.params = data_blob(NULL, 0); + trans->out.data = data_blob(NULL, 0); + + return NT_STATUS_OK; +} + + +/* parse NTTRANS_RENAME request + */ +static NTSTATUS nttrans_rename(struct smbsrv_request *req, + struct smb_nttrans *trans) +{ + return NT_STATUS_FOOBAR; +} + +/* + parse NTTRANS_IOCTL request + */ +static NTSTATUS nttrans_ioctl(struct smbsrv_request *req, + struct smb_nttrans *trans) +{ + union smb_ioctl nt; + uint32_t function; + uint16_t fnum; + uint8_t filter; + BOOL fsctl; + DATA_BLOB *blob; + + /* should have at least 4 setup words */ + if (trans->in.setup_count != 4) { + return NT_STATUS_INVALID_PARAMETER; + } + + function = IVAL(trans->in.setup, 0); + fnum = SVAL(trans->in.setup, 4); + fsctl = CVAL(trans->in.setup, 6); + filter = CVAL(trans->in.setup, 7); + + blob = &trans->in.data; + + nt.ntioctl.level = RAW_IOCTL_NTIOCTL; + nt.ntioctl.in.fnum = fnum; + nt.ntioctl.in.function = function; + nt.ntioctl.in.fsctl = fsctl; + nt.ntioctl.in.filter = filter; + + nttrans_setup_reply(req, trans, 0, 0, 1); + trans->out.setup[0] = 0; + + return ntvfs_ioctl(req, &nt); +} + +/* + backend for nttrans requests +*/ +static NTSTATUS nttrans_backend(struct smbsrv_request *req, + struct smb_nttrans *trans) +{ + /* the nttrans command is in function */ + switch (trans->in.function) { + case NT_TRANSACT_CREATE: + return nttrans_create(req, trans); + case NT_TRANSACT_IOCTL: + return nttrans_ioctl(req, trans); + case NT_TRANSACT_RENAME: + return nttrans_rename(req, trans); + case NT_TRANSACT_QUERY_SECURITY_DESC: + return nttrans_query_sec_desc(req, trans); + case NT_TRANSACT_SET_SECURITY_DESC: + return nttrans_set_sec_desc(req, trans); + } + + /* an unknown nttrans command */ + return NT_STATUS_FOOBAR; +} + + +/**************************************************************************** + Reply to an SMBnttrans request +****************************************************************************/ +void reply_nttrans(struct smbsrv_request *req) +{ + struct smb_nttrans trans; + int i; + uint16_t param_ofs, data_ofs; + uint16_t param_count, data_count; + uint16_t params_left, data_left; + uint16_t param_total, data_total; + uint8_t *params, *data; + NTSTATUS status; + + /* parse request */ + if (req->in.wct < 19) { + req_reply_error(req, NT_STATUS_FOOBAR); + return; + } + + 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) { + req_reply_dos_error(req, ERRSRV, ERRerror); + return; + } + + /* parse out the setup words */ + trans.in.setup = talloc_array(req, uint16_t, trans.in.setup_count); + if (!trans.in.setup) { + req_reply_error(req, NT_STATUS_NO_MEMORY); + return; + } + for (i=0;iin.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)) { + req_reply_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")); + return; + } + + /* its a full request, give it to the backend */ + status = nttrans_backend(req, &trans); + + if (NT_STATUS_IS_ERR(status)) { + req_reply_error(req, status); + return; + } + +#if 0 + /* w2k3 does not check the max_setup count */ + if (trans.out.setup_count > trans.in.max_setup) { + req_reply_error(req, NT_STATUS_BUFFER_TOO_SMALL); + return; + } +#endif + if (trans.out.params.length > trans.in.max_param) { + status = NT_STATUS_BUFFER_TOO_SMALL; + trans.out.params.length = trans.in.max_param; + } + if (trans.out.data.length > trans.in.max_data) { + status = 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; + + req_setup_reply(req, 18 + trans.out.setup_count, 0); + + if (!NT_STATUS_IS_OK(status)) { + req_setup_error(req, status); + } + + /* 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; + + 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 = req_setup_secondary(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;iout.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; + + req_send_reply(this_req); + } while (params_left != 0 || data_left != 0); +} + + +/**************************************************************************** + Reply to an SMBnttranss request +****************************************************************************/ +void reply_nttranss(struct smbsrv_request *req) +{ + req_reply_error(req, NT_STATUS_FOOBAR); +} diff --git a/source4/smb_server/smb/receive.c b/source4/smb_server/smb/receive.c new file mode 100644 index 0000000000..0ee87698c8 --- /dev/null +++ b/source4/smb_server/smb/receive.c @@ -0,0 +1,657 @@ +/* + Unix SMB/CIFS implementation. + process incoming packets - main loop + Copyright (C) Andrew Tridgell 1992-2005 + Copyright (C) James J Myers 2003 + 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 "lib/events/events.h" +#include "system/time.h" +#include "dlinklist.h" +#include "smbd/service_stream.h" +#include "smb_server/smb_server.h" +#include "lib/messaging/irpc.h" +#include "lib/stream/packet.h" + + +/* + send an oplock break request to a client +*/ +BOOL req_send_oplock_break(struct smbsrv_tcon *tcon, uint16_t fnum, uint8_t level) +{ + struct smbsrv_request *req; + + req = init_smb_request(tcon->smb_conn); + + req_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); + SSVAL(req->out.vwv, VWV(2), fnum); + 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); + + req_send_reply(req); + return True; +} + +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; + uint8_t command; + + /* see if its a special NBT packet */ + if (CVAL(blob.data, 0) != 0) { + req = init_smb_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 = timeval_current(); + + reply_special(req); + return NT_STATUS_OK; + } + + if ((NBT_HDR_SIZE + MIN_SMB_SIZE) > blob.length) { + DEBUG(2,("Invalid SMB packet: length %d\n", 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 %d. Terminating connection\n", + blob.length)); + smbsrv_terminate_connection(smb_conn, "Non-SMB packet"); + return NT_STATUS_OK; + } + + req = init_smb_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 = timeval_current(); + 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", req->in.data_size)); + smbsrv_terminate_connection(req->smb_conn, "Invalid SMB packet"); + return NT_STATUS_OK; + } + + req->flags = CVAL(req->in.hdr, HDR_FLG); + req->flags2 = SVAL(req->in.hdr, HDR_FLG2); + req->smbpid = SVAL(req->in.hdr, HDR_PID); + + if (!req_signing_check_incoming(req)) { + req_reply_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 AS_USER (1<<0) +#define SIGNING_NO_REPLY (1<<1) + +/* + 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",reply_mkdir,AS_USER}, +/* 0x01 */ { "SMBrmdir",reply_rmdir,AS_USER}, +/* 0x02 */ { "SMBopen",reply_open,AS_USER}, +/* 0x03 */ { "SMBcreate",reply_mknew,AS_USER}, +/* 0x04 */ { "SMBclose",reply_close,AS_USER}, +/* 0x05 */ { "SMBflush",reply_flush,AS_USER}, +/* 0x06 */ { "SMBunlink",reply_unlink,AS_USER}, +/* 0x07 */ { "SMBmv",reply_mv,AS_USER}, +/* 0x08 */ { "SMBgetatr",reply_getatr,AS_USER}, +/* 0x09 */ { "SMBsetatr",reply_setatr,AS_USER}, +/* 0x0a */ { "SMBread",reply_read,AS_USER}, +/* 0x0b */ { "SMBwrite",reply_write,AS_USER}, +/* 0x0c */ { "SMBlock",reply_lock,AS_USER}, +/* 0x0d */ { "SMBunlock",reply_unlock,AS_USER}, +/* 0x0e */ { "SMBctemp",reply_ctemp,AS_USER }, +/* 0x0f */ { "SMBmknew",reply_mknew,AS_USER}, +/* 0x10 */ { "SMBchkpth",reply_chkpth,AS_USER}, +/* 0x11 */ { "SMBexit",reply_exit,0}, +/* 0x12 */ { "SMBlseek",reply_lseek,AS_USER}, +/* 0x13 */ { "SMBlockread",reply_lockread,AS_USER}, +/* 0x14 */ { "SMBwriteunlock",reply_writeunlock,AS_USER}, +/* 0x15 */ { NULL, NULL, 0 }, +/* 0x16 */ { NULL, NULL, 0 }, +/* 0x17 */ { NULL, NULL, 0 }, +/* 0x18 */ { NULL, NULL, 0 }, +/* 0x19 */ { NULL, NULL, 0 }, +/* 0x1a */ { "SMBreadbraw",reply_readbraw,AS_USER}, +/* 0x1b */ { "SMBreadBmpx",reply_readbmpx,AS_USER}, +/* 0x1c */ { "SMBreadBs",NULL,0 }, +/* 0x1d */ { "SMBwritebraw",reply_writebraw,AS_USER}, +/* 0x1e */ { "SMBwriteBmpx",reply_writebmpx,AS_USER}, +/* 0x1f */ { "SMBwriteBs",reply_writebs,AS_USER}, +/* 0x20 */ { "SMBwritec",NULL,0}, +/* 0x21 */ { NULL, NULL, 0 }, +/* 0x22 */ { "SMBsetattrE",reply_setattrE,AS_USER}, +/* 0x23 */ { "SMBgetattrE",reply_getattrE,AS_USER}, +/* 0x24 */ { "SMBlockingX",reply_lockingX,AS_USER}, +/* 0x25 */ { "SMBtrans",reply_trans,AS_USER}, +/* 0x26 */ { "SMBtranss",reply_transs,AS_USER}, +/* 0x27 */ { "SMBioctl",reply_ioctl,AS_USER}, +/* 0x28 */ { "SMBioctls",NULL,AS_USER}, +/* 0x29 */ { "SMBcopy",reply_copy,AS_USER}, +/* 0x2a */ { "SMBmove",NULL,AS_USER}, +/* 0x2b */ { "SMBecho",reply_echo,0}, +/* 0x2c */ { "SMBwriteclose",reply_writeclose,AS_USER}, +/* 0x2d */ { "SMBopenX",reply_open_and_X,AS_USER}, +/* 0x2e */ { "SMBreadX",reply_read_and_X,AS_USER}, +/* 0x2f */ { "SMBwriteX",reply_write_and_X,AS_USER}, +/* 0x30 */ { NULL, NULL, 0 }, +/* 0x31 */ { NULL, NULL, 0 }, +/* 0x32 */ { "SMBtrans2", reply_trans2, AS_USER}, +/* 0x33 */ { "SMBtranss2", reply_transs2, AS_USER}, +/* 0x34 */ { "SMBfindclose", reply_findclose,AS_USER}, +/* 0x35 */ { "SMBfindnclose", reply_findnclose, AS_USER}, +/* 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",reply_tcon,0}, +/* 0x71 */ { "SMBtdis",reply_tdis,0}, +/* 0x72 */ { "SMBnegprot",reply_negprot,0}, +/* 0x73 */ { "SMBsesssetupX",reply_sesssetup,0}, +/* 0x74 */ { "SMBulogoffX", reply_ulogoffX, 0}, /* ulogoff doesn't give a valid TID */ +/* 0x75 */ { "SMBtconX",reply_tcon_and_X,0}, +/* 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",reply_dskattr,AS_USER}, +/* 0x81 */ { "SMBsearch",reply_search,AS_USER}, +/* 0x82 */ { "SMBffirst",reply_search,AS_USER}, +/* 0x83 */ { "SMBfunique",reply_search,AS_USER}, +/* 0x84 */ { "SMBfclose",reply_fclose,AS_USER}, +/* 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", reply_nttrans, AS_USER}, +/* 0xa1 */ { "SMBnttranss", reply_nttranss, AS_USER}, +/* 0xa2 */ { "SMBntcreateX", reply_ntcreate_and_X, AS_USER}, +/* 0xa3 */ { NULL, NULL, 0 }, +/* 0xa4 */ { "SMBntcancel", reply_ntcancel, AS_USER|SIGNING_NO_REPLY}, +/* 0xa5 */ { "SMBntrename", reply_ntrename, AS_USER}, +/* 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",reply_printopen,AS_USER }, +/* 0xc1 */ { "SMBsplwr",reply_printwrite,AS_USER}, +/* 0xc2 */ { "SMBsplclose",reply_printclose,AS_USER}, +/* 0xc3 */ { "SMBsplretq",reply_printqueue,AS_USER}, +/* 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",reply_sends,0}, +/* 0xd1 */ { "SMBsendb",NULL,0}, +/* 0xd2 */ { "SMBfwdname",NULL,0}, +/* 0xd3 */ { "SMBcancelf",NULL,0}, +/* 0xd4 */ { "SMBgetmac",NULL,0}, +/* 0xd5 */ { "SMBsendstrt",reply_sendstrt,0}, +/* 0xd6 */ { "SMBsendend",reply_sendend,0}, +/* 0xd7 */ { "SMBsendtxt",reply_sendtxt,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)); + reply_unknown(req); + return; + } + + flags = smb_messages[type].flags; + + req->tcon = smbsrv_tcon_find(smb_conn, SVAL(req->in.hdr,HDR_TID)); + + 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)); + } + } + + DEBUG(3,("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) { + req_signing_no_reply(req); + } + + /* see if the vuid is valid */ + if ((flags & AS_USER) && !req->session) { + /* amazingly, the error code depends on the command */ + switch (type) { + case SMBntcreateX: + case SMBntcancel: + status = NT_STATUS_DOS(ERRSRV, ERRbaduid); + break; + default: + 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))); + req_destroy(req); + return; + } + req_reply_error(req, status); + return; + } + + /* does this protocol need a valid tree connection? */ + if ((flags & AS_USER) && !req->tcon) { + /* amazingly, the error code depends on the command */ + switch (type) { + case SMBntcreateX: + case SMBntcancel: + status = NT_STATUS_DOS(ERRSRV, ERRinvnid); + break; + default: + 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))); + req_destroy(req); + return; + } + req_reply_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 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) { + req_reply_dos_error(req, 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); + req_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); + + /* the current request in the chain might have used an async reply, + but that doesn't mean the next element needs to */ + ZERO_STRUCTP(req->async_states); + + switch_message(chain_cmd, req); + return; + +error: + SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE); + SSVAL(req->out.vwv, VWV(1), 0); + req_reply_dos_error(req, 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_init_tcons(smb_conn, UINT16_MAX); + NT_STATUS_NOT_OK_RETURN(status); + + srv_init_signing(smb_conn); + + return NT_STATUS_OK; +} diff --git a/source4/smb_server/smb/reply.c b/source4/smb_server/smb/reply.c new file mode 100644 index 0000000000..eb7b5a1082 --- /dev/null +++ b/source4/smb_server/smb/reply.c @@ -0,0 +1,2461 @@ +/* + Unix SMB/CIFS implementation. + Main SMB reply routines + Copyright (C) Andrew Tridgell 1992-2003 + Copyright (C) James J Myers 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 handles most of the reply_ calls that the server + makes to handle specific protocols +*/ + +#include "includes.h" +#include "smb_server/smb_server.h" +#include "libcli/nbt/libnbt.h" + + +/* useful way of catching wct errors with file and line number */ +#define REQ_CHECK_WCT(req, wcount) do { \ + if ((req)->in.wct != (wcount)) { \ + DEBUG(1,("Unexpected WCT %d at %s(%d) - expected %d\n", \ + (req)->in.wct, __FILE__, __LINE__, wcount)); \ + req_reply_dos_error(req, ERRSRV, ERRerror); \ + return; \ + }} while (0) + +/* check req->async_states->status and if not OK then send an error reply */ +#define CHECK_ASYNC_STATUS do { \ + if (!NT_STATUS_IS_OK(req->async_states->status)) { \ + req_reply_error(req, req->async_states->status); \ + return; \ + }} while (0) + +/* useful wrapper for talloc with NO_MEMORY reply */ +#define REQ_TALLOC(ptr, size) do { \ + ptr = talloc_size(req, size); \ + if (!ptr) { \ + req_reply_error(req, NT_STATUS_NO_MEMORY); \ + return; \ + }} while (0) + +/* + check if the backend wants to handle the request asynchronously. + if it wants it handled synchronously then call the send function + immediately +*/ +#define REQ_ASYNC_TAIL do { \ + if (!(req->async_states->state & NTVFS_ASYNC_STATE_ASYNC)) { \ + req->async_states->send_fn(req); \ + }} while (0) + +/* zero out some reserved fields in a reply */ +#define REQ_VWV_RESERVED(start, count) memset(req->out.vwv + VWV(start), 0, (count)*2) + +/**************************************************************************** + Reply to a simple request (async send) +****************************************************************************/ +static void reply_simple_send(struct smbsrv_request *req) +{ + CHECK_ASYNC_STATUS; + + req_setup_reply(req, 0, 0); + req_send_reply(req); +} + + +/**************************************************************************** + Reply to a tcon. +****************************************************************************/ +void reply_tcon(struct smbsrv_request *req) +{ + union smb_tcon con; + NTSTATUS status; + uint8_t *p; + + /* parse request */ + REQ_CHECK_WCT(req, 0); + + con.tcon.level = RAW_TCON_TCON; + + p = req->in.data; + p += req_pull_ascii4(req, &con.tcon.in.service, p, STR_TERMINATE); + p += req_pull_ascii4(req, &con.tcon.in.password, p, STR_TERMINATE); + p += req_pull_ascii4(req, &con.tcon.in.dev, p, STR_TERMINATE); + + if (!con.tcon.in.service || !con.tcon.in.password || !con.tcon.in.dev) { + req_reply_error(req, NT_STATUS_INVALID_PARAMETER); + return; + } + + /* call backend */ + status = tcon_backend(req, &con); + + if (!NT_STATUS_IS_OK(status)) { + req_reply_error(req, status); + return; + } + + /* construct reply */ + req_setup_reply(req, 2, 0); + + SSVAL(req->out.vwv, VWV(0), con.tcon.out.max_xmit); + SSVAL(req->out.vwv, VWV(1), con.tcon.out.tid); + SSVAL(req->out.hdr, HDR_TID, req->tcon->tid); + + req_send_reply(req); +} + + +/**************************************************************************** + Reply to a tcon and X. +****************************************************************************/ +void reply_tcon_and_X(struct smbsrv_request *req) +{ + NTSTATUS status; + union smb_tcon con; + uint8_t *p; + uint16_t passlen; + + con.tconx.level = RAW_TCON_TCONX; + + /* parse request */ + REQ_CHECK_WCT(req, 4); + + con.tconx.in.flags = SVAL(req->in.vwv, VWV(2)); + passlen = SVAL(req->in.vwv, VWV(3)); + + p = req->in.data; + + if (!req_pull_blob(req, p, passlen, &con.tconx.in.password)) { + req_reply_error(req, NT_STATUS_ILL_FORMED_PASSWORD); + return; + } + p += passlen; + + p += req_pull_string(req, &con.tconx.in.path, p, -1, STR_TERMINATE); + p += req_pull_string(req, &con.tconx.in.device, p, -1, STR_ASCII); + + if (!con.tconx.in.path || !con.tconx.in.device) { + req_reply_error(req, NT_STATUS_BAD_DEVICE_TYPE); + return; + } + + /* call backend */ + status = tcon_backend(req, &con); + + if (!NT_STATUS_IS_OK(status)) { + req_reply_error(req, status); + return; + } + + /* construct reply - two variants */ + if (req->smb_conn->negotiate.protocol < PROTOCOL_NT1) { + req_setup_reply(req, 2, 0); + + SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE); + SSVAL(req->out.vwv, VWV(1), 0); + + req_push_str(req, NULL, con.tconx.out.dev_type, -1, STR_TERMINATE|STR_ASCII); + } else { + req_setup_reply(req, 3, 0); + + SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE); + SSVAL(req->out.vwv, VWV(1), 0); + SSVAL(req->out.vwv, VWV(2), con.tconx.out.options); + + req_push_str(req, NULL, con.tconx.out.dev_type, -1, STR_TERMINATE|STR_ASCII); + req_push_str(req, NULL, con.tconx.out.fs_type, -1, STR_TERMINATE); + } + + /* set the incoming and outgoing tid to the just created one */ + SSVAL(req->in.hdr, HDR_TID, con.tconx.out.tid); + SSVAL(req->out.hdr,HDR_TID, con.tconx.out.tid); + + chain_reply(req); +} + + +/**************************************************************************** + Reply to an unknown request +****************************************************************************/ +void reply_unknown(struct smbsrv_request *req) +{ + int type; + + type = CVAL(req->in.hdr, HDR_COM); + + DEBUG(0,("unknown command type %d (0x%X)\n", type, type)); + + req_reply_dos_error(req, ERRSRV, ERRunknownsmb); +} + + +/**************************************************************************** + Reply to an ioctl (async reply) +****************************************************************************/ +static void reply_ioctl_send(struct smbsrv_request *req) +{ + union smb_ioctl *io = req->async_states->private_data; + + CHECK_ASYNC_STATUS; + + /* the +1 is for nicer alignment */ + req_setup_reply(req, 8, io->ioctl.out.blob.length+1); + SSVAL(req->out.vwv, VWV(1), io->ioctl.out.blob.length); + SSVAL(req->out.vwv, VWV(5), io->ioctl.out.blob.length); + SSVAL(req->out.vwv, VWV(6), PTR_DIFF(req->out.data, req->out.hdr) + 1); + + memcpy(req->out.data+1, io->ioctl.out.blob.data, io->ioctl.out.blob.length); + + req_send_reply(req); +} + +/**************************************************************************** + Reply to an ioctl. +****************************************************************************/ +void reply_ioctl(struct smbsrv_request *req) +{ + union smb_ioctl *io; + + /* parse request */ + REQ_CHECK_WCT(req, 3); + REQ_TALLOC(io, sizeof(*io)); + + io->ioctl.level = RAW_IOCTL_IOCTL; + io->ioctl.in.fnum = req_fnum(req, req->in.vwv, VWV(0)); + io->ioctl.in.request = IVAL(req->in.vwv, VWV(1)); + + req->async_states->state |= NTVFS_ASYNC_STATE_MAY_ASYNC; + req->async_states->send_fn = reply_ioctl_send; + req->async_states->private_data = io; + + /* call backend */ + req->async_states->status = ntvfs_ioctl(req, io); + + REQ_ASYNC_TAIL; +} + + +/**************************************************************************** + Reply to a chkpth. +****************************************************************************/ +void reply_chkpth(struct smbsrv_request *req) +{ + struct smb_chkpath *io; + + REQ_TALLOC(io, sizeof(*io)); + + req_pull_ascii4(req, &io->in.path, req->in.data, STR_TERMINATE); + + req->async_states->state |= NTVFS_ASYNC_STATE_MAY_ASYNC; + req->async_states->send_fn = reply_simple_send; + + req->async_states->status = ntvfs_chkpath(req, io); + + REQ_ASYNC_TAIL; +} + +/**************************************************************************** + Reply to a getatr (async reply) +****************************************************************************/ +static void reply_getatr_send(struct smbsrv_request *req) +{ + union smb_fileinfo *st = req->async_states->private_data; + + CHECK_ASYNC_STATUS; + + /* construct reply */ + req_setup_reply(req, 10, 0); + + SSVAL(req->out.vwv, VWV(0), st->getattr.out.attrib); + srv_push_dos_date3(req->smb_conn, req->out.vwv, VWV(1), st->getattr.out.write_time); + SIVAL(req->out.vwv, VWV(3), st->getattr.out.size); + + REQ_VWV_RESERVED(5, 5); + + req_send_reply(req); +} + + +/**************************************************************************** + Reply to a getatr. +****************************************************************************/ +void reply_getatr(struct smbsrv_request *req) +{ + union smb_fileinfo *st; + + REQ_TALLOC(st, sizeof(*st)); + + st->getattr.level = RAW_FILEINFO_GETATTR; + + /* parse request */ + req_pull_ascii4(req, &st->getattr.in.fname, req->in.data, STR_TERMINATE); + if (!st->getattr.in.fname) { + req_reply_error(req, NT_STATUS_OBJECT_NAME_NOT_FOUND); + return; + } + + req->async_states->state |= NTVFS_ASYNC_STATE_MAY_ASYNC; + req->async_states->send_fn = reply_getatr_send; + req->async_states->private_data = st; + + /* call backend */ + req->async_states->status = ntvfs_qpathinfo(req, st); + + REQ_ASYNC_TAIL; +} + + +/**************************************************************************** + Reply to a setatr. +****************************************************************************/ +void reply_setatr(struct smbsrv_request *req) +{ + union smb_setfileinfo *st; + + /* parse request */ + REQ_CHECK_WCT(req, 8); + REQ_TALLOC(st, sizeof(*st)); + + st->setattr.level = RAW_SFILEINFO_SETATTR; + st->setattr.in.attrib = SVAL(req->in.vwv, VWV(0)); + st->setattr.in.write_time = srv_pull_dos_date3(req->smb_conn, req->in.vwv + VWV(1)); + + req_pull_ascii4(req, &st->setattr.file.fname, req->in.data, STR_TERMINATE); + + if (!st->setattr.file.fname) { + req_reply_error(req, NT_STATUS_OBJECT_NAME_NOT_FOUND); + return; + } + + req->async_states->state |= NTVFS_ASYNC_STATE_MAY_ASYNC; + req->async_states->send_fn = reply_simple_send; + + /* call backend */ + req->async_states->status = ntvfs_setpathinfo(req, st); + + REQ_ASYNC_TAIL; +} + + +/**************************************************************************** + Reply to a dskattr (async reply) +****************************************************************************/ +static void reply_dskattr_send(struct smbsrv_request *req) +{ + union smb_fsinfo *fs = req->async_states->private_data; + + CHECK_ASYNC_STATUS; + + /* construct reply */ + req_setup_reply(req, 5, 0); + + SSVAL(req->out.vwv, VWV(0), fs->dskattr.out.units_total); + SSVAL(req->out.vwv, VWV(1), fs->dskattr.out.blocks_per_unit); + SSVAL(req->out.vwv, VWV(2), fs->dskattr.out.block_size); + SSVAL(req->out.vwv, VWV(3), fs->dskattr.out.units_free); + + REQ_VWV_RESERVED(4, 1); + + req_send_reply(req); +} + + +/**************************************************************************** + Reply to a dskattr. +****************************************************************************/ +void reply_dskattr(struct smbsrv_request *req) +{ + union smb_fsinfo *fs; + + REQ_TALLOC(fs, sizeof(*fs)); + + fs->dskattr.level = RAW_QFS_DSKATTR; + + req->async_states->state |= NTVFS_ASYNC_STATE_MAY_ASYNC; + req->async_states->send_fn = reply_dskattr_send; + req->async_states->private_data = fs; + + /* call backend */ + req->async_states->status = ntvfs_fsinfo(req, fs); + + REQ_ASYNC_TAIL; +} + + + +/**************************************************************************** + Reply to an open (async reply) +****************************************************************************/ +static void reply_open_send(struct smbsrv_request *req) +{ + union smb_open *oi = req->async_states->private_data; + + CHECK_ASYNC_STATUS; + + /* construct reply */ + req_setup_reply(req, 7, 0); + + SSVAL(req->out.vwv, VWV(0), oi->openold.out.fnum); + SSVAL(req->out.vwv, VWV(1), oi->openold.out.attrib); + srv_push_dos_date3(req->smb_conn, req->out.vwv, VWV(2), oi->openold.out.write_time); + SIVAL(req->out.vwv, VWV(4), oi->openold.out.size); + SSVAL(req->out.vwv, VWV(6), oi->openold.out.rmode); + + req_send_reply(req); +} + +/**************************************************************************** + Reply to an open. +****************************************************************************/ +void reply_open(struct smbsrv_request *req) +{ + union smb_open *oi; + + /* parse request */ + REQ_CHECK_WCT(req, 2); + REQ_TALLOC(oi, sizeof(*oi)); + + oi->openold.level = RAW_OPEN_OPEN; + oi->openold.in.open_mode = SVAL(req->in.vwv, VWV(0)); + oi->openold.in.search_attrs = SVAL(req->in.vwv, VWV(1)); + + req_pull_ascii4(req, &oi->openold.in.fname, req->in.data, STR_TERMINATE); + + if (!oi->openold.in.fname) { + req_reply_error(req, NT_STATUS_OBJECT_NAME_NOT_FOUND); + return; + } + + req->async_states->state |= NTVFS_ASYNC_STATE_MAY_ASYNC; + req->async_states->send_fn = reply_open_send; + req->async_states->private_data = oi; + + /* call backend */ + req->async_states->status = ntvfs_openfile(req, oi); + + REQ_ASYNC_TAIL; +} + + +/**************************************************************************** + Reply to an open and X (async reply) +****************************************************************************/ +static void reply_open_and_X_send(struct smbsrv_request *req) +{ + union smb_open *oi = req->async_states->private_data; + + CHECK_ASYNC_STATUS; + + /* build the reply */ + if (oi->openx.in.flags & OPENX_FLAGS_EXTENDED_RETURN) { + req_setup_reply(req, 19, 0); + } else { + req_setup_reply(req, 15, 0); + } + + SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE); + SSVAL(req->out.vwv, VWV(1), 0); + SSVAL(req->out.vwv, VWV(2), oi->openx.out.fnum); + SSVAL(req->out.vwv, VWV(3), oi->openx.out.attrib); + srv_push_dos_date3(req->smb_conn, req->out.vwv, VWV(4), oi->openx.out.write_time); + SIVAL(req->out.vwv, VWV(6), oi->openx.out.size); + SSVAL(req->out.vwv, VWV(8), oi->openx.out.access); + SSVAL(req->out.vwv, VWV(9), oi->openx.out.ftype); + SSVAL(req->out.vwv, VWV(10),oi->openx.out.devstate); + SSVAL(req->out.vwv, VWV(11),oi->openx.out.action); + SIVAL(req->out.vwv, VWV(12),oi->openx.out.unique_fid); + SSVAL(req->out.vwv, VWV(14),0); /* reserved */ + if (oi->openx.in.flags & OPENX_FLAGS_EXTENDED_RETURN) { + SIVAL(req->out.vwv, VWV(15),oi->openx.out.access_mask); + REQ_VWV_RESERVED(17, 2); + } + + req->chained_fnum = oi->openx.out.fnum; + + chain_reply(req); +} + + +/**************************************************************************** + Reply to an open and X. +****************************************************************************/ +void reply_open_and_X(struct smbsrv_request *req) +{ + union smb_open *oi; + + /* parse the request */ + REQ_CHECK_WCT(req, 15); + REQ_TALLOC(oi, sizeof(*oi)); + + oi->openx.level = RAW_OPEN_OPENX; + oi->openx.in.flags = SVAL(req->in.vwv, VWV(2)); + oi->openx.in.open_mode = SVAL(req->in.vwv, VWV(3)); + oi->openx.in.search_attrs = SVAL(req->in.vwv, VWV(4)); + oi->openx.in.file_attrs = SVAL(req->in.vwv, VWV(5)); + oi->openx.in.write_time = srv_pull_dos_date3(req->smb_conn, req->in.vwv + VWV(6)); + oi->openx.in.open_func = SVAL(req->in.vwv, VWV(8)); + oi->openx.in.size = IVAL(req->in.vwv, VWV(9)); + oi->openx.in.timeout = IVAL(req->in.vwv, VWV(11)); + + req_pull_ascii4(req, &oi->openx.in.fname, req->in.data, STR_TERMINATE); + + if (!oi->openx.in.fname) { + req_reply_error(req, NT_STATUS_OBJECT_NAME_NOT_FOUND); + return; + } + + req->async_states->state |= NTVFS_ASYNC_STATE_MAY_ASYNC; + req->async_states->send_fn = reply_open_and_X_send; + req->async_states->private_data = oi; + + /* call the backend */ + req->async_states->status = ntvfs_openfile(req, oi); + + REQ_ASYNC_TAIL; +} + + +/**************************************************************************** + Reply to a mknew or a create. +****************************************************************************/ +static void reply_mknew_send(struct smbsrv_request *req) +{ + union smb_open *oi = req->async_states->private_data; + + CHECK_ASYNC_STATUS; + + /* build the reply */ + req_setup_reply(req, 1, 0); + + SSVAL(req->out.vwv, VWV(0), oi->mknew.out.fnum); + + req_send_reply(req); +} + + +/**************************************************************************** + Reply to a mknew or a create. +****************************************************************************/ +void reply_mknew(struct smbsrv_request *req) +{ + union smb_open *oi; + + /* parse the request */ + REQ_CHECK_WCT(req, 3); + REQ_TALLOC(oi, sizeof(*oi)); + + if (CVAL(req->in.hdr, HDR_COM) == SMBmknew) { + oi->mknew.level = RAW_OPEN_MKNEW; + } else { + oi->mknew.level = RAW_OPEN_CREATE; + } + oi->mknew.in.attrib = SVAL(req->in.vwv, VWV(0)); + oi->mknew.in.write_time = srv_pull_dos_date3(req->smb_conn, req->in.vwv + VWV(1)); + + req_pull_ascii4(req, &oi->mknew.in.fname, req->in.data, STR_TERMINATE); + + if (!oi->mknew.in.fname) { + req_reply_error(req, NT_STATUS_OBJECT_NAME_NOT_FOUND); + return; + } + + req->async_states->state |= NTVFS_ASYNC_STATE_MAY_ASYNC; + req->async_states->send_fn = reply_mknew_send; + req->async_states->private_data = oi; + + /* call the backend */ + req->async_states->status = ntvfs_openfile(req, oi); + + REQ_ASYNC_TAIL; +} + +/**************************************************************************** + Reply to a create temporary file (async reply) +****************************************************************************/ +static void reply_ctemp_send(struct smbsrv_request *req) +{ + union smb_open *oi = req->async_states->private_data; + + CHECK_ASYNC_STATUS; + + /* build the reply */ + req_setup_reply(req, 1, 0); + + SSVAL(req->out.vwv, VWV(0), oi->ctemp.out.fnum); + + /* the returned filename is relative to the directory */ + req_push_str(req, NULL, oi->ctemp.out.name, -1, STR_TERMINATE | STR_ASCII); + + req_send_reply(req); +} + +/**************************************************************************** + Reply to a create temporary file. +****************************************************************************/ +void reply_ctemp(struct smbsrv_request *req) +{ + union smb_open *oi; + + /* parse the request */ + REQ_CHECK_WCT(req, 3); + REQ_TALLOC(oi, sizeof(*oi)); + + oi->ctemp.level = RAW_OPEN_CTEMP; + oi->ctemp.in.attrib = SVAL(req->in.vwv, VWV(0)); + oi->ctemp.in.write_time = srv_pull_dos_date3(req->smb_conn, req->in.vwv + VWV(1)); + + /* the filename is actually a directory name, the server provides a filename + in that directory */ + req_pull_ascii4(req, &oi->ctemp.in.directory, req->in.data, STR_TERMINATE); + + if (!oi->ctemp.in.directory) { + req_reply_error(req, NT_STATUS_OBJECT_NAME_NOT_FOUND); + return; + } + + req->async_states->state |= NTVFS_ASYNC_STATE_MAY_ASYNC; + req->async_states->send_fn = reply_ctemp_send; + req->async_states->private_data = oi; + + /* call the backend */ + req->async_states->status = ntvfs_openfile(req, oi); + + REQ_ASYNC_TAIL; +} + + +/**************************************************************************** + Reply to a unlink +****************************************************************************/ +void reply_unlink(struct smbsrv_request *req) +{ + struct smb_unlink *unl; + + /* parse the request */ + REQ_CHECK_WCT(req, 1); + REQ_TALLOC(unl, sizeof(*unl)); + + unl->in.attrib = SVAL(req->in.vwv, VWV(0)); + + req_pull_ascii4(req, &unl->in.pattern, req->in.data, STR_TERMINATE); + + req->async_states->state |= NTVFS_ASYNC_STATE_MAY_ASYNC; + req->async_states->send_fn = reply_simple_send; + + /* call backend */ + req->async_states->status = ntvfs_unlink(req, unl); + + REQ_ASYNC_TAIL; +} + + +/**************************************************************************** + Reply to a readbraw (core+ protocol). + this is a strange packet because it doesn't use a standard SMB header in the reply, + only the 4 byte NBT header + This command must be replied to synchronously +****************************************************************************/ +void reply_readbraw(struct smbsrv_request *req) +{ + NTSTATUS status; + union smb_read io; + + io.readbraw.level = RAW_READ_READBRAW; + + /* there are two variants, one with 10 and one with 8 command words */ + if (req->in.wct < 8) { + goto failed; + } + + io.readbraw.in.fnum = req_fnum(req, req->in.vwv, VWV(0)); + io.readbraw.in.offset = IVAL(req->in.vwv, VWV(1)); + io.readbraw.in.maxcnt = SVAL(req->in.vwv, VWV(3)); + io.readbraw.in.mincnt = SVAL(req->in.vwv, VWV(4)); + io.readbraw.in.timeout = IVAL(req->in.vwv, VWV(5)); + + /* the 64 bit variant */ + if (req->in.wct == 10) { + uint32_t offset_high = IVAL(req->in.vwv, VWV(8)); + io.readbraw.in.offset |= (((off_t)offset_high) << 32); + } + + /* before calling the backend we setup the raw buffer. This + * saves a copy later */ + req->out.size = io.readbraw.in.maxcnt + NBT_HDR_SIZE; + req->out.buffer = talloc_size(req, req->out.size); + if (req->out.buffer == NULL) { + goto failed; + } + SIVAL(req->out.buffer, 0, 0); /* init NBT header */ + + /* tell the backend where to put the data */ + io.readbraw.out.data = req->out.buffer + NBT_HDR_SIZE; + + /* call the backend */ + status = ntvfs_read(req, &io); + + if (!NT_STATUS_IS_OK(status)) { + goto failed; + } + + req->out.size = io.readbraw.out.nread + NBT_HDR_SIZE; + + req_send_reply_nosign(req); + return; + +failed: + /* any failure in readbraw is equivalent to reading zero bytes */ + req->out.size = 4; + req->out.buffer = talloc_size(req, req->out.size); + SIVAL(req->out.buffer, 0, 0); /* init NBT header */ + + req_send_reply_nosign(req); +} + + +/**************************************************************************** + Reply to a lockread (async reply) +****************************************************************************/ +static void reply_lockread_send(struct smbsrv_request *req) +{ + union smb_read *io = req->async_states->private_data; + + CHECK_ASYNC_STATUS; + + /* trim packet */ + io->lockread.out.nread = MIN(io->lockread.out.nread, + req_max_data(req) - 3); + req_grow_data(req, 3 + io->lockread.out.nread); + + /* construct reply */ + SSVAL(req->out.vwv, VWV(0), io->lockread.out.nread); + REQ_VWV_RESERVED(1, 4); + + SCVAL(req->out.data, 0, SMB_DATA_BLOCK); + SSVAL(req->out.data, 1, io->lockread.out.nread); + + req_send_reply(req); +} + + +/**************************************************************************** + Reply to a lockread (core+ protocol). + note that the lock is a write lock, not a read lock! +****************************************************************************/ +void reply_lockread(struct smbsrv_request *req) +{ + union smb_read *io; + + /* parse request */ + REQ_CHECK_WCT(req, 5); + REQ_TALLOC(io, sizeof(*io)); + + io->lockread.level = RAW_READ_LOCKREAD; + io->lockread.in.fnum = req_fnum(req, req->in.vwv, VWV(0)); + io->lockread.in.count = SVAL(req->in.vwv, VWV(1)); + io->lockread.in.offset = IVAL(req->in.vwv, VWV(2)); + io->lockread.in.remaining = SVAL(req->in.vwv, VWV(4)); + + /* setup the reply packet assuming the maximum possible read */ + req_setup_reply(req, 5, 3 + io->lockread.in.count); + + /* tell the backend where to put the data */ + io->lockread.out.data = req->out.data + 3; + + req->async_states->state |= NTVFS_ASYNC_STATE_MAY_ASYNC; + req->async_states->send_fn = reply_lockread_send; + req->async_states->private_data = io; + + /* call backend */ + req->async_states->status = ntvfs_read(req, io); + + REQ_ASYNC_TAIL; +} + + + +/**************************************************************************** + Reply to a read (async reply) +****************************************************************************/ +static void reply_read_send(struct smbsrv_request *req) +{ + union smb_read *io = req->async_states->private_data; + + CHECK_ASYNC_STATUS; + + /* trim packet */ + io->read.out.nread = MIN(io->read.out.nread, + req_max_data(req) - 3); + req_grow_data(req, 3 + io->read.out.nread); + + /* construct reply */ + SSVAL(req->out.vwv, VWV(0), io->read.out.nread); + REQ_VWV_RESERVED(1, 4); + + SCVAL(req->out.data, 0, SMB_DATA_BLOCK); + SSVAL(req->out.data, 1, io->read.out.nread); + + req_send_reply(req); +} + +/**************************************************************************** + Reply to a read. +****************************************************************************/ +void reply_read(struct smbsrv_request *req) +{ + union smb_read *io; + + /* parse request */ + REQ_CHECK_WCT(req, 5); + REQ_TALLOC(io, sizeof(*io)); + + io->read.level = RAW_READ_READ; + io->read.in.fnum = req_fnum(req, req->in.vwv, VWV(0)); + io->read.in.count = SVAL(req->in.vwv, VWV(1)); + io->read.in.offset = IVAL(req->in.vwv, VWV(2)); + io->read.in.remaining = SVAL(req->in.vwv, VWV(4)); + + /* setup the reply packet assuming the maximum possible read */ + req_setup_reply(req, 5, 3 + io->read.in.count); + + /* tell the backend where to put the data */ + io->read.out.data = req->out.data + 3; + + req->async_states->state |= NTVFS_ASYNC_STATE_MAY_ASYNC; + req->async_states->send_fn = reply_read_send; + req->async_states->private_data = io; + + /* call backend */ + req->async_states->status = ntvfs_read(req, io); + + REQ_ASYNC_TAIL; +} + + + +/**************************************************************************** + Reply to a read and X (async reply) +****************************************************************************/ +static void reply_read_and_X_send(struct smbsrv_request *req) +{ + union smb_read *io = req->async_states->private_data; + + CHECK_ASYNC_STATUS; + + /* readx reply packets can be over-sized */ + req->control_flags |= REQ_CONTROL_LARGE; + if (io->readx.in.maxcnt != 0xFFFF && + io->readx.in.mincnt != 0xFFFF) { + req_grow_data(req, 1 + io->readx.out.nread); + SCVAL(req->out.data, 0, 0); /* padding */ + } else { + req_grow_data(req, io->readx.out.nread); + } + + /* construct reply */ + SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE); + SSVAL(req->out.vwv, VWV(1), 0); + SSVAL(req->out.vwv, VWV(2), io->readx.out.remaining); + SSVAL(req->out.vwv, VWV(3), io->readx.out.compaction_mode); + REQ_VWV_RESERVED(4, 1); + SSVAL(req->out.vwv, VWV(5), io->readx.out.nread); + SSVAL(req->out.vwv, VWV(6), PTR_DIFF(io->readx.out.data, req->out.hdr)); + REQ_VWV_RESERVED(7, 5); + + chain_reply(req); +} + +/**************************************************************************** + Reply to a read and X. +****************************************************************************/ +void reply_read_and_X(struct smbsrv_request *req) +{ + union smb_read *io; + + /* parse request */ + if (req->in.wct != 12) { + REQ_CHECK_WCT(req, 10); + } + + REQ_TALLOC(io, sizeof(*io)); + + io->readx.level = RAW_READ_READX; + io->readx.in.fnum = req_fnum(req, req->in.vwv, VWV(2)); + io->readx.in.offset = IVAL(req->in.vwv, VWV(3)); + io->readx.in.maxcnt = SVAL(req->in.vwv, VWV(5)); + io->readx.in.mincnt = SVAL(req->in.vwv, VWV(6)); + io->readx.in.remaining = SVAL(req->in.vwv, VWV(9)); + + if (req->smb_conn->negotiate.client_caps & CAP_LARGE_READX) { + uint32_t high_part = IVAL(req->in.vwv, VWV(7)); + if (high_part == 1) { + io->readx.in.maxcnt |= high_part << 16; + } + } + + /* the 64 bit variant */ + if (req->in.wct == 12) { + uint32_t offset_high = IVAL(req->in.vwv, VWV(10)); + io->readx.in.offset |= (((uint64_t)offset_high) << 32); + } + + /* setup the reply packet assuming the maximum possible read */ + req_setup_reply(req, 12, 1 + io->readx.in.maxcnt); + + /* tell the backend where to put the data. Notice the pad byte. */ + if (io->readx.in.maxcnt != 0xFFFF && + io->readx.in.mincnt != 0xFFFF) { + io->readx.out.data = req->out.data + 1; + } else { + io->readx.out.data = req->out.data; + } + + req->async_states->state |= NTVFS_ASYNC_STATE_MAY_ASYNC; + req->async_states->send_fn = reply_read_and_X_send; + req->async_states->private_data = io; + + /* call backend */ + req->async_states->status = ntvfs_read(req, io); + + REQ_ASYNC_TAIL; +} + + +/**************************************************************************** + Reply to a writebraw (core+ or LANMAN1.0 protocol). +****************************************************************************/ +void reply_writebraw(struct smbsrv_request *req) +{ + req_reply_dos_error(req, ERRSRV, ERRuseSTD); +} + + +/**************************************************************************** + Reply to a writeunlock (async reply) +****************************************************************************/ +static void reply_writeunlock_send(struct smbsrv_request *req) +{ + union smb_write *io = req->async_states->private_data; + + CHECK_ASYNC_STATUS; + + /* construct reply */ + req_setup_reply(req, 1, 0); + + SSVAL(req->out.vwv, VWV(0), io->writeunlock.out.nwritten); + + req_send_reply(req); +} + +/**************************************************************************** + Reply to a writeunlock (core+). +****************************************************************************/ +void reply_writeunlock(struct smbsrv_request *req) +{ + union smb_write *io; + + REQ_CHECK_WCT(req, 5); + REQ_TALLOC(io, sizeof(*io)); + + io->writeunlock.level = RAW_WRITE_WRITEUNLOCK; + io->writeunlock.in.fnum = req_fnum(req, req->in.vwv, VWV(0)); + io->writeunlock.in.count = SVAL(req->in.vwv, VWV(1)); + io->writeunlock.in.offset = IVAL(req->in.vwv, VWV(2)); + io->writeunlock.in.remaining = SVAL(req->in.vwv, VWV(4)); + io->writeunlock.in.data = req->in.data + 3; + + /* make sure they gave us the data they promised */ + if (io->writeunlock.in.count+3 > req->in.data_size) { + req_reply_error(req, NT_STATUS_FOOBAR); + return; + } + + /* make sure the data block is big enough */ + if (SVAL(req->in.data, 1) < io->writeunlock.in.count) { + req_reply_error(req, NT_STATUS_FOOBAR); + return; + } + + req->async_states->state |= NTVFS_ASYNC_STATE_MAY_ASYNC; + req->async_states->send_fn = reply_writeunlock_send; + req->async_states->private_data = io; + + /* call backend */ + req->async_states->status = ntvfs_write(req, io); + + REQ_ASYNC_TAIL; +} + + + +/**************************************************************************** + Reply to a write (async reply) +****************************************************************************/ +static void reply_write_send(struct smbsrv_request *req) +{ + union smb_write *io = req->async_states->private_data; + + CHECK_ASYNC_STATUS; + + /* construct reply */ + req_setup_reply(req, 1, 0); + + SSVAL(req->out.vwv, VWV(0), io->write.out.nwritten); + + req_send_reply(req); +} + +/**************************************************************************** + Reply to a write +****************************************************************************/ +void reply_write(struct smbsrv_request *req) +{ + union smb_write *io; + + REQ_CHECK_WCT(req, 5); + REQ_TALLOC(io, sizeof(*io)); + + io->write.level = RAW_WRITE_WRITE; + io->write.in.fnum = req_fnum(req, req->in.vwv, VWV(0)); + io->write.in.count = SVAL(req->in.vwv, VWV(1)); + io->write.in.offset = IVAL(req->in.vwv, VWV(2)); + io->write.in.remaining = SVAL(req->in.vwv, VWV(4)); + io->write.in.data = req->in.data + 3; + + /* make sure they gave us the data they promised */ + if (req_data_oob(req, io->write.in.data, io->write.in.count)) { + req_reply_error(req, NT_STATUS_FOOBAR); + return; + } + + /* make sure the data block is big enough */ + if (SVAL(req->in.data, 1) < io->write.in.count) { + req_reply_error(req, NT_STATUS_FOOBAR); + return; + } + + req->async_states->state |= NTVFS_ASYNC_STATE_MAY_ASYNC; + req->async_states->send_fn = reply_write_send; + req->async_states->private_data = io; + + /* call backend */ + req->async_states->status = ntvfs_write(req, io); + + REQ_ASYNC_TAIL; +} + + +/**************************************************************************** + Reply to a write and X (async reply) +****************************************************************************/ +static void reply_write_and_X_send(struct smbsrv_request *req) +{ + union smb_write *io = req->async_states->private_data; + + CHECK_ASYNC_STATUS; + + /* construct reply */ + req_setup_reply(req, 6, 0); + + SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE); + SSVAL(req->out.vwv, VWV(1), 0); + SSVAL(req->out.vwv, VWV(2), io->writex.out.nwritten & 0xFFFF); + SSVAL(req->out.vwv, VWV(3), io->writex.out.remaining); + SSVAL(req->out.vwv, VWV(4), io->writex.out.nwritten >> 16); + REQ_VWV_RESERVED(5, 1); + + chain_reply(req); +} + +/**************************************************************************** + Reply to a write and X. +****************************************************************************/ +void reply_write_and_X(struct smbsrv_request *req) +{ + union smb_write *io; + + if (req->in.wct != 14) { + REQ_CHECK_WCT(req, 12); + } + + REQ_TALLOC(io, sizeof(*io)); + + io->writex.level = RAW_WRITE_WRITEX; + io->writex.in.fnum = req_fnum(req, req->in.vwv, VWV(2)); + io->writex.in.offset = IVAL(req->in.vwv, VWV(3)); + io->writex.in.wmode = SVAL(req->in.vwv, VWV(7)); + io->writex.in.remaining = SVAL(req->in.vwv, VWV(8)); + io->writex.in.count = SVAL(req->in.vwv, VWV(10)); + io->writex.in.data = req->in.hdr + SVAL(req->in.vwv, VWV(11)); + + if (req->in.wct == 14) { + uint32_t offset_high = IVAL(req->in.vwv, VWV(12)); + uint16_t count_high = SVAL(req->in.vwv, VWV(9)); + io->writex.in.offset |= (((uint64_t)offset_high) << 32); + io->writex.in.count |= ((uint32_t)count_high) << 16; + } + + /* make sure the data is in bounds */ + if (req_data_oob(req, io->writex.in.data, io->writex.in.count)) { + req_reply_error(req, NT_STATUS_FOOBAR); + return; + } + + req->async_states->state |= NTVFS_ASYNC_STATE_MAY_ASYNC; + req->async_states->send_fn = reply_write_and_X_send; + req->async_states->private_data = io; + + /* call backend */ + req->async_states->status = ntvfs_write(req, io); + + REQ_ASYNC_TAIL; +} + + +/**************************************************************************** + Reply to a lseek (async reply) +****************************************************************************/ +static void reply_lseek_send(struct smbsrv_request *req) +{ + struct smb_seek *io = req->async_states->private_data; + + CHECK_ASYNC_STATUS; + + /* construct reply */ + req_setup_reply(req, 2, 0); + + SIVALS(req->out.vwv, VWV(0), io->out.offset); + + req_send_reply(req); +} + +/**************************************************************************** + Reply to a lseek. +****************************************************************************/ +void reply_lseek(struct smbsrv_request *req) +{ + struct smb_seek *io; + + REQ_CHECK_WCT(req, 4); + REQ_TALLOC(io, sizeof(*io)); + + io->in.fnum = req_fnum(req, req->in.vwv, VWV(0)); + io->in.mode = SVAL(req->in.vwv, VWV(1)); + io->in.offset = IVALS(req->in.vwv, VWV(2)); + + req->async_states->state |= NTVFS_ASYNC_STATE_MAY_ASYNC; + req->async_states->send_fn = reply_lseek_send; + req->async_states->private_data = io; + + /* call backend */ + req->async_states->status = ntvfs_seek(req, io); + + REQ_ASYNC_TAIL; +} + +/**************************************************************************** + Reply to a flush. +****************************************************************************/ +void reply_flush(struct smbsrv_request *req) +{ + struct smb_flush *io; + + /* parse request */ + REQ_CHECK_WCT(req, 1); + REQ_TALLOC(io, sizeof(*io)); + + io->in.fnum = req_fnum(req, req->in.vwv, VWV(0)); + + req->async_states->state |= NTVFS_ASYNC_STATE_MAY_ASYNC; + req->async_states->send_fn = reply_simple_send; + + /* call backend */ + req->async_states->status = ntvfs_flush(req, io); + + REQ_ASYNC_TAIL; +} + + +/**************************************************************************** + Reply to a exit. This closes all files open by a smbpid +****************************************************************************/ +void reply_exit(struct smbsrv_request *req) +{ + NTSTATUS status; + struct smbsrv_tcon *tcon; + REQ_CHECK_WCT(req, 0); + + for (tcon=req->smb_conn->tcons.list;tcon;tcon=tcon->next) { + req->tcon = tcon; + status = ntvfs_exit(req); + req->tcon = NULL; + if (!NT_STATUS_IS_OK(status)) { + req_reply_error(req, status); + return; + } + } + + req_setup_reply(req, 0, 0); + req_send_reply(req); +} + + +/**************************************************************************** + Reply to a close + + Note that this has to deal with closing a directory opened by NT SMB's. +****************************************************************************/ +void reply_close(struct smbsrv_request *req) +{ + union smb_close *io; + + /* parse request */ + REQ_CHECK_WCT(req, 3); + REQ_TALLOC(io, sizeof(*io)); + + io->close.level = RAW_CLOSE_CLOSE; + io->close.in.fnum = req_fnum(req, req->in.vwv, VWV(0)); + io->close.in.write_time = srv_pull_dos_date3(req->smb_conn, req->in.vwv + VWV(1)); + + req->async_states->state |= NTVFS_ASYNC_STATE_MAY_ASYNC; + req->async_states->send_fn = reply_simple_send; + + /* call backend */ + req->async_states->status = ntvfs_close(req, io); + + REQ_ASYNC_TAIL; +} + + + +/**************************************************************************** + Reply to a writeclose (async reply) +****************************************************************************/ +static void reply_writeclose_send(struct smbsrv_request *req) +{ + union smb_write *io = req->async_states->private_data; + + CHECK_ASYNC_STATUS; + + /* construct reply */ + req_setup_reply(req, 1, 0); + + SSVAL(req->out.vwv, VWV(0), io->write.out.nwritten); + + req_send_reply(req); +} + +/**************************************************************************** + Reply to a writeclose (Core+ protocol). +****************************************************************************/ +void reply_writeclose(struct smbsrv_request *req) +{ + union smb_write *io; + + /* this one is pretty weird - the wct can be 6 or 12 */ + if (req->in.wct != 12) { + REQ_CHECK_WCT(req, 6); + } + + REQ_TALLOC(io, sizeof(*io)); + + io->writeclose.level = RAW_WRITE_WRITECLOSE; + io->writeclose.in.fnum = req_fnum(req, req->in.vwv, VWV(0)); + io->writeclose.in.count = SVAL(req->in.vwv, VWV(1)); + io->writeclose.in.offset = IVAL(req->in.vwv, VWV(2)); + io->writeclose.in.mtime = srv_pull_dos_date3(req->smb_conn, req->in.vwv + VWV(4)); + io->writeclose.in.data = req->in.data + 1; + + /* make sure they gave us the data they promised */ + if (req_data_oob(req, io->writeclose.in.data, io->writeclose.in.count)) { + req_reply_error(req, NT_STATUS_FOOBAR); + return; + } + + req->async_states->state |= NTVFS_ASYNC_STATE_MAY_ASYNC; + req->async_states->send_fn = reply_writeclose_send; + req->async_states->private_data = io; + + /* call backend */ + req->async_states->status = ntvfs_write(req, io); + + REQ_ASYNC_TAIL; +} + +/**************************************************************************** + Reply to a lock. +****************************************************************************/ +void reply_lock(struct smbsrv_request *req) +{ + union smb_lock *lck; + + /* parse request */ + REQ_CHECK_WCT(req, 5); + REQ_TALLOC(lck, sizeof(*lck)); + + lck->lock.level = RAW_LOCK_LOCK; + lck->lock.in.fnum = req_fnum(req, req->in.vwv, VWV(0)); + lck->lock.in.count = IVAL(req->in.vwv, VWV(1)); + lck->lock.in.offset = IVAL(req->in.vwv, VWV(3)); + + req->async_states->state |= NTVFS_ASYNC_STATE_MAY_ASYNC; + req->async_states->send_fn = reply_simple_send; + + /* call backend */ + req->async_states->status = ntvfs_lock(req, lck); + + REQ_ASYNC_TAIL; +} + + +/**************************************************************************** + Reply to a unlock. +****************************************************************************/ +void reply_unlock(struct smbsrv_request *req) +{ + union smb_lock *lck; + + /* parse request */ + REQ_CHECK_WCT(req, 5); + REQ_TALLOC(lck, sizeof(*lck)); + + lck->unlock.level = RAW_LOCK_UNLOCK; + lck->unlock.in.fnum = req_fnum(req, req->in.vwv, VWV(0)); + lck->unlock.in.count = IVAL(req->in.vwv, VWV(1)); + lck->unlock.in.offset = IVAL(req->in.vwv, VWV(3)); + + req->async_states->state |= NTVFS_ASYNC_STATE_MAY_ASYNC; + req->async_states->send_fn = reply_simple_send; + + /* call backend */ + req->async_states->status = ntvfs_lock(req, lck); + + REQ_ASYNC_TAIL; +} + + +/**************************************************************************** + Reply to a tdis. +****************************************************************************/ +void reply_tdis(struct smbsrv_request *req) +{ + REQ_CHECK_WCT(req, 0); + + if (req->tcon == NULL) { + req_reply_error(req, NT_STATUS_INVALID_HANDLE); + return; + } + + talloc_free(req->tcon); + + /* construct reply */ + req_setup_reply(req, 0, 0); + + req_send_reply(req); +} + + +/**************************************************************************** + Reply to a echo. This is one of the few calls that is handled directly (the + backends don't see it at all) +****************************************************************************/ +void reply_echo(struct smbsrv_request *req) +{ + uint16_t count; + int i; + + REQ_CHECK_WCT(req, 0); + + count = SVAL(req->in.vwv, VWV(0)); + + req_setup_reply(req, 1, req->in.data_size); + + memcpy(req->out.data, req->in.data, req->in.data_size); + + for (i=1; i <= count;i++) { + struct smbsrv_request *this_req; + + if (i != count) { + this_req = req_setup_secondary(req); + } else { + this_req = req; + } + + SSVAL(this_req->out.vwv, VWV(0), i); + req_send_reply(this_req); + } +} + + + +/**************************************************************************** + Reply to a printopen (async reply) +****************************************************************************/ +static void reply_printopen_send(struct smbsrv_request *req) +{ + union smb_open *oi = req->async_states->private_data; + + CHECK_ASYNC_STATUS; + + /* construct reply */ + req_setup_reply(req, 1, 0); + + SSVAL(req->out.vwv, VWV(0), oi->openold.out.fnum); + + req_send_reply(req); +} + +/**************************************************************************** + Reply to a printopen. +****************************************************************************/ +void reply_printopen(struct smbsrv_request *req) +{ + union smb_open *oi; + + /* parse request */ + REQ_CHECK_WCT(req, 2); + REQ_TALLOC(oi, sizeof(*oi)); + + oi->splopen.level = RAW_OPEN_SPLOPEN; + oi->splopen.in.setup_length = SVAL(req->in.vwv, VWV(0)); + oi->splopen.in.mode = SVAL(req->in.vwv, VWV(1)); + + req_pull_ascii4(req, &oi->splopen.in.ident, req->in.data, STR_TERMINATE); + + req->async_states->state |= NTVFS_ASYNC_STATE_MAY_ASYNC; + req->async_states->send_fn = reply_printopen_send; + req->async_states->private_data = oi; + + /* call backend */ + req->async_states->status = ntvfs_openfile(req, oi); + + REQ_ASYNC_TAIL; +} + +/**************************************************************************** + Reply to a printclose. +****************************************************************************/ +void reply_printclose(struct smbsrv_request *req) +{ + union smb_close *io; + + /* parse request */ + REQ_CHECK_WCT(req, 3); + REQ_TALLOC(io, sizeof(*io)); + + io->splclose.level = RAW_CLOSE_SPLCLOSE; + io->splclose.in.fnum = req_fnum(req, req->in.vwv, VWV(0)); + + req->async_states->state |= NTVFS_ASYNC_STATE_MAY_ASYNC; + req->async_states->send_fn = reply_simple_send; + + /* call backend */ + req->async_states->status = ntvfs_close(req, io); + + REQ_ASYNC_TAIL; +} + +/**************************************************************************** + Reply to a printqueue. +****************************************************************************/ +void reply_printqueue_send(struct smbsrv_request *req) +{ + union smb_lpq *lpq = req->async_states->private_data; + int i, maxcount; + const uint_t el_size = 28; + + CHECK_ASYNC_STATUS; + + /* construct reply */ + req_setup_reply(req, 2, 0); + + /* truncate the returned list to fit in the negotiated buffer size */ + maxcount = (req_max_data(req) - 3) / el_size; + if (maxcount < lpq->retq.out.count) { + lpq->retq.out.count = maxcount; + } + + /* setup enough space in the reply */ + req_grow_data(req, 3 + el_size*lpq->retq.out.count); + + /* and fill it in */ + SSVAL(req->out.vwv, VWV(0), lpq->retq.out.count); + SSVAL(req->out.vwv, VWV(1), lpq->retq.out.restart_idx); + + SCVAL(req->out.data, 0, SMB_DATA_BLOCK); + SSVAL(req->out.data, 1, el_size*lpq->retq.out.count); + + req->out.ptr = req->out.data + 3; + + for (i=0;iretq.out.count;i++) { + srv_push_dos_date2(req->smb_conn, req->out.ptr, 0 , lpq->retq.out.queue[i].time); + SCVAL(req->out.ptr, 4, lpq->retq.out.queue[i].status); + SSVAL(req->out.ptr, 5, lpq->retq.out.queue[i].job); + SIVAL(req->out.ptr, 7, lpq->retq.out.queue[i].size); + SCVAL(req->out.ptr, 11, 0); /* reserved */ + req_push_str(req, req->out.ptr+12, lpq->retq.out.queue[i].user, 16, STR_ASCII); + req->out.ptr += el_size; + } + + req_send_reply(req); +} + +/**************************************************************************** + Reply to a printqueue. +****************************************************************************/ +void reply_printqueue(struct smbsrv_request *req) +{ + union smb_lpq *lpq; + + /* parse request */ + REQ_CHECK_WCT(req, 2); + REQ_TALLOC(lpq, sizeof(*lpq)); + + lpq->retq.level = RAW_LPQ_RETQ; + lpq->retq.in.maxcount = SVAL(req->in.vwv, VWV(0)); + lpq->retq.in.startidx = SVAL(req->in.vwv, VWV(1)); + + req->async_states->state |= NTVFS_ASYNC_STATE_MAY_ASYNC; + req->async_states->send_fn = reply_printqueue_send; + req->async_states->private_data = lpq; + + /* call backend */ + req->async_states->status = ntvfs_lpq(req, lpq); + + REQ_ASYNC_TAIL; +} + + +/**************************************************************************** + Reply to a printwrite. +****************************************************************************/ +void reply_printwrite(struct smbsrv_request *req) +{ + union smb_write *io; + + /* parse request */ + REQ_CHECK_WCT(req, 1); + REQ_TALLOC(io, sizeof(*io)); + + io->splwrite.level = RAW_WRITE_SPLWRITE; + + if (req->in.data_size < 3) { + req_reply_error(req, NT_STATUS_FOOBAR); + return; + } + + io->splwrite.in.fnum = req_fnum(req, req->in.vwv, VWV(0)); + io->splwrite.in.count = SVAL(req->in.data, 1); + io->splwrite.in.data = req->in.data + 3; + + /* make sure they gave us the data they promised */ + if (req_data_oob(req, io->splwrite.in.data, io->splwrite.in.count)) { + req_reply_error(req, NT_STATUS_FOOBAR); + return; + } + + req->async_states->state |= NTVFS_ASYNC_STATE_MAY_ASYNC; + req->async_states->send_fn = reply_simple_send; + + /* call backend */ + req->async_states->status = ntvfs_write(req, io); + + REQ_ASYNC_TAIL; +} + + +/**************************************************************************** + Reply to a mkdir. +****************************************************************************/ +void reply_mkdir(struct smbsrv_request *req) +{ + union smb_mkdir *io; + + /* parse the request */ + REQ_CHECK_WCT(req, 0); + REQ_TALLOC(io, sizeof(*io)); + + io->generic.level = RAW_MKDIR_MKDIR; + req_pull_ascii4(req, &io->mkdir.in.path, req->in.data, STR_TERMINATE); + + req->async_states->state |= NTVFS_ASYNC_STATE_MAY_ASYNC; + req->async_states->send_fn = reply_simple_send; + + /* call backend */ + req->async_states->status = ntvfs_mkdir(req, io); + + REQ_ASYNC_TAIL; +} + + +/**************************************************************************** + Reply to a rmdir. +****************************************************************************/ +void reply_rmdir(struct smbsrv_request *req) +{ + struct smb_rmdir *io; + + /* parse the request */ + REQ_CHECK_WCT(req, 0); + REQ_TALLOC(io, sizeof(*io)); + + req_pull_ascii4(req, &io->in.path, req->in.data, STR_TERMINATE); + + req->async_states->state |= NTVFS_ASYNC_STATE_MAY_ASYNC; + req->async_states->send_fn = reply_simple_send; + + /* call backend */ + req->async_states->status = ntvfs_rmdir(req, io); + + REQ_ASYNC_TAIL; +} + + +/**************************************************************************** + Reply to a mv. +****************************************************************************/ +void reply_mv(struct smbsrv_request *req) +{ + union smb_rename *io; + uint8_t *p; + + /* parse the request */ + REQ_CHECK_WCT(req, 1); + REQ_TALLOC(io, sizeof(*io)); + + io->generic.level = RAW_RENAME_RENAME; + io->rename.in.attrib = SVAL(req->in.vwv, VWV(0)); + + p = req->in.data; + p += req_pull_ascii4(req, &io->rename.in.pattern1, p, STR_TERMINATE); + p += req_pull_ascii4(req, &io->rename.in.pattern2, p, STR_TERMINATE); + + if (!io->rename.in.pattern1 || !io->rename.in.pattern2) { + req_reply_error(req, NT_STATUS_FOOBAR); + return; + } + + req->async_states->state |= NTVFS_ASYNC_STATE_MAY_ASYNC; + req->async_states->send_fn = reply_simple_send; + + /* call backend */ + req->async_states->status = ntvfs_rename(req, io); + + REQ_ASYNC_TAIL; +} + + +/**************************************************************************** + Reply to an NT rename. +****************************************************************************/ +void reply_ntrename(struct smbsrv_request *req) +{ + union smb_rename *io; + uint8_t *p; + + /* parse the request */ + REQ_CHECK_WCT(req, 4); + REQ_TALLOC(io, sizeof(*io)); + + io->generic.level = RAW_RENAME_NTRENAME; + io->ntrename.in.attrib = SVAL(req->in.vwv, VWV(0)); + io->ntrename.in.flags = SVAL(req->in.vwv, VWV(1)); + io->ntrename.in.cluster_size = IVAL(req->in.vwv, VWV(2)); + + p = req->in.data; + p += req_pull_ascii4(req, &io->ntrename.in.old_name, p, STR_TERMINATE); + p += req_pull_ascii4(req, &io->ntrename.in.new_name, p, STR_TERMINATE); + + if (!io->ntrename.in.old_name || !io->ntrename.in.new_name) { + req_reply_error(req, NT_STATUS_FOOBAR); + return; + } + + req->async_states->state |= NTVFS_ASYNC_STATE_MAY_ASYNC; + req->async_states->send_fn = reply_simple_send; + + /* call backend */ + req->async_states->status = ntvfs_rename(req, io); + + REQ_ASYNC_TAIL; +} + +/**************************************************************************** + Reply to a file copy (async reply) +****************************************************************************/ +static void reply_copy_send(struct smbsrv_request *req) +{ + struct smb_copy *cp = req->async_states->private_data; + + CHECK_ASYNC_STATUS; + + /* build the reply */ + req_setup_reply(req, 1, 0); + + SSVAL(req->out.vwv, VWV(0), cp->out.count); + + req_send_reply(req); +} + +/**************************************************************************** + Reply to a file copy. +****************************************************************************/ +void reply_copy(struct smbsrv_request *req) +{ + struct smb_copy *cp; + uint8_t *p; + + /* parse request */ + REQ_CHECK_WCT(req, 3); + REQ_TALLOC(cp, sizeof(*cp)); + + cp->in.tid2 = SVAL(req->in.vwv, VWV(0)); + cp->in.ofun = SVAL(req->in.vwv, VWV(1)); + cp->in.flags = SVAL(req->in.vwv, VWV(2)); + + p = req->in.data; + p += req_pull_ascii4(req, &cp->in.path1, p, STR_TERMINATE); + p += req_pull_ascii4(req, &cp->in.path2, p, STR_TERMINATE); + + if (!cp->in.path1 || !cp->in.path2) { + req_reply_error(req, NT_STATUS_FOOBAR); + return; + } + + req->async_states->state |= NTVFS_ASYNC_STATE_MAY_ASYNC; + req->async_states->send_fn = reply_copy_send; + req->async_states->private_data = cp; + + /* call backend */ + req->async_states->status = ntvfs_copy(req, cp); + + REQ_ASYNC_TAIL; +} + +/**************************************************************************** + Reply to a lockingX request (async send) +****************************************************************************/ +static void reply_lockingX_send(struct smbsrv_request *req) +{ + union smb_lock *lck = req->async_states->private_data; + + CHECK_ASYNC_STATUS; + + /* if it was an oplock break ack then we only send a reply if + there was an error */ + if (lck->lockx.in.ulock_cnt + lck->lockx.in.lock_cnt == 0) { + req_destroy(req); + return; + } + + /* construct reply */ + req_setup_reply(req, 2, 0); + + SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE); + SSVAL(req->out.vwv, VWV(1), 0); + + chain_reply(req); +} + + +/**************************************************************************** + Reply to a lockingX request. +****************************************************************************/ +void reply_lockingX(struct smbsrv_request *req) +{ + union smb_lock *lck; + uint_t total_locks, i; + uint_t lck_size; + uint8_t *p; + + /* parse request */ + REQ_CHECK_WCT(req, 8); + REQ_TALLOC(lck, sizeof(*lck)); + + lck->lockx.level = RAW_LOCK_LOCKX; + lck->lockx.in.fnum = req_fnum(req, req->in.vwv, VWV(2)); + lck->lockx.in.mode = SVAL(req->in.vwv, VWV(3)); + lck->lockx.in.timeout = IVAL(req->in.vwv, VWV(4)); + lck->lockx.in.ulock_cnt = SVAL(req->in.vwv, VWV(6)); + lck->lockx.in.lock_cnt = SVAL(req->in.vwv, VWV(7)); + + total_locks = lck->lockx.in.ulock_cnt + lck->lockx.in.lock_cnt; + + /* there are two variants, one with 64 bit offsets and counts */ + if (lck->lockx.in.mode & LOCKING_ANDX_LARGE_FILES) { + lck_size = 20; + } else { + lck_size = 10; + } + + /* make sure we got the promised data */ + if (req_data_oob(req, req->in.data, total_locks * lck_size)) { + req_reply_error(req, NT_STATUS_FOOBAR); + return; + } + + /* allocate the locks array */ + if (total_locks) { + REQ_TALLOC(lck->lockx.in.locks, total_locks * sizeof(lck->lockx.in.locks[0])); + } + + p = req->in.data; + + /* construct the locks array */ + for (i=0;ilockx.in.locks[i].pid = SVAL(p, 0); + + if (lck->lockx.in.mode & LOCKING_ANDX_LARGE_FILES) { + ofs_high = IVAL(p, 4); + lck->lockx.in.locks[i].offset = IVAL(p, 8); + count_high = IVAL(p, 12); + lck->lockx.in.locks[i].count = IVAL(p, 16); + } else { + lck->lockx.in.locks[i].offset = IVAL(p, 2); + lck->lockx.in.locks[i].count = IVAL(p, 6); + } + if (ofs_high != 0 || count_high != 0) { + lck->lockx.in.locks[i].count |= ((uint64_t)count_high) << 32; + lck->lockx.in.locks[i].offset |= ((uint64_t)ofs_high) << 32; + } + p += lck_size; + } + + req->async_states->state |= NTVFS_ASYNC_STATE_MAY_ASYNC; + req->async_states->send_fn = reply_lockingX_send; + req->async_states->private_data = lck; + + /* call backend */ + req->async_states->status = ntvfs_lock(req, lck); + + REQ_ASYNC_TAIL; +} + +/**************************************************************************** + Reply to a SMBreadbmpx (read block multiplex) request. +****************************************************************************/ +void reply_readbmpx(struct smbsrv_request *req) +{ + /* tell the client to not use a multiplexed read - its too broken to use */ + req_reply_dos_error(req, ERRSRV, ERRuseSTD); +} + + +/**************************************************************************** + Reply to a SMBsetattrE. +****************************************************************************/ +void reply_setattrE(struct smbsrv_request *req) +{ + union smb_setfileinfo *info; + + /* parse request */ + REQ_CHECK_WCT(req, 7); + REQ_TALLOC(info, sizeof(*info)); + + info->setattre.level = RAW_SFILEINFO_SETATTRE; + info->setattre.file.fnum = req_fnum(req, req->in.vwv, VWV(0)); + info->setattre.in.create_time = srv_pull_dos_date2(req->smb_conn, req->in.vwv + VWV(1)); + info->setattre.in.access_time = srv_pull_dos_date2(req->smb_conn, req->in.vwv + VWV(3)); + info->setattre.in.write_time = srv_pull_dos_date2(req->smb_conn, req->in.vwv + VWV(5)); + + req->async_states->state |= NTVFS_ASYNC_STATE_MAY_ASYNC; + req->async_states->send_fn = reply_simple_send; + + /* call backend */ + req->async_states->status = ntvfs_setfileinfo(req, info); + + REQ_ASYNC_TAIL; +} + + +/**************************************************************************** + Reply to a SMBwritebmpx (write block multiplex primary) request. +****************************************************************************/ +void reply_writebmpx(struct smbsrv_request *req) +{ + req_reply_dos_error(req, ERRSRV, ERRuseSTD); +} + + +/**************************************************************************** + Reply to a SMBwritebs (write block multiplex secondary) request. +****************************************************************************/ +void reply_writebs(struct smbsrv_request *req) +{ + req_reply_dos_error(req, ERRSRV, ERRuseSTD); +} + + + +/**************************************************************************** + Reply to a SMBgetattrE (async reply) +****************************************************************************/ +static void reply_getattrE_send(struct smbsrv_request *req) +{ + union smb_fileinfo *info = req->async_states->private_data; + + CHECK_ASYNC_STATUS; + + /* setup reply */ + req_setup_reply(req, 11, 0); + + srv_push_dos_date2(req->smb_conn, req->out.vwv, VWV(0), info->getattre.out.create_time); + srv_push_dos_date2(req->smb_conn, req->out.vwv, VWV(2), info->getattre.out.access_time); + srv_push_dos_date2(req->smb_conn, req->out.vwv, VWV(4), info->getattre.out.write_time); + SIVAL(req->out.vwv, VWV(6), info->getattre.out.size); + SIVAL(req->out.vwv, VWV(8), info->getattre.out.alloc_size); + SSVAL(req->out.vwv, VWV(10), info->getattre.out.attrib); + + req_send_reply(req); +} + +/**************************************************************************** + Reply to a SMBgetattrE. +****************************************************************************/ +void reply_getattrE(struct smbsrv_request *req) +{ + union smb_fileinfo *info; + + /* parse request */ + REQ_CHECK_WCT(req, 1); + REQ_TALLOC(info, sizeof(*info)); + + info->getattr.level = RAW_FILEINFO_GETATTRE; + info->getattr.in.fnum = req_fnum(req, req->in.vwv, VWV(0)); + + req->async_states->state |= NTVFS_ASYNC_STATE_MAY_ASYNC; + req->async_states->send_fn = reply_getattrE_send; + req->async_states->private_data = info; + + /* call backend */ + req->async_states->status = ntvfs_qfileinfo(req, info); + + REQ_ASYNC_TAIL; +} + + +/**************************************************************************** +reply to an old style session setup command +****************************************************************************/ +static void reply_sesssetup_old(struct smbsrv_request *req) +{ + NTSTATUS status; + union smb_sesssetup sess; + uint8_t *p; + uint16_t passlen; + + sess.old.level = RAW_SESSSETUP_OLD; + + /* parse request */ + sess.old.in.bufsize = SVAL(req->in.vwv, VWV(2)); + sess.old.in.mpx_max = SVAL(req->in.vwv, VWV(3)); + sess.old.in.vc_num = SVAL(req->in.vwv, VWV(4)); + sess.old.in.sesskey = IVAL(req->in.vwv, VWV(5)); + passlen = SVAL(req->in.vwv, VWV(7)); + + /* check the request isn't malformed */ + if (req_data_oob(req, req->in.data, passlen)) { + req_reply_error(req, NT_STATUS_FOOBAR); + return; + } + + p = req->in.data; + if (!req_pull_blob(req, p, passlen, &sess.old.in.password)) { + req_reply_error(req, NT_STATUS_FOOBAR); + return; + } + p += passlen; + + p += req_pull_string(req, &sess.old.in.user, p, -1, STR_TERMINATE); + p += req_pull_string(req, &sess.old.in.domain, p, -1, STR_TERMINATE); + p += req_pull_string(req, &sess.old.in.os, p, -1, STR_TERMINATE); + p += req_pull_string(req, &sess.old.in.lanman, p, -1, STR_TERMINATE); + + /* call the generic handler */ + status = sesssetup_backend(req, &sess); + + if (!NT_STATUS_IS_OK(status)) { + req_reply_error(req, status); + return; + } + + /* construct reply */ + req_setup_reply(req, 3, 0); + + SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE); + SSVAL(req->out.vwv, VWV(1), 0); + SSVAL(req->out.vwv, VWV(2), sess.old.out.action); + + SSVAL(req->out.hdr, HDR_UID, sess.old.out.vuid); + + chain_reply(req); +} + + +/**************************************************************************** +reply to an NT1 style session setup command +****************************************************************************/ +static void reply_sesssetup_nt1(struct smbsrv_request *req) +{ + NTSTATUS status; + union smb_sesssetup sess; + uint8_t *p; + uint16_t passlen1, passlen2; + + sess.nt1.level = RAW_SESSSETUP_NT1; + + /* parse request */ + sess.nt1.in.bufsize = SVAL(req->in.vwv, VWV(2)); + sess.nt1.in.mpx_max = SVAL(req->in.vwv, VWV(3)); + sess.nt1.in.vc_num = SVAL(req->in.vwv, VWV(4)); + sess.nt1.in.sesskey = IVAL(req->in.vwv, VWV(5)); + passlen1 = SVAL(req->in.vwv, VWV(7)); + passlen2 = SVAL(req->in.vwv, VWV(8)); + sess.nt1.in.capabilities = IVAL(req->in.vwv, VWV(11)); + + /* check the request isn't malformed */ + if (req_data_oob(req, req->in.data, passlen1) || + req_data_oob(req, req->in.data + passlen1, passlen2)) { + req_reply_error(req, NT_STATUS_FOOBAR); + return; + } + + p = req->in.data; + if (!req_pull_blob(req, p, passlen1, &sess.nt1.in.password1)) { + req_reply_error(req, NT_STATUS_FOOBAR); + return; + } + p += passlen1; + if (!req_pull_blob(req, p, passlen2, &sess.nt1.in.password2)) { + req_reply_error(req, NT_STATUS_FOOBAR); + return; + } + p += passlen2; + + p += req_pull_string(req, &sess.nt1.in.user, p, -1, STR_TERMINATE); + p += req_pull_string(req, &sess.nt1.in.domain, p, -1, STR_TERMINATE); + p += req_pull_string(req, &sess.nt1.in.os, p, -1, STR_TERMINATE); + p += req_pull_string(req, &sess.nt1.in.lanman, p, -1, STR_TERMINATE); + + /* call the generic handler */ + status = sesssetup_backend(req, &sess); + + if (!NT_STATUS_IS_OK(status)) { + req_reply_error(req, status); + return; + } + + /* construct reply */ + req_setup_reply(req, 3, 0); + + SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE); + SSVAL(req->out.vwv, VWV(1), 0); + SSVAL(req->out.vwv, VWV(2), sess.nt1.out.action); + + SSVAL(req->out.hdr, HDR_UID, sess.nt1.out.vuid); + + req_push_str(req, NULL, sess.nt1.out.os, -1, STR_TERMINATE); + req_push_str(req, NULL, sess.nt1.out.lanman, -1, STR_TERMINATE); + req_push_str(req, NULL, sess.nt1.out.domain, -1, STR_TERMINATE); + + chain_reply(req); +} + + +/**************************************************************************** +reply to an SPNEGO style session setup command +****************************************************************************/ +static void reply_sesssetup_spnego(struct smbsrv_request *req) +{ + NTSTATUS status; + union smb_sesssetup sess; + uint8_t *p; + uint16_t blob_len; + + sess.spnego.level = RAW_SESSSETUP_SPNEGO; + + /* parse request */ + sess.spnego.in.bufsize = SVAL(req->in.vwv, VWV(2)); + sess.spnego.in.mpx_max = SVAL(req->in.vwv, VWV(3)); + sess.spnego.in.vc_num = SVAL(req->in.vwv, VWV(4)); + sess.spnego.in.sesskey = IVAL(req->in.vwv, VWV(5)); + blob_len = SVAL(req->in.vwv, VWV(7)); + sess.spnego.in.capabilities = IVAL(req->in.vwv, VWV(10)); + + p = req->in.data; + if (!req_pull_blob(req, p, blob_len, &sess.spnego.in.secblob)) { + req_reply_error(req, NT_STATUS_FOOBAR); + return; + } + p += blob_len; + + p += req_pull_string(req, &sess.spnego.in.os, p, -1, STR_TERMINATE); + p += req_pull_string(req, &sess.spnego.in.lanman, p, -1, STR_TERMINATE); + p += req_pull_string(req, &sess.spnego.in.workgroup, p, -1, STR_TERMINATE); + + /* call the generic handler */ + status = sesssetup_backend(req, &sess); + + if (!NT_STATUS_IS_OK(status) && + !NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { + req_reply_error(req, status); + return; + } + + /* construct reply */ + req_setup_reply(req, 4, sess.spnego.out.secblob.length); + + if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { + req_setup_error(req, status); + } + + SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE); + SSVAL(req->out.vwv, VWV(1), 0); + SSVAL(req->out.vwv, VWV(2), sess.spnego.out.action); + SSVAL(req->out.vwv, VWV(3), sess.spnego.out.secblob.length); + + SSVAL(req->out.hdr, HDR_UID, sess.spnego.out.vuid); + + memcpy(req->out.data, sess.spnego.out.secblob.data, sess.spnego.out.secblob.length); + req_push_str(req, NULL, sess.spnego.out.os, -1, STR_TERMINATE); + req_push_str(req, NULL, sess.spnego.out.lanman, -1, STR_TERMINATE); + req_push_str(req, NULL, sess.spnego.out.workgroup, -1, STR_TERMINATE); + + chain_reply(req); +} + + +/**************************************************************************** +reply to a session setup command +****************************************************************************/ +void reply_sesssetup(struct smbsrv_request *req) +{ + switch (req->in.wct) { + case 10: + /* a pre-NT1 call */ + reply_sesssetup_old(req); + return; + case 13: + /* a NT1 call */ + reply_sesssetup_nt1(req); + return; + case 12: + /* a SPNEGO call */ + reply_sesssetup_spnego(req); + return; + } + + /* unsupported variant */ + req_reply_error(req, NT_STATUS_FOOBAR); +} + +/**************************************************************************** + Reply to a SMBulogoffX. +****************************************************************************/ +void reply_ulogoffX(struct smbsrv_request *req) +{ + struct smbsrv_tcon *tcon; + NTSTATUS status; + + if (!req->session) { + req_reply_error(req, NT_STATUS_DOS(ERRSRV, ERRbaduid)); + return; + } + + /* in user level security we are supposed to close any files + open by this user on all open tree connects */ + for (tcon=req->smb_conn->tcons.list;tcon;tcon=tcon->next) { + req->tcon = tcon; + status = ntvfs_logoff(req); + req->tcon = NULL; + if (!NT_STATUS_IS_OK(status)) { + req_reply_error(req, status); + return; + } + } + + talloc_free(req->session); + req->session = NULL; /* it is now invalid, don't use on + any chained packets */ + + req_setup_reply(req, 2, 0); + + SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE); + SSVAL(req->out.vwv, VWV(1), 0); + + chain_reply(req); +} + + +/**************************************************************************** + Reply to an SMBfindclose request +****************************************************************************/ +void reply_findclose(struct smbsrv_request *req) +{ + NTSTATUS status; + union smb_search_close io; + + io.findclose.level = RAW_FINDCLOSE_FINDCLOSE; + + /* parse request */ + REQ_CHECK_WCT(req, 1); + + io.findclose.in.handle = SVAL(req->in.vwv, VWV(0)); + + /* call backend */ + status = ntvfs_search_close(req, &io); + + if (!NT_STATUS_IS_OK(status)) { + req_reply_error(req, status); + return; + } + + /* construct reply */ + req_setup_reply(req, 0, 0); + + req_send_reply(req); +} + +/**************************************************************************** + Reply to an SMBfindnclose request +****************************************************************************/ +void reply_findnclose(struct smbsrv_request *req) +{ + req_reply_error(req, NT_STATUS_FOOBAR); +} + + +/**************************************************************************** + Reply to an SMBntcreateX request (async send) +****************************************************************************/ +static void reply_ntcreate_and_X_send(struct smbsrv_request *req) +{ + union smb_open *io = req->async_states->private_data; + + CHECK_ASYNC_STATUS; + + /* construct reply */ + req_setup_reply(req, 34, 0); + + SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE); + SSVAL(req->out.vwv, VWV(1), 0); + SCVAL(req->out.vwv, VWV(2), io->ntcreatex.out.oplock_level); + + /* the rest of the parameters are not aligned! */ + SSVAL(req->out.vwv, 5, io->ntcreatex.out.fnum); + SIVAL(req->out.vwv, 7, io->ntcreatex.out.create_action); + push_nttime(req->out.vwv, 11, io->ntcreatex.out.create_time); + push_nttime(req->out.vwv, 19, io->ntcreatex.out.access_time); + push_nttime(req->out.vwv, 27, io->ntcreatex.out.write_time); + push_nttime(req->out.vwv, 35, io->ntcreatex.out.change_time); + SIVAL(req->out.vwv, 43, io->ntcreatex.out.attrib); + SBVAL(req->out.vwv, 47, io->ntcreatex.out.alloc_size); + SBVAL(req->out.vwv, 55, io->ntcreatex.out.size); + SSVAL(req->out.vwv, 63, io->ntcreatex.out.file_type); + SSVAL(req->out.vwv, 65, io->ntcreatex.out.ipc_state); + SCVAL(req->out.vwv, 67, io->ntcreatex.out.is_directory); + + req->chained_fnum = io->ntcreatex.out.fnum; + + chain_reply(req); +} + +/**************************************************************************** + Reply to an SMBntcreateX request +****************************************************************************/ +void reply_ntcreate_and_X(struct smbsrv_request *req) +{ + union smb_open *io; + uint16_t fname_len; + + /* parse the request */ + REQ_CHECK_WCT(req, 24); + REQ_TALLOC(io, sizeof(*io)); + + io->ntcreatex.level = RAW_OPEN_NTCREATEX; + + /* notice that the word parameters are not word aligned, so we don't use VWV() */ + fname_len = SVAL(req->in.vwv, 5); + io->ntcreatex.in.flags = IVAL(req->in.vwv, 7); + io->ntcreatex.in.root_fid = IVAL(req->in.vwv, 11); + io->ntcreatex.in.access_mask = IVAL(req->in.vwv, 15); + io->ntcreatex.in.alloc_size = BVAL(req->in.vwv, 19); + io->ntcreatex.in.file_attr = IVAL(req->in.vwv, 27); + io->ntcreatex.in.share_access = IVAL(req->in.vwv, 31); + io->ntcreatex.in.open_disposition = IVAL(req->in.vwv, 35); + io->ntcreatex.in.create_options = IVAL(req->in.vwv, 39); + io->ntcreatex.in.impersonation = IVAL(req->in.vwv, 43); + io->ntcreatex.in.security_flags = CVAL(req->in.vwv, 47); + io->ntcreatex.in.ea_list = NULL; + io->ntcreatex.in.sec_desc = NULL; + + /* we need a neater way to handle this alignment */ + if ((req->flags2 & FLAGS2_UNICODE_STRINGS) && + ucs2_align(req->in.buffer, req->in.data, STR_TERMINATE|STR_UNICODE)) { + fname_len++; + } + + req_pull_string(req, &io->ntcreatex.in.fname, req->in.data, fname_len, STR_TERMINATE); + if (!io->ntcreatex.in.fname) { + req_reply_error(req, NT_STATUS_FOOBAR); + return; + } + + req->async_states->state |= NTVFS_ASYNC_STATE_MAY_ASYNC; + req->async_states->send_fn = reply_ntcreate_and_X_send; + req->async_states->private_data = io; + + /* call the backend */ + req->async_states->status = ntvfs_openfile(req, io); + + REQ_ASYNC_TAIL; +} + + +/**************************************************************************** + Reply to an SMBntcancel request +****************************************************************************/ +void reply_ntcancel(struct smbsrv_request *req) +{ + /* NOTE: this request does not generate a reply */ + ntvfs_cancel(req); + req_destroy(req); +} + +/**************************************************************************** + Reply to an SMBsends request +****************************************************************************/ +void reply_sends(struct smbsrv_request *req) +{ + req_reply_error(req, NT_STATUS_FOOBAR); +} + +/**************************************************************************** + Reply to an SMBsendstrt request +****************************************************************************/ +void reply_sendstrt(struct smbsrv_request *req) +{ + req_reply_error(req, NT_STATUS_FOOBAR); +} + +/**************************************************************************** + Reply to an SMBsendend request +****************************************************************************/ +void reply_sendend(struct smbsrv_request *req) +{ + req_reply_error(req, NT_STATUS_FOOBAR); +} + +/**************************************************************************** + Reply to an SMBsendtxt request +****************************************************************************/ +void reply_sendtxt(struct smbsrv_request *req) +{ + req_reply_error(req, NT_STATUS_FOOBAR); +} + + +/* + parse the called/calling names from session request +*/ +static NTSTATUS parse_session_request(struct smbsrv_request *req) +{ + DATA_BLOB blob; + NTSTATUS status; + + blob.data = req->in.buffer + 4; + blob.length = ascii_len_n((const char *)blob.data, req->in.size - PTR_DIFF(blob.data, req->in.buffer)); + if (blob.length == 0) return NT_STATUS_BAD_NETWORK_NAME; + + req->smb_conn->negotiate.called_name = talloc(req->smb_conn, struct nbt_name); + req->smb_conn->negotiate.calling_name = talloc(req->smb_conn, struct nbt_name); + if (req->smb_conn->negotiate.called_name == NULL || + req->smb_conn->negotiate.calling_name == NULL) { + return NT_STATUS_NO_MEMORY; + } + + status = nbt_name_from_blob(req->smb_conn, &blob, + req->smb_conn->negotiate.called_name); + NT_STATUS_NOT_OK_RETURN(status); + + blob.data += blob.length; + blob.length = ascii_len_n((const char *)blob.data, req->in.size - PTR_DIFF(blob.data, req->in.buffer)); + if (blob.length == 0) return NT_STATUS_BAD_NETWORK_NAME; + + status = nbt_name_from_blob(req->smb_conn, &blob, + req->smb_conn->negotiate.calling_name); + NT_STATUS_NOT_OK_RETURN(status); + + req->smb_conn->negotiate.done_nbt_session = True; + + return NT_STATUS_OK; +} + + + +/**************************************************************************** + Reply to a special message - a SMB packet with non zero NBT message type +****************************************************************************/ +void reply_special(struct smbsrv_request *req) +{ + uint8_t msg_type; + uint8_t *buf = talloc_zero_array(req, uint8_t, 4); + + msg_type = CVAL(req->in.buffer,0); + + SIVAL(buf, 0, 0); + + switch (msg_type) { + case 0x81: /* session request */ + if (req->smb_conn->negotiate.done_nbt_session) { + DEBUG(0,("Warning: ignoring secondary session request\n")); + return; + } + + SCVAL(buf,0,0x82); + SCVAL(buf,3,0); + + /* we don't check the status - samba always accepts session + requests for any name */ + parse_session_request(req); + + req->out.buffer = buf; + req->out.size = 4; + req_send_reply_nosign(req); + return; + + case 0x89: /* session keepalive request + (some old clients produce this?) */ + SCVAL(buf, 0, SMBkeepalive); + SCVAL(buf, 3, 0); + req->out.buffer = buf; + req->out.size = 4; + req_send_reply_nosign(req); + return; + + case SMBkeepalive: + /* session keepalive - swallow it */ + req_destroy(req); + return; + } + + DEBUG(0,("Unexpected NBT session packet (%d)\n", msg_type)); + req_destroy(req); +} diff --git a/source4/smb_server/smb/request.c b/source4/smb_server/smb/request.c new file mode 100644 index 0000000000..5491089c8e --- /dev/null +++ b/source4/smb_server/smb/request.c @@ -0,0 +1,675 @@ +/* + 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 "lib/events/events.h" +#include "dlinklist.h" +#include "smb_server/smb_server.h" +#include "smbd/service_stream.h" +#include "lib/stream/packet.h" + + +/* we over allocate the data buffer to prevent too many realloc calls */ +#define REQ_OVER_ALLOCATION 0 + +/* destroy a request structure */ +void req_destroy(struct smbsrv_request *req) +{ + /* ahh, its so nice to destroy a complex structure in such a + * simple way! */ + talloc_free(req); +} + +/**************************************************************************** +construct a basic request packet, mostly used to construct async packets +such as change notify and oplock break requests +****************************************************************************/ +struct smbsrv_request *init_smb_request(struct smbsrv_connection *smb_conn) +{ + struct smbsrv_request *req; + + req = talloc(smb_conn, struct smbsrv_request); + if (!req) { + return NULL; + } + + ZERO_STRUCTP(req); + + /* setup the request context */ + req->smb_conn = smb_conn; + + req->async_states = talloc(req, struct ntvfs_async_state); + if (!req->async_states) { + talloc_free(req); + return NULL; + } + req->async_states->state = 0; + + 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 req_setup_reply(struct smbsrv_request *req, uint_t wct, uint_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 + wct*2 + 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 { + 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 *req_setup_secondary(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, uint_t new_size) +{ + int delta; + + if (!(req->control_flags & 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 req_send_reply_nosign(struct smbsrv_request *req) +{ + DATA_BLOB blob; + NTSTATUS status; + + 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)); + } + req_destroy(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 req_send_reply(struct smbsrv_request *req) +{ + req_sign_packet(req); + + req_send_reply_nosign(req); +} + + + +/* + construct and send an error packet with a forced DOS error code + this is needed to match win2000 behaviour for some parts of the protocol +*/ +void req_reply_dos_error(struct smbsrv_request *req, uint8_t eclass, uint16_t ecode) +{ + /* if the basic packet hasn't been setup yet then do it now */ + if (req->out.buffer == NULL) { + req_setup_reply(req, 0, 0); + } + + 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); + req_send_reply(req); +} + +/* + setup the header of a reply to include an NTSTATUS code +*/ +void req_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 req_reply_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; + } + req_setup_reply(req, 0, 0); + + /* error returns never have any data */ + req_grow_data(req, 0); + + req_setup_error(req, status); + req_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, uint_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 NULL 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 +*/ +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); +} diff --git a/source4/smb_server/smb/search.c b/source4/smb_server/smb/search.c new file mode 100644 index 0000000000..3ec66a0f33 --- /dev/null +++ b/source4/smb_server/smb/search.c @@ -0,0 +1,285 @@ +/* + Unix SMB/CIFS implementation. + SMBsearch handling + Copyright (C) Andrew Tridgell 2003 + Copyright (C) James J Myers 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 handles the parsing of transact2 requests +*/ + +#include "includes.h" +#include "smb_server/smb_server.h" + + +/* check req->async.status and if not OK then send an error reply */ +#define CHECK_ASYNC_STATUS do { \ + if (!NT_STATUS_IS_OK(req->async_states->status)) { \ + req_reply_error(req, req->async_states->status); \ + return; \ + }} while (0) + +/* + check if the backend wants to handle the request asynchronously. + if it wants it handled synchronously then call the send function + immediately +*/ +#define REQ_ASYNC_TAIL do { \ + if (!(req->async_states->state & NTVFS_ASYNC_STATE_ASYNC)) { \ + req->async_states->send_fn(req); \ + }} while (0) + +/* useful wrapper for talloc with NO_MEMORY reply */ +#define REQ_TALLOC(ptr) do { \ + ptr = talloc_size(req, sizeof(*(ptr))); \ + if (!ptr) { \ + req_reply_error(req, NT_STATUS_NO_MEMORY); \ + return; \ + }} while (0) + +#define CHECK_MIN_BLOB_SIZE(blob, size) do { \ + if ((blob)->length < (size)) { \ + return NT_STATUS_INFO_LENGTH_MISMATCH; \ + }} while (0) + +/* 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. +****************************************************************************/ +void reply_search(struct smbsrv_request *req) +{ + union smb_search_first *sf; + union smb_search_next *sn; + uint16_t resume_key_length; + struct search_state state; + uint8_t *p; + NTSTATUS status; + 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; + } + + REQ_TALLOC(sf); + + /* parse request */ + if (req->in.wct != 2) { + req_reply_error(req, NT_STATUS_INVALID_PARAMETER); + return; + } + + p = req->in.data; + p += req_pull_ascii4(req, &sf->search_first.in.pattern, + p, STR_TERMINATE); + if (!sf->search_first.in.pattern) { + req_reply_error(req, NT_STATUS_OBJECT_NAME_NOT_FOUND); + return; + } + + if (req_data_oob(req, p, 3)) { + req_reply_error(req, NT_STATUS_INVALID_PARAMETER); + return; + } + if (*p != 5) { + req_reply_error(req, NT_STATUS_INVALID_PARAMETER); + return; + } + resume_key_length = SVAL(p, 1); + p += 3; + + /* setup state for callback */ + state.req = req; + state.file = NULL; + state.last_entry_offset = 0; + + /* construct reply */ + req_setup_reply(req, 1, 0); + req_append_var_block(req, NULL, 0); + + if (resume_key_length != 0) { + if (resume_key_length != 21 || + req_data_oob(req, p, 21) || + level == RAW_SEARCH_FUNIQUE) { + req_reply_error(req, NT_STATUS_INVALID_PARAMETER); + return; + } + + /* do a search next operation */ + REQ_TALLOC(sn); + + 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.in.max_count = SVAL(req->in.vwv, VWV(0)); + sn->search_next.in.search_attrib = SVAL(req->in.vwv, VWV(1)); + + /* call backend */ + status = ntvfs_search_next(req, sn, &state, find_callback); + SSVAL(req->out.vwv, VWV(0), sn->search_next.out.count); + } else { + /* do a search first operation */ + sf->search_first.level = level; + sf->search_first.in.search_attrib = SVAL(req->in.vwv, VWV(1)); + sf->search_first.in.max_count = SVAL(req->in.vwv, VWV(0)); + + /* call backend */ + status = ntvfs_search_first(req, sf, &state, find_callback); + SSVAL(req->out.vwv, VWV(0), sf->search_first.out.count); + } + + if (!NT_STATUS_IS_OK(status)) { + req_reply_error(req, status); + return; + } + + req_send_reply(req); +} + + +/**************************************************************************** + Reply to a fclose (async reply) +****************************************************************************/ +static void reply_fclose_send(struct smbsrv_request *req) +{ + CHECK_ASYNC_STATUS; + + /* construct reply */ + req_setup_reply(req, 1, 0); + + SSVAL(req->out.vwv, VWV(0), 0); + + req_send_reply(req); +} + + +/**************************************************************************** + Reply to fclose (stop directory search). +****************************************************************************/ +void reply_fclose(struct smbsrv_request *req) +{ + union smb_search_close *sc; + uint16_t resume_key_length; + uint8_t *p; + const char *pattern; + + REQ_TALLOC(sc); + + /* parse request */ + if (req->in.wct != 2) { + req_reply_error(req, NT_STATUS_INVALID_PARAMETER); + return; + } + + p = req->in.data; + p += req_pull_ascii4(req, &pattern, p, STR_TERMINATE); + if (pattern && *pattern) { + req_reply_error(req, NT_STATUS_INVALID_PARAMETER); + return; + } + + if (req_data_oob(req, p, 3)) { + req_reply_error(req, NT_STATUS_INVALID_PARAMETER); + return; + } + if (*p != 5) { + req_reply_error(req, NT_STATUS_INVALID_PARAMETER); + return; + } + resume_key_length = SVAL(p, 1); + p += 3; + + if (resume_key_length != 21) { + req_reply_error(req, NT_STATUS_INVALID_PARAMETER); + return; + } + + if (req_data_oob(req, p, 21)) { + req_reply_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); + + /* do a search close operation */ + req->async_states->state |= NTVFS_ASYNC_STATE_MAY_ASYNC; + req->async_states->send_fn = reply_fclose_send; + req->async_states->private_data = sc; + + /* call backend */ + req->async_states->status = ntvfs_search_close(req, sc); + + REQ_ASYNC_TAIL; +} diff --git a/source4/smb_server/smb/service.c b/source4/smb_server/smb/service.c new file mode 100644 index 0000000000..213cf6726b --- /dev/null +++ b/source4/smb_server/smb/service.c @@ -0,0 +1,194 @@ +/* + 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" + + +/** + * Find a service entry. service is always in dos codepage. + * + * @param service is modified (to canonical form??) + **/ +static int find_service(const char *service) +{ + int iService; + + iService = lp_servicenumber(service); + + if (iService >= 0 && !lp_snum_ok(iService)) { + DEBUG(0,("Invalid snum %d for %s\n",iService, service)); + iService = -1; + } + + if (iService == -1) { + DEBUG(3,("find_service() failed to find service %s\n", service)); + } + + return iService; +} + + +/**************************************************************************** + Make a connection, given the snum to connect to, and the vuser of the + connecting user if appropriate. +****************************************************************************/ +static NTSTATUS make_connection_snum(struct smbsrv_request *req, + int snum, enum ntvfs_type type, + DATA_BLOB password, + const char *dev) +{ + struct smbsrv_tcon *tcon; + NTSTATUS status; + + if (!socket_check_access(req->smb_conn->connection->socket, + lp_servicename(snum), + lp_hostsallow(snum), + lp_hostsdeny(snum))) { + return NT_STATUS_ACCESS_DENIED; + } + + tcon = smbsrv_tcon_new(req->smb_conn); + if (!tcon) { + DEBUG(0,("Couldn't find free connection.\n")); + return NT_STATUS_INSUFFICIENT_RESOURCES; + } + req->tcon = tcon; + + tcon->service = snum; + + /* init ntvfs function pointers */ + status = ntvfs_init_connection(req, type); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("ntvfs_init_connection failed for service %s\n", + lp_servicename(tcon->service))); + req->tcon = NULL; + talloc_free(tcon); + return status; + } + + /* Invoke NTVFS connection hook */ + status = ntvfs_connect(req, lp_servicename(snum)); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0,("make_connection: NTVFS make connection failed!\n")); + req->tcon = NULL; + talloc_free(tcon); + return status; + } + + return NT_STATUS_OK; +} + +/**************************************************************************** + 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) +{ + int snum; + enum ntvfs_type type; + const char *type_str; + + /* TODO: check the password, when it's share level security! */ + + /* 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; + } + } + + snum = find_service(service); + + if (snum == -1) { + DEBUG(0,("couldn't find service %s\n", service)); + return NT_STATUS_BAD_NETWORK_NAME; + } + + /* work out what sort of connection this is */ + if (strcmp(lp_fstype(snum), "IPC") == 0) { + type = NTVFS_IPC; + type_str = "IPC"; + } else if (lp_print_ok(snum)) { + 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_snum(req, snum, type, password, dev); +} + +/* + backend for tree connect call +*/ +NTSTATUS tcon_backend(struct smbsrv_request *req, union smb_tcon *con) +{ + NTSTATUS status; + + /* can only do bare tcon in share level security */ + if (!req->session && lp_security() != SEC_SHARE) { + return NT_STATUS_ACCESS_DENIED; + } + + 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->dev_type); + con->tconx.out.fs_type = talloc_strdup(req, req->tcon->fs_type); + con->tconx.out.options = SMB_SUPPORT_SEARCH_BITS | (lp_csc_policy(req->tcon->service) << 2); + if (lp_msdfs_root(req->tcon->service) && lp_host_msdfs()) { + con->tconx.out.options |= SMB_SHARE_IN_DFS; + } + + return status; +} diff --git a/source4/smb_server/smb/sesssetup.c b/source4/smb_server/smb/sesssetup.c new file mode 100644 index 0000000000..3f09346243 --- /dev/null +++ b/source4/smb_server/smb/sesssetup.c @@ -0,0 +1,370 @@ +/* + Unix SMB/CIFS implementation. + handle SMBsessionsetup + Copyright (C) Andrew Tridgell 1998-2001 + Copyright (C) Andrew Bartlett 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/auth.h" +#include "smb_server/smb_server.h" +#include "smbd/service_stream.h" +#include "libcli/nbt/libnbt.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()); +} + + +/* + handler for old style session setup +*/ +static NTSTATUS sesssetup_old(struct smbsrv_request *req, union smb_sesssetup *sess) +{ + NTSTATUS status; + struct auth_usersupplied_info *user_info = NULL; + struct auth_serversupplied_info *server_info = NULL; + struct auth_session_info *session_info; + struct smbsrv_session *smb_sess; + const char *remote_machine = NULL; + + sess->old.out.vuid = 0; + sess->old.out.action = 0; + + 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; + } + + if (!remote_machine) { + remote_machine = socket_get_peer_addr(req->smb_conn->connection->socket, req); + } + + user_info = talloc(req, struct auth_usersupplied_info); + NT_STATUS_HAVE_NO_MEMORY(user_info); + + 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 = socket_get_peer_addr(req->smb_conn->connection->socket, user_info); + + 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); + + status = auth_check_password(req->smb_conn->negotiate.auth_context, + req, user_info, &server_info); + if (!NT_STATUS_IS_OK(status)) { + return auth_nt_status_squash(status); + } + + /* This references server_info into session_info */ + status = auth_generate_session_info(req, server_info, &session_info); + if (!NT_STATUS_IS_OK(status)) { + return auth_nt_status_squash(status); + } + + /* allocate a new session */ + smb_sess = smbsrv_session_new(req->smb_conn, NULL); + if (!smb_sess) { + return NT_STATUS_ACCESS_DENIED; + } + + /* 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)) { + return auth_nt_status_squash(status); + } + + /* 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; + + sesssetup_common_strings(req, + &sess->old.out.os, + &sess->old.out.lanman, + &sess->old.out.domain); + + return NT_STATUS_OK; +} + + +/* + handler for NT1 style session setup +*/ +static NTSTATUS 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 auth_serversupplied_info *server_info = NULL; + struct auth_session_info *session_info; + struct smbsrv_session *smb_sess; + const char *remote_machine = NULL; + + sess->nt1.out.vuid = 0; + sess->nt1.out.action = 0; + + 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.spnego_negotiated) { + if (sess->nt1.in.user && *sess->nt1.in.user) { + /* We can't accept a normal login, because we + * don't have a challenge */ + return NT_STATUS_LOGON_FAILURE; + } + + /* TODO: should we use just "anonymous" here? */ + status = auth_context_create(req, lp_auth_methods(), + &auth_context, + req->smb_conn->connection->event.ctx); + NT_STATUS_NOT_OK_RETURN(status); + } 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; + } + + if (!remote_machine) { + remote_machine = socket_get_peer_addr(req->smb_conn->connection->socket, req); + } + + user_info = talloc(req, struct auth_usersupplied_info); + NT_STATUS_HAVE_NO_MEMORY(user_info); + + 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 = socket_get_peer_addr(req->smb_conn->connection->socket, user_info); + + 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); + + status = auth_check_password(auth_context, req, user_info, &server_info); + if (!NT_STATUS_IS_OK(status)) { + return auth_nt_status_squash(status); + } + + /* This references server_info into session_info */ + status = auth_generate_session_info(req, server_info, &session_info); + if (!NT_STATUS_IS_OK(status)) { + return auth_nt_status_squash(status); + } + + /* allocate a new session */ + smb_sess = smbsrv_session_new(req->smb_conn, NULL); + if (!smb_sess) { + return NT_STATUS_ACCESS_DENIED; + } + + /* 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)) { + return auth_nt_status_squash(status); + } + + /* 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; + + sesssetup_common_strings(req, + &sess->nt1.out.os, + &sess->nt1.out.lanman, + &sess->nt1.out.domain); + + if (!session_info->server_info->authenticated) { + return NT_STATUS_OK; + } + + if (!srv_setup_signing(req->smb_conn, &session_info->session_key, &sess->nt1.in.password2)) { + /* Already signing, or disabled */ + return NT_STATUS_OK; + } + + /* Force check of the request packet, now we know the session key */ + req_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 */ + + srv_signing_restart(req->smb_conn, &session_info->session_key, &sess->nt1.in.password2); + + return NT_STATUS_OK; +} + + +/* + handler for SPNEGO style session setup +*/ +static NTSTATUS sesssetup_spnego(struct smbsrv_request *req, union smb_sesssetup *sess) +{ + NTSTATUS status = NT_STATUS_ACCESS_DENIED; + struct smbsrv_session *smb_sess; + struct gensec_security *gensec_ctx; + struct auth_session_info *session_info = 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->nt1.in.bufsize; + req->smb_conn->negotiate.client_caps = sess->nt1.in.capabilities; + } + + vuid = SVAL(req->in.hdr,HDR_UID); + smb_sess = smbsrv_session_find_sesssetup(req->smb_conn, vuid); + if (smb_sess) { + gensec_ctx = smb_sess->gensec_ctx; + } else { + status = gensec_server_start(req, &gensec_ctx, + req->smb_conn->connection->event.ctx); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("Failed to start GENSEC server code: %s\n", nt_errstr(status))); + return status; + } + + gensec_set_credentials(gensec_ctx, req->smb_conn->negotiate.server_credentials); + + gensec_set_target_service(gensec_ctx, "cifs"); + + gensec_want_feature(gensec_ctx, GENSEC_FEATURE_SESSION_KEY); + + status = gensec_start_mech_by_oid(gensec_ctx, GENSEC_OID_SPNEGO); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("Failed to start GENSEC SPNEGO server code: %s\n", nt_errstr(status))); + return status; + } + + smb_sess = smbsrv_session_new(req->smb_conn, gensec_ctx); + if (!smb_sess) { + return NT_STATUS_ACCESS_DENIED; + } + } + + status = gensec_update(gensec_ctx, req, sess->spnego.in.secblob, &sess->spnego.out.secblob); + if (NT_STATUS_IS_OK(status)) { + DATA_BLOB session_key; + + status = gensec_session_info(gensec_ctx, &session_info); + if (!NT_STATUS_IS_OK(status)) { + talloc_free(smb_sess); + return status; + } + + status = gensec_session_key(gensec_ctx, + &session_key); +/* TODO: what if getting the session key failed? */ + if (NT_STATUS_IS_OK(status) + && session_info->server_info->authenticated + && srv_setup_signing(req->smb_conn, &session_key, NULL)) { + /* Force check of the request packet, now we know the session key */ + req_signing_check_incoming(req); + + srv_signing_restart(req->smb_conn, &session_key, NULL); + } + + } else if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { + } else { + status = auth_nt_status_squash(status); + + /* This invalidates the VUID of the failed login */ + talloc_free(smb_sess); + return status; + } + + if (NT_STATUS_IS_OK(status)) { + /* 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)) { + return auth_nt_status_squash(status); + } + req->session = smb_sess; + } + sess->spnego.out.vuid = smb_sess->vuid; + + return status; +} + +/* + backend for sessionsetup call - this takes all 3 variants of the call +*/ +NTSTATUS sesssetup_backend(struct smbsrv_request *req, + union smb_sesssetup *sess) +{ + NTSTATUS status = NT_STATUS_INVALID_LEVEL; + + switch (sess->old.level) { + case RAW_SESSSETUP_OLD: + status = sesssetup_old(req, sess); + break; + case RAW_SESSSETUP_NT1: + status = sesssetup_nt1(req, sess); + break; + case RAW_SESSSETUP_SPNEGO: + status = sesssetup_spnego(req, sess); + break; + } + + if (NT_STATUS_IS_OK(status)) { + req->smb_conn->negotiate.done_sesssetup = True; + } + + return status; +} + + diff --git a/source4/smb_server/smb/signing.c b/source4/smb_server/smb/signing.c new file mode 100644 index 0000000000..b461056397 --- /dev/null +++ b/source4/smb_server/smb/signing.c @@ -0,0 +1,182 @@ +/* + 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" + + +/* + sign an outgoing packet +*/ +void req_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 srv_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 srv_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)... */ + srv_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 srv_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 req_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 req_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; +} diff --git a/source4/smb_server/smb/srvtime.c b/source4/smb_server/smb/srvtime.c new file mode 100644 index 0000000000..faeda6faf6 --- /dev/null +++ b/source4/smb_server/smb/srvtime.c @@ -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); +} diff --git a/source4/smb_server/smb/trans2.c b/source4/smb_server/smb/trans2.c new file mode 100644 index 0000000000..c9a357523b --- /dev/null +++ b/source4/smb_server/smb/trans2.c @@ -0,0 +1,1779 @@ +/* + Unix SMB/CIFS implementation. + transaction2 handling + 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 handles the parsing of transact2 requests +*/ + +#include "includes.h" +#include "dlinklist.h" +#include "smb_server/smb_server.h" +#include "librpc/gen_ndr/ndr_misc.h" + +#define CHECK_MIN_BLOB_SIZE(blob, size) do { \ + if ((blob)->length < (size)) { \ + return NT_STATUS_INFO_LENGTH_MISMATCH; \ + }} while (0) + +/* grow the data allocation size of a trans2 reply - this guarantees + that requests to grow the data size later will not change the + pointer */ +static BOOL trans2_grow_data_allocation(struct smbsrv_request *req, + struct smb_trans2 *trans, + uint32_t new_size) +{ + if (new_size <= trans->out.data.length) { + return True; + } + trans->out.data.data = talloc_realloc(req, trans->out.data.data, + uint8_t, new_size); + return (trans->out.data.data != NULL); +} + + +/* grow the data size of a trans2 reply */ +static BOOL trans2_grow_data(struct smbsrv_request *req, + struct smb_trans2 *trans, + uint32_t new_size) +{ + if (!trans2_grow_data_allocation(req, trans, new_size)) { + return False; + } + trans->out.data.length = new_size; + return True; +} + +/* grow the data, zero filling any new bytes */ +static BOOL trans2_grow_data_fill(struct smbsrv_request *req, + struct smb_trans2 *trans, + uint32_t new_size) +{ + uint32_t old_size = trans->out.data.length; + if (!trans2_grow_data(req, trans, new_size)) { + return False; + } + if (new_size > old_size) { + memset(trans->out.data.data + old_size, 0, new_size - old_size); + } + return True; +} + + +/* setup a trans2 reply, given the data and params sizes */ +static void trans2_setup_reply(struct smbsrv_request *req, + struct smb_trans2 *trans, + uint16_t param_size, uint16_t data_size, + uint16_t setup_count) +{ + trans->out.setup_count = setup_count; + if (setup_count != 0) { + trans->out.setup = talloc_zero_array(req, uint16_t, setup_count); + } + trans->out.params = data_blob_talloc(req, NULL, param_size); + trans->out.data = data_blob_talloc(req, NULL, data_size); +} + + +/* + pull a string from a blob in a trans2 request +*/ +static size_t trans2_pull_blob_string(struct smbsrv_request *req, + const DATA_BLOB *blob, + uint16_t offset, + const char **str, + int flags) +{ + /* we use STR_NO_RANGE_CHECK because the params are allocated + separately in a DATA_BLOB, so we need to do our own range + checking */ + if (offset >= blob->length) { + *str = NULL; + return 0; + } + + return req_pull_string(req, str, + blob->data + offset, + blob->length - offset, + STR_NO_RANGE_CHECK | flags); +} + +/* + push a string into the data section of a trans2 request + return the number of bytes consumed in the output +*/ +static size_t trans2_push_data_string(struct smbsrv_request *req, + struct smb_trans2 *trans, + uint32_t len_offset, + uint32_t offset, + const WIRE_STRING *str, + int dest_len, + int flags) +{ + int alignment = 0, ret = 0, pkt_len; + + /* we use STR_NO_RANGE_CHECK because the params are allocated + separately in a DATA_BLOB, so we need to do our own range + checking */ + if (!str->s || offset >= trans->out.data.length) { + if (flags & STR_LEN8BIT) { + SCVAL(trans->out.data.data, len_offset, 0); + } else { + SIVAL(trans->out.data.data, len_offset, 0); + } + return 0; + } + + flags |= STR_NO_RANGE_CHECK; + + if (dest_len == -1 || (dest_len > trans->out.data.length - offset)) { + dest_len = trans->out.data.length - offset; + } + + if (!(flags & (STR_ASCII|STR_UNICODE))) { + flags |= (req->flags2 & FLAGS2_UNICODE_STRINGS) ? STR_UNICODE : STR_ASCII; + } + + if ((offset&1) && (flags & STR_UNICODE) && !(flags & STR_NOALIGN)) { + alignment = 1; + if (dest_len > 0) { + SCVAL(trans->out.data.data + offset, 0, 0); + ret = push_string(trans->out.data.data + offset + 1, str->s, dest_len-1, flags); + } + } else { + ret = push_string(trans->out.data.data + offset, str->s, dest_len, flags); + } + + /* sometimes the string needs to be terminated, but the length + on the wire must not include the termination! */ + pkt_len = ret; + + if ((flags & STR_LEN_NOTERM) && (flags & STR_TERMINATE)) { + if ((flags & STR_UNICODE) && ret >= 2) { + pkt_len = ret-2; + } + if ((flags & STR_ASCII) && ret >= 1) { + pkt_len = ret-1; + } + } + + if (flags & STR_LEN8BIT) { + SCVAL(trans->out.data.data, len_offset, pkt_len); + } else { + SIVAL(trans->out.data.data, len_offset, pkt_len); + } + + return ret + alignment; +} + +/* + append a string to the data section of a trans2 reply + len_offset points to the place in the packet where the length field + should go +*/ +static void trans2_append_data_string(struct smbsrv_request *req, + struct smb_trans2 *trans, + const WIRE_STRING *str, + uint_t len_offset, + int flags) +{ + size_t ret; + uint32_t offset; + const int max_bytes_per_char = 3; + + offset = trans->out.data.length; + trans2_grow_data(req, trans, offset + (2+strlen_m(str->s))*max_bytes_per_char); + ret = trans2_push_data_string(req, trans, len_offset, offset, str, -1, flags); + trans2_grow_data(req, trans, offset + ret); +} + +/* + align the end of the data section of a trans reply on an even boundary +*/ +static void trans2_align_data(struct smbsrv_request *req, struct smb_trans2 *trans) +{ + if ((trans->out.data.length & 1) == 0) { + return; + } + trans2_grow_data(req, trans, trans->out.data.length+1); + SCVAL(trans->out.data.data, trans->out.data.length-1, 0); +} + + +/* + trans2 qfsinfo implementation +*/ +static NTSTATUS trans2_qfsinfo(struct smbsrv_request *req, struct smb_trans2 *trans) +{ + union smb_fsinfo fsinfo; + NTSTATUS status; + uint16_t level; + uint_t i; + DATA_BLOB guid_blob; + + /* make sure we got enough parameters */ + if (trans->in.params.length != 2) { + return NT_STATUS_FOOBAR; + } + + level = SVAL(trans->in.params.data, 0); + + switch (level) { + case SMB_QFS_ALLOCATION: + fsinfo.allocation.level = RAW_QFS_ALLOCATION; + + status = ntvfs_fsinfo(req, &fsinfo); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + trans2_setup_reply(req, trans, 0, 18, 0); + + SIVAL(trans->out.data.data, 0, fsinfo.allocation.out.fs_id); + SIVAL(trans->out.data.data, 4, fsinfo.allocation.out.sectors_per_unit); + SIVAL(trans->out.data.data, 8, fsinfo.allocation.out.total_alloc_units); + SIVAL(trans->out.data.data, 12, fsinfo.allocation.out.avail_alloc_units); + SSVAL(trans->out.data.data, 16, fsinfo.allocation.out.bytes_per_sector); + + return NT_STATUS_OK; + + case SMB_QFS_VOLUME: + fsinfo.volume.level = RAW_QFS_VOLUME; + + status = ntvfs_fsinfo(req, &fsinfo); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + trans2_setup_reply(req, trans, 0, 5, 0); + + SIVAL(trans->out.data.data, 0, fsinfo.volume.out.serial_number); + /* w2k3 implements this incorrectly for unicode - it + * leaves the last byte off the string */ + trans2_append_data_string(req, trans, + &fsinfo.volume.out.volume_name, + 4, STR_LEN8BIT|STR_NOALIGN); + + return NT_STATUS_OK; + + case SMB_QFS_VOLUME_INFO: + case SMB_QFS_VOLUME_INFORMATION: + fsinfo.volume_info.level = RAW_QFS_VOLUME_INFO; + + status = ntvfs_fsinfo(req, &fsinfo); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + trans2_setup_reply(req, trans, 0, 18, 0); + + push_nttime(trans->out.data.data, 0, fsinfo.volume_info.out.create_time); + SIVAL(trans->out.data.data, 8, fsinfo.volume_info.out.serial_number); + SSVAL(trans->out.data.data, 16, 0); /* padding */ + trans2_append_data_string(req, trans, + &fsinfo.volume_info.out.volume_name, + 12, STR_UNICODE); + + return NT_STATUS_OK; + + case SMB_QFS_SIZE_INFO: + case SMB_QFS_SIZE_INFORMATION: + fsinfo.size_info.level = RAW_QFS_SIZE_INFO; + + status = ntvfs_fsinfo(req, &fsinfo); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + trans2_setup_reply(req, trans, 0, 24, 0); + + SBVAL(trans->out.data.data, 0, fsinfo.size_info.out.total_alloc_units); + SBVAL(trans->out.data.data, 8, fsinfo.size_info.out.avail_alloc_units); + SIVAL(trans->out.data.data, 16, fsinfo.size_info.out.sectors_per_unit); + SIVAL(trans->out.data.data, 20, fsinfo.size_info.out.bytes_per_sector); + + return NT_STATUS_OK; + + case SMB_QFS_DEVICE_INFO: + case SMB_QFS_DEVICE_INFORMATION: + fsinfo.device_info.level = RAW_QFS_DEVICE_INFO; + + status = ntvfs_fsinfo(req, &fsinfo); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + trans2_setup_reply(req, trans, 0, 8, 0); + SIVAL(trans->out.data.data, 0, fsinfo.device_info.out.device_type); + SIVAL(trans->out.data.data, 4, fsinfo.device_info.out.characteristics); + return NT_STATUS_OK; + + + case SMB_QFS_ATTRIBUTE_INFO: + case SMB_QFS_ATTRIBUTE_INFORMATION: + fsinfo.attribute_info.level = RAW_QFS_ATTRIBUTE_INFO; + + status = ntvfs_fsinfo(req, &fsinfo); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + trans2_setup_reply(req, trans, 0, 12, 0); + + SIVAL(trans->out.data.data, 0, fsinfo.attribute_info.out.fs_attr); + SIVAL(trans->out.data.data, 4, fsinfo.attribute_info.out.max_file_component_length); + /* this must not be null terminated or win98 gets + confused! also note that w2k3 returns this as + unicode even when ascii is negotiated */ + trans2_append_data_string(req, trans, + &fsinfo.attribute_info.out.fs_type, + 8, STR_UNICODE); + return NT_STATUS_OK; + + + case SMB_QFS_QUOTA_INFORMATION: + fsinfo.quota_information.level = RAW_QFS_QUOTA_INFORMATION; + + status = ntvfs_fsinfo(req, &fsinfo); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + trans2_setup_reply(req, trans, 0, 48, 0); + + SBVAL(trans->out.data.data, 0, fsinfo.quota_information.out.unknown[0]); + SBVAL(trans->out.data.data, 8, fsinfo.quota_information.out.unknown[1]); + SBVAL(trans->out.data.data, 16, fsinfo.quota_information.out.unknown[2]); + SBVAL(trans->out.data.data, 24, fsinfo.quota_information.out.quota_soft); + SBVAL(trans->out.data.data, 32, fsinfo.quota_information.out.quota_hard); + SBVAL(trans->out.data.data, 40, fsinfo.quota_information.out.quota_flags); + + return NT_STATUS_OK; + + + case SMB_QFS_FULL_SIZE_INFORMATION: + fsinfo.full_size_information.level = RAW_QFS_FULL_SIZE_INFORMATION; + + status = ntvfs_fsinfo(req, &fsinfo); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + trans2_setup_reply(req, trans, 0, 32, 0); + + SBVAL(trans->out.data.data, 0, fsinfo.full_size_information.out.total_alloc_units); + SBVAL(trans->out.data.data, 8, fsinfo.full_size_information.out.call_avail_alloc_units); + SBVAL(trans->out.data.data, 16, fsinfo.full_size_information.out.actual_avail_alloc_units); + SIVAL(trans->out.data.data, 24, fsinfo.full_size_information.out.sectors_per_unit); + SIVAL(trans->out.data.data, 28, fsinfo.full_size_information.out.bytes_per_sector); + + return NT_STATUS_OK; + + case SMB_QFS_OBJECTID_INFORMATION: + fsinfo.objectid_information.level = RAW_QFS_OBJECTID_INFORMATION; + + status = ntvfs_fsinfo(req, &fsinfo); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + trans2_setup_reply(req, trans, 0, 64, 0); + + status = ndr_push_struct_blob(&guid_blob, req, + &fsinfo.objectid_information.out.guid, + (ndr_push_flags_fn_t)ndr_push_GUID); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + memcpy(trans->out.data.data, guid_blob.data, guid_blob.length); + + for (i=0;i<6;i++) { + SBVAL(trans->out.data.data, 16 + 8*i, fsinfo.objectid_information.out.unknown[i]); + } + return NT_STATUS_OK; + } + + return NT_STATUS_INVALID_LEVEL; +} + + +/* + trans2 open implementation +*/ +static NTSTATUS trans2_open(struct smbsrv_request *req, struct smb_trans2 *trans) +{ + union smb_open *io; + NTSTATUS status; + + /* make sure we got enough parameters */ + if (trans->in.params.length < 29) { + return NT_STATUS_FOOBAR; + } + + io = talloc(req, union smb_open); + if (io == NULL) { + return NT_STATUS_NO_MEMORY; + } + + io->t2open.level = RAW_OPEN_T2OPEN; + io->t2open.in.flags = SVAL(trans->in.params.data, VWV(0)); + io->t2open.in.open_mode = SVAL(trans->in.params.data, VWV(1)); + io->t2open.in.search_attrs = SVAL(trans->in.params.data, VWV(2)); + io->t2open.in.file_attrs = SVAL(trans->in.params.data, VWV(3)); + io->t2open.in.write_time = srv_pull_dos_date(req->smb_conn, + trans->in.params.data + VWV(4));; + io->t2open.in.open_func = SVAL(trans->in.params.data, VWV(6)); + io->t2open.in.size = IVAL(trans->in.params.data, VWV(7)); + io->t2open.in.timeout = IVAL(trans->in.params.data, VWV(9)); + io->t2open.in.num_eas = 0; + io->t2open.in.eas = NULL; + + trans2_pull_blob_string(req, &trans->in.params, 28, &io->t2open.in.fname, 0); + + status = ea_pull_list(&trans->in.data, io, &io->t2open.in.num_eas, &io->t2open.in.eas); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + status = ntvfs_openfile(req, io); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + trans2_setup_reply(req, trans, 30, 0, 0); + + SSVAL(trans->out.params.data, VWV(0), io->t2open.out.fnum); + SSVAL(trans->out.params.data, VWV(1), io->t2open.out.attrib); + srv_push_dos_date3(req->smb_conn, trans->out.params.data, + VWV(2), io->t2open.out.write_time); + SIVAL(trans->out.params.data, VWV(4), io->t2open.out.size); + SSVAL(trans->out.params.data, VWV(6), io->t2open.out.access); + SSVAL(trans->out.params.data, VWV(7), io->t2open.out.ftype); + SSVAL(trans->out.params.data, VWV(8), io->t2open.out.devstate); + SSVAL(trans->out.params.data, VWV(9), io->t2open.out.action); + SIVAL(trans->out.params.data, VWV(10), 0); /* reserved */ + SSVAL(trans->out.params.data, VWV(12), 0); /* EaErrorOffset */ + SIVAL(trans->out.params.data, VWV(13), 0); /* EaLength */ + + return status; +} + + +/* + trans2 mkdir implementation +*/ +static NTSTATUS trans2_mkdir(struct smbsrv_request *req, struct smb_trans2 *trans) +{ + union smb_mkdir *io; + NTSTATUS status; + + /* make sure we got enough parameters */ + if (trans->in.params.length < 5) { + return NT_STATUS_FOOBAR; + } + + io = talloc(req, union smb_mkdir); + if (io == NULL) { + return NT_STATUS_NO_MEMORY; + } + + io->t2mkdir.level = RAW_MKDIR_T2MKDIR; + trans2_pull_blob_string(req, &trans->in.params, 4, &io->t2mkdir.in.path, 0); + + status = ea_pull_list(&trans->in.data, io, + &io->t2mkdir.in.num_eas, + &io->t2mkdir.in.eas); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + status = ntvfs_mkdir(req, io); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + trans2_setup_reply(req, trans, 2, 0, 0); + + SSVAL(trans->out.params.data, VWV(0), 0); + + return status; +} + +/* + fill in the reply from a qpathinfo or qfileinfo call +*/ +static NTSTATUS trans2_fileinfo_fill(struct smbsrv_request *req, struct smb_trans2 *trans, + union smb_fileinfo *st) +{ + uint_t i; + uint32_t list_size; + + switch (st->generic.level) { + case RAW_FILEINFO_GENERIC: + case RAW_FILEINFO_GETATTR: + case RAW_FILEINFO_GETATTRE: + case RAW_FILEINFO_SEC_DESC: + /* handled elsewhere */ + return NT_STATUS_INVALID_LEVEL; + + case RAW_FILEINFO_BASIC_INFO: + case RAW_FILEINFO_BASIC_INFORMATION: + trans2_setup_reply(req, trans, 2, 40, 0); + + SSVAL(trans->out.params.data, 0, 0); + push_nttime(trans->out.data.data, 0, st->basic_info.out.create_time); + push_nttime(trans->out.data.data, 8, st->basic_info.out.access_time); + push_nttime(trans->out.data.data, 16, st->basic_info.out.write_time); + push_nttime(trans->out.data.data, 24, st->basic_info.out.change_time); + SIVAL(trans->out.data.data, 32, st->basic_info.out.attrib); + SIVAL(trans->out.data.data, 36, 0); /* padding */ + return NT_STATUS_OK; + + case RAW_FILEINFO_STANDARD: + trans2_setup_reply(req, trans, 2, 22, 0); + + SSVAL(trans->out.params.data, 0, 0); + srv_push_dos_date2(req->smb_conn, trans->out.data.data, 0, st->standard.out.create_time); + srv_push_dos_date2(req->smb_conn, trans->out.data.data, 4, st->standard.out.access_time); + srv_push_dos_date2(req->smb_conn, trans->out.data.data, 8, st->standard.out.write_time); + SIVAL(trans->out.data.data, 12, st->standard.out.size); + SIVAL(trans->out.data.data, 16, st->standard.out.alloc_size); + SSVAL(trans->out.data.data, 20, st->standard.out.attrib); + return NT_STATUS_OK; + + case RAW_FILEINFO_EA_SIZE: + trans2_setup_reply(req, trans, 2, 26, 0); + + SSVAL(trans->out.params.data, 0, 0); + srv_push_dos_date2(req->smb_conn, trans->out.data.data, 0, st->ea_size.out.create_time); + srv_push_dos_date2(req->smb_conn, trans->out.data.data, 4, st->ea_size.out.access_time); + srv_push_dos_date2(req->smb_conn, trans->out.data.data, 8, st->ea_size.out.write_time); + SIVAL(trans->out.data.data, 12, st->ea_size.out.size); + SIVAL(trans->out.data.data, 16, st->ea_size.out.alloc_size); + SSVAL(trans->out.data.data, 20, st->ea_size.out.attrib); + SIVAL(trans->out.data.data, 22, st->ea_size.out.ea_size); + return NT_STATUS_OK; + + case RAW_FILEINFO_NETWORK_OPEN_INFORMATION: + trans2_setup_reply(req, trans, 2, 56, 0); + + SSVAL(trans->out.params.data, 0, 0); + push_nttime(trans->out.data.data, 0, st->network_open_information.out.create_time); + push_nttime(trans->out.data.data, 8, st->network_open_information.out.access_time); + push_nttime(trans->out.data.data, 16, st->network_open_information.out.write_time); + push_nttime(trans->out.data.data, 24, st->network_open_information.out.change_time); + SBVAL(trans->out.data.data, 32, st->network_open_information.out.alloc_size); + SBVAL(trans->out.data.data, 40, st->network_open_information.out.size); + SIVAL(trans->out.data.data, 48, st->network_open_information.out.attrib); + SIVAL(trans->out.data.data, 52, 0); /* padding */ + return NT_STATUS_OK; + + case RAW_FILEINFO_STANDARD_INFO: + case RAW_FILEINFO_STANDARD_INFORMATION: + trans2_setup_reply(req, trans, 2, 24, 0); + SSVAL(trans->out.params.data, 0, 0); + SBVAL(trans->out.data.data, 0, st->standard_info.out.alloc_size); + SBVAL(trans->out.data.data, 8, st->standard_info.out.size); + SIVAL(trans->out.data.data, 16, st->standard_info.out.nlink); + SCVAL(trans->out.data.data, 20, st->standard_info.out.delete_pending); + SCVAL(trans->out.data.data, 21, st->standard_info.out.directory); + SSVAL(trans->out.data.data, 22, 0); /* padding */ + return NT_STATUS_OK; + + case RAW_FILEINFO_ATTRIBUTE_TAG_INFORMATION: + trans2_setup_reply(req, trans, 2, 8, 0); + SSVAL(trans->out.params.data, 0, 0); + SIVAL(trans->out.data.data, 0, st->attribute_tag_information.out.attrib); + SIVAL(trans->out.data.data, 4, st->attribute_tag_information.out.reparse_tag); + return NT_STATUS_OK; + + case RAW_FILEINFO_EA_INFO: + case RAW_FILEINFO_EA_INFORMATION: + trans2_setup_reply(req, trans, 2, 4, 0); + SSVAL(trans->out.params.data, 0, 0); + SIVAL(trans->out.data.data, 0, st->ea_info.out.ea_size); + return NT_STATUS_OK; + + case RAW_FILEINFO_MODE_INFORMATION: + trans2_setup_reply(req, trans, 2, 4, 0); + SSVAL(trans->out.params.data, 0, 0); + SIVAL(trans->out.data.data, 0, st->mode_information.out.mode); + return NT_STATUS_OK; + + case RAW_FILEINFO_ALIGNMENT_INFORMATION: + trans2_setup_reply(req, trans, 2, 4, 0); + SSVAL(trans->out.params.data, 0, 0); + SIVAL(trans->out.data.data, 0, + st->alignment_information.out.alignment_requirement); + return NT_STATUS_OK; + + case RAW_FILEINFO_EA_LIST: + list_size = ea_list_size(st->ea_list.out.num_eas, + st->ea_list.out.eas); + trans2_setup_reply(req, trans, 2, list_size, 0); + SSVAL(trans->out.params.data, 0, 0); + ea_put_list(trans->out.data.data, + st->ea_list.out.num_eas, st->ea_list.out.eas); + return NT_STATUS_OK; + + case RAW_FILEINFO_ALL_EAS: + list_size = ea_list_size(st->all_eas.out.num_eas, + st->all_eas.out.eas); + trans2_setup_reply(req, trans, 2, list_size, 0); + SSVAL(trans->out.params.data, 0, 0); + ea_put_list(trans->out.data.data, + st->all_eas.out.num_eas, st->all_eas.out.eas); + return NT_STATUS_OK; + + case RAW_FILEINFO_ACCESS_INFORMATION: + trans2_setup_reply(req, trans, 2, 4, 0); + SSVAL(trans->out.params.data, 0, 0); + SIVAL(trans->out.data.data, 0, st->access_information.out.access_flags); + return NT_STATUS_OK; + + case RAW_FILEINFO_POSITION_INFORMATION: + trans2_setup_reply(req, trans, 2, 8, 0); + SSVAL(trans->out.params.data, 0, 0); + SBVAL(trans->out.data.data, 0, st->position_information.out.position); + return NT_STATUS_OK; + + case RAW_FILEINFO_COMPRESSION_INFO: + case RAW_FILEINFO_COMPRESSION_INFORMATION: + trans2_setup_reply(req, trans, 2, 16, 0); + SSVAL(trans->out.params.data, 0, 0); + SBVAL(trans->out.data.data, 0, st->compression_info.out.compressed_size); + SSVAL(trans->out.data.data, 8, st->compression_info.out.format); + SCVAL(trans->out.data.data, 10, st->compression_info.out.unit_shift); + SCVAL(trans->out.data.data, 11, st->compression_info.out.chunk_shift); + SCVAL(trans->out.data.data, 12, st->compression_info.out.cluster_shift); + SSVAL(trans->out.data.data, 13, 0); /* 3 bytes padding */ + SCVAL(trans->out.data.data, 15, 0); + return NT_STATUS_OK; + + case RAW_FILEINFO_IS_NAME_VALID: + trans2_setup_reply(req, trans, 2, 0, 0); + SSVAL(trans->out.params.data, 0, 0); + return NT_STATUS_OK; + + case RAW_FILEINFO_INTERNAL_INFORMATION: + trans2_setup_reply(req, trans, 2, 8, 0); + SSVAL(trans->out.params.data, 0, 0); + SBVAL(trans->out.data.data, 0, st->internal_information.out.file_id); + return NT_STATUS_OK; + + case RAW_FILEINFO_ALL_INFO: + case RAW_FILEINFO_ALL_INFORMATION: + trans2_setup_reply(req, trans, 2, 72, 0); + + SSVAL(trans->out.params.data, 0, 0); + push_nttime(trans->out.data.data, 0, st->all_info.out.create_time); + push_nttime(trans->out.data.data, 8, st->all_info.out.access_time); + push_nttime(trans->out.data.data, 16, st->all_info.out.write_time); + push_nttime(trans->out.data.data, 24, st->all_info.out.change_time); + SIVAL(trans->out.data.data, 32, st->all_info.out.attrib); + SIVAL(trans->out.data.data, 36, 0); + SBVAL(trans->out.data.data, 40, st->all_info.out.alloc_size); + SBVAL(trans->out.data.data, 48, st->all_info.out.size); + SIVAL(trans->out.data.data, 56, st->all_info.out.nlink); + SCVAL(trans->out.data.data, 60, st->all_info.out.delete_pending); + SCVAL(trans->out.data.data, 61, st->all_info.out.directory); + SSVAL(trans->out.data.data, 62, 0); /* padding */ + SIVAL(trans->out.data.data, 64, st->all_info.out.ea_size); + trans2_append_data_string(req, trans, &st->all_info.out.fname, + 68, STR_UNICODE); + return NT_STATUS_OK; + + case RAW_FILEINFO_NAME_INFO: + case RAW_FILEINFO_NAME_INFORMATION: + trans2_setup_reply(req, trans, 2, 4, 0); + SSVAL(trans->out.params.data, 0, 0); + trans2_append_data_string(req, trans, &st->name_info.out.fname, 0, STR_UNICODE); + return NT_STATUS_OK; + + case RAW_FILEINFO_ALT_NAME_INFO: + case RAW_FILEINFO_ALT_NAME_INFORMATION: + trans2_setup_reply(req, trans, 2, 4, 0); + SSVAL(trans->out.params.data, 0, 0); + trans2_append_data_string(req, trans, &st->alt_name_info.out.fname, 0, STR_UNICODE); + return NT_STATUS_OK; + + case RAW_FILEINFO_STREAM_INFO: + case RAW_FILEINFO_STREAM_INFORMATION: + trans2_setup_reply(req, trans, 2, 0, 0); + + SSVAL(trans->out.params.data, 0, 0); + + for (i=0;istream_info.out.num_streams;i++) { + uint32_t data_size = trans->out.data.length; + uint8_t *data; + + trans2_grow_data(req, trans, data_size + 24); + data = trans->out.data.data + data_size; + SBVAL(data, 8, st->stream_info.out.streams[i].size); + SBVAL(data, 16, st->stream_info.out.streams[i].alloc_size); + trans2_append_data_string(req, trans, + &st->stream_info.out.streams[i].stream_name, + data_size + 4, STR_UNICODE); + if (i == st->stream_info.out.num_streams - 1) { + SIVAL(trans->out.data.data, data_size, 0); + } else { + trans2_grow_data_fill(req, trans, (trans->out.data.length+7)&~7); + SIVAL(trans->out.data.data, data_size, + trans->out.data.length - data_size); + } + } + return NT_STATUS_OK; + + case RAW_FILEINFO_UNIX_BASIC: + case RAW_FILEINFO_UNIX_LINK: + return NT_STATUS_INVALID_LEVEL; + } + + return NT_STATUS_INVALID_LEVEL; +} + +/* + trans2 qpathinfo implementation +*/ +static NTSTATUS trans2_qpathinfo(struct smbsrv_request *req, struct smb_trans2 *trans) +{ + union smb_fileinfo st; + NTSTATUS status; + uint16_t level; + + /* make sure we got enough parameters */ + if (trans->in.params.length < 2) { + return NT_STATUS_FOOBAR; + } + + level = SVAL(trans->in.params.data, 0); + + trans2_pull_blob_string(req, &trans->in.params, 6, &st.generic.in.fname, 0); + if (st.generic.in.fname == NULL) { + return NT_STATUS_FOOBAR; + } + + /* work out the backend level - we make it 1-1 in the header */ + st.generic.level = (enum smb_fileinfo_level)level; + if (st.generic.level >= RAW_FILEINFO_GENERIC) { + return NT_STATUS_INVALID_LEVEL; + } + + if (st.generic.level == RAW_FILEINFO_EA_LIST) { + status = ea_pull_name_list(&trans->in.data, req, + &st.ea_list.in.num_names, + &st.ea_list.in.ea_names); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + } + + /* call the backend */ + status = ntvfs_qpathinfo(req, &st); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + /* fill in the reply parameters */ + status = trans2_fileinfo_fill(req, trans, &st); + + return status; +} + + +/* + trans2 qpathinfo implementation +*/ +static NTSTATUS trans2_qfileinfo(struct smbsrv_request *req, struct smb_trans2 *trans) +{ + union smb_fileinfo st; + NTSTATUS status; + uint16_t level; + + /* make sure we got enough parameters */ + if (trans->in.params.length < 4) { + return NT_STATUS_FOOBAR; + } + + st.generic.in.fnum = SVAL(trans->in.params.data, 0); + level = SVAL(trans->in.params.data, 2); + + /* work out the backend level - we make it 1-1 in the header */ + st.generic.level = (enum smb_fileinfo_level)level; + if (st.generic.level >= RAW_FILEINFO_GENERIC) { + return NT_STATUS_INVALID_LEVEL; + } + + if (st.generic.level == RAW_FILEINFO_EA_LIST) { + status = ea_pull_name_list(&trans->in.data, req, + &st.ea_list.in.num_names, + &st.ea_list.in.ea_names); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + } + + /* call the backend */ + status = ntvfs_qfileinfo(req, &st); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + /* fill in the reply parameters */ + status = trans2_fileinfo_fill(req, trans, &st); + + return status; +} + + +/* + parse a trans2 setfileinfo/setpathinfo data blob +*/ +static NTSTATUS trans2_parse_sfileinfo(struct smbsrv_request *req, + union smb_setfileinfo *st, + const DATA_BLOB *blob) +{ + uint32_t len; + + switch (st->generic.level) { + case RAW_SFILEINFO_GENERIC: + case RAW_SFILEINFO_SETATTR: + case RAW_SFILEINFO_SETATTRE: + case RAW_SFILEINFO_SEC_DESC: + /* handled elsewhere */ + return NT_STATUS_INVALID_LEVEL; + + case RAW_SFILEINFO_STANDARD: + CHECK_MIN_BLOB_SIZE(blob, 12); + st->standard.in.create_time = srv_pull_dos_date2(req->smb_conn, blob->data + 0); + st->standard.in.access_time = srv_pull_dos_date2(req->smb_conn, blob->data + 4); + st->standard.in.write_time = srv_pull_dos_date2(req->smb_conn, blob->data + 8); + return NT_STATUS_OK; + + case RAW_SFILEINFO_EA_SET: + return ea_pull_list(blob, req, + &st->ea_set.in.num_eas, + &st->ea_set.in.eas); + + case SMB_SFILEINFO_BASIC_INFO: + case SMB_SFILEINFO_BASIC_INFORMATION: + CHECK_MIN_BLOB_SIZE(blob, 36); + st->basic_info.in.create_time = pull_nttime(blob->data, 0); + st->basic_info.in.access_time = pull_nttime(blob->data, 8); + st->basic_info.in.write_time = pull_nttime(blob->data, 16); + st->basic_info.in.change_time = pull_nttime(blob->data, 24); + st->basic_info.in.attrib = IVAL(blob->data, 32); + return NT_STATUS_OK; + + case SMB_SFILEINFO_DISPOSITION_INFO: + case SMB_SFILEINFO_DISPOSITION_INFORMATION: + CHECK_MIN_BLOB_SIZE(blob, 1); + st->disposition_info.in.delete_on_close = CVAL(blob->data, 0); + return NT_STATUS_OK; + + case SMB_SFILEINFO_ALLOCATION_INFO: + case SMB_SFILEINFO_ALLOCATION_INFORMATION: + CHECK_MIN_BLOB_SIZE(blob, 8); + st->allocation_info.in.alloc_size = BVAL(blob->data, 0); + return NT_STATUS_OK; + + case RAW_SFILEINFO_END_OF_FILE_INFO: + case RAW_SFILEINFO_END_OF_FILE_INFORMATION: + CHECK_MIN_BLOB_SIZE(blob, 8); + st->end_of_file_info.in.size = BVAL(blob->data, 0); + return NT_STATUS_OK; + + case RAW_SFILEINFO_RENAME_INFORMATION: { + DATA_BLOB blob2; + + CHECK_MIN_BLOB_SIZE(blob, 12); + st->rename_information.in.overwrite = CVAL(blob->data, 0); + st->rename_information.in.root_fid = IVAL(blob->data, 4); + len = IVAL(blob->data, 8); + blob2.data = blob->data+12; + blob2.length = MIN(blob->length, len); + trans2_pull_blob_string(req, &blob2, 0, + &st->rename_information.in.new_name, STR_UNICODE); + return NT_STATUS_OK; + } + + case RAW_SFILEINFO_POSITION_INFORMATION: + CHECK_MIN_BLOB_SIZE(blob, 8); + st->position_information.in.position = BVAL(blob->data, 0); + return NT_STATUS_OK; + + case RAW_SFILEINFO_MODE_INFORMATION: + CHECK_MIN_BLOB_SIZE(blob, 4); + st->mode_information.in.mode = IVAL(blob->data, 0); + return NT_STATUS_OK; + + case RAW_SFILEINFO_UNIX_BASIC: + case RAW_SFILEINFO_UNIX_LINK: + case RAW_SFILEINFO_UNIX_HLINK: + case RAW_SFILEINFO_1023: + case RAW_SFILEINFO_1025: + case RAW_SFILEINFO_1029: + case RAW_SFILEINFO_1032: + case RAW_SFILEINFO_1039: + case RAW_SFILEINFO_1040: + return NT_STATUS_INVALID_LEVEL; + } + + return NT_STATUS_INVALID_LEVEL; +} + +/* + trans2 setfileinfo implementation +*/ +static NTSTATUS trans2_setfileinfo(struct smbsrv_request *req, struct smb_trans2 *trans) +{ + union smb_setfileinfo st; + NTSTATUS status; + uint16_t level, fnum; + DATA_BLOB *blob; + + /* make sure we got enough parameters */ + if (trans->in.params.length < 4) { + return NT_STATUS_FOOBAR; + } + + fnum = SVAL(trans->in.params.data, 0); + level = SVAL(trans->in.params.data, 2); + + blob = &trans->in.data; + + st.generic.file.fnum = fnum; + st.generic.level = (enum smb_setfileinfo_level)level; + + status = trans2_parse_sfileinfo(req, &st, blob); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + status = ntvfs_setfileinfo(req, &st); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + trans2_setup_reply(req, trans, 2, 0, 0); + SSVAL(trans->out.params.data, 0, 0); + return NT_STATUS_OK; +} + +/* + trans2 setpathinfo implementation +*/ +static NTSTATUS trans2_setpathinfo(struct smbsrv_request *req, struct smb_trans2 *trans) +{ + union smb_setfileinfo st; + NTSTATUS status; + uint16_t level; + DATA_BLOB *blob; + + /* make sure we got enough parameters */ + if (trans->in.params.length < 4) { + return NT_STATUS_FOOBAR; + } + + level = SVAL(trans->in.params.data, 0); + blob = &trans->in.data; + st.generic.level = (enum smb_setfileinfo_level)level; + + trans2_pull_blob_string(req, &trans->in.params, 6, &st.generic.file.fname, 0); + if (st.generic.file.fname == NULL) { + return NT_STATUS_FOOBAR; + } + + status = trans2_parse_sfileinfo(req, &st, blob); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + status = ntvfs_setpathinfo(req, &st); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + trans2_setup_reply(req, trans, 2, 0, 0); + SSVAL(trans->out.params.data, 0, 0); + return NT_STATUS_OK; +} + + +/* a structure to encapsulate the state information about an in-progress ffirst/fnext operation */ +struct find_state { + struct smbsrv_request *req; + struct smb_trans2 *trans; + enum smb_search_level level; + uint16_t last_entry_offset; + uint16_t flags; +}; + +/* + fill a single entry in a trans2 find reply +*/ +static BOOL find_fill_info(struct smbsrv_request *req, + struct smb_trans2 *trans, + struct find_state *state, + union smb_search_data *file) +{ + uint8_t *data; + uint_t ofs = trans->out.data.length; + uint32_t ea_size; + + switch (state->level) { + case RAW_SEARCH_SEARCH: + case RAW_SEARCH_FFIRST: + case RAW_SEARCH_FUNIQUE: + case RAW_SEARCH_GENERIC: + /* handled elsewhere */ + break; + + case RAW_SEARCH_STANDARD: + if (state->flags & FLAG_TRANS2_FIND_REQUIRE_RESUME) { + trans2_grow_data(req, trans, ofs + 27); + SIVAL(trans->out.data.data, ofs, file->standard.resume_key); + ofs += 4; + } else { + trans2_grow_data(req, trans, ofs + 23); + } + data = trans->out.data.data + ofs; + srv_push_dos_date2(req->smb_conn, data, 0, file->standard.create_time); + srv_push_dos_date2(req->smb_conn, data, 4, file->standard.access_time); + srv_push_dos_date2(req->smb_conn, data, 8, file->standard.write_time); + SIVAL(data, 12, file->standard.size); + SIVAL(data, 16, file->standard.alloc_size); + SSVAL(data, 20, file->standard.attrib); + trans2_append_data_string(req, trans, &file->standard.name, + ofs + 22, STR_LEN8BIT | STR_TERMINATE | STR_LEN_NOTERM); + break; + + case RAW_SEARCH_EA_SIZE: + if (state->flags & FLAG_TRANS2_FIND_REQUIRE_RESUME) { + trans2_grow_data(req, trans, ofs + 31); + SIVAL(trans->out.data.data, ofs, file->ea_size.resume_key); + ofs += 4; + } else { + trans2_grow_data(req, trans, ofs + 27); + } + data = trans->out.data.data + ofs; + srv_push_dos_date2(req->smb_conn, data, 0, file->ea_size.create_time); + srv_push_dos_date2(req->smb_conn, data, 4, file->ea_size.access_time); + srv_push_dos_date2(req->smb_conn, data, 8, file->ea_size.write_time); + SIVAL(data, 12, file->ea_size.size); + SIVAL(data, 16, file->ea_size.alloc_size); + SSVAL(data, 20, file->ea_size.attrib); + SIVAL(data, 22, file->ea_size.ea_size); + trans2_append_data_string(req, trans, &file->ea_size.name, + ofs + 26, STR_LEN8BIT | STR_NOALIGN); + trans2_grow_data(req, trans, trans->out.data.length + 1); + trans->out.data.data[trans->out.data.length-1] = 0; + break; + + case RAW_SEARCH_EA_LIST: + ea_size = ea_list_size(file->ea_list.eas.num_eas, file->ea_list.eas.eas); + if (state->flags & FLAG_TRANS2_FIND_REQUIRE_RESUME) { + if (!trans2_grow_data(req, trans, ofs + 27 + ea_size)) { + return False; + } + SIVAL(trans->out.data.data, ofs, file->ea_list.resume_key); + ofs += 4; + } else { + if (!trans2_grow_data(req, trans, ofs + 23 + ea_size)) { + return False; + } + } + data = trans->out.data.data + ofs; + srv_push_dos_date2(req->smb_conn, data, 0, file->ea_list.create_time); + srv_push_dos_date2(req->smb_conn, data, 4, file->ea_list.access_time); + srv_push_dos_date2(req->smb_conn, data, 8, file->ea_list.write_time); + SIVAL(data, 12, file->ea_list.size); + SIVAL(data, 16, file->ea_list.alloc_size); + SSVAL(data, 20, file->ea_list.attrib); + ea_put_list(data+22, file->ea_list.eas.num_eas, file->ea_list.eas.eas); + trans2_append_data_string(req, trans, &file->ea_list.name, + ofs + 22 + ea_size, STR_LEN8BIT | STR_NOALIGN); + trans2_grow_data(req, trans, trans->out.data.length + 1); + trans->out.data.data[trans->out.data.length-1] = 0; + break; + + case RAW_SEARCH_DIRECTORY_INFO: + trans2_grow_data(req, trans, ofs + 64); + data = trans->out.data.data + ofs; + SIVAL(data, 4, file->directory_info.file_index); + push_nttime(data, 8, file->directory_info.create_time); + push_nttime(data, 16, file->directory_info.access_time); + push_nttime(data, 24, file->directory_info.write_time); + push_nttime(data, 32, file->directory_info.change_time); + SBVAL(data, 40, file->directory_info.size); + SBVAL(data, 48, file->directory_info.alloc_size); + SIVAL(data, 56, file->directory_info.attrib); + trans2_append_data_string(req, trans, &file->directory_info.name, + ofs + 60, STR_TERMINATE_ASCII); + data = trans->out.data.data + ofs; + SIVAL(data, 0, trans->out.data.length - ofs); + break; + + case RAW_SEARCH_FULL_DIRECTORY_INFO: + trans2_grow_data(req, trans, ofs + 68); + data = trans->out.data.data + ofs; + SIVAL(data, 4, file->full_directory_info.file_index); + push_nttime(data, 8, file->full_directory_info.create_time); + push_nttime(data, 16, file->full_directory_info.access_time); + push_nttime(data, 24, file->full_directory_info.write_time); + push_nttime(data, 32, file->full_directory_info.change_time); + SBVAL(data, 40, file->full_directory_info.size); + SBVAL(data, 48, file->full_directory_info.alloc_size); + SIVAL(data, 56, file->full_directory_info.attrib); + SIVAL(data, 64, file->full_directory_info.ea_size); + trans2_append_data_string(req, trans, &file->full_directory_info.name, + ofs + 60, STR_TERMINATE_ASCII); + data = trans->out.data.data + ofs; + SIVAL(data, 0, trans->out.data.length - ofs); + break; + + case RAW_SEARCH_NAME_INFO: + trans2_grow_data(req, trans, ofs + 12); + data = trans->out.data.data + ofs; + SIVAL(data, 4, file->name_info.file_index); + trans2_append_data_string(req, trans, &file->name_info.name, + ofs + 8, STR_TERMINATE_ASCII); + data = trans->out.data.data + ofs; + SIVAL(data, 0, trans->out.data.length - ofs); + break; + + case RAW_SEARCH_BOTH_DIRECTORY_INFO: + trans2_grow_data(req, trans, ofs + 94); + data = trans->out.data.data + ofs; + SIVAL(data, 4, file->both_directory_info.file_index); + push_nttime(data, 8, file->both_directory_info.create_time); + push_nttime(data, 16, file->both_directory_info.access_time); + push_nttime(data, 24, file->both_directory_info.write_time); + push_nttime(data, 32, file->both_directory_info.change_time); + SBVAL(data, 40, file->both_directory_info.size); + SBVAL(data, 48, file->both_directory_info.alloc_size); + SIVAL(data, 56, file->both_directory_info.attrib); + SIVAL(data, 64, file->both_directory_info.ea_size); + SCVAL(data, 69, 0); /* reserved */ + memset(data+70,0,24); + trans2_push_data_string(req, trans, + 68 + ofs, 70 + ofs, + &file->both_directory_info.short_name, + 24, STR_UNICODE | STR_LEN8BIT); + trans2_append_data_string(req, trans, &file->both_directory_info.name, + ofs + 60, STR_TERMINATE_ASCII); + trans2_align_data(req, trans); + data = trans->out.data.data + ofs; + SIVAL(data, 0, trans->out.data.length - ofs); + break; + + case RAW_SEARCH_ID_FULL_DIRECTORY_INFO: + trans2_grow_data(req, trans, ofs + 80); + data = trans->out.data.data + ofs; + SIVAL(data, 4, file->id_full_directory_info.file_index); + push_nttime(data, 8, file->id_full_directory_info.create_time); + push_nttime(data, 16, file->id_full_directory_info.access_time); + push_nttime(data, 24, file->id_full_directory_info.write_time); + push_nttime(data, 32, file->id_full_directory_info.change_time); + SBVAL(data, 40, file->id_full_directory_info.size); + SBVAL(data, 48, file->id_full_directory_info.alloc_size); + SIVAL(data, 56, file->id_full_directory_info.attrib); + SIVAL(data, 64, file->id_full_directory_info.ea_size); + SIVAL(data, 68, 0); /* padding */ + SBVAL(data, 72, file->id_full_directory_info.file_id); + trans2_append_data_string(req, trans, &file->id_full_directory_info.name, + ofs + 60, STR_TERMINATE_ASCII); + data = trans->out.data.data + ofs; + SIVAL(data, 0, trans->out.data.length - ofs); + break; + + case RAW_SEARCH_ID_BOTH_DIRECTORY_INFO: + trans2_grow_data(req, trans, ofs + 104); + data = trans->out.data.data + ofs; + SIVAL(data, 4, file->id_both_directory_info.file_index); + push_nttime(data, 8, file->id_both_directory_info.create_time); + push_nttime(data, 16, file->id_both_directory_info.access_time); + push_nttime(data, 24, file->id_both_directory_info.write_time); + push_nttime(data, 32, file->id_both_directory_info.change_time); + SBVAL(data, 40, file->id_both_directory_info.size); + SBVAL(data, 48, file->id_both_directory_info.alloc_size); + SIVAL(data, 56, file->id_both_directory_info.attrib); + SIVAL(data, 64, file->id_both_directory_info.ea_size); + SCVAL(data, 69, 0); /* reserved */ + memset(data+70,0,26); + trans2_push_data_string(req, trans, + 68 + ofs, 70 + ofs, + &file->id_both_directory_info.short_name, + 24, STR_UNICODE | STR_LEN8BIT); + SBVAL(data, 96, file->id_both_directory_info.file_id); + trans2_append_data_string(req, trans, &file->id_both_directory_info.name, + ofs + 60, STR_TERMINATE_ASCII); + data = trans->out.data.data + ofs; + SIVAL(data, 0, trans->out.data.length - ofs); + break; + } + + return True; +} + +/* callback function for trans2 findfirst/findnext */ +static BOOL find_callback(void *private, union smb_search_data *file) +{ + struct find_state *state = (struct find_state *)private; + struct smb_trans2 *trans = state->trans; + uint_t old_length; + + old_length = trans->out.data.length; + + if (!find_fill_info(state->req, trans, state, file) || + trans->out.data.length > trans->in.max_data) { + /* restore the old length and tell the backend to stop */ + trans2_grow_data(state->req, trans, old_length); + return False; + } + + state->last_entry_offset = old_length; + return True; +} + + +/* + trans2 findfirst implementation +*/ +static NTSTATUS trans2_findfirst(struct smbsrv_request *req, struct smb_trans2 *trans) +{ + union smb_search_first search; + NTSTATUS status; + uint16_t level; + uint8_t *param; + struct find_state state; + + /* make sure we got all the parameters */ + if (trans->in.params.length < 14) { + return NT_STATUS_FOOBAR; + } + + search.t2ffirst.in.search_attrib = SVAL(trans->in.params.data, 0); + search.t2ffirst.in.max_count = SVAL(trans->in.params.data, 2); + search.t2ffirst.in.flags = SVAL(trans->in.params.data, 4); + level = SVAL(trans->in.params.data, 6); + search.t2ffirst.in.storage_type = IVAL(trans->in.params.data, 8); + + trans2_pull_blob_string(req, &trans->in.params, 12, &search.t2ffirst.in.pattern, 0); + if (search.t2ffirst.in.pattern == NULL) { + return NT_STATUS_FOOBAR; + } + + search.t2ffirst.level = (enum smb_search_level)level; + if (search.t2ffirst.level >= RAW_SEARCH_GENERIC) { + return NT_STATUS_INVALID_LEVEL; + } + + if (search.t2ffirst.level == RAW_SEARCH_EA_LIST) { + status = ea_pull_name_list(&trans->in.data, req, + &search.t2ffirst.in.num_names, + &search.t2ffirst.in.ea_names); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + } + + /* setup the private state structure that the backend will + give us in the callback */ + state.req = req; + state.trans = trans; + state.level = search.t2ffirst.level; + state.last_entry_offset = 0; + state.flags = search.t2ffirst.in.flags; + + /* setup for just a header in the reply */ + trans2_setup_reply(req, trans, 10, 0, 0); + + /* call the backend */ + status = ntvfs_search_first(req, &search, &state, find_callback); + if (!NT_STATUS_IS_OK(status)) { + trans2_setup_reply(req, trans, 0, 0, 0); + return status; + } + + /* fill in the findfirst reply header */ + param = trans->out.params.data; + SSVAL(param, VWV(0), search.t2ffirst.out.handle); + SSVAL(param, VWV(1), search.t2ffirst.out.count); + SSVAL(param, VWV(2), search.t2ffirst.out.end_of_search); + SSVAL(param, VWV(3), 0); + SSVAL(param, VWV(4), state.last_entry_offset); + + return NT_STATUS_OK; +} + + +/* + trans2 findnext implementation +*/ +static NTSTATUS trans2_findnext(struct smbsrv_request *req, struct smb_trans2 *trans) +{ + union smb_search_next search; + NTSTATUS status; + uint16_t level; + uint8_t *param; + struct find_state state; + + /* make sure we got all the parameters */ + if (trans->in.params.length < 12) { + return NT_STATUS_FOOBAR; + } + + search.t2fnext.in.handle = SVAL(trans->in.params.data, 0); + search.t2fnext.in.max_count = SVAL(trans->in.params.data, 2); + level = SVAL(trans->in.params.data, 4); + search.t2fnext.in.resume_key = IVAL(trans->in.params.data, 6); + search.t2fnext.in.flags = SVAL(trans->in.params.data, 10); + + trans2_pull_blob_string(req, &trans->in.params, 12, &search.t2fnext.in.last_name, 0); + if (search.t2fnext.in.last_name == NULL) { + return NT_STATUS_FOOBAR; + } + + search.t2fnext.level = (enum smb_search_level)level; + if (search.t2fnext.level >= RAW_SEARCH_GENERIC) { + return NT_STATUS_INVALID_LEVEL; + } + + if (search.t2fnext.level == RAW_SEARCH_EA_LIST) { + status = ea_pull_name_list(&trans->in.data, req, + &search.t2fnext.in.num_names, + &search.t2fnext.in.ea_names); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + } + + /* setup the private state structure that the backend will give us in the callback */ + state.req = req; + state.trans = trans; + state.level = search.t2fnext.level; + state.last_entry_offset = 0; + state.flags = search.t2fnext.in.flags; + + /* setup for just a header in the reply */ + trans2_setup_reply(req, trans, 8, 0, 0); + + /* call the backend */ + status = ntvfs_search_next(req, &search, &state, find_callback); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + /* fill in the findfirst reply header */ + param = trans->out.params.data; + SSVAL(param, VWV(0), search.t2fnext.out.count); + SSVAL(param, VWV(1), search.t2fnext.out.end_of_search); + SSVAL(param, VWV(2), 0); + SSVAL(param, VWV(3), state.last_entry_offset); + + return NT_STATUS_OK; +} + + +/* + backend for trans2 requests +*/ +static NTSTATUS trans2_backend(struct smbsrv_request *req, struct smb_trans2 *trans) +{ + NTSTATUS status; + + /* direct trans2 pass thru */ + status = ntvfs_trans2(req, trans); + if (!NT_STATUS_EQUAL(NT_STATUS_NOT_IMPLEMENTED, status)) { + return status; + } + + /* must have at least one setup word */ + if (trans->in.setup_count < 1) { + return NT_STATUS_FOOBAR; + } + + /* the trans2 command is in setup[0] */ + switch (trans->in.setup[0]) { + case TRANSACT2_FINDFIRST: + return trans2_findfirst(req, trans); + case TRANSACT2_FINDNEXT: + return trans2_findnext(req, trans); + case TRANSACT2_QPATHINFO: + return trans2_qpathinfo(req, trans); + case TRANSACT2_QFILEINFO: + return trans2_qfileinfo(req, trans); + case TRANSACT2_SETFILEINFO: + return trans2_setfileinfo(req, trans); + case TRANSACT2_SETPATHINFO: + return trans2_setpathinfo(req, trans); + case TRANSACT2_QFSINFO: + return trans2_qfsinfo(req, trans); + case TRANSACT2_OPEN: + return trans2_open(req, trans); + case TRANSACT2_MKDIR: + return trans2_mkdir(req, trans); + } + + /* an unknown trans2 command */ + return NT_STATUS_FOOBAR; +} + + +/* + send a continue request +*/ +static void reply_trans_continue(struct smbsrv_request *req, uint8_t command, + struct smb_trans2 *trans) +{ + struct smbsrv_trans_partial *tp; + int count; + + /* make sure they don't flood us */ + for (count=0,tp=req->smb_conn->trans_partial;tp;tp=tp->next) count++; + if (count > 100) { + req_reply_error(req, NT_STATUS_INSUFFICIENT_RESOURCES); + return; + } + + tp = talloc(req, struct smbsrv_trans_partial); + + tp->req = talloc_reference(tp, req); + tp->trans = trans; + tp->command = command; + + DLIST_ADD(req->smb_conn->trans_partial, tp); + + /* send a 'please continue' reply */ + req_setup_reply(req, 0, 0); + req_send_reply(req); +} + + +/* + answer a reconstructed trans request +*/ +static void reply_trans_complete(struct smbsrv_request *req, uint8_t command, + struct smb_trans2 *trans) +{ + uint16_t params_left, data_left; + uint8_t *params, *data; + NTSTATUS status; + int i; + + /* its a full request, give it to the backend */ + if (command == SMBtrans) { + status = ntvfs_trans(req, trans); + } else { + status = trans2_backend(req, trans); + } + + if (NT_STATUS_IS_ERR(status)) { + req_reply_error(req, status); + return; + } + + params_left = trans->out.params.length; + data_left = trans->out.data.length; + params = trans->out.params.data; + data = trans->out.data.data; + + req_setup_reply(req, 10 + trans->out.setup_count, 0); + + if (!NT_STATUS_IS_OK(status)) { + req_setup_error(req, status); + } + + /* 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; + + 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 = req_setup_secondary(req); + } else { + this_req = req; + } + + req_grow_data(this_req, this_param + this_data + (align1 + align2)); + + SSVAL(this_req->out.vwv, VWV(0), trans->out.params.length); + SSVAL(this_req->out.vwv, VWV(1), trans->out.data.length); + SSVAL(this_req->out.vwv, VWV(2), 0); + + SSVAL(this_req->out.vwv, VWV(3), this_param); + SSVAL(this_req->out.vwv, VWV(4), align1 + PTR_DIFF(this_req->out.data, this_req->out.hdr)); + SSVAL(this_req->out.vwv, VWV(5), PTR_DIFF(params, trans->out.params.data)); + + SSVAL(this_req->out.vwv, VWV(6), this_data); + SSVAL(this_req->out.vwv, VWV(7), align1 + align2 + + PTR_DIFF(this_req->out.data + this_param, this_req->out.hdr)); + SSVAL(this_req->out.vwv, VWV(8), PTR_DIFF(data, trans->out.data.data)); + + SSVAL(this_req->out.vwv, VWV(9), trans->out.setup_count); + for (i=0;iout.setup_count;i++) { + SSVAL(this_req->out.vwv, VWV(10+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; + + req_send_reply(this_req); + } while (params_left != 0 || data_left != 0); +} + + +/* + Reply to an SMBtrans or SMBtrans2 request +*/ +void reply_trans_generic(struct smbsrv_request *req, uint8_t command) +{ + struct smb_trans2 *trans; + int i; + uint16_t param_ofs, data_ofs; + uint16_t param_count, data_count; + uint16_t param_total, data_total; + + trans = talloc(req, struct smb_trans2); + if (trans == NULL) { + req_reply_error(req, NT_STATUS_NO_MEMORY); + return; + } + + /* parse request */ + if (req->in.wct < 14) { + req_reply_error(req, NT_STATUS_INVALID_PARAMETER); + return; + } + + param_total = SVAL(req->in.vwv, VWV(0)); + data_total = SVAL(req->in.vwv, VWV(1)); + trans->in.max_param = SVAL(req->in.vwv, VWV(2)); + trans->in.max_data = SVAL(req->in.vwv, VWV(3)); + trans->in.max_setup = CVAL(req->in.vwv, VWV(4)); + trans->in.flags = SVAL(req->in.vwv, VWV(5)); + trans->in.timeout = IVAL(req->in.vwv, VWV(6)); + param_count = SVAL(req->in.vwv, VWV(9)); + param_ofs = SVAL(req->in.vwv, VWV(10)); + data_count = SVAL(req->in.vwv, VWV(11)); + data_ofs = SVAL(req->in.vwv, VWV(12)); + trans->in.setup_count = CVAL(req->in.vwv, VWV(13)); + + if (req->in.wct != 14 + trans->in.setup_count) { + req_reply_dos_error(req, ERRSRV, ERRerror); + return; + } + + /* parse out the setup words */ + trans->in.setup = talloc_array(req, uint16_t, trans->in.setup_count); + if (trans->in.setup_count && !trans->in.setup) { + req_reply_error(req, NT_STATUS_NO_MEMORY); + return; + } + for (i=0;iin.setup_count;i++) { + trans->in.setup[i] = SVAL(req->in.vwv, VWV(14+i)); + } + + if (command == SMBtrans) { + req_pull_string(req, &trans->in.trans_name, req->in.data, -1, STR_TERMINATE); + } + + 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)) { + req_reply_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) { + reply_trans_continue(req, command, trans); + return; + } + + reply_trans_complete(req, command, trans); +} + + +/* + Reply to an SMBtranss2 request +*/ +static void reply_transs_generic(struct smbsrv_request *req, uint8_t command) +{ + struct smbsrv_trans_partial *tp; + struct smb_trans2 *trans = NULL; + uint16_t param_ofs, data_ofs; + uint16_t param_count, data_count; + uint16_t param_disp, data_disp; + uint16_t param_total, data_total; + DATA_BLOB params, data; + + for (tp=req->smb_conn->trans_partial;tp;tp=tp->next) { + if (tp->command == command && + SVAL(tp->req->in.hdr, HDR_MID) == SVAL(req->in.hdr, HDR_MID)) { + break; + } + } + + if (tp == NULL) { + req_reply_error(req, NT_STATUS_INVALID_PARAMETER); + return; + } + + trans = tp->trans; + + /* parse request */ + if (req->in.wct < 8) { + req_reply_error(req, NT_STATUS_INVALID_PARAMETER); + return; + } + + param_total = SVAL(req->in.vwv, VWV(0)); + data_total = SVAL(req->in.vwv, VWV(1)); + param_count = SVAL(req->in.vwv, VWV(2)); + param_ofs = SVAL(req->in.vwv, VWV(3)); + param_disp = SVAL(req->in.vwv, VWV(4)); + data_count = SVAL(req->in.vwv, VWV(5)); + data_ofs = SVAL(req->in.vwv, VWV(6)); + data_disp = SVAL(req->in.vwv, VWV(7)); + + if (!req_pull_blob(req, req->in.hdr + param_ofs, param_count, ¶ms) || + !req_pull_blob(req, req->in.hdr + data_ofs, data_count, &data)) { + req_reply_error(req, NT_STATUS_INVALID_PARAMETER); + return; + } + + /* only allow contiguous requests */ + if ((param_count != 0 && + param_disp != trans->in.params.length) || + (data_count != 0 && + data_disp != trans->in.data.length)) { + req_reply_error(req, NT_STATUS_INVALID_PARAMETER); + return; + } + + /* add to the existing request */ + if (param_count != 0) { + trans->in.params.data = talloc_realloc(trans, + trans->in.params.data, + uint8_t, + param_disp + param_count); + if (trans->in.params.data == NULL) { + goto failed; + } + trans->in.params.length = param_disp + param_count; + } + + if (data_count != 0) { + trans->in.data.data = talloc_realloc(trans, + trans->in.data.data, + uint8_t, + data_disp + data_count); + if (trans->in.data.data == NULL) { + goto failed; + } + trans->in.data.length = data_disp + data_count; + } + + memcpy(trans->in.params.data + param_disp, params.data, params.length); + memcpy(trans->in.data.data + data_disp, data.data, data.length); + + /* the sequence number of the reply is taken from the last secondary + response */ + tp->req->seq_num = req->seq_num; + + /* we don't reply to Transs2 requests */ + talloc_free(req); + + if (trans->in.params.length == param_total && + trans->in.data.length == data_total) { + /* its now complete */ + reply_trans_complete(tp->req, command, trans); + DLIST_REMOVE(tp->req->smb_conn->trans_partial, tp); + talloc_free(tp); + } + return; + +failed: + req_reply_error(tp->req, NT_STATUS_NO_MEMORY); + DLIST_REMOVE(req->smb_conn->trans_partial, tp); + talloc_free(req); + talloc_free(tp); +} + + +/* + Reply to an SMBtrans2 +*/ +void reply_trans2(struct smbsrv_request *req) +{ + reply_trans_generic(req, SMBtrans2); +} + +/* + Reply to an SMBtrans +*/ +void reply_trans(struct smbsrv_request *req) +{ + reply_trans_generic(req, SMBtrans); +} + +/* + Reply to an SMBtranss request +*/ +void reply_transs(struct smbsrv_request *req) +{ + reply_transs_generic(req, SMBtrans); +} + +/* + Reply to an SMBtranss2 request +*/ +void reply_transs2(struct smbsrv_request *req) +{ + reply_transs_generic(req, SMBtrans2); +} + diff --git a/source4/smb_server/srvtime.c b/source4/smb_server/srvtime.c deleted file mode 100644 index faeda6faf6..0000000000 --- a/source4/smb_server/srvtime.c +++ /dev/null @@ -1,83 +0,0 @@ -/* - 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); -} diff --git a/source4/smb_server/trans2.c b/source4/smb_server/trans2.c deleted file mode 100644 index c9a357523b..0000000000 --- a/source4/smb_server/trans2.c +++ /dev/null @@ -1,1779 +0,0 @@ -/* - Unix SMB/CIFS implementation. - transaction2 handling - 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 handles the parsing of transact2 requests -*/ - -#include "includes.h" -#include "dlinklist.h" -#include "smb_server/smb_server.h" -#include "librpc/gen_ndr/ndr_misc.h" - -#define CHECK_MIN_BLOB_SIZE(blob, size) do { \ - if ((blob)->length < (size)) { \ - return NT_STATUS_INFO_LENGTH_MISMATCH; \ - }} while (0) - -/* grow the data allocation size of a trans2 reply - this guarantees - that requests to grow the data size later will not change the - pointer */ -static BOOL trans2_grow_data_allocation(struct smbsrv_request *req, - struct smb_trans2 *trans, - uint32_t new_size) -{ - if (new_size <= trans->out.data.length) { - return True; - } - trans->out.data.data = talloc_realloc(req, trans->out.data.data, - uint8_t, new_size); - return (trans->out.data.data != NULL); -} - - -/* grow the data size of a trans2 reply */ -static BOOL trans2_grow_data(struct smbsrv_request *req, - struct smb_trans2 *trans, - uint32_t new_size) -{ - if (!trans2_grow_data_allocation(req, trans, new_size)) { - return False; - } - trans->out.data.length = new_size; - return True; -} - -/* grow the data, zero filling any new bytes */ -static BOOL trans2_grow_data_fill(struct smbsrv_request *req, - struct smb_trans2 *trans, - uint32_t new_size) -{ - uint32_t old_size = trans->out.data.length; - if (!trans2_grow_data(req, trans, new_size)) { - return False; - } - if (new_size > old_size) { - memset(trans->out.data.data + old_size, 0, new_size - old_size); - } - return True; -} - - -/* setup a trans2 reply, given the data and params sizes */ -static void trans2_setup_reply(struct smbsrv_request *req, - struct smb_trans2 *trans, - uint16_t param_size, uint16_t data_size, - uint16_t setup_count) -{ - trans->out.setup_count = setup_count; - if (setup_count != 0) { - trans->out.setup = talloc_zero_array(req, uint16_t, setup_count); - } - trans->out.params = data_blob_talloc(req, NULL, param_size); - trans->out.data = data_blob_talloc(req, NULL, data_size); -} - - -/* - pull a string from a blob in a trans2 request -*/ -static size_t trans2_pull_blob_string(struct smbsrv_request *req, - const DATA_BLOB *blob, - uint16_t offset, - const char **str, - int flags) -{ - /* we use STR_NO_RANGE_CHECK because the params are allocated - separately in a DATA_BLOB, so we need to do our own range - checking */ - if (offset >= blob->length) { - *str = NULL; - return 0; - } - - return req_pull_string(req, str, - blob->data + offset, - blob->length - offset, - STR_NO_RANGE_CHECK | flags); -} - -/* - push a string into the data section of a trans2 request - return the number of bytes consumed in the output -*/ -static size_t trans2_push_data_string(struct smbsrv_request *req, - struct smb_trans2 *trans, - uint32_t len_offset, - uint32_t offset, - const WIRE_STRING *str, - int dest_len, - int flags) -{ - int alignment = 0, ret = 0, pkt_len; - - /* we use STR_NO_RANGE_CHECK because the params are allocated - separately in a DATA_BLOB, so we need to do our own range - checking */ - if (!str->s || offset >= trans->out.data.length) { - if (flags & STR_LEN8BIT) { - SCVAL(trans->out.data.data, len_offset, 0); - } else { - SIVAL(trans->out.data.data, len_offset, 0); - } - return 0; - } - - flags |= STR_NO_RANGE_CHECK; - - if (dest_len == -1 || (dest_len > trans->out.data.length - offset)) { - dest_len = trans->out.data.length - offset; - } - - if (!(flags & (STR_ASCII|STR_UNICODE))) { - flags |= (req->flags2 & FLAGS2_UNICODE_STRINGS) ? STR_UNICODE : STR_ASCII; - } - - if ((offset&1) && (flags & STR_UNICODE) && !(flags & STR_NOALIGN)) { - alignment = 1; - if (dest_len > 0) { - SCVAL(trans->out.data.data + offset, 0, 0); - ret = push_string(trans->out.data.data + offset + 1, str->s, dest_len-1, flags); - } - } else { - ret = push_string(trans->out.data.data + offset, str->s, dest_len, flags); - } - - /* sometimes the string needs to be terminated, but the length - on the wire must not include the termination! */ - pkt_len = ret; - - if ((flags & STR_LEN_NOTERM) && (flags & STR_TERMINATE)) { - if ((flags & STR_UNICODE) && ret >= 2) { - pkt_len = ret-2; - } - if ((flags & STR_ASCII) && ret >= 1) { - pkt_len = ret-1; - } - } - - if (flags & STR_LEN8BIT) { - SCVAL(trans->out.data.data, len_offset, pkt_len); - } else { - SIVAL(trans->out.data.data, len_offset, pkt_len); - } - - return ret + alignment; -} - -/* - append a string to the data section of a trans2 reply - len_offset points to the place in the packet where the length field - should go -*/ -static void trans2_append_data_string(struct smbsrv_request *req, - struct smb_trans2 *trans, - const WIRE_STRING *str, - uint_t len_offset, - int flags) -{ - size_t ret; - uint32_t offset; - const int max_bytes_per_char = 3; - - offset = trans->out.data.length; - trans2_grow_data(req, trans, offset + (2+strlen_m(str->s))*max_bytes_per_char); - ret = trans2_push_data_string(req, trans, len_offset, offset, str, -1, flags); - trans2_grow_data(req, trans, offset + ret); -} - -/* - align the end of the data section of a trans reply on an even boundary -*/ -static void trans2_align_data(struct smbsrv_request *req, struct smb_trans2 *trans) -{ - if ((trans->out.data.length & 1) == 0) { - return; - } - trans2_grow_data(req, trans, trans->out.data.length+1); - SCVAL(trans->out.data.data, trans->out.data.length-1, 0); -} - - -/* - trans2 qfsinfo implementation -*/ -static NTSTATUS trans2_qfsinfo(struct smbsrv_request *req, struct smb_trans2 *trans) -{ - union smb_fsinfo fsinfo; - NTSTATUS status; - uint16_t level; - uint_t i; - DATA_BLOB guid_blob; - - /* make sure we got enough parameters */ - if (trans->in.params.length != 2) { - return NT_STATUS_FOOBAR; - } - - level = SVAL(trans->in.params.data, 0); - - switch (level) { - case SMB_QFS_ALLOCATION: - fsinfo.allocation.level = RAW_QFS_ALLOCATION; - - status = ntvfs_fsinfo(req, &fsinfo); - if (!NT_STATUS_IS_OK(status)) { - return status; - } - - trans2_setup_reply(req, trans, 0, 18, 0); - - SIVAL(trans->out.data.data, 0, fsinfo.allocation.out.fs_id); - SIVAL(trans->out.data.data, 4, fsinfo.allocation.out.sectors_per_unit); - SIVAL(trans->out.data.data, 8, fsinfo.allocation.out.total_alloc_units); - SIVAL(trans->out.data.data, 12, fsinfo.allocation.out.avail_alloc_units); - SSVAL(trans->out.data.data, 16, fsinfo.allocation.out.bytes_per_sector); - - return NT_STATUS_OK; - - case SMB_QFS_VOLUME: - fsinfo.volume.level = RAW_QFS_VOLUME; - - status = ntvfs_fsinfo(req, &fsinfo); - if (!NT_STATUS_IS_OK(status)) { - return status; - } - - trans2_setup_reply(req, trans, 0, 5, 0); - - SIVAL(trans->out.data.data, 0, fsinfo.volume.out.serial_number); - /* w2k3 implements this incorrectly for unicode - it - * leaves the last byte off the string */ - trans2_append_data_string(req, trans, - &fsinfo.volume.out.volume_name, - 4, STR_LEN8BIT|STR_NOALIGN); - - return NT_STATUS_OK; - - case SMB_QFS_VOLUME_INFO: - case SMB_QFS_VOLUME_INFORMATION: - fsinfo.volume_info.level = RAW_QFS_VOLUME_INFO; - - status = ntvfs_fsinfo(req, &fsinfo); - if (!NT_STATUS_IS_OK(status)) { - return status; - } - - trans2_setup_reply(req, trans, 0, 18, 0); - - push_nttime(trans->out.data.data, 0, fsinfo.volume_info.out.create_time); - SIVAL(trans->out.data.data, 8, fsinfo.volume_info.out.serial_number); - SSVAL(trans->out.data.data, 16, 0); /* padding */ - trans2_append_data_string(req, trans, - &fsinfo.volume_info.out.volume_name, - 12, STR_UNICODE); - - return NT_STATUS_OK; - - case SMB_QFS_SIZE_INFO: - case SMB_QFS_SIZE_INFORMATION: - fsinfo.size_info.level = RAW_QFS_SIZE_INFO; - - status = ntvfs_fsinfo(req, &fsinfo); - if (!NT_STATUS_IS_OK(status)) { - return status; - } - - trans2_setup_reply(req, trans, 0, 24, 0); - - SBVAL(trans->out.data.data, 0, fsinfo.size_info.out.total_alloc_units); - SBVAL(trans->out.data.data, 8, fsinfo.size_info.out.avail_alloc_units); - SIVAL(trans->out.data.data, 16, fsinfo.size_info.out.sectors_per_unit); - SIVAL(trans->out.data.data, 20, fsinfo.size_info.out.bytes_per_sector); - - return NT_STATUS_OK; - - case SMB_QFS_DEVICE_INFO: - case SMB_QFS_DEVICE_INFORMATION: - fsinfo.device_info.level = RAW_QFS_DEVICE_INFO; - - status = ntvfs_fsinfo(req, &fsinfo); - if (!NT_STATUS_IS_OK(status)) { - return status; - } - trans2_setup_reply(req, trans, 0, 8, 0); - SIVAL(trans->out.data.data, 0, fsinfo.device_info.out.device_type); - SIVAL(trans->out.data.data, 4, fsinfo.device_info.out.characteristics); - return NT_STATUS_OK; - - - case SMB_QFS_ATTRIBUTE_INFO: - case SMB_QFS_ATTRIBUTE_INFORMATION: - fsinfo.attribute_info.level = RAW_QFS_ATTRIBUTE_INFO; - - status = ntvfs_fsinfo(req, &fsinfo); - if (!NT_STATUS_IS_OK(status)) { - return status; - } - - trans2_setup_reply(req, trans, 0, 12, 0); - - SIVAL(trans->out.data.data, 0, fsinfo.attribute_info.out.fs_attr); - SIVAL(trans->out.data.data, 4, fsinfo.attribute_info.out.max_file_component_length); - /* this must not be null terminated or win98 gets - confused! also note that w2k3 returns this as - unicode even when ascii is negotiated */ - trans2_append_data_string(req, trans, - &fsinfo.attribute_info.out.fs_type, - 8, STR_UNICODE); - return NT_STATUS_OK; - - - case SMB_QFS_QUOTA_INFORMATION: - fsinfo.quota_information.level = RAW_QFS_QUOTA_INFORMATION; - - status = ntvfs_fsinfo(req, &fsinfo); - if (!NT_STATUS_IS_OK(status)) { - return status; - } - - trans2_setup_reply(req, trans, 0, 48, 0); - - SBVAL(trans->out.data.data, 0, fsinfo.quota_information.out.unknown[0]); - SBVAL(trans->out.data.data, 8, fsinfo.quota_information.out.unknown[1]); - SBVAL(trans->out.data.data, 16, fsinfo.quota_information.out.unknown[2]); - SBVAL(trans->out.data.data, 24, fsinfo.quota_information.out.quota_soft); - SBVAL(trans->out.data.data, 32, fsinfo.quota_information.out.quota_hard); - SBVAL(trans->out.data.data, 40, fsinfo.quota_information.out.quota_flags); - - return NT_STATUS_OK; - - - case SMB_QFS_FULL_SIZE_INFORMATION: - fsinfo.full_size_information.level = RAW_QFS_FULL_SIZE_INFORMATION; - - status = ntvfs_fsinfo(req, &fsinfo); - if (!NT_STATUS_IS_OK(status)) { - return status; - } - - trans2_setup_reply(req, trans, 0, 32, 0); - - SBVAL(trans->out.data.data, 0, fsinfo.full_size_information.out.total_alloc_units); - SBVAL(trans->out.data.data, 8, fsinfo.full_size_information.out.call_avail_alloc_units); - SBVAL(trans->out.data.data, 16, fsinfo.full_size_information.out.actual_avail_alloc_units); - SIVAL(trans->out.data.data, 24, fsinfo.full_size_information.out.sectors_per_unit); - SIVAL(trans->out.data.data, 28, fsinfo.full_size_information.out.bytes_per_sector); - - return NT_STATUS_OK; - - case SMB_QFS_OBJECTID_INFORMATION: - fsinfo.objectid_information.level = RAW_QFS_OBJECTID_INFORMATION; - - status = ntvfs_fsinfo(req, &fsinfo); - if (!NT_STATUS_IS_OK(status)) { - return status; - } - - trans2_setup_reply(req, trans, 0, 64, 0); - - status = ndr_push_struct_blob(&guid_blob, req, - &fsinfo.objectid_information.out.guid, - (ndr_push_flags_fn_t)ndr_push_GUID); - if (!NT_STATUS_IS_OK(status)) { - return status; - } - - memcpy(trans->out.data.data, guid_blob.data, guid_blob.length); - - for (i=0;i<6;i++) { - SBVAL(trans->out.data.data, 16 + 8*i, fsinfo.objectid_information.out.unknown[i]); - } - return NT_STATUS_OK; - } - - return NT_STATUS_INVALID_LEVEL; -} - - -/* - trans2 open implementation -*/ -static NTSTATUS trans2_open(struct smbsrv_request *req, struct smb_trans2 *trans) -{ - union smb_open *io; - NTSTATUS status; - - /* make sure we got enough parameters */ - if (trans->in.params.length < 29) { - return NT_STATUS_FOOBAR; - } - - io = talloc(req, union smb_open); - if (io == NULL) { - return NT_STATUS_NO_MEMORY; - } - - io->t2open.level = RAW_OPEN_T2OPEN; - io->t2open.in.flags = SVAL(trans->in.params.data, VWV(0)); - io->t2open.in.open_mode = SVAL(trans->in.params.data, VWV(1)); - io->t2open.in.search_attrs = SVAL(trans->in.params.data, VWV(2)); - io->t2open.in.file_attrs = SVAL(trans->in.params.data, VWV(3)); - io->t2open.in.write_time = srv_pull_dos_date(req->smb_conn, - trans->in.params.data + VWV(4));; - io->t2open.in.open_func = SVAL(trans->in.params.data, VWV(6)); - io->t2open.in.size = IVAL(trans->in.params.data, VWV(7)); - io->t2open.in.timeout = IVAL(trans->in.params.data, VWV(9)); - io->t2open.in.num_eas = 0; - io->t2open.in.eas = NULL; - - trans2_pull_blob_string(req, &trans->in.params, 28, &io->t2open.in.fname, 0); - - status = ea_pull_list(&trans->in.data, io, &io->t2open.in.num_eas, &io->t2open.in.eas); - if (!NT_STATUS_IS_OK(status)) { - return status; - } - - status = ntvfs_openfile(req, io); - if (!NT_STATUS_IS_OK(status)) { - return status; - } - - trans2_setup_reply(req, trans, 30, 0, 0); - - SSVAL(trans->out.params.data, VWV(0), io->t2open.out.fnum); - SSVAL(trans->out.params.data, VWV(1), io->t2open.out.attrib); - srv_push_dos_date3(req->smb_conn, trans->out.params.data, - VWV(2), io->t2open.out.write_time); - SIVAL(trans->out.params.data, VWV(4), io->t2open.out.size); - SSVAL(trans->out.params.data, VWV(6), io->t2open.out.access); - SSVAL(trans->out.params.data, VWV(7), io->t2open.out.ftype); - SSVAL(trans->out.params.data, VWV(8), io->t2open.out.devstate); - SSVAL(trans->out.params.data, VWV(9), io->t2open.out.action); - SIVAL(trans->out.params.data, VWV(10), 0); /* reserved */ - SSVAL(trans->out.params.data, VWV(12), 0); /* EaErrorOffset */ - SIVAL(trans->out.params.data, VWV(13), 0); /* EaLength */ - - return status; -} - - -/* - trans2 mkdir implementation -*/ -static NTSTATUS trans2_mkdir(struct smbsrv_request *req, struct smb_trans2 *trans) -{ - union smb_mkdir *io; - NTSTATUS status; - - /* make sure we got enough parameters */ - if (trans->in.params.length < 5) { - return NT_STATUS_FOOBAR; - } - - io = talloc(req, union smb_mkdir); - if (io == NULL) { - return NT_STATUS_NO_MEMORY; - } - - io->t2mkdir.level = RAW_MKDIR_T2MKDIR; - trans2_pull_blob_string(req, &trans->in.params, 4, &io->t2mkdir.in.path, 0); - - status = ea_pull_list(&trans->in.data, io, - &io->t2mkdir.in.num_eas, - &io->t2mkdir.in.eas); - if (!NT_STATUS_IS_OK(status)) { - return status; - } - - status = ntvfs_mkdir(req, io); - if (!NT_STATUS_IS_OK(status)) { - return status; - } - - trans2_setup_reply(req, trans, 2, 0, 0); - - SSVAL(trans->out.params.data, VWV(0), 0); - - return status; -} - -/* - fill in the reply from a qpathinfo or qfileinfo call -*/ -static NTSTATUS trans2_fileinfo_fill(struct smbsrv_request *req, struct smb_trans2 *trans, - union smb_fileinfo *st) -{ - uint_t i; - uint32_t list_size; - - switch (st->generic.level) { - case RAW_FILEINFO_GENERIC: - case RAW_FILEINFO_GETATTR: - case RAW_FILEINFO_GETATTRE: - case RAW_FILEINFO_SEC_DESC: - /* handled elsewhere */ - return NT_STATUS_INVALID_LEVEL; - - case RAW_FILEINFO_BASIC_INFO: - case RAW_FILEINFO_BASIC_INFORMATION: - trans2_setup_reply(req, trans, 2, 40, 0); - - SSVAL(trans->out.params.data, 0, 0); - push_nttime(trans->out.data.data, 0, st->basic_info.out.create_time); - push_nttime(trans->out.data.data, 8, st->basic_info.out.access_time); - push_nttime(trans->out.data.data, 16, st->basic_info.out.write_time); - push_nttime(trans->out.data.data, 24, st->basic_info.out.change_time); - SIVAL(trans->out.data.data, 32, st->basic_info.out.attrib); - SIVAL(trans->out.data.data, 36, 0); /* padding */ - return NT_STATUS_OK; - - case RAW_FILEINFO_STANDARD: - trans2_setup_reply(req, trans, 2, 22, 0); - - SSVAL(trans->out.params.data, 0, 0); - srv_push_dos_date2(req->smb_conn, trans->out.data.data, 0, st->standard.out.create_time); - srv_push_dos_date2(req->smb_conn, trans->out.data.data, 4, st->standard.out.access_time); - srv_push_dos_date2(req->smb_conn, trans->out.data.data, 8, st->standard.out.write_time); - SIVAL(trans->out.data.data, 12, st->standard.out.size); - SIVAL(trans->out.data.data, 16, st->standard.out.alloc_size); - SSVAL(trans->out.data.data, 20, st->standard.out.attrib); - return NT_STATUS_OK; - - case RAW_FILEINFO_EA_SIZE: - trans2_setup_reply(req, trans, 2, 26, 0); - - SSVAL(trans->out.params.data, 0, 0); - srv_push_dos_date2(req->smb_conn, trans->out.data.data, 0, st->ea_size.out.create_time); - srv_push_dos_date2(req->smb_conn, trans->out.data.data, 4, st->ea_size.out.access_time); - srv_push_dos_date2(req->smb_conn, trans->out.data.data, 8, st->ea_size.out.write_time); - SIVAL(trans->out.data.data, 12, st->ea_size.out.size); - SIVAL(trans->out.data.data, 16, st->ea_size.out.alloc_size); - SSVAL(trans->out.data.data, 20, st->ea_size.out.attrib); - SIVAL(trans->out.data.data, 22, st->ea_size.out.ea_size); - return NT_STATUS_OK; - - case RAW_FILEINFO_NETWORK_OPEN_INFORMATION: - trans2_setup_reply(req, trans, 2, 56, 0); - - SSVAL(trans->out.params.data, 0, 0); - push_nttime(trans->out.data.data, 0, st->network_open_information.out.create_time); - push_nttime(trans->out.data.data, 8, st->network_open_information.out.access_time); - push_nttime(trans->out.data.data, 16, st->network_open_information.out.write_time); - push_nttime(trans->out.data.data, 24, st->network_open_information.out.change_time); - SBVAL(trans->out.data.data, 32, st->network_open_information.out.alloc_size); - SBVAL(trans->out.data.data, 40, st->network_open_information.out.size); - SIVAL(trans->out.data.data, 48, st->network_open_information.out.attrib); - SIVAL(trans->out.data.data, 52, 0); /* padding */ - return NT_STATUS_OK; - - case RAW_FILEINFO_STANDARD_INFO: - case RAW_FILEINFO_STANDARD_INFORMATION: - trans2_setup_reply(req, trans, 2, 24, 0); - SSVAL(trans->out.params.data, 0, 0); - SBVAL(trans->out.data.data, 0, st->standard_info.out.alloc_size); - SBVAL(trans->out.data.data, 8, st->standard_info.out.size); - SIVAL(trans->out.data.data, 16, st->standard_info.out.nlink); - SCVAL(trans->out.data.data, 20, st->standard_info.out.delete_pending); - SCVAL(trans->out.data.data, 21, st->standard_info.out.directory); - SSVAL(trans->out.data.data, 22, 0); /* padding */ - return NT_STATUS_OK; - - case RAW_FILEINFO_ATTRIBUTE_TAG_INFORMATION: - trans2_setup_reply(req, trans, 2, 8, 0); - SSVAL(trans->out.params.data, 0, 0); - SIVAL(trans->out.data.data, 0, st->attribute_tag_information.out.attrib); - SIVAL(trans->out.data.data, 4, st->attribute_tag_information.out.reparse_tag); - return NT_STATUS_OK; - - case RAW_FILEINFO_EA_INFO: - case RAW_FILEINFO_EA_INFORMATION: - trans2_setup_reply(req, trans, 2, 4, 0); - SSVAL(trans->out.params.data, 0, 0); - SIVAL(trans->out.data.data, 0, st->ea_info.out.ea_size); - return NT_STATUS_OK; - - case RAW_FILEINFO_MODE_INFORMATION: - trans2_setup_reply(req, trans, 2, 4, 0); - SSVAL(trans->out.params.data, 0, 0); - SIVAL(trans->out.data.data, 0, st->mode_information.out.mode); - return NT_STATUS_OK; - - case RAW_FILEINFO_ALIGNMENT_INFORMATION: - trans2_setup_reply(req, trans, 2, 4, 0); - SSVAL(trans->out.params.data, 0, 0); - SIVAL(trans->out.data.data, 0, - st->alignment_information.out.alignment_requirement); - return NT_STATUS_OK; - - case RAW_FILEINFO_EA_LIST: - list_size = ea_list_size(st->ea_list.out.num_eas, - st->ea_list.out.eas); - trans2_setup_reply(req, trans, 2, list_size, 0); - SSVAL(trans->out.params.data, 0, 0); - ea_put_list(trans->out.data.data, - st->ea_list.out.num_eas, st->ea_list.out.eas); - return NT_STATUS_OK; - - case RAW_FILEINFO_ALL_EAS: - list_size = ea_list_size(st->all_eas.out.num_eas, - st->all_eas.out.eas); - trans2_setup_reply(req, trans, 2, list_size, 0); - SSVAL(trans->out.params.data, 0, 0); - ea_put_list(trans->out.data.data, - st->all_eas.out.num_eas, st->all_eas.out.eas); - return NT_STATUS_OK; - - case RAW_FILEINFO_ACCESS_INFORMATION: - trans2_setup_reply(req, trans, 2, 4, 0); - SSVAL(trans->out.params.data, 0, 0); - SIVAL(trans->out.data.data, 0, st->access_information.out.access_flags); - return NT_STATUS_OK; - - case RAW_FILEINFO_POSITION_INFORMATION: - trans2_setup_reply(req, trans, 2, 8, 0); - SSVAL(trans->out.params.data, 0, 0); - SBVAL(trans->out.data.data, 0, st->position_information.out.position); - return NT_STATUS_OK; - - case RAW_FILEINFO_COMPRESSION_INFO: - case RAW_FILEINFO_COMPRESSION_INFORMATION: - trans2_setup_reply(req, trans, 2, 16, 0); - SSVAL(trans->out.params.data, 0, 0); - SBVAL(trans->out.data.data, 0, st->compression_info.out.compressed_size); - SSVAL(trans->out.data.data, 8, st->compression_info.out.format); - SCVAL(trans->out.data.data, 10, st->compression_info.out.unit_shift); - SCVAL(trans->out.data.data, 11, st->compression_info.out.chunk_shift); - SCVAL(trans->out.data.data, 12, st->compression_info.out.cluster_shift); - SSVAL(trans->out.data.data, 13, 0); /* 3 bytes padding */ - SCVAL(trans->out.data.data, 15, 0); - return NT_STATUS_OK; - - case RAW_FILEINFO_IS_NAME_VALID: - trans2_setup_reply(req, trans, 2, 0, 0); - SSVAL(trans->out.params.data, 0, 0); - return NT_STATUS_OK; - - case RAW_FILEINFO_INTERNAL_INFORMATION: - trans2_setup_reply(req, trans, 2, 8, 0); - SSVAL(trans->out.params.data, 0, 0); - SBVAL(trans->out.data.data, 0, st->internal_information.out.file_id); - return NT_STATUS_OK; - - case RAW_FILEINFO_ALL_INFO: - case RAW_FILEINFO_ALL_INFORMATION: - trans2_setup_reply(req, trans, 2, 72, 0); - - SSVAL(trans->out.params.data, 0, 0); - push_nttime(trans->out.data.data, 0, st->all_info.out.create_time); - push_nttime(trans->out.data.data, 8, st->all_info.out.access_time); - push_nttime(trans->out.data.data, 16, st->all_info.out.write_time); - push_nttime(trans->out.data.data, 24, st->all_info.out.change_time); - SIVAL(trans->out.data.data, 32, st->all_info.out.attrib); - SIVAL(trans->out.data.data, 36, 0); - SBVAL(trans->out.data.data, 40, st->all_info.out.alloc_size); - SBVAL(trans->out.data.data, 48, st->all_info.out.size); - SIVAL(trans->out.data.data, 56, st->all_info.out.nlink); - SCVAL(trans->out.data.data, 60, st->all_info.out.delete_pending); - SCVAL(trans->out.data.data, 61, st->all_info.out.directory); - SSVAL(trans->out.data.data, 62, 0); /* padding */ - SIVAL(trans->out.data.data, 64, st->all_info.out.ea_size); - trans2_append_data_string(req, trans, &st->all_info.out.fname, - 68, STR_UNICODE); - return NT_STATUS_OK; - - case RAW_FILEINFO_NAME_INFO: - case RAW_FILEINFO_NAME_INFORMATION: - trans2_setup_reply(req, trans, 2, 4, 0); - SSVAL(trans->out.params.data, 0, 0); - trans2_append_data_string(req, trans, &st->name_info.out.fname, 0, STR_UNICODE); - return NT_STATUS_OK; - - case RAW_FILEINFO_ALT_NAME_INFO: - case RAW_FILEINFO_ALT_NAME_INFORMATION: - trans2_setup_reply(req, trans, 2, 4, 0); - SSVAL(trans->out.params.data, 0, 0); - trans2_append_data_string(req, trans, &st->alt_name_info.out.fname, 0, STR_UNICODE); - return NT_STATUS_OK; - - case RAW_FILEINFO_STREAM_INFO: - case RAW_FILEINFO_STREAM_INFORMATION: - trans2_setup_reply(req, trans, 2, 0, 0); - - SSVAL(trans->out.params.data, 0, 0); - - for (i=0;istream_info.out.num_streams;i++) { - uint32_t data_size = trans->out.data.length; - uint8_t *data; - - trans2_grow_data(req, trans, data_size + 24); - data = trans->out.data.data + data_size; - SBVAL(data, 8, st->stream_info.out.streams[i].size); - SBVAL(data, 16, st->stream_info.out.streams[i].alloc_size); - trans2_append_data_string(req, trans, - &st->stream_info.out.streams[i].stream_name, - data_size + 4, STR_UNICODE); - if (i == st->stream_info.out.num_streams - 1) { - SIVAL(trans->out.data.data, data_size, 0); - } else { - trans2_grow_data_fill(req, trans, (trans->out.data.length+7)&~7); - SIVAL(trans->out.data.data, data_size, - trans->out.data.length - data_size); - } - } - return NT_STATUS_OK; - - case RAW_FILEINFO_UNIX_BASIC: - case RAW_FILEINFO_UNIX_LINK: - return NT_STATUS_INVALID_LEVEL; - } - - return NT_STATUS_INVALID_LEVEL; -} - -/* - trans2 qpathinfo implementation -*/ -static NTSTATUS trans2_qpathinfo(struct smbsrv_request *req, struct smb_trans2 *trans) -{ - union smb_fileinfo st; - NTSTATUS status; - uint16_t level; - - /* make sure we got enough parameters */ - if (trans->in.params.length < 2) { - return NT_STATUS_FOOBAR; - } - - level = SVAL(trans->in.params.data, 0); - - trans2_pull_blob_string(req, &trans->in.params, 6, &st.generic.in.fname, 0); - if (st.generic.in.fname == NULL) { - return NT_STATUS_FOOBAR; - } - - /* work out the backend level - we make it 1-1 in the header */ - st.generic.level = (enum smb_fileinfo_level)level; - if (st.generic.level >= RAW_FILEINFO_GENERIC) { - return NT_STATUS_INVALID_LEVEL; - } - - if (st.generic.level == RAW_FILEINFO_EA_LIST) { - status = ea_pull_name_list(&trans->in.data, req, - &st.ea_list.in.num_names, - &st.ea_list.in.ea_names); - if (!NT_STATUS_IS_OK(status)) { - return status; - } - } - - /* call the backend */ - status = ntvfs_qpathinfo(req, &st); - if (!NT_STATUS_IS_OK(status)) { - return status; - } - - /* fill in the reply parameters */ - status = trans2_fileinfo_fill(req, trans, &st); - - return status; -} - - -/* - trans2 qpathinfo implementation -*/ -static NTSTATUS trans2_qfileinfo(struct smbsrv_request *req, struct smb_trans2 *trans) -{ - union smb_fileinfo st; - NTSTATUS status; - uint16_t level; - - /* make sure we got enough parameters */ - if (trans->in.params.length < 4) { - return NT_STATUS_FOOBAR; - } - - st.generic.in.fnum = SVAL(trans->in.params.data, 0); - level = SVAL(trans->in.params.data, 2); - - /* work out the backend level - we make it 1-1 in the header */ - st.generic.level = (enum smb_fileinfo_level)level; - if (st.generic.level >= RAW_FILEINFO_GENERIC) { - return NT_STATUS_INVALID_LEVEL; - } - - if (st.generic.level == RAW_FILEINFO_EA_LIST) { - status = ea_pull_name_list(&trans->in.data, req, - &st.ea_list.in.num_names, - &st.ea_list.in.ea_names); - if (!NT_STATUS_IS_OK(status)) { - return status; - } - } - - /* call the backend */ - status = ntvfs_qfileinfo(req, &st); - if (!NT_STATUS_IS_OK(status)) { - return status; - } - - /* fill in the reply parameters */ - status = trans2_fileinfo_fill(req, trans, &st); - - return status; -} - - -/* - parse a trans2 setfileinfo/setpathinfo data blob -*/ -static NTSTATUS trans2_parse_sfileinfo(struct smbsrv_request *req, - union smb_setfileinfo *st, - const DATA_BLOB *blob) -{ - uint32_t len; - - switch (st->generic.level) { - case RAW_SFILEINFO_GENERIC: - case RAW_SFILEINFO_SETATTR: - case RAW_SFILEINFO_SETATTRE: - case RAW_SFILEINFO_SEC_DESC: - /* handled elsewhere */ - return NT_STATUS_INVALID_LEVEL; - - case RAW_SFILEINFO_STANDARD: - CHECK_MIN_BLOB_SIZE(blob, 12); - st->standard.in.create_time = srv_pull_dos_date2(req->smb_conn, blob->data + 0); - st->standard.in.access_time = srv_pull_dos_date2(req->smb_conn, blob->data + 4); - st->standard.in.write_time = srv_pull_dos_date2(req->smb_conn, blob->data + 8); - return NT_STATUS_OK; - - case RAW_SFILEINFO_EA_SET: - return ea_pull_list(blob, req, - &st->ea_set.in.num_eas, - &st->ea_set.in.eas); - - case SMB_SFILEINFO_BASIC_INFO: - case SMB_SFILEINFO_BASIC_INFORMATION: - CHECK_MIN_BLOB_SIZE(blob, 36); - st->basic_info.in.create_time = pull_nttime(blob->data, 0); - st->basic_info.in.access_time = pull_nttime(blob->data, 8); - st->basic_info.in.write_time = pull_nttime(blob->data, 16); - st->basic_info.in.change_time = pull_nttime(blob->data, 24); - st->basic_info.in.attrib = IVAL(blob->data, 32); - return NT_STATUS_OK; - - case SMB_SFILEINFO_DISPOSITION_INFO: - case SMB_SFILEINFO_DISPOSITION_INFORMATION: - CHECK_MIN_BLOB_SIZE(blob, 1); - st->disposition_info.in.delete_on_close = CVAL(blob->data, 0); - return NT_STATUS_OK; - - case SMB_SFILEINFO_ALLOCATION_INFO: - case SMB_SFILEINFO_ALLOCATION_INFORMATION: - CHECK_MIN_BLOB_SIZE(blob, 8); - st->allocation_info.in.alloc_size = BVAL(blob->data, 0); - return NT_STATUS_OK; - - case RAW_SFILEINFO_END_OF_FILE_INFO: - case RAW_SFILEINFO_END_OF_FILE_INFORMATION: - CHECK_MIN_BLOB_SIZE(blob, 8); - st->end_of_file_info.in.size = BVAL(blob->data, 0); - return NT_STATUS_OK; - - case RAW_SFILEINFO_RENAME_INFORMATION: { - DATA_BLOB blob2; - - CHECK_MIN_BLOB_SIZE(blob, 12); - st->rename_information.in.overwrite = CVAL(blob->data, 0); - st->rename_information.in.root_fid = IVAL(blob->data, 4); - len = IVAL(blob->data, 8); - blob2.data = blob->data+12; - blob2.length = MIN(blob->length, len); - trans2_pull_blob_string(req, &blob2, 0, - &st->rename_information.in.new_name, STR_UNICODE); - return NT_STATUS_OK; - } - - case RAW_SFILEINFO_POSITION_INFORMATION: - CHECK_MIN_BLOB_SIZE(blob, 8); - st->position_information.in.position = BVAL(blob->data, 0); - return NT_STATUS_OK; - - case RAW_SFILEINFO_MODE_INFORMATION: - CHECK_MIN_BLOB_SIZE(blob, 4); - st->mode_information.in.mode = IVAL(blob->data, 0); - return NT_STATUS_OK; - - case RAW_SFILEINFO_UNIX_BASIC: - case RAW_SFILEINFO_UNIX_LINK: - case RAW_SFILEINFO_UNIX_HLINK: - case RAW_SFILEINFO_1023: - case RAW_SFILEINFO_1025: - case RAW_SFILEINFO_1029: - case RAW_SFILEINFO_1032: - case RAW_SFILEINFO_1039: - case RAW_SFILEINFO_1040: - return NT_STATUS_INVALID_LEVEL; - } - - return NT_STATUS_INVALID_LEVEL; -} - -/* - trans2 setfileinfo implementation -*/ -static NTSTATUS trans2_setfileinfo(struct smbsrv_request *req, struct smb_trans2 *trans) -{ - union smb_setfileinfo st; - NTSTATUS status; - uint16_t level, fnum; - DATA_BLOB *blob; - - /* make sure we got enough parameters */ - if (trans->in.params.length < 4) { - return NT_STATUS_FOOBAR; - } - - fnum = SVAL(trans->in.params.data, 0); - level = SVAL(trans->in.params.data, 2); - - blob = &trans->in.data; - - st.generic.file.fnum = fnum; - st.generic.level = (enum smb_setfileinfo_level)level; - - status = trans2_parse_sfileinfo(req, &st, blob); - if (!NT_STATUS_IS_OK(status)) { - return status; - } - - status = ntvfs_setfileinfo(req, &st); - if (!NT_STATUS_IS_OK(status)) { - return status; - } - - trans2_setup_reply(req, trans, 2, 0, 0); - SSVAL(trans->out.params.data, 0, 0); - return NT_STATUS_OK; -} - -/* - trans2 setpathinfo implementation -*/ -static NTSTATUS trans2_setpathinfo(struct smbsrv_request *req, struct smb_trans2 *trans) -{ - union smb_setfileinfo st; - NTSTATUS status; - uint16_t level; - DATA_BLOB *blob; - - /* make sure we got enough parameters */ - if (trans->in.params.length < 4) { - return NT_STATUS_FOOBAR; - } - - level = SVAL(trans->in.params.data, 0); - blob = &trans->in.data; - st.generic.level = (enum smb_setfileinfo_level)level; - - trans2_pull_blob_string(req, &trans->in.params, 6, &st.generic.file.fname, 0); - if (st.generic.file.fname == NULL) { - return NT_STATUS_FOOBAR; - } - - status = trans2_parse_sfileinfo(req, &st, blob); - if (!NT_STATUS_IS_OK(status)) { - return status; - } - - status = ntvfs_setpathinfo(req, &st); - if (!NT_STATUS_IS_OK(status)) { - return status; - } - - trans2_setup_reply(req, trans, 2, 0, 0); - SSVAL(trans->out.params.data, 0, 0); - return NT_STATUS_OK; -} - - -/* a structure to encapsulate the state information about an in-progress ffirst/fnext operation */ -struct find_state { - struct smbsrv_request *req; - struct smb_trans2 *trans; - enum smb_search_level level; - uint16_t last_entry_offset; - uint16_t flags; -}; - -/* - fill a single entry in a trans2 find reply -*/ -static BOOL find_fill_info(struct smbsrv_request *req, - struct smb_trans2 *trans, - struct find_state *state, - union smb_search_data *file) -{ - uint8_t *data; - uint_t ofs = trans->out.data.length; - uint32_t ea_size; - - switch (state->level) { - case RAW_SEARCH_SEARCH: - case RAW_SEARCH_FFIRST: - case RAW_SEARCH_FUNIQUE: - case RAW_SEARCH_GENERIC: - /* handled elsewhere */ - break; - - case RAW_SEARCH_STANDARD: - if (state->flags & FLAG_TRANS2_FIND_REQUIRE_RESUME) { - trans2_grow_data(req, trans, ofs + 27); - SIVAL(trans->out.data.data, ofs, file->standard.resume_key); - ofs += 4; - } else { - trans2_grow_data(req, trans, ofs + 23); - } - data = trans->out.data.data + ofs; - srv_push_dos_date2(req->smb_conn, data, 0, file->standard.create_time); - srv_push_dos_date2(req->smb_conn, data, 4, file->standard.access_time); - srv_push_dos_date2(req->smb_conn, data, 8, file->standard.write_time); - SIVAL(data, 12, file->standard.size); - SIVAL(data, 16, file->standard.alloc_size); - SSVAL(data, 20, file->standard.attrib); - trans2_append_data_string(req, trans, &file->standard.name, - ofs + 22, STR_LEN8BIT | STR_TERMINATE | STR_LEN_NOTERM); - break; - - case RAW_SEARCH_EA_SIZE: - if (state->flags & FLAG_TRANS2_FIND_REQUIRE_RESUME) { - trans2_grow_data(req, trans, ofs + 31); - SIVAL(trans->out.data.data, ofs, file->ea_size.resume_key); - ofs += 4; - } else { - trans2_grow_data(req, trans, ofs + 27); - } - data = trans->out.data.data + ofs; - srv_push_dos_date2(req->smb_conn, data, 0, file->ea_size.create_time); - srv_push_dos_date2(req->smb_conn, data, 4, file->ea_size.access_time); - srv_push_dos_date2(req->smb_conn, data, 8, file->ea_size.write_time); - SIVAL(data, 12, file->ea_size.size); - SIVAL(data, 16, file->ea_size.alloc_size); - SSVAL(data, 20, file->ea_size.attrib); - SIVAL(data, 22, file->ea_size.ea_size); - trans2_append_data_string(req, trans, &file->ea_size.name, - ofs + 26, STR_LEN8BIT | STR_NOALIGN); - trans2_grow_data(req, trans, trans->out.data.length + 1); - trans->out.data.data[trans->out.data.length-1] = 0; - break; - - case RAW_SEARCH_EA_LIST: - ea_size = ea_list_size(file->ea_list.eas.num_eas, file->ea_list.eas.eas); - if (state->flags & FLAG_TRANS2_FIND_REQUIRE_RESUME) { - if (!trans2_grow_data(req, trans, ofs + 27 + ea_size)) { - return False; - } - SIVAL(trans->out.data.data, ofs, file->ea_list.resume_key); - ofs += 4; - } else { - if (!trans2_grow_data(req, trans, ofs + 23 + ea_size)) { - return False; - } - } - data = trans->out.data.data + ofs; - srv_push_dos_date2(req->smb_conn, data, 0, file->ea_list.create_time); - srv_push_dos_date2(req->smb_conn, data, 4, file->ea_list.access_time); - srv_push_dos_date2(req->smb_conn, data, 8, file->ea_list.write_time); - SIVAL(data, 12, file->ea_list.size); - SIVAL(data, 16, file->ea_list.alloc_size); - SSVAL(data, 20, file->ea_list.attrib); - ea_put_list(data+22, file->ea_list.eas.num_eas, file->ea_list.eas.eas); - trans2_append_data_string(req, trans, &file->ea_list.name, - ofs + 22 + ea_size, STR_LEN8BIT | STR_NOALIGN); - trans2_grow_data(req, trans, trans->out.data.length + 1); - trans->out.data.data[trans->out.data.length-1] = 0; - break; - - case RAW_SEARCH_DIRECTORY_INFO: - trans2_grow_data(req, trans, ofs + 64); - data = trans->out.data.data + ofs; - SIVAL(data, 4, file->directory_info.file_index); - push_nttime(data, 8, file->directory_info.create_time); - push_nttime(data, 16, file->directory_info.access_time); - push_nttime(data, 24, file->directory_info.write_time); - push_nttime(data, 32, file->directory_info.change_time); - SBVAL(data, 40, file->directory_info.size); - SBVAL(data, 48, file->directory_info.alloc_size); - SIVAL(data, 56, file->directory_info.attrib); - trans2_append_data_string(req, trans, &file->directory_info.name, - ofs + 60, STR_TERMINATE_ASCII); - data = trans->out.data.data + ofs; - SIVAL(data, 0, trans->out.data.length - ofs); - break; - - case RAW_SEARCH_FULL_DIRECTORY_INFO: - trans2_grow_data(req, trans, ofs + 68); - data = trans->out.data.data + ofs; - SIVAL(data, 4, file->full_directory_info.file_index); - push_nttime(data, 8, file->full_directory_info.create_time); - push_nttime(data, 16, file->full_directory_info.access_time); - push_nttime(data, 24, file->full_directory_info.write_time); - push_nttime(data, 32, file->full_directory_info.change_time); - SBVAL(data, 40, file->full_directory_info.size); - SBVAL(data, 48, file->full_directory_info.alloc_size); - SIVAL(data, 56, file->full_directory_info.attrib); - SIVAL(data, 64, file->full_directory_info.ea_size); - trans2_append_data_string(req, trans, &file->full_directory_info.name, - ofs + 60, STR_TERMINATE_ASCII); - data = trans->out.data.data + ofs; - SIVAL(data, 0, trans->out.data.length - ofs); - break; - - case RAW_SEARCH_NAME_INFO: - trans2_grow_data(req, trans, ofs + 12); - data = trans->out.data.data + ofs; - SIVAL(data, 4, file->name_info.file_index); - trans2_append_data_string(req, trans, &file->name_info.name, - ofs + 8, STR_TERMINATE_ASCII); - data = trans->out.data.data + ofs; - SIVAL(data, 0, trans->out.data.length - ofs); - break; - - case RAW_SEARCH_BOTH_DIRECTORY_INFO: - trans2_grow_data(req, trans, ofs + 94); - data = trans->out.data.data + ofs; - SIVAL(data, 4, file->both_directory_info.file_index); - push_nttime(data, 8, file->both_directory_info.create_time); - push_nttime(data, 16, file->both_directory_info.access_time); - push_nttime(data, 24, file->both_directory_info.write_time); - push_nttime(data, 32, file->both_directory_info.change_time); - SBVAL(data, 40, file->both_directory_info.size); - SBVAL(data, 48, file->both_directory_info.alloc_size); - SIVAL(data, 56, file->both_directory_info.attrib); - SIVAL(data, 64, file->both_directory_info.ea_size); - SCVAL(data, 69, 0); /* reserved */ - memset(data+70,0,24); - trans2_push_data_string(req, trans, - 68 + ofs, 70 + ofs, - &file->both_directory_info.short_name, - 24, STR_UNICODE | STR_LEN8BIT); - trans2_append_data_string(req, trans, &file->both_directory_info.name, - ofs + 60, STR_TERMINATE_ASCII); - trans2_align_data(req, trans); - data = trans->out.data.data + ofs; - SIVAL(data, 0, trans->out.data.length - ofs); - break; - - case RAW_SEARCH_ID_FULL_DIRECTORY_INFO: - trans2_grow_data(req, trans, ofs + 80); - data = trans->out.data.data + ofs; - SIVAL(data, 4, file->id_full_directory_info.file_index); - push_nttime(data, 8, file->id_full_directory_info.create_time); - push_nttime(data, 16, file->id_full_directory_info.access_time); - push_nttime(data, 24, file->id_full_directory_info.write_time); - push_nttime(data, 32, file->id_full_directory_info.change_time); - SBVAL(data, 40, file->id_full_directory_info.size); - SBVAL(data, 48, file->id_full_directory_info.alloc_size); - SIVAL(data, 56, file->id_full_directory_info.attrib); - SIVAL(data, 64, file->id_full_directory_info.ea_size); - SIVAL(data, 68, 0); /* padding */ - SBVAL(data, 72, file->id_full_directory_info.file_id); - trans2_append_data_string(req, trans, &file->id_full_directory_info.name, - ofs + 60, STR_TERMINATE_ASCII); - data = trans->out.data.data + ofs; - SIVAL(data, 0, trans->out.data.length - ofs); - break; - - case RAW_SEARCH_ID_BOTH_DIRECTORY_INFO: - trans2_grow_data(req, trans, ofs + 104); - data = trans->out.data.data + ofs; - SIVAL(data, 4, file->id_both_directory_info.file_index); - push_nttime(data, 8, file->id_both_directory_info.create_time); - push_nttime(data, 16, file->id_both_directory_info.access_time); - push_nttime(data, 24, file->id_both_directory_info.write_time); - push_nttime(data, 32, file->id_both_directory_info.change_time); - SBVAL(data, 40, file->id_both_directory_info.size); - SBVAL(data, 48, file->id_both_directory_info.alloc_size); - SIVAL(data, 56, file->id_both_directory_info.attrib); - SIVAL(data, 64, file->id_both_directory_info.ea_size); - SCVAL(data, 69, 0); /* reserved */ - memset(data+70,0,26); - trans2_push_data_string(req, trans, - 68 + ofs, 70 + ofs, - &file->id_both_directory_info.short_name, - 24, STR_UNICODE | STR_LEN8BIT); - SBVAL(data, 96, file->id_both_directory_info.file_id); - trans2_append_data_string(req, trans, &file->id_both_directory_info.name, - ofs + 60, STR_TERMINATE_ASCII); - data = trans->out.data.data + ofs; - SIVAL(data, 0, trans->out.data.length - ofs); - break; - } - - return True; -} - -/* callback function for trans2 findfirst/findnext */ -static BOOL find_callback(void *private, union smb_search_data *file) -{ - struct find_state *state = (struct find_state *)private; - struct smb_trans2 *trans = state->trans; - uint_t old_length; - - old_length = trans->out.data.length; - - if (!find_fill_info(state->req, trans, state, file) || - trans->out.data.length > trans->in.max_data) { - /* restore the old length and tell the backend to stop */ - trans2_grow_data(state->req, trans, old_length); - return False; - } - - state->last_entry_offset = old_length; - return True; -} - - -/* - trans2 findfirst implementation -*/ -static NTSTATUS trans2_findfirst(struct smbsrv_request *req, struct smb_trans2 *trans) -{ - union smb_search_first search; - NTSTATUS status; - uint16_t level; - uint8_t *param; - struct find_state state; - - /* make sure we got all the parameters */ - if (trans->in.params.length < 14) { - return NT_STATUS_FOOBAR; - } - - search.t2ffirst.in.search_attrib = SVAL(trans->in.params.data, 0); - search.t2ffirst.in.max_count = SVAL(trans->in.params.data, 2); - search.t2ffirst.in.flags = SVAL(trans->in.params.data, 4); - level = SVAL(trans->in.params.data, 6); - search.t2ffirst.in.storage_type = IVAL(trans->in.params.data, 8); - - trans2_pull_blob_string(req, &trans->in.params, 12, &search.t2ffirst.in.pattern, 0); - if (search.t2ffirst.in.pattern == NULL) { - return NT_STATUS_FOOBAR; - } - - search.t2ffirst.level = (enum smb_search_level)level; - if (search.t2ffirst.level >= RAW_SEARCH_GENERIC) { - return NT_STATUS_INVALID_LEVEL; - } - - if (search.t2ffirst.level == RAW_SEARCH_EA_LIST) { - status = ea_pull_name_list(&trans->in.data, req, - &search.t2ffirst.in.num_names, - &search.t2ffirst.in.ea_names); - if (!NT_STATUS_IS_OK(status)) { - return status; - } - } - - /* setup the private state structure that the backend will - give us in the callback */ - state.req = req; - state.trans = trans; - state.level = search.t2ffirst.level; - state.last_entry_offset = 0; - state.flags = search.t2ffirst.in.flags; - - /* setup for just a header in the reply */ - trans2_setup_reply(req, trans, 10, 0, 0); - - /* call the backend */ - status = ntvfs_search_first(req, &search, &state, find_callback); - if (!NT_STATUS_IS_OK(status)) { - trans2_setup_reply(req, trans, 0, 0, 0); - return status; - } - - /* fill in the findfirst reply header */ - param = trans->out.params.data; - SSVAL(param, VWV(0), search.t2ffirst.out.handle); - SSVAL(param, VWV(1), search.t2ffirst.out.count); - SSVAL(param, VWV(2), search.t2ffirst.out.end_of_search); - SSVAL(param, VWV(3), 0); - SSVAL(param, VWV(4), state.last_entry_offset); - - return NT_STATUS_OK; -} - - -/* - trans2 findnext implementation -*/ -static NTSTATUS trans2_findnext(struct smbsrv_request *req, struct smb_trans2 *trans) -{ - union smb_search_next search; - NTSTATUS status; - uint16_t level; - uint8_t *param; - struct find_state state; - - /* make sure we got all the parameters */ - if (trans->in.params.length < 12) { - return NT_STATUS_FOOBAR; - } - - search.t2fnext.in.handle = SVAL(trans->in.params.data, 0); - search.t2fnext.in.max_count = SVAL(trans->in.params.data, 2); - level = SVAL(trans->in.params.data, 4); - search.t2fnext.in.resume_key = IVAL(trans->in.params.data, 6); - search.t2fnext.in.flags = SVAL(trans->in.params.data, 10); - - trans2_pull_blob_string(req, &trans->in.params, 12, &search.t2fnext.in.last_name, 0); - if (search.t2fnext.in.last_name == NULL) { - return NT_STATUS_FOOBAR; - } - - search.t2fnext.level = (enum smb_search_level)level; - if (search.t2fnext.level >= RAW_SEARCH_GENERIC) { - return NT_STATUS_INVALID_LEVEL; - } - - if (search.t2fnext.level == RAW_SEARCH_EA_LIST) { - status = ea_pull_name_list(&trans->in.data, req, - &search.t2fnext.in.num_names, - &search.t2fnext.in.ea_names); - if (!NT_STATUS_IS_OK(status)) { - return status; - } - } - - /* setup the private state structure that the backend will give us in the callback */ - state.req = req; - state.trans = trans; - state.level = search.t2fnext.level; - state.last_entry_offset = 0; - state.flags = search.t2fnext.in.flags; - - /* setup for just a header in the reply */ - trans2_setup_reply(req, trans, 8, 0, 0); - - /* call the backend */ - status = ntvfs_search_next(req, &search, &state, find_callback); - if (!NT_STATUS_IS_OK(status)) { - return status; - } - - /* fill in the findfirst reply header */ - param = trans->out.params.data; - SSVAL(param, VWV(0), search.t2fnext.out.count); - SSVAL(param, VWV(1), search.t2fnext.out.end_of_search); - SSVAL(param, VWV(2), 0); - SSVAL(param, VWV(3), state.last_entry_offset); - - return NT_STATUS_OK; -} - - -/* - backend for trans2 requests -*/ -static NTSTATUS trans2_backend(struct smbsrv_request *req, struct smb_trans2 *trans) -{ - NTSTATUS status; - - /* direct trans2 pass thru */ - status = ntvfs_trans2(req, trans); - if (!NT_STATUS_EQUAL(NT_STATUS_NOT_IMPLEMENTED, status)) { - return status; - } - - /* must have at least one setup word */ - if (trans->in.setup_count < 1) { - return NT_STATUS_FOOBAR; - } - - /* the trans2 command is in setup[0] */ - switch (trans->in.setup[0]) { - case TRANSACT2_FINDFIRST: - return trans2_findfirst(req, trans); - case TRANSACT2_FINDNEXT: - return trans2_findnext(req, trans); - case TRANSACT2_QPATHINFO: - return trans2_qpathinfo(req, trans); - case TRANSACT2_QFILEINFO: - return trans2_qfileinfo(req, trans); - case TRANSACT2_SETFILEINFO: - return trans2_setfileinfo(req, trans); - case TRANSACT2_SETPATHINFO: - return trans2_setpathinfo(req, trans); - case TRANSACT2_QFSINFO: - return trans2_qfsinfo(req, trans); - case TRANSACT2_OPEN: - return trans2_open(req, trans); - case TRANSACT2_MKDIR: - return trans2_mkdir(req, trans); - } - - /* an unknown trans2 command */ - return NT_STATUS_FOOBAR; -} - - -/* - send a continue request -*/ -static void reply_trans_continue(struct smbsrv_request *req, uint8_t command, - struct smb_trans2 *trans) -{ - struct smbsrv_trans_partial *tp; - int count; - - /* make sure they don't flood us */ - for (count=0,tp=req->smb_conn->trans_partial;tp;tp=tp->next) count++; - if (count > 100) { - req_reply_error(req, NT_STATUS_INSUFFICIENT_RESOURCES); - return; - } - - tp = talloc(req, struct smbsrv_trans_partial); - - tp->req = talloc_reference(tp, req); - tp->trans = trans; - tp->command = command; - - DLIST_ADD(req->smb_conn->trans_partial, tp); - - /* send a 'please continue' reply */ - req_setup_reply(req, 0, 0); - req_send_reply(req); -} - - -/* - answer a reconstructed trans request -*/ -static void reply_trans_complete(struct smbsrv_request *req, uint8_t command, - struct smb_trans2 *trans) -{ - uint16_t params_left, data_left; - uint8_t *params, *data; - NTSTATUS status; - int i; - - /* its a full request, give it to the backend */ - if (command == SMBtrans) { - status = ntvfs_trans(req, trans); - } else { - status = trans2_backend(req, trans); - } - - if (NT_STATUS_IS_ERR(status)) { - req_reply_error(req, status); - return; - } - - params_left = trans->out.params.length; - data_left = trans->out.data.length; - params = trans->out.params.data; - data = trans->out.data.data; - - req_setup_reply(req, 10 + trans->out.setup_count, 0); - - if (!NT_STATUS_IS_OK(status)) { - req_setup_error(req, status); - } - - /* 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; - - 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 = req_setup_secondary(req); - } else { - this_req = req; - } - - req_grow_data(this_req, this_param + this_data + (align1 + align2)); - - SSVAL(this_req->out.vwv, VWV(0), trans->out.params.length); - SSVAL(this_req->out.vwv, VWV(1), trans->out.data.length); - SSVAL(this_req->out.vwv, VWV(2), 0); - - SSVAL(this_req->out.vwv, VWV(3), this_param); - SSVAL(this_req->out.vwv, VWV(4), align1 + PTR_DIFF(this_req->out.data, this_req->out.hdr)); - SSVAL(this_req->out.vwv, VWV(5), PTR_DIFF(params, trans->out.params.data)); - - SSVAL(this_req->out.vwv, VWV(6), this_data); - SSVAL(this_req->out.vwv, VWV(7), align1 + align2 + - PTR_DIFF(this_req->out.data + this_param, this_req->out.hdr)); - SSVAL(this_req->out.vwv, VWV(8), PTR_DIFF(data, trans->out.data.data)); - - SSVAL(this_req->out.vwv, VWV(9), trans->out.setup_count); - for (i=0;iout.setup_count;i++) { - SSVAL(this_req->out.vwv, VWV(10+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; - - req_send_reply(this_req); - } while (params_left != 0 || data_left != 0); -} - - -/* - Reply to an SMBtrans or SMBtrans2 request -*/ -void reply_trans_generic(struct smbsrv_request *req, uint8_t command) -{ - struct smb_trans2 *trans; - int i; - uint16_t param_ofs, data_ofs; - uint16_t param_count, data_count; - uint16_t param_total, data_total; - - trans = talloc(req, struct smb_trans2); - if (trans == NULL) { - req_reply_error(req, NT_STATUS_NO_MEMORY); - return; - } - - /* parse request */ - if (req->in.wct < 14) { - req_reply_error(req, NT_STATUS_INVALID_PARAMETER); - return; - } - - param_total = SVAL(req->in.vwv, VWV(0)); - data_total = SVAL(req->in.vwv, VWV(1)); - trans->in.max_param = SVAL(req->in.vwv, VWV(2)); - trans->in.max_data = SVAL(req->in.vwv, VWV(3)); - trans->in.max_setup = CVAL(req->in.vwv, VWV(4)); - trans->in.flags = SVAL(req->in.vwv, VWV(5)); - trans->in.timeout = IVAL(req->in.vwv, VWV(6)); - param_count = SVAL(req->in.vwv, VWV(9)); - param_ofs = SVAL(req->in.vwv, VWV(10)); - data_count = SVAL(req->in.vwv, VWV(11)); - data_ofs = SVAL(req->in.vwv, VWV(12)); - trans->in.setup_count = CVAL(req->in.vwv, VWV(13)); - - if (req->in.wct != 14 + trans->in.setup_count) { - req_reply_dos_error(req, ERRSRV, ERRerror); - return; - } - - /* parse out the setup words */ - trans->in.setup = talloc_array(req, uint16_t, trans->in.setup_count); - if (trans->in.setup_count && !trans->in.setup) { - req_reply_error(req, NT_STATUS_NO_MEMORY); - return; - } - for (i=0;iin.setup_count;i++) { - trans->in.setup[i] = SVAL(req->in.vwv, VWV(14+i)); - } - - if (command == SMBtrans) { - req_pull_string(req, &trans->in.trans_name, req->in.data, -1, STR_TERMINATE); - } - - 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)) { - req_reply_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) { - reply_trans_continue(req, command, trans); - return; - } - - reply_trans_complete(req, command, trans); -} - - -/* - Reply to an SMBtranss2 request -*/ -static void reply_transs_generic(struct smbsrv_request *req, uint8_t command) -{ - struct smbsrv_trans_partial *tp; - struct smb_trans2 *trans = NULL; - uint16_t param_ofs, data_ofs; - uint16_t param_count, data_count; - uint16_t param_disp, data_disp; - uint16_t param_total, data_total; - DATA_BLOB params, data; - - for (tp=req->smb_conn->trans_partial;tp;tp=tp->next) { - if (tp->command == command && - SVAL(tp->req->in.hdr, HDR_MID) == SVAL(req->in.hdr, HDR_MID)) { - break; - } - } - - if (tp == NULL) { - req_reply_error(req, NT_STATUS_INVALID_PARAMETER); - return; - } - - trans = tp->trans; - - /* parse request */ - if (req->in.wct < 8) { - req_reply_error(req, NT_STATUS_INVALID_PARAMETER); - return; - } - - param_total = SVAL(req->in.vwv, VWV(0)); - data_total = SVAL(req->in.vwv, VWV(1)); - param_count = SVAL(req->in.vwv, VWV(2)); - param_ofs = SVAL(req->in.vwv, VWV(3)); - param_disp = SVAL(req->in.vwv, VWV(4)); - data_count = SVAL(req->in.vwv, VWV(5)); - data_ofs = SVAL(req->in.vwv, VWV(6)); - data_disp = SVAL(req->in.vwv, VWV(7)); - - if (!req_pull_blob(req, req->in.hdr + param_ofs, param_count, ¶ms) || - !req_pull_blob(req, req->in.hdr + data_ofs, data_count, &data)) { - req_reply_error(req, NT_STATUS_INVALID_PARAMETER); - return; - } - - /* only allow contiguous requests */ - if ((param_count != 0 && - param_disp != trans->in.params.length) || - (data_count != 0 && - data_disp != trans->in.data.length)) { - req_reply_error(req, NT_STATUS_INVALID_PARAMETER); - return; - } - - /* add to the existing request */ - if (param_count != 0) { - trans->in.params.data = talloc_realloc(trans, - trans->in.params.data, - uint8_t, - param_disp + param_count); - if (trans->in.params.data == NULL) { - goto failed; - } - trans->in.params.length = param_disp + param_count; - } - - if (data_count != 0) { - trans->in.data.data = talloc_realloc(trans, - trans->in.data.data, - uint8_t, - data_disp + data_count); - if (trans->in.data.data == NULL) { - goto failed; - } - trans->in.data.length = data_disp + data_count; - } - - memcpy(trans->in.params.data + param_disp, params.data, params.length); - memcpy(trans->in.data.data + data_disp, data.data, data.length); - - /* the sequence number of the reply is taken from the last secondary - response */ - tp->req->seq_num = req->seq_num; - - /* we don't reply to Transs2 requests */ - talloc_free(req); - - if (trans->in.params.length == param_total && - trans->in.data.length == data_total) { - /* its now complete */ - reply_trans_complete(tp->req, command, trans); - DLIST_REMOVE(tp->req->smb_conn->trans_partial, tp); - talloc_free(tp); - } - return; - -failed: - req_reply_error(tp->req, NT_STATUS_NO_MEMORY); - DLIST_REMOVE(req->smb_conn->trans_partial, tp); - talloc_free(req); - talloc_free(tp); -} - - -/* - Reply to an SMBtrans2 -*/ -void reply_trans2(struct smbsrv_request *req) -{ - reply_trans_generic(req, SMBtrans2); -} - -/* - Reply to an SMBtrans -*/ -void reply_trans(struct smbsrv_request *req) -{ - reply_trans_generic(req, SMBtrans); -} - -/* - Reply to an SMBtranss request -*/ -void reply_transs(struct smbsrv_request *req) -{ - reply_transs_generic(req, SMBtrans); -} - -/* - Reply to an SMBtranss2 request -*/ -void reply_transs2(struct smbsrv_request *req) -{ - reply_transs_generic(req, SMBtrans2); -} - -- cgit