summaryrefslogtreecommitdiffstats
path: root/source4/smb_server/smb
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-05 17:47:29 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-05 17:47:29 +0000
commit4f5791ebd03eaec1c7da0865a383175b05102712 (patch)
tree8ce7b00f7a76baa386372422adebbe64510812d4 /source4/smb_server/smb
parentInitial commit. (diff)
downloadsamba-upstream.tar.xz
samba-upstream.zip
Adding upstream version 2:4.17.12+dfsg.upstream/2%4.17.12+dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--source4/smb_server/smb/negprot.c564
-rw-r--r--source4/smb_server/smb/nttrans.c815
-rw-r--r--source4/smb_server/smb/receive.c679
-rw-r--r--source4/smb_server/smb/reply.c2379
-rw-r--r--source4/smb_server/smb/request.c779
-rw-r--r--source4/smb_server/smb/search.c283
-rw-r--r--source4/smb_server/smb/service.c202
-rw-r--r--source4/smb_server/smb/sesssetup.c644
-rw-r--r--source4/smb_server/smb/signing.c147
-rw-r--r--source4/smb_server/smb/srvtime.c82
-rw-r--r--source4/smb_server/smb/trans2.c1562
-rw-r--r--source4/smb_server/smb/wscript_build10
-rw-r--r--source4/smb_server/smb2/fileinfo.c377
-rw-r--r--source4/smb_server/smb2/fileio.c547
-rw-r--r--source4/smb_server/smb2/find.c167
-rw-r--r--source4/smb_server/smb2/keepalive.c71
-rw-r--r--source4/smb_server/smb2/negprot.c327
-rw-r--r--source4/smb_server/smb2/receive.c710
-rw-r--r--source4/smb_server/smb2/sesssetup.c326
-rw-r--r--source4/smb_server/smb2/smb2_server.h192
-rw-r--r--source4/smb_server/smb2/tcon.c446
-rw-r--r--source4/smb_server/smb2/wscript_build9
-rw-r--r--source4/smb_server/smb_server.c203
-rw-r--r--source4/smb_server/smb_server.h522
24 files changed, 12043 insertions, 0 deletions
diff --git a/source4/smb_server/smb/negprot.c b/source4/smb_server/smb/negprot.c
new file mode 100644
index 0000000..cd2451d
--- /dev/null
+++ b/source4/smb_server/smb/negprot.c
@@ -0,0 +1,564 @@
+/*
+ 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 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "system/filesys.h"
+#include "auth/credentials/credentials.h"
+#include "auth/gensec/gensec.h"
+#include "auth/auth.h"
+#include "smb_server/smb_server.h"
+#include "libcli/smb2/smb2.h"
+#include "smb_server/smb2/smb2_server.h"
+#include "samba/service_stream.h"
+#include "lib/stream/packet.h"
+#include "param/param.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;
+
+ /* 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,
+ smb_conn->connection->event.ctx,
+ smb_conn->connection->msg_ctx,
+ smb_conn->lp_ctx,
+ &smb_conn->negotiate.auth_context);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(0, ("auth_context_create() returned %s", nt_errstr(nt_status)));
+ return nt_status;
+ }
+
+ nt_status = auth_get_challenge(smb_conn->negotiate.auth_context, buff);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(0, ("auth_get_challenge() returned %s", nt_errstr(nt_status)));
+ return nt_status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/****************************************************************************
+ Reply for the core protocol.
+****************************************************************************/
+static void reply_corep(struct smbsrv_request *req, uint16_t choice)
+{
+ smbsrv_setup_reply(req, 1, 0);
+
+ SSVAL(req->out.vwv, VWV(0), choice);
+
+ req->smb_conn->negotiate.protocol = PROTOCOL_CORE;
+
+ if (req->smb_conn->signing.mandatory_signing) {
+ smbsrv_terminate_connection(req->smb_conn,
+ "CORE does not support SMB signing, and it is mandatory\n");
+ return;
+ }
+
+ smbsrv_send_reply(req);
+}
+
+/****************************************************************************
+ Reply for the coreplus protocol.
+this is quite incomplete - we only fill in a small part of the reply, but as nobody uses
+this any more it probably doesn't matter
+****************************************************************************/
+static void reply_coreplus(struct smbsrv_request *req, uint16_t choice)
+{
+ uint16_t raw;
+ if (lpcfg_async_smb_echo_handler(req->smb_conn->lp_ctx)) {
+ raw = 0;
+ } else {
+ raw = (lpcfg_read_raw(req->smb_conn->lp_ctx)?1:0) |
+ (lpcfg_write_raw(req->smb_conn->lp_ctx)?2:0);
+ }
+
+ smbsrv_setup_reply(req, 13, 0);
+
+ /* Reply, SMBlockread, SMBwritelock supported. */
+ SCVAL(req->out.hdr,HDR_FLG,
+ CVAL(req->out.hdr, HDR_FLG) | FLAG_SUPPORT_LOCKREAD);
+
+ SSVAL(req->out.vwv, VWV(0), choice);
+ SSVAL(req->out.vwv, VWV(1), 0x1); /* user level security, don't encrypt */
+
+ /* tell redirector we support
+ readbraw and writebraw (possibly) */
+ SSVAL(req->out.vwv, VWV(5), raw);
+
+ req->smb_conn->negotiate.protocol = PROTOCOL_COREPLUS;
+
+ if (req->smb_conn->signing.mandatory_signing) {
+ smbsrv_terminate_connection(req->smb_conn,
+ "COREPLUS does not support SMB signing, and it is mandatory\n");
+ return;
+ }
+
+ smbsrv_send_reply(req);
+}
+
+/****************************************************************************
+ Reply for the lanman 1.0 protocol.
+****************************************************************************/
+static void reply_lanman1(struct smbsrv_request *req, uint16_t choice)
+{
+ int secword=0;
+ time_t t = req->request_time.tv_sec;
+ uint16_t raw;
+
+ if (lpcfg_async_smb_echo_handler(req->smb_conn->lp_ctx)) {
+ raw = 0;
+ } else {
+ raw = (lpcfg_read_raw(req->smb_conn->lp_ctx)?1:0) |
+ (lpcfg_write_raw(req->smb_conn->lp_ctx)?2:0);
+ }
+
+ req->smb_conn->negotiate.encrypted_passwords = lpcfg_encrypt_passwords(req->smb_conn->lp_ctx);
+
+ if (req->smb_conn->negotiate.encrypted_passwords)
+ secword |= NEGOTIATE_SECURITY_CHALLENGE_RESPONSE;
+
+ req->smb_conn->negotiate.protocol = PROTOCOL_LANMAN1;
+
+ smbsrv_setup_reply(req, 13, req->smb_conn->negotiate.encrypted_passwords ? 8 : 0);
+
+ /* SMBlockread, SMBwritelock supported. */
+ SCVAL(req->out.hdr,HDR_FLG,
+ CVAL(req->out.hdr, HDR_FLG) | FLAG_SUPPORT_LOCKREAD);
+
+ SSVAL(req->out.vwv, VWV(0), choice);
+ SSVAL(req->out.vwv, VWV(1), secword);
+ SSVAL(req->out.vwv, VWV(2), req->smb_conn->negotiate.max_recv);
+ SSVAL(req->out.vwv, VWV(3), lpcfg_max_mux(req->smb_conn->lp_ctx));
+ 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.pid);
+ srv_push_dos_date(req->smb_conn, req->out.vwv, VWV(8), t);
+ SSVAL(req->out.vwv, VWV(10), req->smb_conn->negotiate.zone_offset/60);
+ SIVAL(req->out.vwv, VWV(11), 0); /* reserved */
+
+ /* Create a token value and add it to the outgoing packet. */
+ if (req->smb_conn->negotiate.encrypted_passwords) {
+ NTSTATUS nt_status;
+
+ SSVAL(req->out.vwv, VWV(11), 8);
+
+ nt_status = get_challenge(req->smb_conn, req->out.data);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ smbsrv_terminate_connection(req->smb_conn, "LANMAN1 get_challenge failed\n");
+ return;
+ }
+ }
+
+ if (req->smb_conn->signing.mandatory_signing) {
+ smbsrv_terminate_connection(req->smb_conn,
+ "LANMAN1 does not support SMB signing, and it is mandatory\n");
+ return;
+ }
+
+ smbsrv_send_reply(req);
+}
+
+/****************************************************************************
+ Reply for the lanman 2.0 protocol.
+****************************************************************************/
+static void reply_lanman2(struct smbsrv_request *req, uint16_t choice)
+{
+ int secword=0;
+ time_t t = req->request_time.tv_sec;
+ uint16_t raw;
+ if (lpcfg_async_smb_echo_handler(req->smb_conn->lp_ctx)) {
+ raw = 0;
+ } else {
+ raw = (lpcfg_read_raw(req->smb_conn->lp_ctx)?1:0) |
+ (lpcfg_write_raw(req->smb_conn->lp_ctx)?2:0);
+ }
+
+ req->smb_conn->negotiate.encrypted_passwords = lpcfg_encrypt_passwords(req->smb_conn->lp_ctx);
+
+ if (req->smb_conn->negotiate.encrypted_passwords)
+ secword |= NEGOTIATE_SECURITY_CHALLENGE_RESPONSE;
+
+ req->smb_conn->negotiate.protocol = PROTOCOL_LANMAN2;
+
+ smbsrv_setup_reply(req, 13, 0);
+
+ SSVAL(req->out.vwv, VWV(0), choice);
+ SSVAL(req->out.vwv, VWV(1), secword);
+ SSVAL(req->out.vwv, VWV(2), req->smb_conn->negotiate.max_recv);
+ SSVAL(req->out.vwv, VWV(3), lpcfg_max_mux(req->smb_conn->lp_ctx));
+ 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.pid);
+ 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, lpcfg_workgroup(req->smb_conn->lp_ctx), -1, STR_TERMINATE);
+
+ if (req->smb_conn->signing.mandatory_signing) {
+ smbsrv_terminate_connection(req->smb_conn,
+ "LANMAN2 does not support SMB signing, and it is mandatory\n");
+ return;
+ }
+
+ smbsrv_send_reply(req);
+}
+
+static void reply_nt1_orig(struct smbsrv_request *req)
+{
+ /* Create a token value and add it to the outgoing packet. */
+ if (req->smb_conn->negotiate.encrypted_passwords) {
+ req_grow_data(req, 8);
+ /* note that we do not send a challenge at all if
+ we are using plaintext */
+ get_challenge(req->smb_conn, req->out.ptr);
+ req->out.ptr += 8;
+ SCVAL(req->out.vwv+1, VWV(16), 8);
+ }
+ req_push_str(req, NULL, lpcfg_workgroup(req->smb_conn->lp_ctx), -1, STR_UNICODE|STR_TERMINATE|STR_NOALIGN);
+ req_push_str(req, NULL, lpcfg_netbios_name(req->smb_conn->lp_ctx), -1, STR_UNICODE|STR_TERMINATE|STR_NOALIGN);
+ DEBUG(3,("not using extended security (SPNEGO or NTLMSSP)\n"));
+}
+
+/*
+ try to determine if the filesystem supports large files
+*/
+static bool large_file_support(const char *path)
+{
+ int fd;
+ ssize_t ret;
+ char c;
+
+ fd = open(path, O_RDWR|O_CREAT, 0600);
+ unlink(path);
+ if (fd == -1) {
+ /* have to assume large files are OK */
+ return true;
+ }
+ ret = pread(fd, &c, 1, ((uint64_t)1)<<32);
+ close(fd);
+ return ret == 0;
+}
+
+/****************************************************************************
+ Reply for the nt protocol.
+****************************************************************************/
+static void reply_nt1(struct smbsrv_request *req, uint16_t choice)
+{
+ /* dual names + lock_and_read + nt SMBs + remote API calls */
+ int capabilities;
+ int secword=0;
+ time_t t = req->request_time.tv_sec;
+ NTTIME nttime;
+ bool negotiate_spnego = false;
+ char *large_test_path;
+
+ unix_to_nt_time(&nttime, t);
+
+ capabilities =
+ CAP_NT_FIND | CAP_LOCK_AND_READ |
+ CAP_LEVEL_II_OPLOCKS | CAP_NT_SMBS | CAP_RPC_REMOTE_APIS;
+
+ req->smb_conn->negotiate.encrypted_passwords = lpcfg_encrypt_passwords(req->smb_conn->lp_ctx);
+
+ /* do spnego in user level security if the client
+ supports it and we can do encrypted passwords */
+
+ if (req->smb_conn->negotiate.encrypted_passwords &&
+ (req->flags2 & FLAGS2_EXTENDED_SECURITY)) {
+ negotiate_spnego = true;
+ capabilities |= CAP_EXTENDED_SECURITY;
+ }
+
+ if (lpcfg_large_readwrite(req->smb_conn->lp_ctx)) {
+ capabilities |= CAP_LARGE_READX | CAP_LARGE_WRITEX | CAP_W2K_SMBS;
+ }
+
+ large_test_path = lpcfg_lock_path(req, req->smb_conn->lp_ctx, "large_test.dat");
+ if (large_file_support(large_test_path)) {
+ capabilities |= CAP_LARGE_FILES;
+ }
+
+ if (!lpcfg_async_smb_echo_handler(req->smb_conn->lp_ctx) &&
+ lpcfg_read_raw(req->smb_conn->lp_ctx) &&
+ lpcfg_write_raw(req->smb_conn->lp_ctx)) {
+ capabilities |= CAP_RAW_MODE;
+ }
+
+ /* allow for disabling unicode */
+ if (lpcfg_unicode(req->smb_conn->lp_ctx)) {
+ capabilities |= CAP_UNICODE;
+ }
+
+ if (lpcfg_nt_status_support(req->smb_conn->lp_ctx)) {
+ capabilities |= CAP_STATUS32;
+ }
+
+ if (lpcfg_host_msdfs(req->smb_conn->lp_ctx)) {
+ capabilities |= CAP_DFS;
+ }
+
+ secword |= NEGOTIATE_SECURITY_USER_LEVEL;
+
+ if (req->smb_conn->negotiate.encrypted_passwords) {
+ secword |= NEGOTIATE_SECURITY_CHALLENGE_RESPONSE;
+ }
+
+ if (req->smb_conn->signing.allow_smb_signing) {
+ secword |= NEGOTIATE_SECURITY_SIGNATURES_ENABLED;
+ }
+
+ if (req->smb_conn->signing.mandatory_signing) {
+ secword |= NEGOTIATE_SECURITY_SIGNATURES_REQUIRED;
+ }
+
+ req->smb_conn->negotiate.protocol = PROTOCOL_NT1;
+
+ smbsrv_setup_reply(req, 17, 0);
+
+ SSVAL(req->out.vwv, VWV(0), choice);
+ SCVAL(req->out.vwv, VWV(1), secword);
+
+ /* notice the strange +1 on vwv here? That's because
+ this is the one and only SMB packet that is malformed in
+ the specification - all the command words after the secword
+ are offset by 1 byte */
+ SSVAL(req->out.vwv+1, VWV(1), lpcfg_max_mux(req->smb_conn->lp_ctx));
+ 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.pid); /* session key */
+
+ SIVAL(req->out.vwv+1, VWV(9), capabilities);
+ push_nttime(req->out.vwv+1, VWV(11), nttime);
+ SSVALS(req->out.vwv+1,VWV(15), req->smb_conn->negotiate.zone_offset/60);
+
+ if (!negotiate_spnego) {
+ reply_nt1_orig(req);
+ } else {
+ struct cli_credentials *server_credentials;
+ struct gensec_security *gensec_security;
+ DATA_BLOB null_data_blob = data_blob(NULL, 0);
+ DATA_BLOB blob = data_blob_null;
+ const char *oid;
+ NTSTATUS nt_status;
+
+ server_credentials =
+ cli_credentials_init_server(req, req->smb_conn->lp_ctx);
+ if (server_credentials == NULL) {
+ DBG_DEBUG("Failed to obtain server credentials, "
+ "perhaps a standalone server?\n");
+ /*
+ * Create anon server credentials for for the
+ * spoolss.notify test.
+ */
+ server_credentials = cli_credentials_init_anon(req);
+ if (server_credentials == NULL) {
+ smbsrv_terminate_connection(req->smb_conn,
+ "Failed to init server credentials\n");
+ return;
+ }
+ }
+
+ nt_status = samba_server_gensec_start(req,
+ req->smb_conn->connection->event.ctx,
+ req->smb_conn->connection->msg_ctx,
+ req->smb_conn->lp_ctx,
+ server_credentials,
+ "cifs",
+ &gensec_security);
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(0, ("Failed to start GENSEC: %s\n", nt_errstr(nt_status)));
+ smbsrv_terminate_connection(req->smb_conn, "Failed to start GENSEC\n");
+ return;
+ }
+
+ 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;
+ }
+ req->smb_conn->negotiate.server_credentials = talloc_reparent(req, req->smb_conn, server_credentials);
+
+ oid = GENSEC_OID_SPNEGO;
+ nt_status = gensec_start_mech_by_oid(gensec_security, oid);
+
+ if (NT_STATUS_IS_OK(nt_status)) {
+ /* Get and push the proposed OID list into the packets */
+ nt_status = gensec_update(gensec_security, req,
+ null_data_blob, &blob);
+
+ if (!NT_STATUS_IS_OK(nt_status) && !NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ DEBUG(1, ("Failed to get SPNEGO to give us the first token: %s\n", nt_errstr(nt_status)));
+ }
+ }
+
+ if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ DEBUG(3,("using SPNEGO\n"));
+ } else {
+ DEBUG(5, ("Failed to start SPNEGO, falling back to NTLMSSP only: %s\n", nt_errstr(nt_status)));
+ oid = GENSEC_OID_NTLMSSP;
+ nt_status = gensec_start_mech_by_oid(gensec_security, oid);
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(0, ("Failed to start SPNEGO as well as NTLMSSP fallback: %s\n", nt_errstr(nt_status)));
+ reply_nt1_orig(req);
+ return;
+ }
+ /* NTLMSSP is a client-first exchange */
+ blob = data_blob(NULL, 0);
+ DEBUG(3,("using raw-NTLMSSP\n"));
+ }
+
+ req->smb_conn->negotiate.oid = oid;
+
+ req_grow_data(req, blob.length + 16);
+ /* a NOT very random guid, perhaps we should get it
+ * from the credentials (kitchen sink...) */
+ memset(req->out.ptr, '\0', 16);
+ req->out.ptr += 16;
+
+ memcpy(req->out.ptr, blob.data, blob.length);
+ SCVAL(req->out.vwv+1, VWV(16), blob.length + 16);
+ req->out.ptr += blob.length;
+ }
+
+ smbsrv_send_reply_nosign(req);
+}
+
+/****************************************************************************
+ Reply for the SMB2 2.001 protocol
+****************************************************************************/
+static void reply_smb2(struct smbsrv_request *req, uint16_t choice)
+{
+ struct smbsrv_connection *smb_conn = req->smb_conn;
+ NTSTATUS status;
+
+ talloc_free(smb_conn->sessions.idtree_vuid);
+ ZERO_STRUCT(smb_conn->sessions);
+ talloc_free(smb_conn->smb_tcons.idtree_tid);
+ ZERO_STRUCT(smb_conn->smb_tcons);
+ ZERO_STRUCT(smb_conn->signing);
+
+ /* reply with a SMB2 packet */
+ status = smbsrv_init_smb2_connection(smb_conn);
+ if (!NT_STATUS_IS_OK(status)) {
+ smbsrv_terminate_connection(smb_conn, nt_errstr(status));
+ talloc_free(req);
+ return;
+ }
+ packet_set_callback(smb_conn->packet, smbsrv_recv_smb2_request);
+ smb2srv_reply_smb_negprot(req);
+ req = NULL;
+}
+
+/* List of supported protocols, most desired first */
+static const struct {
+ const char *proto_name;
+ const char *short_name;
+ void (*proto_reply_fn)(struct smbsrv_request *req, uint16_t choice);
+ int protocol_level;
+} supported_protocols[] = {
+ {"SMB 2.002", "SMB2", reply_smb2, PROTOCOL_SMB2_02},
+ {"NT LANMAN 1.0", "NT1", reply_nt1, PROTOCOL_NT1},
+ {"NT LM 0.12", "NT1", reply_nt1, PROTOCOL_NT1},
+ {"LANMAN2.1", "LANMAN2", reply_lanman2, PROTOCOL_LANMAN2},
+ {"LM1.2X002", "LANMAN2", reply_lanman2, PROTOCOL_LANMAN2},
+ {"Samba", "LANMAN2", reply_lanman2, PROTOCOL_LANMAN2},
+ {"DOS LM1.2X002", "LANMAN2", reply_lanman2, PROTOCOL_LANMAN2},
+ {"Windows for Workgroups 3.1a", "LANMAN1", reply_lanman1, PROTOCOL_LANMAN1},
+ {"LANMAN1.0", "LANMAN1", reply_lanman1, PROTOCOL_LANMAN1},
+ {"MICROSOFT NETWORKS 3.0", "LANMAN1", reply_lanman1, PROTOCOL_LANMAN1},
+ {"MICROSOFT NETWORKS 1.03", "COREPLUS", reply_coreplus, PROTOCOL_COREPLUS},
+ {"PC NETWORK PROGRAM 1.0", "CORE", reply_corep, PROTOCOL_CORE},
+ {NULL,NULL,NULL,0},
+};
+
+/****************************************************************************
+ Reply to a negprot.
+****************************************************************************/
+
+void smbsrv_reply_negprot(struct smbsrv_request *req)
+{
+ int protocol;
+ uint8_t *p;
+ uint32_t protos_count = 0;
+ const char **protos = NULL;
+
+ if (req->smb_conn->negotiate.done_negprot) {
+ smbsrv_terminate_connection(req->smb_conn, "multiple negprot's are not permitted");
+ return;
+ }
+ req->smb_conn->negotiate.done_negprot = true;
+
+ p = req->in.data;
+ while (true) {
+ size_t len;
+
+ protos = talloc_realloc(req, protos, const char *, protos_count + 1);
+ if (!protos) {
+ smbsrv_terminate_connection(req->smb_conn, nt_errstr(NT_STATUS_NO_MEMORY));
+ return;
+ }
+ protos[protos_count] = NULL;
+ len = req_pull_ascii4(&req->in.bufinfo, &protos[protos_count], p, STR_ASCII|STR_TERMINATE);
+ p += len;
+ if (len == 0 || !protos[protos_count]) break;
+
+ DEBUG(5,("Requested protocol [%d][%s]\n", protos_count, protos[protos_count]));
+ protos_count++;
+ }
+
+ /* Check for protocols, most desirable first */
+ for (protocol = 0; supported_protocols[protocol].proto_name; protocol++) {
+ int i;
+
+ if (supported_protocols[protocol].protocol_level > lpcfg_server_max_protocol(req->smb_conn->lp_ctx))
+ continue;
+ if (supported_protocols[protocol].protocol_level < lpcfg_server_min_protocol(req->smb_conn->lp_ctx))
+ continue;
+
+ for (i = 0; i < protos_count; i++) {
+ if (strcmp(supported_protocols[protocol].proto_name, protos[i]) != 0) continue;
+
+ supported_protocols[protocol].proto_reply_fn(req, i);
+ DEBUG(3,("Selected protocol [%d][%s]\n",
+ i, supported_protocols[protocol].proto_name));
+ return;
+ }
+ }
+
+ smbsrv_terminate_connection(req->smb_conn, "No protocol supported !");
+}
diff --git a/source4/smb_server/smb/nttrans.c b/source4/smb_server/smb/nttrans.c
new file mode 100644
index 0000000..8e4d004
--- /dev/null
+++ b/source4/smb_server/smb/nttrans.c
@@ -0,0 +1,815 @@
+/*
+ Unix SMB/CIFS implementation.
+ NT transaction handling
+ Copyright (C) Andrew Tridgell 2003
+ Copyright (C) James J Myers 2003 <myersjj@samba.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+/*
+ This file handles the parsing of transact2 requests
+*/
+
+#include "includes.h"
+#include "smb_server/smb_server.h"
+#include "ntvfs/ntvfs.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "librpc/gen_ndr/ndr_security.h"
+
+/*
+ hold the state of a nttrans op while in progress. Needed to allow for async backend
+ functions.
+*/
+struct nttrans_op {
+ struct smb_nttrans *trans;
+ NTSTATUS (*send_fn)(struct nttrans_op *);
+ void *op_info;
+};
+
+
+/* setup a nttrans reply, given the data and params sizes */
+static NTSTATUS nttrans_setup_reply(struct nttrans_op *op,
+ struct smb_nttrans *trans,
+ uint32_t param_size, uint32_t data_size,
+ uint8_t setup_count)
+{
+ trans->out.setup_count = setup_count;
+ if (setup_count != 0) {
+ trans->out.setup = talloc_zero_array(op, uint8_t, setup_count*2);
+ NT_STATUS_HAVE_NO_MEMORY(trans->out.setup);
+ }
+ trans->out.params = data_blob_talloc(op, NULL, param_size);
+ if (param_size != 0) {
+ NT_STATUS_HAVE_NO_MEMORY(trans->out.params.data);
+ }
+ trans->out.data = data_blob_talloc(op, NULL, data_size);
+ if (data_size != 0) {
+ NT_STATUS_HAVE_NO_MEMORY(trans->out.data.data);
+ }
+ return NT_STATUS_OK;
+}
+
+/*
+ send a nttrans create reply
+*/
+static NTSTATUS nttrans_create_send(struct nttrans_op *op)
+{
+ union smb_open *io = talloc_get_type(op->op_info, union smb_open);
+ uint8_t *params;
+ NTSTATUS status;
+
+ status = nttrans_setup_reply(op, op->trans, 69, 0, 0);
+ NT_STATUS_NOT_OK_RETURN(status);
+ params = op->trans->out.params.data;
+
+ SSVAL(params, 0, io->ntcreatex.out.oplock_level);
+ smbsrv_push_fnum(params, 2, io->ntcreatex.out.file.ntvfs);
+ SIVAL(params, 4, io->ntcreatex.out.create_action);
+ SIVAL(params, 8, 0); /* ea error offset */
+ push_nttime(params, 12, io->ntcreatex.out.create_time);
+ push_nttime(params, 20, io->ntcreatex.out.access_time);
+ push_nttime(params, 28, io->ntcreatex.out.write_time);
+ push_nttime(params, 36, io->ntcreatex.out.change_time);
+ SIVAL(params, 44, io->ntcreatex.out.attrib);
+ SBVAL(params, 48, io->ntcreatex.out.alloc_size);
+ SBVAL(params, 56, io->ntcreatex.out.size);
+ SSVAL(params, 64, io->ntcreatex.out.file_type);
+ SSVAL(params, 66, io->ntcreatex.out.ipc_state);
+ SCVAL(params, 68, io->ntcreatex.out.is_directory);
+
+ return NT_STATUS_OK;
+}
+
+/*
+ parse NTTRANS_CREATE request
+ */
+static NTSTATUS nttrans_create(struct smbsrv_request *req,
+ struct nttrans_op *op)
+{
+ struct smb_nttrans *trans = op->trans;
+ union smb_open *io;
+ uint16_t fname_len;
+ uint32_t sd_length, ea_length;
+ NTSTATUS status;
+ uint8_t *params;
+ enum ndr_err_code ndr_err;
+
+ if (trans->in.params.length < 54) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* parse the request */
+ io = talloc(op, union smb_open);
+ NT_STATUS_HAVE_NO_MEMORY(io);
+
+ io->ntcreatex.level = RAW_OPEN_NTTRANS_CREATE;
+
+ params = trans->in.params.data;
+
+ io->ntcreatex.in.flags = IVAL(params, 0);
+ io->ntcreatex.in.root_fid.ntvfs = smbsrv_pull_fnum(req, 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;
+ io->ntcreatex.in.query_maximal_access = false;
+ io->ntcreatex.in.query_on_disk_id = false;
+ io->ntcreatex.in.private_flags = 0;
+
+ req_pull_string(&req->in.bufinfo, &io->ntcreatex.in.fname,
+ params + 53,
+ MIN(fname_len+1, trans->in.params.length - 53),
+ STR_NO_RANGE_CHECK | STR_TERMINATE);
+ if (!io->ntcreatex.in.fname) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (sd_length > trans->in.data.length ||
+ ea_length > trans->in.data.length ||
+ (sd_length+ea_length) > trans->in.data.length) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* this call has an optional security descriptor */
+ if (sd_length != 0) {
+ DATA_BLOB blob;
+ blob.data = trans->in.data.data;
+ blob.length = sd_length;
+ io->ntcreatex.in.sec_desc = talloc(io, struct security_descriptor);
+ if (io->ntcreatex.in.sec_desc == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ ndr_err = ndr_pull_struct_blob(&blob, io,
+ io->ntcreatex.in.sec_desc,
+ (ndr_pull_flags_fn_t)ndr_pull_security_descriptor);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return ndr_map_error2ntstatus(ndr_err);
+ }
+ }
+
+ /* and an optional ea_list */
+ if (ea_length > 4) {
+ DATA_BLOB blob;
+ blob.data = trans->in.data.data + sd_length;
+ blob.length = ea_length;
+ io->ntcreatex.in.ea_list = talloc(io, struct smb_ea_list);
+ if (io->ntcreatex.in.ea_list == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = ea_pull_list_chained(&blob, io,
+ &io->ntcreatex.in.ea_list->num_eas,
+ &io->ntcreatex.in.ea_list->eas);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ }
+
+ op->send_fn = nttrans_create_send;
+ op->op_info = io;
+
+ return ntvfs_open(req->ntvfs, io);
+}
+
+
+/*
+ send NTTRANS_QUERY_SEC_DESC reply
+ */
+static NTSTATUS nttrans_query_sec_desc_send(struct nttrans_op *op)
+{
+ union smb_fileinfo *io = talloc_get_type(op->op_info, union smb_fileinfo);
+ uint8_t *params;
+ NTSTATUS status;
+ enum ndr_err_code ndr_err;
+
+ status = nttrans_setup_reply(op, op->trans, 4, 0, 0);
+ NT_STATUS_NOT_OK_RETURN(status);
+ params = op->trans->out.params.data;
+
+ ndr_err = ndr_push_struct_blob(&op->trans->out.data, op,
+ io->query_secdesc.out.sd,
+ (ndr_push_flags_fn_t)ndr_push_security_descriptor);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return ndr_map_error2ntstatus(ndr_err);
+ }
+
+ SIVAL(params, 0, op->trans->out.data.length);
+
+ return NT_STATUS_OK;
+}
+
+/*
+ parse NTTRANS_QUERY_SEC_DESC request
+ */
+static NTSTATUS nttrans_query_sec_desc(struct smbsrv_request *req,
+ struct nttrans_op *op)
+{
+ struct smb_nttrans *trans = op->trans;
+ union smb_fileinfo *io;
+
+ if (trans->in.params.length < 8) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* parse the request */
+ io = talloc(op, union smb_fileinfo);
+ NT_STATUS_HAVE_NO_MEMORY(io);
+
+ io->query_secdesc.level = RAW_FILEINFO_SEC_DESC;
+ io->query_secdesc.in.file.ntvfs = smbsrv_pull_fnum(req, trans->in.params.data, 0);
+ io->query_secdesc.in.secinfo_flags = IVAL(trans->in.params.data, 4);
+
+ op->op_info = io;
+ op->send_fn = nttrans_query_sec_desc_send;
+
+ SMBSRV_CHECK_FILE_HANDLE_NTSTATUS(io->query_secdesc.in.file.ntvfs);
+ return ntvfs_qfileinfo(req->ntvfs, io);
+}
+
+
+/*
+ parse NTTRANS_SET_SEC_DESC request
+ */
+static NTSTATUS nttrans_set_sec_desc(struct smbsrv_request *req,
+ struct nttrans_op *op)
+{
+ struct smb_nttrans *trans = op->trans;
+ union smb_setfileinfo *io;
+ enum ndr_err_code ndr_err;
+
+ if (trans->in.params.length < 8) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* parse the request */
+ io = talloc(req, union smb_setfileinfo);
+ NT_STATUS_HAVE_NO_MEMORY(io);
+
+ io->set_secdesc.level = RAW_SFILEINFO_SEC_DESC;
+ io->set_secdesc.in.file.ntvfs = smbsrv_pull_fnum(req, trans->in.params.data, 0);
+ io->set_secdesc.in.secinfo_flags = IVAL(trans->in.params.data, 4);
+
+ io->set_secdesc.in.sd = talloc(io, struct security_descriptor);
+ NT_STATUS_HAVE_NO_MEMORY(io->set_secdesc.in.sd);
+
+ ndr_err = ndr_pull_struct_blob(&trans->in.data, req,
+ io->set_secdesc.in.sd,
+ (ndr_pull_flags_fn_t)ndr_pull_security_descriptor);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return ndr_map_error2ntstatus(ndr_err);
+ }
+
+ SMBSRV_CHECK_FILE_HANDLE_NTSTATUS(io->set_secdesc.in.file.ntvfs);
+ return ntvfs_setfileinfo(req->ntvfs, io);
+}
+
+
+/* parse NTTRANS_RENAME request
+ */
+static NTSTATUS nttrans_rename(struct smbsrv_request *req,
+ struct nttrans_op *op)
+{
+ struct smb_nttrans *trans = op->trans;
+ union smb_rename *io;
+
+ if (trans->in.params.length < 5) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* parse the request */
+ io = talloc(req, union smb_rename);
+ NT_STATUS_HAVE_NO_MEMORY(io);
+
+ io->nttrans.level = RAW_RENAME_NTTRANS;
+ io->nttrans.in.file.ntvfs = smbsrv_pull_fnum(req, trans->in.params.data, 0);
+ io->nttrans.in.flags = SVAL(trans->in.params.data, 2);
+
+ smbsrv_blob_pull_string(&req->in.bufinfo, &trans->in.params, 4,
+ &io->nttrans.in.new_name,
+ STR_TERMINATE);
+ if (!io->nttrans.in.new_name) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ SMBSRV_CHECK_FILE_HANDLE_NTSTATUS(io->nttrans.in.file.ntvfs);
+ return ntvfs_rename(req->ntvfs, io);
+}
+
+/*
+ parse NTTRANS_IOCTL send
+ */
+static NTSTATUS nttrans_ioctl_send(struct nttrans_op *op)
+{
+ union smb_ioctl *info = talloc_get_type(op->op_info, union smb_ioctl);
+ NTSTATUS status;
+
+ /*
+ * we pass 0 as data_count here,
+ * because we reuse the DATA_BLOB from the smb_ioctl
+ * struct
+ */
+ status = nttrans_setup_reply(op, op->trans, 0, 0, 1);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ op->trans->out.setup[0] = 0;
+ op->trans->out.data = info->ntioctl.out.blob;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ parse NTTRANS_IOCTL request
+ */
+static NTSTATUS nttrans_ioctl(struct smbsrv_request *req,
+ struct nttrans_op *op)
+{
+ struct smb_nttrans *trans = op->trans;
+ union smb_ioctl *nt;
+
+ /* should have at least 4 setup words */
+ if (trans->in.setup_count != 4) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ nt = talloc(op, union smb_ioctl);
+ NT_STATUS_HAVE_NO_MEMORY(nt);
+
+ nt->ntioctl.level = RAW_IOCTL_NTIOCTL;
+ nt->ntioctl.in.function = IVAL(trans->in.setup, 0);
+ nt->ntioctl.in.file.ntvfs = smbsrv_pull_fnum(req, (uint8_t *)trans->in.setup, 4);
+ nt->ntioctl.in.fsctl = CVAL(trans->in.setup, 6);
+ nt->ntioctl.in.filter = CVAL(trans->in.setup, 7);
+ nt->ntioctl.in.max_data = trans->in.max_data;
+ nt->ntioctl.in.blob = trans->in.data;
+
+ op->op_info = nt;
+ op->send_fn = nttrans_ioctl_send;
+
+ SMBSRV_CHECK_FILE_HANDLE_NTSTATUS(nt->ntioctl.in.file.ntvfs);
+ return ntvfs_ioctl(req->ntvfs, nt);
+}
+
+
+/*
+ send NTTRANS_NOTIFY_CHANGE reply
+ */
+static NTSTATUS nttrans_notify_change_send(struct nttrans_op *op)
+{
+ union smb_notify *info = talloc_get_type(op->op_info, union smb_notify);
+ size_t size = 0;
+ int i;
+ NTSTATUS status;
+ uint8_t *p;
+#define MAX_BYTES_PER_CHAR 3
+
+ /* work out how big the reply buffer could be */
+ for (i=0;i<info->nttrans.out.num_changes;i++) {
+ size += 12 + 3 + (1+strlen(info->nttrans.out.changes[i].name.s)) * MAX_BYTES_PER_CHAR;
+ }
+
+ status = nttrans_setup_reply(op, op->trans, size, 0, 0);
+ NT_STATUS_NOT_OK_RETURN(status);
+ p = op->trans->out.params.data;
+
+ /* construct the changes buffer */
+ for (i=0;i<info->nttrans.out.num_changes;i++) {
+ uint32_t ofs;
+ ssize_t len;
+
+ SIVAL(p, 4, info->nttrans.out.changes[i].action);
+ len = push_string(p + 12, info->nttrans.out.changes[i].name.s,
+ op->trans->out.params.length -
+ (p+12 - op->trans->out.params.data), STR_UNICODE);
+ SIVAL(p, 8, len);
+
+ ofs = len + 12;
+
+ if (ofs & 3) {
+ int pad = 4 - (ofs & 3);
+ memset(p+ofs, 0, pad);
+ ofs += pad;
+ }
+
+ if (i == info->nttrans.out.num_changes-1) {
+ SIVAL(p, 0, 0);
+ } else {
+ SIVAL(p, 0, ofs);
+ }
+
+ p += ofs;
+ }
+
+ op->trans->out.params.length = p - op->trans->out.params.data;
+
+ return NT_STATUS_OK;
+}
+
+/*
+ parse NTTRANS_NOTIFY_CHANGE request
+ */
+static NTSTATUS nttrans_notify_change(struct smbsrv_request *req,
+ struct nttrans_op *op)
+{
+ struct smb_nttrans *trans = op->trans;
+ union smb_notify *info;
+
+ /* should have at least 4 setup words */
+ if (trans->in.setup_count != 4) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ info = talloc(op, union smb_notify);
+ NT_STATUS_HAVE_NO_MEMORY(info);
+
+ info->nttrans.level = RAW_NOTIFY_NTTRANS;
+ info->nttrans.in.completion_filter = IVAL(trans->in.setup, 0);
+ info->nttrans.in.file.ntvfs = smbsrv_pull_fnum(req, (uint8_t *)trans->in.setup, 4);
+ info->nttrans.in.recursive = SVAL(trans->in.setup, 6);
+ info->nttrans.in.buffer_size = trans->in.max_param;
+
+ op->op_info = info;
+ op->send_fn = nttrans_notify_change_send;
+
+ SMBSRV_CHECK_FILE_HANDLE_NTSTATUS(info->nttrans.in.file.ntvfs);
+ return ntvfs_notify(req->ntvfs, info);
+}
+
+/*
+ backend for nttrans requests
+*/
+static NTSTATUS nttrans_backend(struct smbsrv_request *req,
+ struct nttrans_op *op)
+{
+ /* the nttrans command is in function */
+ switch (op->trans->in.function) {
+ case NT_TRANSACT_CREATE:
+ return nttrans_create(req, op);
+ case NT_TRANSACT_IOCTL:
+ return nttrans_ioctl(req, op);
+ case NT_TRANSACT_RENAME:
+ return nttrans_rename(req, op);
+ case NT_TRANSACT_QUERY_SECURITY_DESC:
+ return nttrans_query_sec_desc(req, op);
+ case NT_TRANSACT_SET_SECURITY_DESC:
+ return nttrans_set_sec_desc(req, op);
+ case NT_TRANSACT_NOTIFY_CHANGE:
+ return nttrans_notify_change(req, op);
+ }
+
+ /* an unknown nttrans command */
+ return NT_STATUS_DOS(ERRSRV, ERRerror);
+}
+
+
+static void reply_nttrans_send(struct ntvfs_request *ntvfs)
+{
+ struct smbsrv_request *req;
+ uint32_t params_left, data_left;
+ uint8_t *params, *data;
+ struct smb_nttrans *trans;
+ struct nttrans_op *op;
+
+ SMBSRV_CHECK_ASYNC_STATUS(op, struct nttrans_op);
+
+ trans = op->trans;
+
+ /* if this function needs work to form the nttrans reply buffer, then
+ call that now */
+ if (op->send_fn != NULL) {
+ NTSTATUS status;
+ status = op->send_fn(op);
+ if (!NT_STATUS_IS_OK(status)) {
+ smbsrv_send_error(req, status);
+ return;
+ }
+ }
+
+ smbsrv_setup_reply(req, 18 + trans->out.setup_count, 0);
+
+ /* note that we don't check the max_setup count (matching w2k3
+ behaviour) */
+
+ if (trans->out.params.length > trans->in.max_param) {
+ smbsrv_setup_error(req, NT_STATUS_BUFFER_TOO_SMALL);
+ trans->out.params.length = trans->in.max_param;
+ }
+ if (trans->out.data.length > trans->in.max_data) {
+ smbsrv_setup_error(req, NT_STATUS_BUFFER_TOO_SMALL);
+ trans->out.data.length = trans->in.max_data;
+ }
+
+ params_left = trans->out.params.length;
+ data_left = trans->out.data.length;
+ params = trans->out.params.data;
+ data = trans->out.data.data;
+
+ /* we need to divide up the reply into chunks that fit into
+ the negotiated buffer size */
+ do {
+ uint32_t this_data, this_param, max_bytes;
+ unsigned int 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 = smbsrv_setup_secondary_request(req);
+ } else {
+ this_req = req;
+ }
+
+ req_grow_data(this_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);
+ if (trans->out.setup_count > 0) {
+ memcpy((char *)(this_req->out.vwv) + VWV(18),
+ trans->out.setup,
+ sizeof(uint16_t) * trans->out.setup_count);
+ }
+ memset(this_req->out.data, 0, align1);
+ if (this_param != 0) {
+ memcpy(this_req->out.data + align1, params, this_param);
+ }
+ memset(this_req->out.data+this_param+align1, 0, align2);
+ if (this_data != 0) {
+ memcpy(this_req->out.data+this_param+align1+align2,
+ data, this_data);
+ }
+
+ params_left -= this_param;
+ data_left -= this_data;
+ params += this_param;
+ data += this_data;
+
+ smbsrv_send_reply(this_req);
+ } while (params_left != 0 || data_left != 0);
+}
+
+/*
+ send a continue request
+*/
+static void reply_nttrans_continue(struct smbsrv_request *req, struct smb_nttrans *trans)
+{
+ struct smbsrv_request *req2;
+ 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) {
+ smbsrv_send_error(req, NT_STATUS_INSUFFICIENT_RESOURCES);
+ return;
+ }
+
+ tp = talloc(req, struct smbsrv_trans_partial);
+
+ tp->req = req;
+ tp->u.nttrans = trans;
+ tp->command = SMBnttrans;
+
+ DLIST_ADD(req->smb_conn->trans_partial, tp);
+ talloc_set_destructor(tp, smbsrv_trans_partial_destructor);
+
+ req2 = smbsrv_setup_secondary_request(req);
+
+ /* send a 'please continue' reply */
+ smbsrv_setup_reply(req2, 0, 0);
+ smbsrv_send_reply(req2);
+}
+
+
+/*
+ answer a reconstructed trans request
+*/
+static void reply_nttrans_complete(struct smbsrv_request *req, struct smb_nttrans *trans)
+{
+ struct nttrans_op *op;
+
+ SMBSRV_TALLOC_IO_PTR(op, struct nttrans_op);
+ SMBSRV_SETUP_NTVFS_REQUEST(reply_nttrans_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ op->trans = trans;
+ op->op_info = NULL;
+ op->send_fn = NULL;
+
+ /* its a full request, give it to the backend */
+ ZERO_STRUCT(trans->out);
+ SMBSRV_CALL_NTVFS_BACKEND(nttrans_backend(req, op));
+}
+
+
+/****************************************************************************
+ Reply to an SMBnttrans request
+****************************************************************************/
+void smbsrv_reply_nttrans(struct smbsrv_request *req)
+{
+ struct smb_nttrans *trans;
+ uint32_t param_ofs, data_ofs;
+ uint32_t param_count, data_count;
+ uint32_t param_total, data_total;
+
+ /* parse request */
+ if (req->in.wct < 19) {
+ smbsrv_send_error(req, NT_STATUS_FOOBAR);
+ return;
+ }
+
+ trans = talloc(req, struct smb_nttrans);
+ if (trans == NULL) {
+ smbsrv_send_error(req, NT_STATUS_NO_MEMORY);
+ 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) {
+ smbsrv_send_error(req, NT_STATUS_DOS(ERRSRV, ERRerror));
+ return;
+ }
+
+ /* parse out the setup words */
+ trans->in.setup = talloc_array(req, uint8_t, trans->in.setup_count*2);
+ if (!trans->in.setup) {
+ smbsrv_send_error(req, NT_STATUS_NO_MEMORY);
+ return;
+ }
+ memcpy(trans->in.setup, (char *)(req->in.vwv) + VWV(19),
+ sizeof(uint16_t) * trans->in.setup_count);
+
+ if (!req_pull_blob(&req->in.bufinfo, req->in.hdr + param_ofs, param_count, &trans->in.params) ||
+ !req_pull_blob(&req->in.bufinfo, req->in.hdr + data_ofs, data_count, &trans->in.data)) {
+ smbsrv_send_error(req, NT_STATUS_FOOBAR);
+ return;
+ }
+
+ /* is it a partial request? if so, then send a 'send more' message */
+ if (param_total > param_count || data_total > data_count) {
+ reply_nttrans_continue(req, trans);
+ return;
+ }
+
+ reply_nttrans_complete(req, trans);
+}
+
+
+/****************************************************************************
+ Reply to an SMBnttranss request
+****************************************************************************/
+void smbsrv_reply_nttranss(struct smbsrv_request *req)
+{
+ struct smbsrv_trans_partial *tp;
+ struct smb_nttrans *trans = NULL;
+ uint32_t param_ofs, data_ofs;
+ uint32_t param_count, data_count;
+ uint32_t param_disp, data_disp;
+ uint32_t param_total, data_total;
+ DATA_BLOB params, data;
+
+ /* parse request */
+ if (req->in.wct != 18) {
+ smbsrv_send_error(req, NT_STATUS_DOS(ERRSRV, ERRerror));
+ return;
+ }
+
+ for (tp=req->smb_conn->trans_partial;tp;tp=tp->next) {
+ if (tp->command == SMBnttrans &&
+ SVAL(tp->req->in.hdr, HDR_MID) == SVAL(req->in.hdr, HDR_MID)) {
+/* TODO: check the VUID, PID and TID too? */
+ break;
+ }
+ }
+
+ if (tp == NULL) {
+ smbsrv_send_error(req, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+
+ trans = tp->u.nttrans;
+
+ param_total = IVAL(req->in.vwv, 3);
+ data_total = IVAL(req->in.vwv, 7);
+ param_count = IVAL(req->in.vwv, 11);
+ param_ofs = IVAL(req->in.vwv, 15);
+ param_disp = IVAL(req->in.vwv, 19);
+ data_count = IVAL(req->in.vwv, 23);
+ data_ofs = IVAL(req->in.vwv, 27);
+ data_disp = IVAL(req->in.vwv, 31);
+
+ if (!req_pull_blob(&req->in.bufinfo, req->in.hdr + param_ofs, param_count, &params) ||
+ !req_pull_blob(&req->in.bufinfo, req->in.hdr + data_ofs, data_count, &data)) {
+ smbsrv_send_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)) {
+ smbsrv_send_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) {
+ smbsrv_send_error(tp->req, NT_STATUS_NO_MEMORY);
+ return;
+ }
+ 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) {
+ smbsrv_send_error(tp->req, NT_STATUS_NO_MEMORY);
+ return;
+ }
+ 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 */
+ req = tp->req;
+ talloc_free(tp);
+ reply_nttrans_complete(req, trans);
+ }
+ return;
+}
diff --git a/source4/smb_server/smb/receive.c b/source4/smb_server/smb/receive.c
new file mode 100644
index 0000000..b96cdbd
--- /dev/null
+++ b/source4/smb_server/smb/receive.c
@@ -0,0 +1,679 @@
+/*
+ Unix SMB/CIFS implementation.
+ process incoming packets - main loop
+ Copyright (C) Andrew Tridgell 1992-2005
+ Copyright (C) James J Myers 2003 <myersjj@samba.org>
+ Copyright (C) Stefan Metzmacher 2004-2005
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "system/time.h"
+#include "lib/util/server_id.h"
+#include "samba/service_stream.h"
+#include "smb_server/smb_server.h"
+#include "system/filesys.h"
+#include "param/param.h"
+#include "cluster/cluster.h"
+
+/*
+ send an oplock break request to a client
+*/
+NTSTATUS smbsrv_send_oplock_break(void *p, struct ntvfs_handle *ntvfs, uint8_t level)
+{
+ struct smbsrv_tcon *tcon = talloc_get_type(p, struct smbsrv_tcon);
+ struct smbsrv_request *req;
+
+ req = smbsrv_init_request(tcon->smb_conn);
+ NT_STATUS_HAVE_NO_MEMORY(req);
+
+ smbsrv_setup_reply(req, 8, 0);
+
+ SCVAL(req->out.hdr,HDR_COM,SMBlockingX);
+ SSVAL(req->out.hdr,HDR_TID,tcon->tid);
+ SSVAL(req->out.hdr,HDR_PID,0xFFFF);
+ SSVAL(req->out.hdr,HDR_UID,0);
+ SSVAL(req->out.hdr,HDR_MID,0xFFFF);
+ SCVAL(req->out.hdr,HDR_FLG,0);
+ SSVAL(req->out.hdr,HDR_FLG2,0);
+
+ SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE);
+ SSVAL(req->out.vwv, VWV(1), 0);
+ smbsrv_push_fnum(req->out.vwv, VWV(2), ntvfs);
+ SCVAL(req->out.vwv, VWV(3), LOCKING_ANDX_OPLOCK_RELEASE);
+ SCVAL(req->out.vwv, VWV(3)+1, level);
+ SIVAL(req->out.vwv, VWV(4), 0);
+ SSVAL(req->out.vwv, VWV(6), 0);
+ SSVAL(req->out.vwv, VWV(7), 0);
+
+ smbsrv_send_reply(req);
+ return NT_STATUS_OK;
+}
+
+static void switch_message(int type, struct smbsrv_request *req);
+
+/*
+ These flags determine some of the permissions required to do an operation
+*/
+#define NEED_SESS (1<<0)
+#define NEED_TCON (1<<1)
+#define SIGNING_NO_REPLY (1<<2)
+/* does VWV(0) of the request hold chaining information */
+#define AND_X (1<<3)
+/* The 64Kb question: are requests > 64K valid? */
+#define LARGE_REQUEST (1<<4)
+
+/*
+ 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 *);
+#define message_flags(type) smb_messages[(type) & 0xff].flags
+ int flags;
+}
+ smb_messages[256] = {
+/* 0x00 */ { "SMBmkdir", smbsrv_reply_mkdir, NEED_SESS|NEED_TCON },
+/* 0x01 */ { "SMBrmdir", smbsrv_reply_rmdir, NEED_SESS|NEED_TCON },
+/* 0x02 */ { "SMBopen", smbsrv_reply_open, NEED_SESS|NEED_TCON },
+/* 0x03 */ { "SMBcreate", smbsrv_reply_mknew, NEED_SESS|NEED_TCON },
+/* 0x04 */ { "SMBclose", smbsrv_reply_close, NEED_SESS|NEED_TCON },
+/* 0x05 */ { "SMBflush", smbsrv_reply_flush, NEED_SESS|NEED_TCON },
+/* 0x06 */ { "SMBunlink", smbsrv_reply_unlink, NEED_SESS|NEED_TCON },
+/* 0x07 */ { "SMBmv", smbsrv_reply_mv, NEED_SESS|NEED_TCON },
+/* 0x08 */ { "SMBgetatr", smbsrv_reply_getatr, NEED_SESS|NEED_TCON },
+/* 0x09 */ { "SMBsetatr", smbsrv_reply_setatr, NEED_SESS|NEED_TCON },
+/* 0x0a */ { "SMBread", smbsrv_reply_read, NEED_SESS|NEED_TCON },
+/* 0x0b */ { "SMBwrite", smbsrv_reply_write, NEED_SESS|NEED_TCON },
+/* 0x0c */ { "SMBlock", smbsrv_reply_lock, NEED_SESS|NEED_TCON },
+/* 0x0d */ { "SMBunlock", smbsrv_reply_unlock, NEED_SESS|NEED_TCON },
+/* 0x0e */ { "SMBctemp", smbsrv_reply_ctemp, NEED_SESS|NEED_TCON },
+/* 0x0f */ { "SMBmknew", smbsrv_reply_mknew, NEED_SESS|NEED_TCON },
+/* 0x10 */ { "SMBcheckpath", smbsrv_reply_chkpth, NEED_SESS|NEED_TCON },
+/* 0x11 */ { "SMBexit", smbsrv_reply_exit, NEED_SESS },
+/* 0x12 */ { "SMBlseek", smbsrv_reply_lseek, NEED_SESS|NEED_TCON },
+/* 0x13 */ { "SMBlockread", smbsrv_reply_lockread, NEED_SESS|NEED_TCON },
+/* 0x14 */ { "SMBwriteunlock", smbsrv_reply_writeunlock, NEED_SESS|NEED_TCON },
+/* 0x15 */ { NULL, NULL, 0 },
+/* 0x16 */ { NULL, NULL, 0 },
+/* 0x17 */ { NULL, NULL, 0 },
+/* 0x18 */ { NULL, NULL, 0 },
+/* 0x19 */ { NULL, NULL, 0 },
+/* 0x1a */ { "SMBreadbraw", smbsrv_reply_readbraw, NEED_SESS|NEED_TCON },
+/* 0x1b */ { "SMBreadBmpx", smbsrv_reply_readbmpx, NEED_SESS|NEED_TCON },
+/* 0x1c */ { "SMBreadBs", NULL, 0 },
+/* 0x1d */ { "SMBwritebraw", smbsrv_reply_writebraw, NEED_SESS|NEED_TCON },
+/* 0x1e */ { "SMBwriteBmpx", smbsrv_reply_writebmpx, NEED_SESS|NEED_TCON },
+/* 0x1f */ { "SMBwriteBs", smbsrv_reply_writebs, NEED_SESS|NEED_TCON },
+/* 0x20 */ { "SMBwritec", NULL, 0 },
+/* 0x21 */ { NULL, NULL, 0 },
+/* 0x22 */ { "SMBsetattrE", smbsrv_reply_setattrE, NEED_SESS|NEED_TCON },
+/* 0x23 */ { "SMBgetattrE", smbsrv_reply_getattrE, NEED_SESS|NEED_TCON },
+/* 0x24 */ { "SMBlockingX", smbsrv_reply_lockingX, NEED_SESS|NEED_TCON|AND_X },
+/* 0x25 */ { "SMBtrans", smbsrv_reply_trans, NEED_SESS|NEED_TCON },
+/* 0x26 */ { "SMBtranss", smbsrv_reply_transs, NEED_SESS|NEED_TCON },
+/* 0x27 */ { "SMBioctl", smbsrv_reply_ioctl, NEED_SESS|NEED_TCON },
+/* 0x28 */ { "SMBioctls", NULL, NEED_SESS|NEED_TCON },
+/* 0x29 */ { "SMBcopy", smbsrv_reply_copy, NEED_SESS|NEED_TCON },
+/* 0x2a */ { "SMBmove", NULL, NEED_SESS|NEED_TCON },
+/* 0x2b */ { "SMBecho", smbsrv_reply_echo, 0 },
+/* 0x2c */ { "SMBwriteclose", smbsrv_reply_writeclose, NEED_SESS|NEED_TCON },
+/* 0x2d */ { "SMBopenX", smbsrv_reply_open_and_X, NEED_SESS|NEED_TCON|AND_X },
+/* 0x2e */ { "SMBreadX", smbsrv_reply_read_and_X, NEED_SESS|NEED_TCON|AND_X },
+/* 0x2f */ { "SMBwriteX", smbsrv_reply_write_and_X, NEED_SESS|NEED_TCON|AND_X|LARGE_REQUEST},
+/* 0x30 */ { NULL, NULL, 0 },
+/* 0x31 */ { NULL, NULL, 0 },
+/* 0x32 */ { "SMBtrans2", smbsrv_reply_trans2, NEED_SESS|NEED_TCON },
+/* 0x33 */ { "SMBtranss2", smbsrv_reply_transs2, NEED_SESS|NEED_TCON },
+/* 0x34 */ { "SMBfindclose", smbsrv_reply_findclose, NEED_SESS|NEED_TCON },
+/* 0x35 */ { "SMBfindnclose", smbsrv_reply_findnclose, NEED_SESS|NEED_TCON },
+/* 0x36 */ { NULL, NULL, 0 },
+/* 0x37 */ { NULL, NULL, 0 },
+/* 0x38 */ { NULL, NULL, 0 },
+/* 0x39 */ { NULL, NULL, 0 },
+/* 0x3a */ { NULL, NULL, 0 },
+/* 0x3b */ { NULL, NULL, 0 },
+/* 0x3c */ { NULL, NULL, 0 },
+/* 0x3d */ { NULL, NULL, 0 },
+/* 0x3e */ { NULL, NULL, 0 },
+/* 0x3f */ { NULL, NULL, 0 },
+/* 0x40 */ { NULL, NULL, 0 },
+/* 0x41 */ { NULL, NULL, 0 },
+/* 0x42 */ { NULL, NULL, 0 },
+/* 0x43 */ { NULL, NULL, 0 },
+/* 0x44 */ { NULL, NULL, 0 },
+/* 0x45 */ { NULL, NULL, 0 },
+/* 0x46 */ { NULL, NULL, 0 },
+/* 0x47 */ { NULL, NULL, 0 },
+/* 0x48 */ { NULL, NULL, 0 },
+/* 0x49 */ { NULL, NULL, 0 },
+/* 0x4a */ { NULL, NULL, 0 },
+/* 0x4b */ { NULL, NULL, 0 },
+/* 0x4c */ { NULL, NULL, 0 },
+/* 0x4d */ { NULL, NULL, 0 },
+/* 0x4e */ { NULL, NULL, 0 },
+/* 0x4f */ { NULL, NULL, 0 },
+/* 0x50 */ { NULL, NULL, 0 },
+/* 0x51 */ { NULL, NULL, 0 },
+/* 0x52 */ { NULL, NULL, 0 },
+/* 0x53 */ { NULL, NULL, 0 },
+/* 0x54 */ { NULL, NULL, 0 },
+/* 0x55 */ { NULL, NULL, 0 },
+/* 0x56 */ { NULL, NULL, 0 },
+/* 0x57 */ { NULL, NULL, 0 },
+/* 0x58 */ { NULL, NULL, 0 },
+/* 0x59 */ { NULL, NULL, 0 },
+/* 0x5a */ { NULL, NULL, 0 },
+/* 0x5b */ { NULL, NULL, 0 },
+/* 0x5c */ { NULL, NULL, 0 },
+/* 0x5d */ { NULL, NULL, 0 },
+/* 0x5e */ { NULL, NULL, 0 },
+/* 0x5f */ { NULL, NULL, 0 },
+/* 0x60 */ { NULL, NULL, 0 },
+/* 0x61 */ { NULL, NULL, 0 },
+/* 0x62 */ { NULL, NULL, 0 },
+/* 0x63 */ { NULL, NULL, 0 },
+/* 0x64 */ { NULL, NULL, 0 },
+/* 0x65 */ { NULL, NULL, 0 },
+/* 0x66 */ { NULL, NULL, 0 },
+/* 0x67 */ { NULL, NULL, 0 },
+/* 0x68 */ { NULL, NULL, 0 },
+/* 0x69 */ { NULL, NULL, 0 },
+/* 0x6a */ { NULL, NULL, 0 },
+/* 0x6b */ { NULL, NULL, 0 },
+/* 0x6c */ { NULL, NULL, 0 },
+/* 0x6d */ { NULL, NULL, 0 },
+/* 0x6e */ { NULL, NULL, 0 },
+/* 0x6f */ { NULL, NULL, 0 },
+/* 0x70 */ { "SMBtcon", smbsrv_reply_tcon, NEED_SESS },
+/* 0x71 */ { "SMBtdis", smbsrv_reply_tdis, NEED_TCON },
+/* 0x72 */ { "SMBnegprot", smbsrv_reply_negprot, 0 },
+/* 0x73 */ { "SMBsesssetupX", smbsrv_reply_sesssetup, AND_X },
+/* 0x74 */ { "SMBulogoffX", smbsrv_reply_ulogoffX, NEED_SESS|AND_X }, /* ulogoff doesn't give a valid TID */
+/* 0x75 */ { "SMBtconX", smbsrv_reply_tcon_and_X, NEED_SESS|AND_X },
+/* 0x76 */ { NULL, NULL, 0 },
+/* 0x77 */ { NULL, NULL, 0 },
+/* 0x78 */ { NULL, NULL, 0 },
+/* 0x79 */ { NULL, NULL, 0 },
+/* 0x7a */ { NULL, NULL, 0 },
+/* 0x7b */ { NULL, NULL, 0 },
+/* 0x7c */ { NULL, NULL, 0 },
+/* 0x7d */ { NULL, NULL, 0 },
+/* 0x7e */ { NULL, NULL, 0 },
+/* 0x7f */ { NULL, NULL, 0 },
+/* 0x80 */ { "SMBdskattr", smbsrv_reply_dskattr, NEED_SESS|NEED_TCON },
+/* 0x81 */ { "SMBsearch", smbsrv_reply_search, NEED_SESS|NEED_TCON },
+/* 0x82 */ { "SMBffirst", smbsrv_reply_search, NEED_SESS|NEED_TCON },
+/* 0x83 */ { "SMBfunique", smbsrv_reply_search, NEED_SESS|NEED_TCON },
+/* 0x84 */ { "SMBfclose", smbsrv_reply_fclose, NEED_SESS|NEED_TCON },
+/* 0x85 */ { NULL, NULL, 0 },
+/* 0x86 */ { NULL, NULL, 0 },
+/* 0x87 */ { NULL, NULL, 0 },
+/* 0x88 */ { NULL, NULL, 0 },
+/* 0x89 */ { NULL, NULL, 0 },
+/* 0x8a */ { NULL, NULL, 0 },
+/* 0x8b */ { NULL, NULL, 0 },
+/* 0x8c */ { NULL, NULL, 0 },
+/* 0x8d */ { NULL, NULL, 0 },
+/* 0x8e */ { NULL, NULL, 0 },
+/* 0x8f */ { NULL, NULL, 0 },
+/* 0x90 */ { NULL, NULL, 0 },
+/* 0x91 */ { NULL, NULL, 0 },
+/* 0x92 */ { NULL, NULL, 0 },
+/* 0x93 */ { NULL, NULL, 0 },
+/* 0x94 */ { NULL, NULL, 0 },
+/* 0x95 */ { NULL, NULL, 0 },
+/* 0x96 */ { NULL, NULL, 0 },
+/* 0x97 */ { NULL, NULL, 0 },
+/* 0x98 */ { NULL, NULL, 0 },
+/* 0x99 */ { NULL, NULL, 0 },
+/* 0x9a */ { NULL, NULL, 0 },
+/* 0x9b */ { NULL, NULL, 0 },
+/* 0x9c */ { NULL, NULL, 0 },
+/* 0x9d */ { NULL, NULL, 0 },
+/* 0x9e */ { NULL, NULL, 0 },
+/* 0x9f */ { NULL, NULL, 0 },
+/* 0xa0 */ { "SMBnttrans", smbsrv_reply_nttrans, NEED_SESS|NEED_TCON|LARGE_REQUEST },
+/* 0xa1 */ { "SMBnttranss", smbsrv_reply_nttranss, NEED_SESS|NEED_TCON },
+/* 0xa2 */ { "SMBntcreateX", smbsrv_reply_ntcreate_and_X, NEED_SESS|NEED_TCON|AND_X },
+/* 0xa3 */ { NULL, NULL, 0 },
+/* 0xa4 */ { "SMBntcancel", smbsrv_reply_ntcancel, NEED_SESS|NEED_TCON|SIGNING_NO_REPLY },
+/* 0xa5 */ { "SMBntrename", smbsrv_reply_ntrename, NEED_SESS|NEED_TCON },
+/* 0xa6 */ { NULL, NULL, 0 },
+/* 0xa7 */ { NULL, NULL, 0 },
+/* 0xa8 */ { NULL, NULL, 0 },
+/* 0xa9 */ { NULL, NULL, 0 },
+/* 0xaa */ { NULL, NULL, 0 },
+/* 0xab */ { NULL, NULL, 0 },
+/* 0xac */ { NULL, NULL, 0 },
+/* 0xad */ { NULL, NULL, 0 },
+/* 0xae */ { NULL, NULL, 0 },
+/* 0xaf */ { NULL, NULL, 0 },
+/* 0xb0 */ { NULL, NULL, 0 },
+/* 0xb1 */ { NULL, NULL, 0 },
+/* 0xb2 */ { NULL, NULL, 0 },
+/* 0xb3 */ { NULL, NULL, 0 },
+/* 0xb4 */ { NULL, NULL, 0 },
+/* 0xb5 */ { NULL, NULL, 0 },
+/* 0xb6 */ { NULL, NULL, 0 },
+/* 0xb7 */ { NULL, NULL, 0 },
+/* 0xb8 */ { NULL, NULL, 0 },
+/* 0xb9 */ { NULL, NULL, 0 },
+/* 0xba */ { NULL, NULL, 0 },
+/* 0xbb */ { NULL, NULL, 0 },
+/* 0xbc */ { NULL, NULL, 0 },
+/* 0xbd */ { NULL, NULL, 0 },
+/* 0xbe */ { NULL, NULL, 0 },
+/* 0xbf */ { NULL, NULL, 0 },
+/* 0xc0 */ { "SMBsplopen", smbsrv_reply_printopen, NEED_SESS|NEED_TCON },
+/* 0xc1 */ { "SMBsplwr", smbsrv_reply_printwrite, NEED_SESS|NEED_TCON },
+/* 0xc2 */ { "SMBsplclose", smbsrv_reply_printclose, NEED_SESS|NEED_TCON },
+/* 0xc3 */ { "SMBsplretq", smbsrv_reply_printqueue, NEED_SESS|NEED_TCON },
+/* 0xc4 */ { NULL, NULL, 0 },
+/* 0xc5 */ { NULL, NULL, 0 },
+/* 0xc6 */ { NULL, NULL, 0 },
+/* 0xc7 */ { NULL, NULL, 0 },
+/* 0xc8 */ { NULL, NULL, 0 },
+/* 0xc9 */ { NULL, NULL, 0 },
+/* 0xca */ { NULL, NULL, 0 },
+/* 0xcb */ { NULL, NULL, 0 },
+/* 0xcc */ { NULL, NULL, 0 },
+/* 0xcd */ { NULL, NULL, 0 },
+/* 0xce */ { NULL, NULL, 0 },
+/* 0xcf */ { NULL, NULL, 0 },
+/* 0xd0 */ { "SMBsends", NULL, 0 },
+/* 0xd1 */ { "SMBsendb", NULL, 0 },
+/* 0xd2 */ { "SMBfwdname", NULL, 0 },
+/* 0xd3 */ { "SMBcancelf", NULL, 0 },
+/* 0xd4 */ { "SMBgetmac", NULL, 0 },
+/* 0xd5 */ { "SMBsendstrt", NULL, 0 },
+/* 0xd6 */ { "SMBsendend", NULL, 0 },
+/* 0xd7 */ { "SMBsendtxt", NULL, 0 },
+/* 0xd8 */ { NULL, NULL, 0 },
+/* 0xd9 */ { NULL, NULL, 0 },
+/* 0xda */ { NULL, NULL, 0 },
+/* 0xdb */ { NULL, NULL, 0 },
+/* 0xdc */ { NULL, NULL, 0 },
+/* 0xdd */ { NULL, NULL, 0 },
+/* 0xde */ { NULL, NULL, 0 },
+/* 0xdf */ { NULL, NULL, 0 },
+/* 0xe0 */ { NULL, NULL, 0 },
+/* 0xe1 */ { NULL, NULL, 0 },
+/* 0xe2 */ { NULL, NULL, 0 },
+/* 0xe3 */ { NULL, NULL, 0 },
+/* 0xe4 */ { NULL, NULL, 0 },
+/* 0xe5 */ { NULL, NULL, 0 },
+/* 0xe6 */ { NULL, NULL, 0 },
+/* 0xe7 */ { NULL, NULL, 0 },
+/* 0xe8 */ { NULL, NULL, 0 },
+/* 0xe9 */ { NULL, NULL, 0 },
+/* 0xea */ { NULL, NULL, 0 },
+/* 0xeb */ { NULL, NULL, 0 },
+/* 0xec */ { NULL, NULL, 0 },
+/* 0xed */ { NULL, NULL, 0 },
+/* 0xee */ { NULL, NULL, 0 },
+/* 0xef */ { NULL, NULL, 0 },
+/* 0xf0 */ { NULL, NULL, 0 },
+/* 0xf1 */ { NULL, NULL, 0 },
+/* 0xf2 */ { NULL, NULL, 0 },
+/* 0xf3 */ { NULL, NULL, 0 },
+/* 0xf4 */ { NULL, NULL, 0 },
+/* 0xf5 */ { NULL, NULL, 0 },
+/* 0xf6 */ { NULL, NULL, 0 },
+/* 0xf7 */ { NULL, NULL, 0 },
+/* 0xf8 */ { NULL, NULL, 0 },
+/* 0xf9 */ { NULL, NULL, 0 },
+/* 0xfa */ { NULL, NULL, 0 },
+/* 0xfb */ { NULL, NULL, 0 },
+/* 0xfc */ { NULL, NULL, 0 },
+/* 0xfd */ { NULL, NULL, 0 },
+/* 0xfe */ { NULL, NULL, 0 },
+/* 0xff */ { NULL, NULL, 0 }
+};
+
+/****************************************************************************
+receive a SMB request header from the wire, forming a request_context
+from the result
+****************************************************************************/
+NTSTATUS smbsrv_recv_smb_request(void *private_data, DATA_BLOB blob)
+{
+ struct smbsrv_connection *smb_conn = talloc_get_type(private_data, struct smbsrv_connection);
+ struct smbsrv_request *req;
+ struct timeval cur_time = timeval_current();
+ uint8_t command;
+
+ smb_conn->statistics.last_request_time = cur_time;
+
+ /* see if its a special NBT packet */
+ if (CVAL(blob.data, 0) != 0) {
+ req = smbsrv_init_request(smb_conn);
+ NT_STATUS_HAVE_NO_MEMORY(req);
+
+ ZERO_STRUCT(req->in);
+
+ req->in.buffer = talloc_steal(req, blob.data);
+ req->in.size = blob.length;
+ req->request_time = cur_time;
+
+ smbsrv_reply_special(req);
+ return NT_STATUS_OK;
+ }
+
+ if ((NBT_HDR_SIZE + MIN_SMB_SIZE) > blob.length) {
+ DEBUG(2,("Invalid SMB packet: length %ld\n", (long)blob.length));
+ smbsrv_terminate_connection(smb_conn, "Invalid SMB packet");
+ return NT_STATUS_OK;
+ }
+
+ /* Make sure this is an SMB packet */
+ if (IVAL(blob.data, NBT_HDR_SIZE) != SMB_MAGIC) {
+ DEBUG(2,("Non-SMB packet of length %ld. Terminating connection\n",
+ (long)blob.length));
+ smbsrv_terminate_connection(smb_conn, "Non-SMB packet");
+ return NT_STATUS_OK;
+ }
+
+ req = smbsrv_init_request(smb_conn);
+ NT_STATUS_HAVE_NO_MEMORY(req);
+
+ req->in.buffer = talloc_steal(req, blob.data);
+ req->in.size = blob.length;
+ req->request_time = cur_time;
+ req->chained_fnum = -1;
+ req->in.allocated = req->in.size;
+ req->in.hdr = req->in.buffer + NBT_HDR_SIZE;
+ req->in.vwv = req->in.hdr + HDR_VWV;
+ req->in.wct = CVAL(req->in.hdr, HDR_WCT);
+
+ command = CVAL(req->in.hdr, HDR_COM);
+
+ 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));
+
+ /* special handling for oversize calls. Windows seems
+ to take the maximum of the BCC value and the
+ computed buffer size. This handles oversized writeX
+ calls, and possibly oversized SMBtrans calls */
+ if ((message_flags(command) & LARGE_REQUEST) &&
+ ( !(message_flags(command) & AND_X) ||
+ (req->in.wct < 1 || SVAL(req->in.vwv, VWV(0)) == SMB_CHAIN_NONE)) &&
+ req->in.data_size < req->in.size - PTR_DIFF(req->in.data,req->in.buffer)) {
+ req->in.data_size = req->in.size - PTR_DIFF(req->in.data,req->in.buffer);
+ }
+ }
+
+ if (NBT_HDR_SIZE + MIN_SMB_SIZE + 2*req->in.wct > req->in.size) {
+ DEBUG(2,("Invalid SMB word count %d\n", req->in.wct));
+ smbsrv_terminate_connection(req->smb_conn, "Invalid SMB packet");
+ return NT_STATUS_OK;
+ }
+
+ if (NBT_HDR_SIZE + MIN_SMB_SIZE + 2*req->in.wct + req->in.data_size > req->in.size) {
+ DEBUG(2,("Invalid SMB buffer length count %d\n",
+ (int)req->in.data_size));
+ smbsrv_terminate_connection(req->smb_conn, "Invalid SMB packet");
+ return NT_STATUS_OK;
+ }
+
+ req->flags2 = SVAL(req->in.hdr, HDR_FLG2);
+
+ /* fix the bufinfo */
+ smbsrv_setup_bufinfo(req);
+
+ if (!smbsrv_signing_check_incoming(req)) {
+ smbsrv_send_error(req, NT_STATUS_ACCESS_DENIED);
+ return NT_STATUS_OK;
+ }
+
+ command = CVAL(req->in.hdr, HDR_COM);
+ switch_message(command, req);
+ return NT_STATUS_OK;
+}
+
+/****************************************************************************
+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;
+ struct server_id_buf idbuf;
+
+ type &= 0xff;
+
+ errno = 0;
+
+ if (smb_messages[type].fn == NULL) {
+ DEBUG(0,("Unknown message type %d!\n",type));
+ smbsrv_reply_unknown(req);
+ return;
+ }
+
+ flags = smb_messages[type].flags;
+
+ req->tcon = smbsrv_smb_tcon_find(smb_conn, SVAL(req->in.hdr,HDR_TID), req->request_time);
+
+ if (!req->session) {
+ /* setup the user context for this request if it
+ hasn't already been initialised (to cope with SMB
+ chaining) */
+
+ req->session = smbsrv_session_find(req->smb_conn, SVAL(req->in.hdr,HDR_UID), req->request_time);
+ }
+
+ DEBUG(5, ("switch message %s (task_id %s)\n",
+ smb_fn_name(type),
+ server_id_str_buf(req->smb_conn->connection->server_id,
+ &idbuf)));
+
+ /* this must be called before we do any reply */
+ if (flags & SIGNING_NO_REPLY) {
+ smbsrv_signing_no_reply(req);
+ }
+
+ /* see if the vuid is valid */
+ if ((flags & NEED_SESS) && !req->session) {
+ status = NT_STATUS_DOS(ERRSRV, ERRbaduid);
+ /* amazingly, the error code depends on the command */
+ switch (type) {
+ case SMBntcreateX:
+ case SMBntcancel:
+ case SMBulogoffX:
+ break;
+ default:
+ if (req->smb_conn->config.nt_status_support &&
+ req->smb_conn->negotiate.client_caps & CAP_STATUS32) {
+ status = NT_STATUS_INVALID_HANDLE;
+ }
+ break;
+ }
+ /*
+ * TODO:
+ * don't know how to handle smb signing for this case
+ * so just skip the reply
+ */
+ if ((flags & SIGNING_NO_REPLY) &&
+ (req->smb_conn->signing.signing_state != SMB_SIGNING_ENGINE_OFF)) {
+ DEBUG(1,("SKIP ERROR REPLY: %s %s because of unknown smb signing case\n",
+ smb_fn_name(type), nt_errstr(status)));
+ talloc_free(req);
+ return;
+ }
+ smbsrv_send_error(req, status);
+ return;
+ }
+
+ /* does this protocol need a valid tree connection? */
+ if ((flags & NEED_TCON) && !req->tcon) {
+ status = NT_STATUS_DOS(ERRSRV, ERRinvnid);
+ /* amazingly, the error code depends on the command */
+ switch (type) {
+ case SMBntcreateX:
+ case SMBntcancel:
+ case SMBtdis:
+ break;
+ default:
+ if (req->smb_conn->config.nt_status_support &&
+ req->smb_conn->negotiate.client_caps & CAP_STATUS32) {
+ status = NT_STATUS_INVALID_HANDLE;
+ }
+ break;
+ }
+ /*
+ * TODO:
+ * don't know how to handle smb signing for this case
+ * so just skip the reply
+ */
+ if ((flags & SIGNING_NO_REPLY) &&
+ (req->smb_conn->signing.signing_state != SMB_SIGNING_ENGINE_OFF)) {
+ DEBUG(1,("SKIP ERROR REPLY: %s %s because of unknown smb signing case\n",
+ smb_fn_name(type), nt_errstr(status)));
+ talloc_free(req);
+ return;
+ }
+ smbsrv_send_error(req, status);
+ return;
+ }
+
+ smb_messages[type].fn(req);
+}
+
+/*
+ we call this when first first part of a possibly chained request has been completed
+ and we need to call the 2nd part, if any
+*/
+void smbsrv_chain_reply(struct smbsrv_request *req)
+{
+ uint16_t chain_cmd, chain_offset;
+ uint8_t *vwv, *data;
+ uint16_t wct;
+ uint16_t data_size;
+
+ if (req->in.wct < 2 || req->out.wct < 2) {
+ smbsrv_send_error(req, NT_STATUS_DOS(ERRSRV, ERRerror));
+ return;
+ }
+
+ chain_cmd = CVAL(req->in.vwv, VWV(0));
+ chain_offset = SVAL(req->in.vwv, VWV(1));
+
+ if (chain_cmd == SMB_CHAIN_NONE) {
+ /* end of chain */
+ SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE);
+ SSVAL(req->out.vwv, VWV(1), 0);
+ smbsrv_send_reply(req);
+ return;
+ }
+
+ if (chain_offset + req->in.hdr >= req->in.buffer + req->in.size) {
+ goto error;
+ }
+
+ wct = CVAL(req->in.hdr, chain_offset);
+ vwv = req->in.hdr + chain_offset + 1;
+
+ if (vwv + VWV(wct) + 2 > req->in.buffer + req->in.size) {
+ goto error;
+ }
+
+ data_size = SVAL(vwv, VWV(wct));
+ data = vwv + VWV(wct) + 2;
+
+ if (data + data_size > req->in.buffer + req->in.size) {
+ goto error;
+ }
+
+ /* all seems legit */
+ req->in.vwv = vwv;
+ req->in.wct = wct;
+ req->in.data = data;
+ req->in.data_size = data_size;
+ req->in.ptr = data;
+
+ /* fix the bufinfo */
+ smbsrv_setup_bufinfo(req);
+
+ req->chain_count++;
+
+ SSVAL(req->out.vwv, VWV(0), chain_cmd);
+ SSVAL(req->out.vwv, VWV(1), req->out.size - NBT_HDR_SIZE);
+
+ /* cleanup somestuff for the next request */
+ DLIST_REMOVE(req->smb_conn->requests, req);
+ talloc_unlink(req, req->ntvfs);
+ req->ntvfs = NULL;
+ talloc_free(req->io_ptr);
+ req->io_ptr = NULL;
+
+ switch_message(chain_cmd, req);
+ return;
+
+error:
+ SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE);
+ SSVAL(req->out.vwv, VWV(1), 0);
+ smbsrv_send_error(req, NT_STATUS_DOS(ERRSRV, ERRerror));
+}
+
+/*
+ * init the SMB protocol related stuff
+ */
+NTSTATUS smbsrv_init_smb_connection(struct smbsrv_connection *smb_conn, struct loadparm_context *lp_ctx)
+{
+ NTSTATUS status;
+
+ /* now initialise a few default values associated with this smb socket */
+ smb_conn->negotiate.max_send = 0xFFFF;
+
+ /* this is the size that w2k uses, and it appears to be important for
+ good performance */
+ smb_conn->negotiate.max_recv = lpcfg_max_xmit(lp_ctx);
+
+ smb_conn->negotiate.zone_offset = get_time_zone(time(NULL));
+
+ smb_conn->config.nt_status_support = lpcfg_nt_status_support(lp_ctx);
+
+ status = smbsrv_init_sessions(smb_conn, UINT16_MAX);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ status = smbsrv_smb_init_tcons(smb_conn);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ smbsrv_init_signing(smb_conn);
+
+ return NT_STATUS_OK;
+}
diff --git a/source4/smb_server/smb/reply.c b/source4/smb_server/smb/reply.c
new file mode 100644
index 0000000..8511b8f
--- /dev/null
+++ b/source4/smb_server/smb/reply.c
@@ -0,0 +1,2379 @@
+/*
+ Unix SMB/CIFS implementation.
+ Main SMB reply routines
+ Copyright (C) Andrew Tridgell 1992-2003
+ Copyright (C) James J Myers 2003 <myersjj@samba.org>
+ Copyright (C) Stefan Metzmacher 2006
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+/*
+ This file handles most of the reply_ calls that the server
+ makes to handle specific SMB commands
+*/
+
+#include "includes.h"
+#include "smb_server/smb_server.h"
+#include "ntvfs/ntvfs.h"
+#include "librpc/gen_ndr/ndr_nbt.h"
+#include "libcli/nbt/libnbt.h"
+
+
+/****************************************************************************
+ Reply to a simple request (async send)
+****************************************************************************/
+static void reply_simple_send(struct ntvfs_request *ntvfs)
+{
+ struct smbsrv_request *req;
+
+ SMBSRV_CHECK_ASYNC_STATUS_SIMPLE;
+
+ smbsrv_setup_reply(req, 0, 0);
+ smbsrv_send_reply(req);
+}
+
+
+/****************************************************************************
+ Reply to a tcon (async reply)
+****************************************************************************/
+static void reply_tcon_send(struct ntvfs_request *ntvfs)
+{
+ struct smbsrv_request *req;
+ union smb_tcon *con;
+
+ SMBSRV_CHECK_ASYNC_STATUS(con, union smb_tcon);
+
+ /* construct reply */
+ smbsrv_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);
+
+ smbsrv_send_reply(req);
+}
+
+/****************************************************************************
+ Reply to a tcon.
+****************************************************************************/
+void smbsrv_reply_tcon(struct smbsrv_request *req)
+{
+ union smb_tcon *con;
+ NTSTATUS status;
+ uint8_t *p;
+
+ /* parse request */
+ SMBSRV_CHECK_WCT(req, 0);
+
+ SMBSRV_TALLOC_IO_PTR(con, union smb_tcon);
+
+ con->tcon.level = RAW_TCON_TCON;
+
+ p = req->in.data;
+ p += req_pull_ascii4(&req->in.bufinfo, &con->tcon.in.service, p, STR_TERMINATE);
+ p += req_pull_ascii4(&req->in.bufinfo, &con->tcon.in.password, p, STR_TERMINATE);
+ p += req_pull_ascii4(&req->in.bufinfo, &con->tcon.in.dev, p, STR_TERMINATE);
+
+ if (!con->tcon.in.service || !con->tcon.in.password || !con->tcon.in.dev) {
+ smbsrv_send_error(req, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+
+ /* Instantiate backend */
+ status = smbsrv_tcon_backend(req, con);
+ if (!NT_STATUS_IS_OK(status)) {
+ smbsrv_send_error(req, status);
+ return;
+ }
+
+ SMBSRV_SETUP_NTVFS_REQUEST(reply_tcon_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ /* Invoke NTVFS connection hook */
+ SMBSRV_CALL_NTVFS_BACKEND(ntvfs_connect(req->ntvfs, con));
+}
+
+
+/****************************************************************************
+ Reply to a tcon and X (async reply)
+****************************************************************************/
+static void reply_tcon_and_X_send(struct ntvfs_request *ntvfs)
+{
+ struct smbsrv_request *req;
+ union smb_tcon *con;
+
+ SMBSRV_CHECK_ASYNC_STATUS(con, union smb_tcon);
+
+ /* construct reply - two variants */
+ if (req->smb_conn->negotiate.protocol < PROTOCOL_NT1) {
+ smbsrv_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 {
+ smbsrv_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);
+
+ smbsrv_chain_reply(req);
+}
+
+/****************************************************************************
+ Reply to a tcon and X.
+****************************************************************************/
+void smbsrv_reply_tcon_and_X(struct smbsrv_request *req)
+{
+ NTSTATUS status;
+ union smb_tcon *con;
+ uint8_t *p;
+ uint16_t passlen;
+
+ SMBSRV_TALLOC_IO_PTR(con, union smb_tcon);
+
+ con->tconx.level = RAW_TCON_TCONX;
+
+ /* parse request */
+ SMBSRV_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->in.bufinfo, p, passlen, &con->tconx.in.password)) {
+ smbsrv_send_error(req, NT_STATUS_ILL_FORMED_PASSWORD);
+ return;
+ }
+ p += passlen;
+
+ p += req_pull_string(&req->in.bufinfo, &con->tconx.in.path, p, -1, STR_TERMINATE);
+ p += req_pull_string(&req->in.bufinfo, &con->tconx.in.device, p, -1, STR_ASCII);
+
+ if (!con->tconx.in.path || !con->tconx.in.device) {
+ smbsrv_send_error(req, NT_STATUS_BAD_DEVICE_TYPE);
+ return;
+ }
+
+ /* Instantiate backend */
+ status = smbsrv_tcon_backend(req, con);
+ if (!NT_STATUS_IS_OK(status)) {
+ smbsrv_send_error(req, status);
+ return;
+ }
+
+ SMBSRV_SETUP_NTVFS_REQUEST(reply_tcon_and_X_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ /* Invoke NTVFS connection hook */
+ SMBSRV_CALL_NTVFS_BACKEND(ntvfs_connect(req->ntvfs, con));
+}
+
+
+/****************************************************************************
+ Reply to an unknown request
+****************************************************************************/
+void smbsrv_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));
+
+ smbsrv_send_error(req, NT_STATUS_DOS(ERRSRV, ERRunknownsmb));
+}
+
+
+/****************************************************************************
+ Reply to an ioctl (async reply)
+****************************************************************************/
+static void reply_ioctl_send(struct ntvfs_request *ntvfs)
+{
+ struct smbsrv_request *req;
+ union smb_ioctl *io;
+
+ SMBSRV_CHECK_ASYNC_STATUS(io, union smb_ioctl);
+
+ /* the +1 is for nicer alignment */
+ smbsrv_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);
+
+ smbsrv_send_reply(req);
+}
+
+/****************************************************************************
+ Reply to an ioctl.
+****************************************************************************/
+void smbsrv_reply_ioctl(struct smbsrv_request *req)
+{
+ union smb_ioctl *io;
+
+ /* parse request */
+ SMBSRV_CHECK_WCT(req, 3);
+ SMBSRV_TALLOC_IO_PTR(io, union smb_ioctl);
+ SMBSRV_SETUP_NTVFS_REQUEST(reply_ioctl_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ io->ioctl.level = RAW_IOCTL_IOCTL;
+ io->ioctl.in.file.ntvfs = smbsrv_pull_fnum(req, req->in.vwv, VWV(0));
+ io->ioctl.in.request = IVAL(req->in.vwv, VWV(1));
+
+ SMBSRV_CHECK_FILE_HANDLE_ERROR(io->ioctl.in.file.ntvfs,
+ NT_STATUS_DOS(ERRSRV, ERRerror));
+ SMBSRV_CALL_NTVFS_BACKEND(ntvfs_ioctl(req->ntvfs, io));
+}
+
+
+/****************************************************************************
+ Reply to a chkpth.
+****************************************************************************/
+void smbsrv_reply_chkpth(struct smbsrv_request *req)
+{
+ union smb_chkpath *io;
+
+ SMBSRV_TALLOC_IO_PTR(io, union smb_chkpath);
+ SMBSRV_SETUP_NTVFS_REQUEST(reply_simple_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ req_pull_ascii4(&req->in.bufinfo, &io->chkpath.in.path, req->in.data, STR_TERMINATE);
+
+ SMBSRV_CALL_NTVFS_BACKEND(ntvfs_chkpath(req->ntvfs, io));
+}
+
+/****************************************************************************
+ Reply to a getatr (async reply)
+****************************************************************************/
+static void reply_getatr_send(struct ntvfs_request *ntvfs)
+{
+ struct smbsrv_request *req;
+ union smb_fileinfo *st;
+
+ SMBSRV_CHECK_ASYNC_STATUS(st, union smb_fileinfo);
+
+ /* construct reply */
+ smbsrv_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);
+
+ SMBSRV_VWV_RESERVED(5, 5);
+
+ smbsrv_send_reply(req);
+}
+
+
+/****************************************************************************
+ Reply to a getatr.
+****************************************************************************/
+void smbsrv_reply_getatr(struct smbsrv_request *req)
+{
+ union smb_fileinfo *st;
+
+ SMBSRV_TALLOC_IO_PTR(st, union smb_fileinfo);
+ SMBSRV_SETUP_NTVFS_REQUEST(reply_getatr_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ st->getattr.level = RAW_FILEINFO_GETATTR;
+
+ /* parse request */
+ req_pull_ascii4(&req->in.bufinfo, &st->getattr.in.file.path, req->in.data, STR_TERMINATE);
+ if (!st->getattr.in.file.path) {
+ smbsrv_send_error(req, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+ return;
+ }
+
+ SMBSRV_CALL_NTVFS_BACKEND(ntvfs_qpathinfo(req->ntvfs, st));
+}
+
+
+/****************************************************************************
+ Reply to a setatr.
+****************************************************************************/
+void smbsrv_reply_setatr(struct smbsrv_request *req)
+{
+ union smb_setfileinfo *st;
+
+ /* parse request */
+ SMBSRV_CHECK_WCT(req, 8);
+ SMBSRV_TALLOC_IO_PTR(st, union smb_setfileinfo);
+ SMBSRV_SETUP_NTVFS_REQUEST(reply_simple_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ 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->in.bufinfo, &st->setattr.in.file.path, req->in.data, STR_TERMINATE);
+
+ if (!st->setattr.in.file.path) {
+ smbsrv_send_error(req, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+ return;
+ }
+
+ SMBSRV_CALL_NTVFS_BACKEND(ntvfs_setpathinfo(req->ntvfs, st));
+}
+
+
+/****************************************************************************
+ Reply to a dskattr (async reply)
+****************************************************************************/
+static void reply_dskattr_send(struct ntvfs_request *ntvfs)
+{
+ struct smbsrv_request *req;
+ union smb_fsinfo *fs;
+
+ SMBSRV_CHECK_ASYNC_STATUS(fs, union smb_fsinfo);
+
+ /* construct reply */
+ smbsrv_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);
+
+ SMBSRV_VWV_RESERVED(4, 1);
+
+ smbsrv_send_reply(req);
+}
+
+
+/****************************************************************************
+ Reply to a dskattr.
+****************************************************************************/
+void smbsrv_reply_dskattr(struct smbsrv_request *req)
+{
+ union smb_fsinfo *fs;
+
+ SMBSRV_TALLOC_IO_PTR(fs, union smb_fsinfo);
+ SMBSRV_SETUP_NTVFS_REQUEST(reply_dskattr_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ fs->dskattr.level = RAW_QFS_DSKATTR;
+
+ SMBSRV_CALL_NTVFS_BACKEND(ntvfs_fsinfo(req->ntvfs, fs));
+}
+
+
+/****************************************************************************
+ Reply to an open (async reply)
+****************************************************************************/
+static void reply_open_send(struct ntvfs_request *ntvfs)
+{
+ struct smbsrv_request *req;
+ union smb_open *oi;
+
+ SMBSRV_CHECK_ASYNC_STATUS(oi, union smb_open);
+
+ /* construct reply */
+ smbsrv_setup_reply(req, 7, 0);
+
+ smbsrv_push_fnum(req->out.vwv, VWV(0), oi->openold.out.file.ntvfs);
+ 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);
+
+ smbsrv_send_reply(req);
+}
+
+/****************************************************************************
+ Reply to an open.
+****************************************************************************/
+void smbsrv_reply_open(struct smbsrv_request *req)
+{
+ union smb_open *oi;
+
+ /* parse request */
+ SMBSRV_CHECK_WCT(req, 2);
+ SMBSRV_TALLOC_IO_PTR(oi, union smb_open);
+ SMBSRV_SETUP_NTVFS_REQUEST(reply_open_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ 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->in.bufinfo, &oi->openold.in.fname, req->in.data, STR_TERMINATE);
+
+ if (!oi->openold.in.fname) {
+ smbsrv_send_error(req, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+ return;
+ }
+
+ SMBSRV_CALL_NTVFS_BACKEND(ntvfs_open(req->ntvfs, oi));
+}
+
+
+/****************************************************************************
+ Reply to an open and X (async reply)
+****************************************************************************/
+static void reply_open_and_X_send(struct ntvfs_request *ntvfs)
+{
+ struct smbsrv_request *req;
+ union smb_open *oi;
+
+ SMBSRV_CHECK_ASYNC_STATUS(oi, union smb_open);
+
+ /* build the reply */
+ if (oi->openx.in.flags & OPENX_FLAGS_EXTENDED_RETURN) {
+ smbsrv_setup_reply(req, 19, 0);
+ } else {
+ smbsrv_setup_reply(req, 15, 0);
+ }
+
+ SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE);
+ SSVAL(req->out.vwv, VWV(1), 0);
+ smbsrv_push_fnum(req->out.vwv, VWV(2), oi->openx.out.file.ntvfs);
+ 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);
+ SMBSRV_VWV_RESERVED(17, 2);
+ }
+
+ req->chained_fnum = SVAL(req->out.vwv, VWV(2));
+
+ smbsrv_chain_reply(req);
+}
+
+
+/****************************************************************************
+ Reply to an open and X.
+****************************************************************************/
+void smbsrv_reply_open_and_X(struct smbsrv_request *req)
+{
+ union smb_open *oi;
+
+ /* parse the request */
+ SMBSRV_CHECK_WCT(req, 15);
+ SMBSRV_TALLOC_IO_PTR(oi, union smb_open);
+ SMBSRV_SETUP_NTVFS_REQUEST(reply_open_and_X_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ 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->in.bufinfo, &oi->openx.in.fname, req->in.data, STR_TERMINATE);
+
+ if (!oi->openx.in.fname) {
+ smbsrv_send_error(req, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+ return;
+ }
+
+ SMBSRV_CALL_NTVFS_BACKEND(ntvfs_open(req->ntvfs, oi));
+}
+
+
+/****************************************************************************
+ Reply to a mknew or a create.
+****************************************************************************/
+static void reply_mknew_send(struct ntvfs_request *ntvfs)
+{
+ struct smbsrv_request *req;
+ union smb_open *oi;
+
+ SMBSRV_CHECK_ASYNC_STATUS(oi, union smb_open);
+
+ /* build the reply */
+ smbsrv_setup_reply(req, 1, 0);
+
+ smbsrv_push_fnum(req->out.vwv, VWV(0), oi->mknew.out.file.ntvfs);
+
+ smbsrv_send_reply(req);
+}
+
+
+/****************************************************************************
+ Reply to a mknew or a create.
+****************************************************************************/
+void smbsrv_reply_mknew(struct smbsrv_request *req)
+{
+ union smb_open *oi;
+
+ /* parse the request */
+ SMBSRV_CHECK_WCT(req, 3);
+ SMBSRV_TALLOC_IO_PTR(oi, union smb_open);
+ SMBSRV_SETUP_NTVFS_REQUEST(reply_mknew_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ 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->in.bufinfo, &oi->mknew.in.fname, req->in.data, STR_TERMINATE);
+
+ if (!oi->mknew.in.fname) {
+ smbsrv_send_error(req, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+ return;
+ }
+
+ SMBSRV_CALL_NTVFS_BACKEND(ntvfs_open(req->ntvfs, oi));
+}
+
+/****************************************************************************
+ Reply to a create temporary file (async reply)
+****************************************************************************/
+static void reply_ctemp_send(struct ntvfs_request *ntvfs)
+{
+ struct smbsrv_request *req;
+ union smb_open *oi;
+
+ SMBSRV_CHECK_ASYNC_STATUS(oi, union smb_open);
+
+ /* build the reply */
+ smbsrv_setup_reply(req, 1, 0);
+
+ smbsrv_push_fnum(req->out.vwv, VWV(0), oi->ctemp.out.file.ntvfs);
+
+ /* the returned filename is relative to the directory */
+ req_push_str(req, NULL, oi->ctemp.out.name, -1, STR_TERMINATE | STR_ASCII);
+
+ smbsrv_send_reply(req);
+}
+
+/****************************************************************************
+ Reply to a create temporary file.
+****************************************************************************/
+void smbsrv_reply_ctemp(struct smbsrv_request *req)
+{
+ union smb_open *oi;
+
+ /* parse the request */
+ SMBSRV_CHECK_WCT(req, 3);
+ SMBSRV_TALLOC_IO_PTR(oi, union smb_open);
+ SMBSRV_SETUP_NTVFS_REQUEST(reply_ctemp_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ 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->in.bufinfo, &oi->ctemp.in.directory, req->in.data, STR_TERMINATE);
+
+ if (!oi->ctemp.in.directory) {
+ smbsrv_send_error(req, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+ return;
+ }
+
+ SMBSRV_CALL_NTVFS_BACKEND(ntvfs_open(req->ntvfs, oi));
+}
+
+
+/****************************************************************************
+ Reply to a unlink
+****************************************************************************/
+void smbsrv_reply_unlink(struct smbsrv_request *req)
+{
+ union smb_unlink *unl;
+
+ /* parse the request */
+ SMBSRV_CHECK_WCT(req, 1);
+ SMBSRV_TALLOC_IO_PTR(unl, union smb_unlink);
+ SMBSRV_SETUP_NTVFS_REQUEST(reply_simple_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ unl->unlink.in.attrib = SVAL(req->in.vwv, VWV(0));
+
+ req_pull_ascii4(&req->in.bufinfo, &unl->unlink.in.pattern, req->in.data, STR_TERMINATE);
+
+ SMBSRV_CALL_NTVFS_BACKEND(ntvfs_unlink(req->ntvfs, unl));
+}
+
+
+/****************************************************************************
+ 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 smbsrv_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.file.ntvfs = smbsrv_pull_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));
+
+ if (!io.readbraw.in.file.ntvfs) {
+ goto failed;
+ }
+
+ /* 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;
+
+ /* prepare the ntvfs request */
+ req->ntvfs = ntvfs_request_create(req->tcon->ntvfs, req,
+ req->session->session_info,
+ SVAL(req->in.hdr,HDR_PID),
+ req->request_time,
+ req, NULL, 0);
+ if (!req->ntvfs) {
+ goto failed;
+ }
+
+ /* call the backend */
+ status = ntvfs_read(req->ntvfs, &io);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto failed;
+ }
+
+ req->out.size = io.readbraw.out.nread + NBT_HDR_SIZE;
+
+ smbsrv_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 */
+
+ smbsrv_send_reply_nosign(req);
+}
+
+
+/****************************************************************************
+ Reply to a lockread (async reply)
+****************************************************************************/
+static void reply_lockread_send(struct ntvfs_request *ntvfs)
+{
+ struct smbsrv_request *req;
+ union smb_read *io;
+
+ SMBSRV_CHECK_ASYNC_STATUS(io, union smb_read);
+
+ /* 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);
+ SMBSRV_VWV_RESERVED(1, 4);
+
+ SCVAL(req->out.data, 0, SMB_DATA_BLOCK);
+ SSVAL(req->out.data, 1, io->lockread.out.nread);
+
+ smbsrv_send_reply(req);
+}
+
+
+/****************************************************************************
+ Reply to a lockread (core+ protocol).
+ note that the lock is a write lock, not a read lock!
+****************************************************************************/
+void smbsrv_reply_lockread(struct smbsrv_request *req)
+{
+ union smb_read *io;
+
+ /* parse request */
+ SMBSRV_CHECK_WCT(req, 5);
+ SMBSRV_TALLOC_IO_PTR(io, union smb_read);
+ SMBSRV_SETUP_NTVFS_REQUEST(reply_lockread_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ io->lockread.level = RAW_READ_LOCKREAD;
+ io->lockread.in.file.ntvfs= smbsrv_pull_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 */
+ smbsrv_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;
+
+ SMBSRV_CHECK_FILE_HANDLE(io->lockread.in.file.ntvfs);
+ SMBSRV_CALL_NTVFS_BACKEND(ntvfs_read(req->ntvfs, io));
+}
+
+
+
+/****************************************************************************
+ Reply to a read (async reply)
+****************************************************************************/
+static void reply_read_send(struct ntvfs_request *ntvfs)
+{
+ struct smbsrv_request *req;
+ union smb_read *io;
+
+ SMBSRV_CHECK_ASYNC_STATUS(io, union smb_read);
+
+ /* 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);
+ SMBSRV_VWV_RESERVED(1, 4);
+
+ SCVAL(req->out.data, 0, SMB_DATA_BLOCK);
+ SSVAL(req->out.data, 1, io->read.out.nread);
+
+ smbsrv_send_reply(req);
+}
+
+/****************************************************************************
+ Reply to a read.
+****************************************************************************/
+void smbsrv_reply_read(struct smbsrv_request *req)
+{
+ union smb_read *io;
+
+ /* parse request */
+ SMBSRV_CHECK_WCT(req, 5);
+ SMBSRV_TALLOC_IO_PTR(io, union smb_read);
+ SMBSRV_SETUP_NTVFS_REQUEST(reply_read_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ io->read.level = RAW_READ_READ;
+ io->read.in.file.ntvfs = smbsrv_pull_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 */
+ smbsrv_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;
+
+ SMBSRV_CHECK_FILE_HANDLE(io->read.in.file.ntvfs);
+ SMBSRV_CALL_NTVFS_BACKEND(ntvfs_read(req->ntvfs, io));
+}
+
+/****************************************************************************
+ Reply to a read and X (async reply)
+****************************************************************************/
+static void reply_read_and_X_send(struct ntvfs_request *ntvfs)
+{
+ struct smbsrv_request *req;
+ union smb_read *io;
+
+ SMBSRV_CHECK_ASYNC_STATUS_ERR(io, union smb_read);
+
+ /* readx reply packets can be over-sized */
+ req->control_flags |= SMBSRV_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);
+ SMBSRV_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));
+ SSVAL(req->out.vwv, VWV(7), (io->readx.out.nread>>16));
+ SMBSRV_VWV_RESERVED(8, 4);
+
+ if (!NT_STATUS_IS_OK(req->ntvfs->async_states->status)) {
+ smbsrv_setup_error(req, req->ntvfs->async_states->status);
+ }
+
+ smbsrv_chain_reply(req);
+}
+
+/****************************************************************************
+ Reply to a read and X.
+****************************************************************************/
+void smbsrv_reply_read_and_X(struct smbsrv_request *req)
+{
+ union smb_read *io;
+ uint16_t high_part = 0;
+
+ /* parse request */
+ if (req->in.wct != 12) {
+ SMBSRV_CHECK_WCT(req, 10);
+ }
+
+ SMBSRV_TALLOC_IO_PTR(io, union smb_read);
+ SMBSRV_SETUP_NTVFS_REQUEST(reply_read_and_X_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ io->readx.level = RAW_READ_READX;
+ io->readx.in.file.ntvfs = smbsrv_pull_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->flags2 & FLAGS2_READ_PERMIT_EXECUTE) {
+ io->readx.in.read_for_execute = true;
+ } else {
+ io->readx.in.read_for_execute = false;
+ }
+
+ if (req->smb_conn->negotiate.protocol == PROTOCOL_NT1) {
+ high_part = SVAL(req->in.vwv, VWV(7));
+ }
+ if (high_part != UINT16_MAX) {
+ io->readx.in.maxcnt |= high_part << 16;
+ }
+
+ /*
+ * Windows truncates the length to 0x10000
+ */
+ io->readx.in.maxcnt = MIN(io->readx.in.maxcnt, 0x10000);
+
+ /* 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 */
+ smbsrv_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;
+ }
+
+ SMBSRV_CHECK_FILE_HANDLE(io->readx.in.file.ntvfs);
+ SMBSRV_CALL_NTVFS_BACKEND(ntvfs_read(req->ntvfs, io));
+}
+
+
+/****************************************************************************
+ Reply to a writebraw (core+ or LANMAN1.0 protocol).
+****************************************************************************/
+void smbsrv_reply_writebraw(struct smbsrv_request *req)
+{
+ smbsrv_send_error(req, NT_STATUS_DOS(ERRSRV, ERRuseSTD));
+}
+
+
+/****************************************************************************
+ Reply to a writeunlock (async reply)
+****************************************************************************/
+static void reply_writeunlock_send(struct ntvfs_request *ntvfs)
+{
+ struct smbsrv_request *req;
+ union smb_write *io;
+
+ SMBSRV_CHECK_ASYNC_STATUS(io, union smb_write);
+
+ /* construct reply */
+ smbsrv_setup_reply(req, 1, 0);
+
+ SSVAL(req->out.vwv, VWV(0), io->writeunlock.out.nwritten);
+
+ smbsrv_send_reply(req);
+}
+
+/****************************************************************************
+ Reply to a writeunlock (core+).
+****************************************************************************/
+void smbsrv_reply_writeunlock(struct smbsrv_request *req)
+{
+ union smb_write *io;
+
+ SMBSRV_CHECK_WCT(req, 5);
+ SMBSRV_TALLOC_IO_PTR(io, union smb_write);
+ SMBSRV_SETUP_NTVFS_REQUEST(reply_writeunlock_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ io->writeunlock.level = RAW_WRITE_WRITEUNLOCK;
+ io->writeunlock.in.file.ntvfs = smbsrv_pull_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) {
+ smbsrv_send_error(req, NT_STATUS_FOOBAR);
+ return;
+ }
+
+ /* make sure the data block is big enough */
+ if (SVAL(req->in.data, 1) < io->writeunlock.in.count) {
+ smbsrv_send_error(req, NT_STATUS_FOOBAR);
+ return;
+ }
+
+ SMBSRV_CHECK_FILE_HANDLE(io->writeunlock.in.file.ntvfs);
+ SMBSRV_CALL_NTVFS_BACKEND(ntvfs_write(req->ntvfs, io));
+}
+
+
+
+/****************************************************************************
+ Reply to a write (async reply)
+****************************************************************************/
+static void reply_write_send(struct ntvfs_request *ntvfs)
+{
+ struct smbsrv_request *req;
+ union smb_write *io;
+
+ SMBSRV_CHECK_ASYNC_STATUS(io, union smb_write);
+
+ /* construct reply */
+ smbsrv_setup_reply(req, 1, 0);
+
+ SSVAL(req->out.vwv, VWV(0), io->write.out.nwritten);
+
+ smbsrv_send_reply(req);
+}
+
+/****************************************************************************
+ Reply to a write
+****************************************************************************/
+void smbsrv_reply_write(struct smbsrv_request *req)
+{
+ union smb_write *io;
+
+ SMBSRV_CHECK_WCT(req, 5);
+ SMBSRV_TALLOC_IO_PTR(io, union smb_write);
+ SMBSRV_SETUP_NTVFS_REQUEST(reply_write_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ io->write.level = RAW_WRITE_WRITE;
+ io->write.in.file.ntvfs = smbsrv_pull_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->in.bufinfo, io->write.in.data, io->write.in.count)) {
+ smbsrv_send_error(req, NT_STATUS_FOOBAR);
+ return;
+ }
+
+ /* make sure the data block is big enough */
+ if (SVAL(req->in.data, 1) < io->write.in.count) {
+ smbsrv_send_error(req, NT_STATUS_FOOBAR);
+ return;
+ }
+
+ SMBSRV_CHECK_FILE_HANDLE(io->write.in.file.ntvfs);
+ SMBSRV_CALL_NTVFS_BACKEND(ntvfs_write(req->ntvfs, io));
+}
+
+
+/****************************************************************************
+ Reply to a write and X (async reply)
+****************************************************************************/
+static void reply_write_and_X_send(struct ntvfs_request *ntvfs)
+{
+ struct smbsrv_request *req;
+ union smb_write *io;
+
+ SMBSRV_CHECK_ASYNC_STATUS(io, union smb_write);
+
+ /* construct reply */
+ smbsrv_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);
+ SMBSRV_VWV_RESERVED(5, 1);
+
+ smbsrv_chain_reply(req);
+}
+
+/****************************************************************************
+ Reply to a write and X.
+****************************************************************************/
+void smbsrv_reply_write_and_X(struct smbsrv_request *req)
+{
+ union smb_write *io;
+
+ if (req->in.wct != 14) {
+ SMBSRV_CHECK_WCT(req, 12);
+ }
+
+ SMBSRV_TALLOC_IO_PTR(io, union smb_write);
+ SMBSRV_SETUP_NTVFS_REQUEST(reply_write_and_X_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ io->writex.level = RAW_WRITE_WRITEX;
+ io->writex.in.file.ntvfs= smbsrv_pull_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->in.bufinfo, io->writex.in.data, io->writex.in.count)) {
+ smbsrv_send_error(req, NT_STATUS_DOS(ERRSRV, ERRerror));
+ return;
+ }
+
+ SMBSRV_CHECK_FILE_HANDLE(io->writex.in.file.ntvfs);
+ SMBSRV_CALL_NTVFS_BACKEND(ntvfs_write(req->ntvfs, io));
+}
+
+
+/****************************************************************************
+ Reply to a lseek (async reply)
+****************************************************************************/
+static void reply_lseek_send(struct ntvfs_request *ntvfs)
+{
+ struct smbsrv_request *req;
+ union smb_seek *io;
+
+ SMBSRV_CHECK_ASYNC_STATUS(io, union smb_seek);
+
+ /* construct reply */
+ smbsrv_setup_reply(req, 2, 0);
+
+ SIVALS(req->out.vwv, VWV(0), io->lseek.out.offset);
+
+ smbsrv_send_reply(req);
+}
+
+/****************************************************************************
+ Reply to a lseek.
+****************************************************************************/
+void smbsrv_reply_lseek(struct smbsrv_request *req)
+{
+ union smb_seek *io;
+
+ SMBSRV_CHECK_WCT(req, 4);
+ SMBSRV_TALLOC_IO_PTR(io, union smb_seek);
+ SMBSRV_SETUP_NTVFS_REQUEST(reply_lseek_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ io->lseek.in.file.ntvfs = smbsrv_pull_fnum(req, req->in.vwv, VWV(0));
+ io->lseek.in.mode = SVAL(req->in.vwv, VWV(1));
+ io->lseek.in.offset = IVALS(req->in.vwv, VWV(2));
+
+ SMBSRV_CHECK_FILE_HANDLE(io->lseek.in.file.ntvfs);
+ SMBSRV_CALL_NTVFS_BACKEND(ntvfs_seek(req->ntvfs, io));
+}
+
+/****************************************************************************
+ Reply to a flush.
+****************************************************************************/
+void smbsrv_reply_flush(struct smbsrv_request *req)
+{
+ union smb_flush *io;
+ uint16_t fnum;
+
+ /* parse request */
+ SMBSRV_CHECK_WCT(req, 1);
+ SMBSRV_TALLOC_IO_PTR(io, union smb_flush);
+ SMBSRV_SETUP_NTVFS_REQUEST(reply_simple_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ fnum = SVAL(req->in.vwv, VWV(0));
+ if (fnum == 0xFFFF) {
+ io->flush_all.level = RAW_FLUSH_ALL;
+ } else {
+ io->flush.level = RAW_FLUSH_FLUSH;
+ io->flush.in.file.ntvfs = smbsrv_pull_fnum(req, req->in.vwv, VWV(0));
+ SMBSRV_CHECK_FILE_HANDLE(io->flush.in.file.ntvfs);
+ }
+
+ SMBSRV_CALL_NTVFS_BACKEND(ntvfs_flush(req->ntvfs, io));
+}
+
+/****************************************************************************
+ Reply to a close
+
+ Note that this has to deal with closing a directory opened by NT SMB's.
+****************************************************************************/
+void smbsrv_reply_close(struct smbsrv_request *req)
+{
+ union smb_close *io;
+
+ /* parse request */
+ SMBSRV_CHECK_WCT(req, 3);
+ SMBSRV_TALLOC_IO_PTR(io, union smb_close);
+ SMBSRV_SETUP_NTVFS_REQUEST(reply_simple_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ io->close.level = RAW_CLOSE_CLOSE;
+ io->close.in.file.ntvfs = smbsrv_pull_fnum(req, req->in.vwv, VWV(0));
+ io->close.in.write_time = srv_pull_dos_date3(req->smb_conn, req->in.vwv + VWV(1));
+
+ SMBSRV_CHECK_FILE_HANDLE(io->close.in.file.ntvfs);
+ SMBSRV_CALL_NTVFS_BACKEND(ntvfs_close(req->ntvfs, io));
+}
+
+
+/****************************************************************************
+ Reply to a writeclose (async reply)
+****************************************************************************/
+static void reply_writeclose_send(struct ntvfs_request *ntvfs)
+{
+ struct smbsrv_request *req;
+ union smb_write *io;
+
+ SMBSRV_CHECK_ASYNC_STATUS(io, union smb_write);
+
+ /* construct reply */
+ smbsrv_setup_reply(req, 1, 0);
+
+ SSVAL(req->out.vwv, VWV(0), io->write.out.nwritten);
+
+ smbsrv_send_reply(req);
+}
+
+/****************************************************************************
+ Reply to a writeclose (Core+ protocol).
+****************************************************************************/
+void smbsrv_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) {
+ SMBSRV_CHECK_WCT(req, 6);
+ }
+
+ SMBSRV_TALLOC_IO_PTR(io, union smb_write);
+ SMBSRV_SETUP_NTVFS_REQUEST(reply_writeclose_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ io->writeclose.level = RAW_WRITE_WRITECLOSE;
+ io->writeclose.in.file.ntvfs = smbsrv_pull_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->in.bufinfo, io->writeclose.in.data, io->writeclose.in.count)) {
+ smbsrv_send_error(req, NT_STATUS_FOOBAR);
+ return;
+ }
+
+ SMBSRV_CHECK_FILE_HANDLE(io->writeclose.in.file.ntvfs);
+ SMBSRV_CALL_NTVFS_BACKEND(ntvfs_write(req->ntvfs, io));
+}
+
+/****************************************************************************
+ Reply to a lock.
+****************************************************************************/
+void smbsrv_reply_lock(struct smbsrv_request *req)
+{
+ union smb_lock *lck;
+
+ /* parse request */
+ SMBSRV_CHECK_WCT(req, 5);
+ SMBSRV_TALLOC_IO_PTR(lck, union smb_lock);
+ SMBSRV_SETUP_NTVFS_REQUEST(reply_simple_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ lck->lock.level = RAW_LOCK_LOCK;
+ lck->lock.in.file.ntvfs = smbsrv_pull_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));
+
+ SMBSRV_CHECK_FILE_HANDLE(lck->lock.in.file.ntvfs);
+ SMBSRV_CALL_NTVFS_BACKEND(ntvfs_lock(req->ntvfs, lck));
+}
+
+
+/****************************************************************************
+ Reply to a unlock.
+****************************************************************************/
+void smbsrv_reply_unlock(struct smbsrv_request *req)
+{
+ union smb_lock *lck;
+
+ /* parse request */
+ SMBSRV_CHECK_WCT(req, 5);
+ SMBSRV_TALLOC_IO_PTR(lck, union smb_lock);
+ SMBSRV_SETUP_NTVFS_REQUEST(reply_simple_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ lck->unlock.level = RAW_LOCK_UNLOCK;
+ lck->unlock.in.file.ntvfs = smbsrv_pull_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));
+
+ SMBSRV_CHECK_FILE_HANDLE(lck->unlock.in.file.ntvfs);
+ SMBSRV_CALL_NTVFS_BACKEND(ntvfs_lock(req->ntvfs, lck));
+}
+
+
+/****************************************************************************
+ Reply to a tdis.
+****************************************************************************/
+void smbsrv_reply_tdis(struct smbsrv_request *req)
+{
+ struct smbsrv_handle *h, *nh;
+
+ SMBSRV_CHECK_WCT(req, 0);
+
+ /*
+ * TODO: cancel all pending requests on this tcon
+ */
+
+ /*
+ * close all handles on this tcon
+ */
+ for (h=req->tcon->handles.list; h; h=nh) {
+ nh = h->next;
+ talloc_free(h);
+ }
+
+ /* finally destroy the tcon */
+ talloc_free(req->tcon);
+ req->tcon = NULL;
+
+ smbsrv_setup_reply(req, 0, 0);
+ smbsrv_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 smbsrv_reply_echo(struct smbsrv_request *req)
+{
+ uint16_t count;
+ int i;
+
+ SMBSRV_CHECK_WCT(req, 1);
+
+ count = SVAL(req->in.vwv, VWV(0));
+
+ smbsrv_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 = smbsrv_setup_secondary_request(req);
+ } else {
+ this_req = req;
+ }
+
+ SSVAL(this_req->out.vwv, VWV(0), i);
+ smbsrv_send_reply(this_req);
+ }
+}
+
+
+
+/****************************************************************************
+ Reply to a printopen (async reply)
+****************************************************************************/
+static void reply_printopen_send(struct ntvfs_request *ntvfs)
+{
+ struct smbsrv_request *req;
+ union smb_open *oi;
+
+ SMBSRV_CHECK_ASYNC_STATUS(oi, union smb_open);
+
+ /* construct reply */
+ smbsrv_setup_reply(req, 1, 0);
+
+ smbsrv_push_fnum(req->out.vwv, VWV(0), oi->openold.out.file.ntvfs);
+
+ smbsrv_send_reply(req);
+}
+
+/****************************************************************************
+ Reply to a printopen.
+****************************************************************************/
+void smbsrv_reply_printopen(struct smbsrv_request *req)
+{
+ union smb_open *oi;
+
+ /* parse request */
+ SMBSRV_CHECK_WCT(req, 2);
+ SMBSRV_TALLOC_IO_PTR(oi, union smb_open);
+ SMBSRV_SETUP_NTVFS_REQUEST(reply_printopen_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ 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->in.bufinfo, &oi->splopen.in.ident, req->in.data, STR_TERMINATE);
+
+ SMBSRV_CALL_NTVFS_BACKEND(ntvfs_open(req->ntvfs, oi));
+}
+
+/****************************************************************************
+ Reply to a printclose.
+****************************************************************************/
+void smbsrv_reply_printclose(struct smbsrv_request *req)
+{
+ union smb_close *io;
+
+ /* parse request */
+ SMBSRV_CHECK_WCT(req, 3);
+ SMBSRV_TALLOC_IO_PTR(io, union smb_close);
+ SMBSRV_SETUP_NTVFS_REQUEST(reply_simple_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ io->splclose.level = RAW_CLOSE_SPLCLOSE;
+ io->splclose.in.file.ntvfs = smbsrv_pull_fnum(req, req->in.vwv, VWV(0));
+
+ SMBSRV_CHECK_FILE_HANDLE(io->splclose.in.file.ntvfs);
+ SMBSRV_CALL_NTVFS_BACKEND(ntvfs_close(req->ntvfs, io));
+}
+
+/****************************************************************************
+ Reply to a printqueue.
+****************************************************************************/
+static void reply_printqueue_send(struct ntvfs_request *ntvfs)
+{
+ struct smbsrv_request *req;
+ union smb_lpq *lpq;
+ int i, maxcount;
+ const unsigned int el_size = 28;
+
+ SMBSRV_CHECK_ASYNC_STATUS(lpq,union smb_lpq);
+
+ /* construct reply */
+ smbsrv_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;i<lpq->retq.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;
+ }
+
+ smbsrv_send_reply(req);
+}
+
+/****************************************************************************
+ Reply to a printqueue.
+****************************************************************************/
+void smbsrv_reply_printqueue(struct smbsrv_request *req)
+{
+ union smb_lpq *lpq;
+
+ /* parse request */
+ SMBSRV_CHECK_WCT(req, 2);
+ SMBSRV_TALLOC_IO_PTR(lpq, union smb_lpq);
+ SMBSRV_SETUP_NTVFS_REQUEST(reply_printqueue_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ 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));
+
+ SMBSRV_CALL_NTVFS_BACKEND(ntvfs_lpq(req->ntvfs, lpq));
+}
+
+
+/****************************************************************************
+ Reply to a printwrite.
+****************************************************************************/
+void smbsrv_reply_printwrite(struct smbsrv_request *req)
+{
+ union smb_write *io;
+
+ /* parse request */
+ SMBSRV_CHECK_WCT(req, 1);
+ SMBSRV_TALLOC_IO_PTR(io, union smb_write);
+ SMBSRV_SETUP_NTVFS_REQUEST(reply_simple_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ if (req->in.data_size < 3) {
+ smbsrv_send_error(req, NT_STATUS_FOOBAR);
+ return;
+ }
+
+ io->splwrite.level = RAW_WRITE_SPLWRITE;
+ io->splwrite.in.file.ntvfs = smbsrv_pull_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->in.bufinfo, io->splwrite.in.data, io->splwrite.in.count)) {
+ smbsrv_send_error(req, NT_STATUS_FOOBAR);
+ return;
+ }
+
+ SMBSRV_CHECK_FILE_HANDLE(io->splwrite.in.file.ntvfs);
+ SMBSRV_CALL_NTVFS_BACKEND(ntvfs_write(req->ntvfs, io));
+}
+
+
+/****************************************************************************
+ Reply to a mkdir.
+****************************************************************************/
+void smbsrv_reply_mkdir(struct smbsrv_request *req)
+{
+ union smb_mkdir *io;
+
+ /* parse the request */
+ SMBSRV_CHECK_WCT(req, 0);
+ SMBSRV_TALLOC_IO_PTR(io, union smb_mkdir);
+ SMBSRV_SETUP_NTVFS_REQUEST(reply_simple_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ io->generic.level = RAW_MKDIR_MKDIR;
+ req_pull_ascii4(&req->in.bufinfo, &io->mkdir.in.path, req->in.data, STR_TERMINATE);
+
+ SMBSRV_CALL_NTVFS_BACKEND(ntvfs_mkdir(req->ntvfs, io));
+}
+
+
+/****************************************************************************
+ Reply to a rmdir.
+****************************************************************************/
+void smbsrv_reply_rmdir(struct smbsrv_request *req)
+{
+ struct smb_rmdir *io;
+
+ /* parse the request */
+ SMBSRV_CHECK_WCT(req, 0);
+ SMBSRV_TALLOC_IO_PTR(io, struct smb_rmdir);
+ SMBSRV_SETUP_NTVFS_REQUEST(reply_simple_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ req_pull_ascii4(&req->in.bufinfo, &io->in.path, req->in.data, STR_TERMINATE);
+
+ SMBSRV_CALL_NTVFS_BACKEND(ntvfs_rmdir(req->ntvfs, io));
+}
+
+
+/****************************************************************************
+ Reply to a mv.
+****************************************************************************/
+void smbsrv_reply_mv(struct smbsrv_request *req)
+{
+ union smb_rename *io;
+ uint8_t *p;
+
+ /* parse the request */
+ SMBSRV_CHECK_WCT(req, 1);
+ SMBSRV_TALLOC_IO_PTR(io, union smb_rename);
+ SMBSRV_SETUP_NTVFS_REQUEST(reply_simple_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ 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->in.bufinfo, &io->rename.in.pattern1, p, STR_TERMINATE);
+ p += req_pull_ascii4(&req->in.bufinfo, &io->rename.in.pattern2, p, STR_TERMINATE);
+
+ if (!io->rename.in.pattern1 || !io->rename.in.pattern2) {
+ smbsrv_send_error(req, NT_STATUS_FOOBAR);
+ return;
+ }
+
+ SMBSRV_CALL_NTVFS_BACKEND(ntvfs_rename(req->ntvfs, io));
+}
+
+
+/****************************************************************************
+ Reply to an NT rename.
+****************************************************************************/
+void smbsrv_reply_ntrename(struct smbsrv_request *req)
+{
+ union smb_rename *io;
+ uint8_t *p;
+
+ /* parse the request */
+ SMBSRV_CHECK_WCT(req, 4);
+ SMBSRV_TALLOC_IO_PTR(io, union smb_rename);
+ SMBSRV_SETUP_NTVFS_REQUEST(reply_simple_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ 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->in.bufinfo, &io->ntrename.in.old_name, p, STR_TERMINATE);
+ p += req_pull_ascii4(&req->in.bufinfo, &io->ntrename.in.new_name, p, STR_TERMINATE);
+
+ if (!io->ntrename.in.old_name || !io->ntrename.in.new_name) {
+ smbsrv_send_error(req, NT_STATUS_FOOBAR);
+ return;
+ }
+
+ SMBSRV_CALL_NTVFS_BACKEND(ntvfs_rename(req->ntvfs, io));
+}
+
+/****************************************************************************
+ Reply to a file copy (async reply)
+****************************************************************************/
+static void reply_copy_send(struct ntvfs_request *ntvfs)
+{
+ struct smbsrv_request *req;
+ struct smb_copy *cp;
+
+ SMBSRV_CHECK_ASYNC_STATUS(cp, struct smb_copy);
+
+ /* build the reply */
+ smbsrv_setup_reply(req, 1, 0);
+
+ SSVAL(req->out.vwv, VWV(0), cp->out.count);
+
+ smbsrv_send_reply(req);
+}
+
+/****************************************************************************
+ Reply to a file copy.
+****************************************************************************/
+void smbsrv_reply_copy(struct smbsrv_request *req)
+{
+ struct smb_copy *cp;
+ uint8_t *p;
+
+ /* parse request */
+ SMBSRV_CHECK_WCT(req, 3);
+ SMBSRV_TALLOC_IO_PTR(cp, struct smb_copy);
+ SMBSRV_SETUP_NTVFS_REQUEST(reply_copy_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ 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->in.bufinfo, &cp->in.path1, p, STR_TERMINATE);
+ p += req_pull_ascii4(&req->in.bufinfo, &cp->in.path2, p, STR_TERMINATE);
+
+ if (!cp->in.path1 || !cp->in.path2) {
+ smbsrv_send_error(req, NT_STATUS_FOOBAR);
+ return;
+ }
+
+ SMBSRV_CALL_NTVFS_BACKEND(ntvfs_copy(req->ntvfs, cp));
+}
+
+/****************************************************************************
+ Reply to a lockingX request (async send)
+****************************************************************************/
+static void reply_lockingX_send(struct ntvfs_request *ntvfs)
+{
+ struct smbsrv_request *req;
+ union smb_lock *lck;
+
+ SMBSRV_CHECK_ASYNC_STATUS(lck, union smb_lock);
+
+ /* 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) {
+ talloc_free(req);
+ return;
+ }
+
+ /* construct reply */
+ smbsrv_setup_reply(req, 2, 0);
+
+ SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE);
+ SSVAL(req->out.vwv, VWV(1), 0);
+
+ smbsrv_chain_reply(req);
+}
+
+
+/****************************************************************************
+ Reply to a lockingX request.
+****************************************************************************/
+void smbsrv_reply_lockingX(struct smbsrv_request *req)
+{
+ union smb_lock *lck;
+ unsigned int total_locks, i;
+ unsigned int lck_size;
+ uint8_t *p;
+
+ /* parse request */
+ SMBSRV_CHECK_WCT(req, 8);
+ SMBSRV_TALLOC_IO_PTR(lck, union smb_lock);
+ SMBSRV_SETUP_NTVFS_REQUEST(reply_lockingX_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ lck->lockx.level = RAW_LOCK_LOCKX;
+ lck->lockx.in.file.ntvfs= smbsrv_pull_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->in.bufinfo, req->in.data, total_locks * lck_size)) {
+ smbsrv_send_error(req, NT_STATUS_FOOBAR);
+ return;
+ }
+
+ /* allocate the locks array */
+ if (total_locks) {
+ lck->lockx.in.locks = talloc_array(req, struct smb_lock_entry,
+ total_locks);
+ if (lck->lockx.in.locks == NULL) {
+ smbsrv_send_error(req, NT_STATUS_NO_MEMORY);
+ return;
+ }
+ }
+
+ p = req->in.data;
+
+ /* construct the locks array */
+ for (i=0;i<total_locks;i++) {
+ uint32_t ofs_high=0, count_high=0;
+
+ lck->lockx.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;
+ }
+
+ SMBSRV_CHECK_FILE_HANDLE(lck->lockx.in.file.ntvfs);
+ SMBSRV_CALL_NTVFS_BACKEND(ntvfs_lock(req->ntvfs, lck));
+}
+
+/****************************************************************************
+ Reply to a SMBreadbmpx (read block multiplex) request.
+****************************************************************************/
+void smbsrv_reply_readbmpx(struct smbsrv_request *req)
+{
+ /* tell the client to not use a multiplexed read - its too broken to use */
+ smbsrv_send_error(req, NT_STATUS_DOS(ERRSRV, ERRuseSTD));
+}
+
+
+/****************************************************************************
+ Reply to a SMBsetattrE.
+****************************************************************************/
+void smbsrv_reply_setattrE(struct smbsrv_request *req)
+{
+ union smb_setfileinfo *info;
+
+ /* parse request */
+ SMBSRV_CHECK_WCT(req, 7);
+ SMBSRV_TALLOC_IO_PTR(info, union smb_setfileinfo);
+ SMBSRV_SETUP_NTVFS_REQUEST(reply_simple_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ info->setattre.level = RAW_SFILEINFO_SETATTRE;
+ info->setattre.in.file.ntvfs = smbsrv_pull_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));
+
+ SMBSRV_CHECK_FILE_HANDLE(info->setattre.in.file.ntvfs);
+ SMBSRV_CALL_NTVFS_BACKEND(ntvfs_setfileinfo(req->ntvfs, info));
+}
+
+
+/****************************************************************************
+ Reply to a SMBwritebmpx (write block multiplex primary) request.
+****************************************************************************/
+void smbsrv_reply_writebmpx(struct smbsrv_request *req)
+{
+ smbsrv_send_error(req, NT_STATUS_DOS(ERRSRV, ERRuseSTD));
+}
+
+
+/****************************************************************************
+ Reply to a SMBwritebs (write block multiplex secondary) request.
+****************************************************************************/
+void smbsrv_reply_writebs(struct smbsrv_request *req)
+{
+ smbsrv_send_error(req, NT_STATUS_DOS(ERRSRV, ERRuseSTD));
+}
+
+
+
+/****************************************************************************
+ Reply to a SMBgetattrE (async reply)
+****************************************************************************/
+static void reply_getattrE_send(struct ntvfs_request *ntvfs)
+{
+ struct smbsrv_request *req;
+ union smb_fileinfo *info;
+
+ SMBSRV_CHECK_ASYNC_STATUS(info, union smb_fileinfo);
+
+ /* setup reply */
+ smbsrv_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);
+
+ smbsrv_send_reply(req);
+}
+
+/****************************************************************************
+ Reply to a SMBgetattrE.
+****************************************************************************/
+void smbsrv_reply_getattrE(struct smbsrv_request *req)
+{
+ union smb_fileinfo *info;
+
+ /* parse request */
+ SMBSRV_CHECK_WCT(req, 1);
+ SMBSRV_TALLOC_IO_PTR(info, union smb_fileinfo);
+ SMBSRV_SETUP_NTVFS_REQUEST(reply_getattrE_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ info->getattr.level = RAW_FILEINFO_GETATTRE;
+ info->getattr.in.file.ntvfs = smbsrv_pull_fnum(req, req->in.vwv, VWV(0));
+
+ SMBSRV_CHECK_FILE_HANDLE(info->getattr.in.file.ntvfs);
+ SMBSRV_CALL_NTVFS_BACKEND(ntvfs_qfileinfo(req->ntvfs, info));
+}
+
+void smbsrv_reply_sesssetup_send(struct smbsrv_request *req,
+ union smb_sesssetup *io,
+ NTSTATUS status)
+{
+ switch (io->old.level) {
+ case RAW_SESSSETUP_OLD:
+ if (!NT_STATUS_IS_OK(status)) {
+ smbsrv_send_error(req, status);
+ return;
+ }
+
+ /* construct reply */
+ smbsrv_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), io->old.out.action);
+
+ SSVAL(req->out.hdr, HDR_UID, io->old.out.vuid);
+
+ smbsrv_chain_reply(req);
+ return;
+
+ case RAW_SESSSETUP_NT1:
+ if (!NT_STATUS_IS_OK(status)) {
+ smbsrv_send_error(req, status);
+ return;
+ }
+
+ /* construct reply */
+ smbsrv_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), io->nt1.out.action);
+
+ SSVAL(req->out.hdr, HDR_UID, io->nt1.out.vuid);
+
+ req_push_str(req, NULL, io->nt1.out.os, -1, STR_TERMINATE);
+ req_push_str(req, NULL, io->nt1.out.lanman, -1, STR_TERMINATE);
+ req_push_str(req, NULL, io->nt1.out.domain, -1, STR_TERMINATE);
+
+ smbsrv_chain_reply(req);
+ return;
+
+ case RAW_SESSSETUP_SPNEGO:
+ if (!NT_STATUS_IS_OK(status) &&
+ !NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ smbsrv_send_error(req, status);
+ return;
+ }
+
+ /* construct reply */
+ smbsrv_setup_reply(req, 4, io->spnego.out.secblob.length);
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ smbsrv_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), io->spnego.out.action);
+ SSVAL(req->out.vwv, VWV(3), io->spnego.out.secblob.length);
+
+ SSVAL(req->out.hdr, HDR_UID, io->spnego.out.vuid);
+
+ memcpy(req->out.data, io->spnego.out.secblob.data, io->spnego.out.secblob.length);
+ req_push_str(req, NULL, io->spnego.out.os, -1, STR_TERMINATE);
+ req_push_str(req, NULL, io->spnego.out.lanman, -1, STR_TERMINATE);
+ req_push_str(req, NULL, io->spnego.out.workgroup, -1, STR_TERMINATE);
+
+ smbsrv_chain_reply(req);
+ return;
+
+ case RAW_SESSSETUP_SMB2:
+ break;
+ }
+
+ smbsrv_send_error(req, NT_STATUS_INTERNAL_ERROR);
+}
+
+/****************************************************************************
+reply to an old style session setup command
+****************************************************************************/
+static void reply_sesssetup_old(struct smbsrv_request *req)
+{
+ uint8_t *p;
+ uint16_t passlen;
+ union smb_sesssetup *io;
+
+ SMBSRV_TALLOC_IO_PTR(io, union smb_sesssetup);
+
+ io->old.level = RAW_SESSSETUP_OLD;
+
+ /* parse request */
+ io->old.in.bufsize = SVAL(req->in.vwv, VWV(2));
+ io->old.in.mpx_max = SVAL(req->in.vwv, VWV(3));
+ io->old.in.vc_num = SVAL(req->in.vwv, VWV(4));
+ io->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->in.bufinfo, req->in.data, passlen)) {
+ smbsrv_send_error(req, NT_STATUS_FOOBAR);
+ return;
+ }
+
+ p = req->in.data;
+ if (!req_pull_blob(&req->in.bufinfo, p, passlen, &io->old.in.password)) {
+ smbsrv_send_error(req, NT_STATUS_FOOBAR);
+ return;
+ }
+ p += passlen;
+
+ p += req_pull_string(&req->in.bufinfo, &io->old.in.user, p, -1, STR_TERMINATE);
+ p += req_pull_string(&req->in.bufinfo, &io->old.in.domain, p, -1, STR_TERMINATE);
+ p += req_pull_string(&req->in.bufinfo, &io->old.in.os, p, -1, STR_TERMINATE);
+ p += req_pull_string(&req->in.bufinfo, &io->old.in.lanman, p, -1, STR_TERMINATE);
+
+ /* call the generic handler */
+ smbsrv_sesssetup_backend(req, io);
+}
+
+/****************************************************************************
+reply to an NT1 style session setup command
+****************************************************************************/
+static void reply_sesssetup_nt1(struct smbsrv_request *req)
+{
+ uint8_t *p;
+ uint16_t passlen1, passlen2;
+ union smb_sesssetup *io;
+
+ SMBSRV_TALLOC_IO_PTR(io, union smb_sesssetup);
+
+ io->nt1.level = RAW_SESSSETUP_NT1;
+
+ /* parse request */
+ io->nt1.in.bufsize = SVAL(req->in.vwv, VWV(2));
+ io->nt1.in.mpx_max = SVAL(req->in.vwv, VWV(3));
+ io->nt1.in.vc_num = SVAL(req->in.vwv, VWV(4));
+ io->nt1.in.sesskey = IVAL(req->in.vwv, VWV(5));
+ passlen1 = SVAL(req->in.vwv, VWV(7));
+ passlen2 = SVAL(req->in.vwv, VWV(8));
+ io->nt1.in.capabilities = IVAL(req->in.vwv, VWV(11));
+
+ /* check the request isn't malformed */
+ if (req_data_oob(&req->in.bufinfo, req->in.data, passlen1) ||
+ req_data_oob(&req->in.bufinfo, req->in.data + passlen1, passlen2)) {
+ smbsrv_send_error(req, NT_STATUS_FOOBAR);
+ return;
+ }
+
+ p = req->in.data;
+ if (!req_pull_blob(&req->in.bufinfo, p, passlen1, &io->nt1.in.password1)) {
+ smbsrv_send_error(req, NT_STATUS_FOOBAR);
+ return;
+ }
+ p += passlen1;
+ if (!req_pull_blob(&req->in.bufinfo, p, passlen2, &io->nt1.in.password2)) {
+ smbsrv_send_error(req, NT_STATUS_FOOBAR);
+ return;
+ }
+ p += passlen2;
+
+ p += req_pull_string(&req->in.bufinfo, &io->nt1.in.user, p, -1, STR_TERMINATE);
+ p += req_pull_string(&req->in.bufinfo, &io->nt1.in.domain, p, -1, STR_TERMINATE);
+ p += req_pull_string(&req->in.bufinfo, &io->nt1.in.os, p, -1, STR_TERMINATE);
+ p += req_pull_string(&req->in.bufinfo, &io->nt1.in.lanman, p, -1, STR_TERMINATE);
+
+ /* call the generic handler */
+ smbsrv_sesssetup_backend(req, io);
+}
+
+
+/****************************************************************************
+reply to an SPNEGO style session setup command
+****************************************************************************/
+static void reply_sesssetup_spnego(struct smbsrv_request *req)
+{
+ uint8_t *p;
+ uint16_t blob_len;
+ union smb_sesssetup *io;
+
+ SMBSRV_TALLOC_IO_PTR(io, union smb_sesssetup);
+
+ io->spnego.level = RAW_SESSSETUP_SPNEGO;
+
+ /* parse request */
+ io->spnego.in.bufsize = SVAL(req->in.vwv, VWV(2));
+ io->spnego.in.mpx_max = SVAL(req->in.vwv, VWV(3));
+ io->spnego.in.vc_num = SVAL(req->in.vwv, VWV(4));
+ io->spnego.in.sesskey = IVAL(req->in.vwv, VWV(5));
+ blob_len = SVAL(req->in.vwv, VWV(7));
+ io->spnego.in.capabilities = IVAL(req->in.vwv, VWV(10));
+
+ p = req->in.data;
+ if (!req_pull_blob(&req->in.bufinfo, p, blob_len, &io->spnego.in.secblob)) {
+ smbsrv_send_error(req, NT_STATUS_FOOBAR);
+ return;
+ }
+ p += blob_len;
+
+ p += req_pull_string(&req->in.bufinfo, &io->spnego.in.os, p, -1, STR_TERMINATE);
+ p += req_pull_string(&req->in.bufinfo, &io->spnego.in.lanman, p, -1, STR_TERMINATE);
+ p += req_pull_string(&req->in.bufinfo, &io->spnego.in.workgroup, p, -1, STR_TERMINATE);
+
+ /* call the generic handler */
+ smbsrv_sesssetup_backend(req, io);
+}
+
+
+/****************************************************************************
+reply to a session setup command
+****************************************************************************/
+void smbsrv_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 */
+ smbsrv_send_error(req, NT_STATUS_FOOBAR);
+}
+
+/****************************************************************************
+ Reply to a exit. This closes all files open by a smbpid
+****************************************************************************/
+void smbsrv_reply_exit(struct smbsrv_request *req)
+{
+ struct smbsrv_handle_session_item *i, *ni;
+ struct smbsrv_handle *h;
+ struct smbsrv_tcon *tcon;
+ uint16_t smbpid;
+
+ SMBSRV_CHECK_WCT(req, 0);
+
+ smbpid = SVAL(req->in.hdr,HDR_PID);
+
+ /* first destroy all handles, which have the same PID as the request */
+ for (i=req->session->handles; i; i=ni) {
+ ni = i->next;
+ h = i->handle;
+ if (h->smbpid != smbpid) continue;
+
+ talloc_free(h);
+ }
+
+ /*
+ * then let the ntvfs backends proxy the call if they want to,
+ * but we didn't check the return value of the backends,
+ * as for the SMB client the call succeed
+ */
+ for (tcon=req->smb_conn->smb_tcons.list;tcon;tcon=tcon->next) {
+ req->tcon = tcon;
+ SMBSRV_SETUP_NTVFS_REQUEST(NULL,0);
+ ntvfs_exit(req->ntvfs);
+ talloc_free(req->ntvfs);
+ req->ntvfs = NULL;
+ req->tcon = NULL;
+ }
+
+ smbsrv_setup_reply(req, 0, 0);
+ smbsrv_send_reply(req);
+}
+
+/****************************************************************************
+ Reply to a SMBulogoffX.
+****************************************************************************/
+void smbsrv_reply_ulogoffX(struct smbsrv_request *req)
+{
+ struct smbsrv_handle_session_item *i, *ni;
+ struct smbsrv_handle *h;
+ struct smbsrv_tcon *tcon;
+
+ SMBSRV_CHECK_WCT(req, 2);
+
+ /*
+ * TODO: cancel all pending requests
+ */
+
+
+ /* destroy all handles */
+ for (i=req->session->handles; i; i=ni) {
+ ni = i->next;
+ h = i->handle;
+ talloc_free(h);
+ }
+
+ /*
+ * then let the ntvfs backends proxy the call if they want to,
+ * but we didn't check the return value of the backends,
+ * as for the SMB client the call succeed
+ */
+ for (tcon=req->smb_conn->smb_tcons.list;tcon;tcon=tcon->next) {
+ req->tcon = tcon;
+ SMBSRV_SETUP_NTVFS_REQUEST(NULL,0);
+ ntvfs_logoff(req->ntvfs);
+ talloc_free(req->ntvfs);
+ req->ntvfs = NULL;
+ req->tcon = NULL;
+ }
+
+ talloc_free(req->session);
+ req->session = NULL; /* it is now invalid, don't use on
+ any chained packets */
+
+ smbsrv_setup_reply(req, 2, 0);
+
+ SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE);
+ SSVAL(req->out.vwv, VWV(1), 0);
+
+ smbsrv_chain_reply(req);
+}
+
+/****************************************************************************
+ Reply to an SMBfindclose request
+****************************************************************************/
+void smbsrv_reply_findclose(struct smbsrv_request *req)
+{
+ union smb_search_close *io;
+
+ /* parse request */
+ SMBSRV_CHECK_WCT(req, 1);
+ SMBSRV_TALLOC_IO_PTR(io, union smb_search_close);
+ SMBSRV_SETUP_NTVFS_REQUEST(reply_simple_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ io->findclose.level = RAW_FINDCLOSE_FINDCLOSE;
+ io->findclose.in.handle = SVAL(req->in.vwv, VWV(0));
+
+ SMBSRV_CALL_NTVFS_BACKEND(ntvfs_search_close(req->ntvfs, io));
+}
+
+/****************************************************************************
+ Reply to an SMBfindnclose request
+****************************************************************************/
+void smbsrv_reply_findnclose(struct smbsrv_request *req)
+{
+ smbsrv_send_error(req, NT_STATUS_FOOBAR);
+}
+
+
+/****************************************************************************
+ Reply to an SMBntcreateX request (async send)
+****************************************************************************/
+static void reply_ntcreate_and_X_send(struct ntvfs_request *ntvfs)
+{
+ struct smbsrv_request *req;
+ union smb_open *io;
+
+ SMBSRV_CHECK_ASYNC_STATUS(io, union smb_open);
+
+ /* construct reply */
+ smbsrv_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! */
+ smbsrv_push_fnum(req->out.vwv, 5, io->ntcreatex.out.file.ntvfs);
+ 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 = SVAL(req->out.vwv, 5);
+
+ smbsrv_chain_reply(req);
+}
+
+/****************************************************************************
+ Reply to an SMBntcreateX request
+****************************************************************************/
+void smbsrv_reply_ntcreate_and_X(struct smbsrv_request *req)
+{
+ union smb_open *io;
+ uint16_t fname_len;
+
+ /* parse the request */
+ SMBSRV_CHECK_WCT(req, 24);
+ SMBSRV_TALLOC_IO_PTR(io, union smb_open);
+ SMBSRV_SETUP_NTVFS_REQUEST(reply_ntcreate_and_X_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ 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.ntvfs = smbsrv_pull_fnum(req, 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;
+ io->ntcreatex.in.query_maximal_access = false;
+ io->ntcreatex.in.query_on_disk_id = false;
+ io->ntcreatex.in.private_flags = 0;
+
+ /* 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->in.bufinfo, &io->ntcreatex.in.fname, req->in.data, fname_len, STR_TERMINATE);
+ if (!io->ntcreatex.in.fname) {
+ smbsrv_send_error(req, NT_STATUS_FOOBAR);
+ return;
+ }
+
+ SMBSRV_CALL_NTVFS_BACKEND(ntvfs_open(req->ntvfs, io));
+}
+
+
+/****************************************************************************
+ Reply to an SMBntcancel request
+****************************************************************************/
+void smbsrv_reply_ntcancel(struct smbsrv_request *req)
+{
+ struct smbsrv_request *r;
+ uint16_t tid = SVAL(req->in.hdr,HDR_TID);
+ uint16_t uid = SVAL(req->in.hdr,HDR_UID);
+ uint16_t mid = SVAL(req->in.hdr,HDR_MID);
+ uint16_t pid = SVAL(req->in.hdr,HDR_PID);
+
+ for (r = req->smb_conn->requests; r; r = r->next) {
+ if (tid != SVAL(r->in.hdr,HDR_TID)) continue;
+ if (uid != SVAL(r->in.hdr,HDR_UID)) continue;
+ if (mid != SVAL(r->in.hdr,HDR_MID)) continue;
+ if (pid != SVAL(r->in.hdr,HDR_PID)) continue;
+
+ SMBSRV_CHECK(ntvfs_cancel(r->ntvfs));
+
+ /* NOTE: this request does not generate a reply */
+ talloc_free(req);
+ return;
+ }
+
+ /* TODO: workout the correct error code,
+ * until we know how the smb signing works
+ * for ntcancel replies, don't send an error
+ */
+ /*smbsrv_send_error(req, NT_STATUS_FOOBAR);*/
+ talloc_free(req);
+}
+
+/*
+ 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 smbsrv_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 NBSSrequest: /* 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;
+ smbsrv_send_reply_nosign(req);
+ return;
+
+ case 0x89: /* session keepalive request
+ (some old clients produce this?) */
+ SCVAL(buf, 0, NBSSkeepalive);
+ SCVAL(buf, 3, 0);
+ req->out.buffer = buf;
+ req->out.size = 4;
+ smbsrv_send_reply_nosign(req);
+ return;
+
+ case NBSSkeepalive:
+ /* session keepalive - swallow it */
+ talloc_free(req);
+ return;
+ }
+
+ DEBUG(0,("Unexpected NBT session packet (%d)\n", msg_type));
+ talloc_free(req);
+}
diff --git a/source4/smb_server/smb/request.c b/source4/smb_server/smb/request.c
new file mode 100644
index 0000000..0f3171f
--- /dev/null
+++ b/source4/smb_server/smb/request.c
@@ -0,0 +1,779 @@
+/*
+ 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 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ this file implements functions for manipulating the 'struct smbsrv_request' structure in smbd
+*/
+
+#include "includes.h"
+#include "smb_server/smb_server.h"
+#include "samba/service_stream.h"
+#include "lib/stream/packet.h"
+#include "ntvfs/ntvfs.h"
+
+
+/* we over allocate the data buffer to prevent too many realloc calls */
+#define REQ_OVER_ALLOCATION 0
+
+/* setup the bufinfo used for strings and range checking */
+void smbsrv_setup_bufinfo(struct smbsrv_request *req)
+{
+ req->in.bufinfo.mem_ctx = req;
+ req->in.bufinfo.flags = 0;
+ if (req->flags2 & FLAGS2_UNICODE_STRINGS) {
+ req->in.bufinfo.flags |= BUFINFO_FLAG_UNICODE;
+ }
+ req->in.bufinfo.align_base = req->in.buffer;
+ req->in.bufinfo.data = req->in.data;
+ req->in.bufinfo.data_size = req->in.data_size;
+}
+
+
+static int smbsrv_request_destructor(struct smbsrv_request *req)
+{
+ DLIST_REMOVE(req->smb_conn->requests, req);
+ return 0;
+}
+
+/****************************************************************************
+construct a basic request packet, mostly used to construct async packets
+such as change notify and oplock break requests
+****************************************************************************/
+struct smbsrv_request *smbsrv_init_request(struct smbsrv_connection *smb_conn)
+{
+ struct smbsrv_request *req;
+
+ req = talloc_zero(smb_conn, struct smbsrv_request);
+ if (!req) {
+ return NULL;
+ }
+
+ /* setup the request context */
+ req->smb_conn = smb_conn;
+
+ talloc_set_destructor(req, smbsrv_request_destructor);
+
+ return req;
+}
+
+
+/*
+ setup a chained reply in req->out with the given word count and initial data buffer size.
+*/
+static void req_setup_chain_reply(struct smbsrv_request *req, unsigned int wct, unsigned int buflen)
+{
+ uint32_t chain_base_size = req->out.size;
+
+ /* we need room for the wct value, the words, the buffer length and the buffer */
+ req->out.size += 1 + VWV(wct) + 2 + buflen;
+
+ /* over allocate by a small amount */
+ req->out.allocated = req->out.size + REQ_OVER_ALLOCATION;
+
+ req->out.buffer = talloc_realloc(req, req->out.buffer,
+ uint8_t, req->out.allocated);
+ if (!req->out.buffer) {
+ smbsrv_terminate_connection(req->smb_conn, "allocation failed");
+ return;
+ }
+
+ req->out.hdr = req->out.buffer + NBT_HDR_SIZE;
+ req->out.vwv = req->out.buffer + chain_base_size + 1;
+ req->out.wct = wct;
+ req->out.data = req->out.vwv + VWV(wct) + 2;
+ req->out.data_size = buflen;
+ req->out.ptr = req->out.data;
+
+ SCVAL(req->out.buffer, chain_base_size, wct);
+ SSVAL(req->out.vwv, VWV(wct), buflen);
+}
+
+
+/*
+ setup a reply in req->out with the given word count and initial data buffer size.
+ the caller will then fill in the command words and data before calling req_send_reply() to
+ send the reply on its way
+*/
+void smbsrv_setup_reply(struct smbsrv_request *req, unsigned int wct, size_t buflen)
+{
+ uint16_t flags2;
+
+ if (req->chain_count != 0) {
+ req_setup_chain_reply(req, wct, buflen);
+ return;
+ }
+
+ req->out.size = NBT_HDR_SIZE + MIN_SMB_SIZE + VWV(wct) + buflen;
+
+ /* over allocate by a small amount */
+ req->out.allocated = req->out.size + REQ_OVER_ALLOCATION;
+
+ req->out.buffer = talloc_size(req, req->out.allocated);
+ if (!req->out.buffer) {
+ smbsrv_terminate_connection(req->smb_conn, "allocation failed");
+ return;
+ }
+
+ flags2 = FLAGS2_LONG_PATH_COMPONENTS |
+ FLAGS2_EXTENDED_ATTRIBUTES |
+ FLAGS2_IS_LONG_NAME;
+#define _SMB_FLAGS2_ECHOED_FLAGS ( \
+ FLAGS2_UNICODE_STRINGS | \
+ FLAGS2_EXTENDED_SECURITY | \
+ FLAGS2_SMB_SECURITY_SIGNATURES \
+)
+ flags2 |= (req->flags2 & _SMB_FLAGS2_ECHOED_FLAGS);
+ if (req->smb_conn->negotiate.client_caps & CAP_STATUS32) {
+ flags2 |= FLAGS2_32_BIT_ERROR_CODES;
+ }
+
+ req->out.hdr = req->out.buffer + NBT_HDR_SIZE;
+ req->out.vwv = req->out.hdr + HDR_VWV;
+ req->out.wct = wct;
+ req->out.data = req->out.vwv + VWV(wct) + 2;
+ req->out.data_size = buflen;
+ req->out.ptr = req->out.data;
+
+ SIVAL(req->out.hdr, HDR_RCLS, 0);
+
+ SCVAL(req->out.hdr, HDR_WCT, wct);
+ SSVAL(req->out.vwv, VWV(wct), buflen);
+
+ memcpy(req->out.hdr, "\377SMB", 4);
+ SCVAL(req->out.hdr,HDR_FLG, FLAG_REPLY | FLAG_CASELESS_PATHNAMES);
+ SSVAL(req->out.hdr,HDR_FLG2, flags2);
+ SSVAL(req->out.hdr,HDR_PIDHIGH,0);
+ memset(req->out.hdr + HDR_SS_FIELD, 0, 10);
+
+ if (req->in.hdr) {
+ /* copy the cmd, tid, pid, uid and mid from the request */
+ SCVAL(req->out.hdr,HDR_COM,CVAL(req->in.hdr,HDR_COM));
+ SSVAL(req->out.hdr,HDR_TID,SVAL(req->in.hdr,HDR_TID));
+ SSVAL(req->out.hdr,HDR_PID,SVAL(req->in.hdr,HDR_PID));
+ SSVAL(req->out.hdr,HDR_UID,SVAL(req->in.hdr,HDR_UID));
+ SSVAL(req->out.hdr,HDR_MID,SVAL(req->in.hdr,HDR_MID));
+ } else {
+ SCVAL(req->out.hdr,HDR_COM,0);
+ SSVAL(req->out.hdr,HDR_TID,0);
+ SSVAL(req->out.hdr,HDR_PID,0);
+ SSVAL(req->out.hdr,HDR_UID,0);
+ SSVAL(req->out.hdr,HDR_MID,0);
+ }
+}
+
+
+/*
+ setup a copy of a request, used when the server needs to send
+ more than one reply for a single request packet
+*/
+struct smbsrv_request *smbsrv_setup_secondary_request(struct smbsrv_request *old_req)
+{
+ struct smbsrv_request *req;
+ ptrdiff_t diff;
+
+ req = talloc_memdup(old_req, old_req, sizeof(struct smbsrv_request));
+ if (req == NULL) {
+ return NULL;
+ }
+
+ req->out.buffer = talloc_memdup(req, req->out.buffer, req->out.allocated);
+ if (req->out.buffer == NULL) {
+ talloc_free(req);
+ return NULL;
+ }
+
+ diff = req->out.buffer - old_req->out.buffer;
+
+ req->out.hdr += diff;
+ req->out.vwv += diff;
+ req->out.data += diff;
+ req->out.ptr += diff;
+
+ return req;
+}
+
+/*
+ work out the maximum data size we will allow for this reply, given
+ the negotiated max_xmit. The basic reply packet must be setup before
+ this call
+
+ note that this is deliberately a signed integer reply
+*/
+int req_max_data(struct smbsrv_request *req)
+{
+ int ret;
+ ret = req->smb_conn->negotiate.max_send;
+ ret -= PTR_DIFF(req->out.data, req->out.hdr);
+ if (ret < 0) ret = 0;
+ return ret;
+}
+
+
+/*
+ grow the allocation of the data buffer portion of a reply
+ packet. Note that as this can reallocate the packet buffer this
+ invalidates any local pointers into the packet.
+
+ To cope with this req->out.ptr is supplied. This will be updated to
+ point at the same offset into the packet as before this call
+*/
+static void req_grow_allocation(struct smbsrv_request *req, unsigned int new_size)
+{
+ int delta;
+ uint8_t *buf2;
+
+ delta = new_size - req->out.data_size;
+ if (delta + req->out.size <= req->out.allocated) {
+ /* it fits in the preallocation */
+ return;
+ }
+
+ /* we need to realloc */
+ req->out.allocated = req->out.size + delta + REQ_OVER_ALLOCATION;
+ buf2 = talloc_realloc(req, req->out.buffer, uint8_t, req->out.allocated);
+ if (buf2 == NULL) {
+ smb_panic("out of memory in req_grow_allocation");
+ }
+
+ if (buf2 == req->out.buffer) {
+ /* the malloc library gave us the same pointer */
+ return;
+ }
+
+ /* update the pointers into the packet */
+ req->out.data = buf2 + PTR_DIFF(req->out.data, req->out.buffer);
+ req->out.ptr = buf2 + PTR_DIFF(req->out.ptr, req->out.buffer);
+ req->out.vwv = buf2 + PTR_DIFF(req->out.vwv, req->out.buffer);
+ req->out.hdr = buf2 + PTR_DIFF(req->out.hdr, req->out.buffer);
+
+ req->out.buffer = buf2;
+}
+
+
+/*
+ grow the data buffer portion of a reply packet. Note that as this
+ can reallocate the packet buffer this invalidates any local pointers
+ into the packet.
+
+ To cope with this req->out.ptr is supplied. This will be updated to
+ point at the same offset into the packet as before this call
+*/
+void req_grow_data(struct smbsrv_request *req, size_t new_size)
+{
+ int delta;
+
+ if (!(req->control_flags & SMBSRV_REQ_CONTROL_LARGE) && new_size > req_max_data(req)) {
+ smb_panic("reply buffer too large!");
+ }
+
+ req_grow_allocation(req, new_size);
+
+ delta = new_size - req->out.data_size;
+
+ req->out.size += delta;
+ req->out.data_size += delta;
+
+ /* set the BCC to the new data size */
+ SSVAL(req->out.vwv, VWV(req->out.wct), new_size);
+}
+
+/*
+ send a reply and destroy the request buffer
+
+ note that this only looks at req->out.buffer and req->out.size, allowing manually
+ constructed packets to be sent
+*/
+void smbsrv_send_reply_nosign(struct smbsrv_request *req)
+{
+ DATA_BLOB blob;
+ NTSTATUS status;
+
+ if (req->smb_conn->connection->event.fde == NULL) {
+ /* we are in the process of shutting down this connection */
+ talloc_free(req);
+ return;
+ }
+
+ if (req->out.size > NBT_HDR_SIZE) {
+ _smb_setlen_nbt(req->out.buffer, req->out.size - NBT_HDR_SIZE);
+ }
+
+ blob = data_blob_const(req->out.buffer, req->out.size);
+ status = packet_send(req->smb_conn->packet, blob);
+ if (!NT_STATUS_IS_OK(status)) {
+ smbsrv_terminate_connection(req->smb_conn, nt_errstr(status));
+ }
+ talloc_free(req);
+}
+
+/*
+ possibly sign a message then send a reply and destroy the request buffer
+
+ note that this only looks at req->out.buffer and req->out.size, allowing manually
+ constructed packets to be sent
+*/
+void smbsrv_send_reply(struct smbsrv_request *req)
+{
+ if (req->smb_conn->connection->event.fde == NULL) {
+ /* we are in the process of shutting down this connection */
+ talloc_free(req);
+ return;
+ }
+ smbsrv_sign_packet(req);
+
+ smbsrv_send_reply_nosign(req);
+}
+
+/*
+ setup the header of a reply to include an NTSTATUS code
+*/
+void smbsrv_setup_error(struct smbsrv_request *req, NTSTATUS status)
+{
+ if (!req->smb_conn->config.nt_status_support || !(req->smb_conn->negotiate.client_caps & CAP_STATUS32)) {
+ /* convert to DOS error codes */
+ uint8_t eclass;
+ uint32_t ecode;
+ ntstatus_to_dos(status, &eclass, &ecode);
+ SCVAL(req->out.hdr, HDR_RCLS, eclass);
+ SSVAL(req->out.hdr, HDR_ERR, ecode);
+ SSVAL(req->out.hdr, HDR_FLG2, SVAL(req->out.hdr, HDR_FLG2) & ~FLAGS2_32_BIT_ERROR_CODES);
+ return;
+ }
+
+ if (NT_STATUS_IS_DOS(status)) {
+ /* its a encoded DOS error, using the reserved range */
+ SSVAL(req->out.hdr, HDR_RCLS, NT_STATUS_DOS_CLASS(status));
+ SSVAL(req->out.hdr, HDR_ERR, NT_STATUS_DOS_CODE(status));
+ SSVAL(req->out.hdr, HDR_FLG2, SVAL(req->out.hdr, HDR_FLG2) & ~FLAGS2_32_BIT_ERROR_CODES);
+ } else {
+ SIVAL(req->out.hdr, HDR_RCLS, NT_STATUS_V(status));
+ SSVAL(req->out.hdr, HDR_FLG2, SVAL(req->out.hdr, HDR_FLG2) | FLAGS2_32_BIT_ERROR_CODES);
+ }
+}
+
+/*
+ construct and send an error packet, then destroy the request
+ auto-converts to DOS error format when appropriate
+*/
+void smbsrv_send_error(struct smbsrv_request *req, NTSTATUS status)
+{
+ if (req->smb_conn->connection->event.fde == NULL) {
+ /* the socket has been destroyed - no point trying to send an error! */
+ talloc_free(req);
+ return;
+ }
+ smbsrv_setup_reply(req, 0, 0);
+
+ /* error returns never have any data */
+ req_grow_data(req, 0);
+
+ smbsrv_setup_error(req, status);
+ smbsrv_send_reply(req);
+}
+
+
+/*
+ push a string into the data portion of the request packet, growing it if necessary
+ this gets quite tricky - please be very careful to cover all cases when modifying this
+
+ if dest is NULL, then put the string at the end of the data portion of the packet
+
+ if dest_len is -1 then no limit applies
+*/
+size_t req_push_str(struct smbsrv_request *req, uint8_t *dest, const char *str, int dest_len, size_t flags)
+{
+ size_t len;
+ unsigned int 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 request_bufinfo *bufinfo, const char **dest, const uint8_t *src, int byte_len, unsigned int flags)
+{
+ int src_len, src_len2, alignment=0;
+ bool ret;
+ char *dest2;
+ size_t converted_size = 0;
+
+ if (!(flags & STR_NOALIGN) && ucs2_align(bufinfo->align_base, src, flags)) {
+ src++;
+ alignment=1;
+ if (byte_len != -1) {
+ byte_len--;
+ }
+ }
+
+ if (flags & STR_NO_RANGE_CHECK) {
+ src_len = byte_len;
+ } else {
+ src_len = bufinfo->data_size - PTR_DIFF(src, bufinfo->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(bufinfo->mem_ctx, "");
+ return src_len2 + alignment;
+ }
+
+ ret = convert_string_talloc(bufinfo->mem_ctx, CH_UTF16, CH_UNIX, src, src_len2, (void **)&dest2, &converted_size);
+
+ if (!ret) {
+ *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 request_bufinfo *bufinfo, const char **dest, const uint8_t *src, int byte_len, unsigned int flags)
+{
+ int src_len, src_len2;
+ bool ret;
+ char *dest2;
+ size_t converted_size = 0;
+
+ if (flags & STR_NO_RANGE_CHECK) {
+ src_len = byte_len;
+ } else {
+ src_len = bufinfo->data_size - PTR_DIFF(src, bufinfo->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(bufinfo->mem_ctx, CH_DOS, CH_UNIX, src, src_len2, (void **)&dest2, &converted_size);
+
+ if (!ret) {
+ *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 request_bufinfo *bufinfo, const char **dest, const uint8_t *src, int byte_len, unsigned int flags)
+{
+ if (!(flags & STR_ASCII) &&
+ (((flags & STR_UNICODE) || (bufinfo->flags & BUFINFO_FLAG_UNICODE)))) {
+ return req_pull_ucs2(bufinfo, dest, src, byte_len, flags);
+ }
+
+ return req_pull_ascii(bufinfo, 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 request_bufinfo *bufinfo, const char **dest, const uint8_t *src, unsigned int flags)
+{
+ ssize_t ret;
+
+ if (PTR_DIFF(src, bufinfo->data) + 1 > bufinfo->data_size) {
+ /* win2000 treats this as the empty string! */
+ (*dest) = talloc_strdup(bufinfo->mem_ctx, "");
+ 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(bufinfo, dest, src, -1, flags);
+ if (ret == -1) {
+ (*dest) = talloc_strdup(bufinfo->mem_ctx, "");
+ 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 request_bufinfo *bufinfo, const uint8_t *src, int len, DATA_BLOB *blob)
+{
+ if (len != 0 && req_data_oob(bufinfo, src, len)) {
+ return false;
+ }
+
+ (*blob) = data_blob_talloc(bufinfo->mem_ctx, 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 request_bufinfo *bufinfo, const uint8_t *ptr, uint32_t count)
+{
+ if (count == 0) {
+ return false;
+ }
+
+ /* be careful with wraparound! */
+ if ((uintptr_t)ptr < (uintptr_t)bufinfo->data ||
+ (uintptr_t)ptr >= (uintptr_t)bufinfo->data + bufinfo->data_size ||
+ count > bufinfo->data_size ||
+ (uintptr_t)ptr + count > (uintptr_t)bufinfo->data + bufinfo->data_size) {
+ return true;
+ }
+ return false;
+}
+
+
+/*
+ pull an open file handle from a packet, taking account of the chained_fnum
+*/
+static uint16_t req_fnum(struct smbsrv_request *req, const uint8_t *base, unsigned int offset)
+{
+ if (req->chained_fnum != -1) {
+ return req->chained_fnum;
+ }
+ return SVAL(base, offset);
+}
+
+struct ntvfs_handle *smbsrv_pull_fnum(struct smbsrv_request *req, const uint8_t *base, unsigned int offset)
+{
+ struct smbsrv_handle *handle;
+ uint16_t fnum = req_fnum(req, base, offset);
+
+ handle = smbsrv_smb_handle_find(req->tcon, fnum, req->request_time);
+ if (!handle) {
+ return NULL;
+ }
+
+ /*
+ * For SMB tcons and sessions can be mixed!
+ * But we need to make sure that file handles
+ * are only accessed by the opening session!
+ *
+ * So check if the handle is valid for the given session!
+ */
+ if (handle->session != req->session) {
+ return NULL;
+ }
+
+ return handle->ntvfs;
+}
+
+void smbsrv_push_fnum(uint8_t *base, unsigned int offset, struct ntvfs_handle *ntvfs)
+{
+ struct smbsrv_handle *handle = talloc_get_type(ntvfs->frontend_data.private_data,
+ struct smbsrv_handle);
+ SSVAL(base, offset, handle->hid);
+}
+
+NTSTATUS smbsrv_handle_create_new(void *private_data, struct ntvfs_request *ntvfs, struct ntvfs_handle **_h)
+{
+ struct smbsrv_request *req = talloc_get_type(ntvfs->frontend_data.private_data,
+ struct smbsrv_request);
+ struct smbsrv_handle *handle;
+ struct ntvfs_handle *h;
+
+ handle = smbsrv_handle_new(req->session, req->tcon, req, req->request_time);
+ if (!handle) return NT_STATUS_INSUFFICIENT_RESOURCES;
+
+ h = talloc_zero(handle, struct ntvfs_handle);
+ if (!h) goto nomem;
+
+ /*
+ * note: we don't set handle->ntvfs yet,
+ * this will be done by smbsrv_handle_make_valid()
+ * this makes sure the handle is invalid for clients
+ * until the ntvfs subsystem has made it valid
+ */
+ h->ctx = ntvfs->ctx;
+ h->session_info = ntvfs->session_info;
+ h->smbpid = ntvfs->smbpid;
+
+ h->frontend_data.private_data = handle;
+
+ *_h = h;
+ return NT_STATUS_OK;
+nomem:
+ talloc_free(handle);
+ return NT_STATUS_NO_MEMORY;
+}
+
+NTSTATUS smbsrv_handle_make_valid(void *private_data, struct ntvfs_handle *h)
+{
+ struct smbsrv_tcon *tcon = talloc_get_type(private_data, struct smbsrv_tcon);
+ struct smbsrv_handle *handle = talloc_get_type(h->frontend_data.private_data,
+ struct smbsrv_handle);
+ /* this tells the frontend that the handle is valid */
+ handle->ntvfs = h;
+ /* this moves the smbsrv_request to the smbsrv_tcon memory context */
+ talloc_steal(tcon, handle);
+ return NT_STATUS_OK;
+}
+
+void smbsrv_handle_destroy(void *private_data, struct ntvfs_handle *h)
+{
+ struct smbsrv_handle *handle = talloc_get_type(h->frontend_data.private_data,
+ struct smbsrv_handle);
+ talloc_free(handle);
+}
+
+struct ntvfs_handle *smbsrv_handle_search_by_wire_key(void *private_data, struct ntvfs_request *ntvfs, const DATA_BLOB *key)
+{
+ struct smbsrv_request *req = talloc_get_type(ntvfs->frontend_data.private_data,
+ struct smbsrv_request);
+
+ if (key->length != 2) return NULL;
+
+ return smbsrv_pull_fnum(req, key->data, 0);
+}
+
+DATA_BLOB smbsrv_handle_get_wire_key(void *private_data, struct ntvfs_handle *handle, TALLOC_CTX *mem_ctx)
+{
+ uint8_t key[2];
+
+ smbsrv_push_fnum(key, 0, handle);
+
+ return data_blob_talloc(mem_ctx, key, sizeof(key));
+}
diff --git a/source4/smb_server/smb/search.c b/source4/smb_server/smb/search.c
new file mode 100644
index 0000000..bf47217
--- /dev/null
+++ b/source4/smb_server/smb/search.c
@@ -0,0 +1,283 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMBsearch handling
+ Copyright (C) Andrew Tridgell 2003
+ Copyright (C) James J Myers 2003 <myersjj@samba.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+/*
+ This file handles the parsing of transact2 requests
+*/
+
+#include "includes.h"
+#include "smb_server/smb_server.h"
+#include "ntvfs/ntvfs.h"
+
+
+/* a structure to encapsulate the state information about
+ * an in-progress search first/next operation */
+struct search_state {
+ struct smbsrv_request *req;
+ union smb_search_data *file;
+ uint16_t last_entry_offset;
+};
+
+/*
+ fill a single entry in a search find reply
+*/
+static bool find_fill_info(struct smbsrv_request *req,
+ const 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_data, const union smb_search_data *file)
+{
+ struct search_state *state = (struct search_state *)private_data;
+
+ return find_fill_info(state->req, file);
+}
+
+/****************************************************************************
+ Reply to a search first (async reply)
+****************************************************************************/
+static void reply_search_first_send(struct ntvfs_request *ntvfs)
+{
+ struct smbsrv_request *req;
+ union smb_search_first *sf;
+
+ SMBSRV_CHECK_ASYNC_STATUS(sf, union smb_search_first);
+
+ SSVAL(req->out.vwv, VWV(0), sf->search_first.out.count);
+
+ smbsrv_send_reply(req);
+}
+
+/****************************************************************************
+ Reply to a search next (async reply)
+****************************************************************************/
+static void reply_search_next_send(struct ntvfs_request *ntvfs)
+{
+ struct smbsrv_request *req;
+ union smb_search_next *sn;
+
+ SMBSRV_CHECK_ASYNC_STATUS(sn, union smb_search_next);
+
+ SSVAL(req->out.vwv, VWV(0), sn->search_next.out.count);
+
+ smbsrv_send_reply(req);
+}
+
+/****************************************************************************
+ Reply to a search.
+****************************************************************************/
+void smbsrv_reply_search(struct smbsrv_request *req)
+{
+ union smb_search_first *sf;
+ uint16_t resume_key_length;
+ struct search_state *state;
+ uint8_t *p;
+ enum smb_search_level level = RAW_SEARCH_SEARCH;
+ uint8_t op = CVAL(req->in.hdr,HDR_COM);
+
+ if (op == SMBffirst) {
+ level = RAW_SEARCH_FFIRST;
+ } else if (op == SMBfunique) {
+ level = RAW_SEARCH_FUNIQUE;
+ }
+
+ /* parse request */
+ if (req->in.wct != 2) {
+ smbsrv_send_error(req, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+
+ SMBSRV_TALLOC_IO_PTR(sf, union smb_search_first);
+
+ p = req->in.data;
+ p += req_pull_ascii4(&req->in.bufinfo, &sf->search_first.in.pattern,
+ p, STR_TERMINATE);
+ if (!sf->search_first.in.pattern) {
+ smbsrv_send_error(req, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+ return;
+ }
+
+ if (req_data_oob(&req->in.bufinfo, p, 3)) {
+ smbsrv_send_error(req, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+ if (*p != 5) {
+ smbsrv_send_error(req, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+ resume_key_length = SVAL(p, 1);
+ p += 3;
+
+ /* setup state for callback */
+ state = talloc(req, struct search_state);
+ if (!state) {
+ smbsrv_send_error(req, NT_STATUS_NO_MEMORY);
+ return;
+ }
+
+ state->req = req;
+ state->file = NULL;
+ state->last_entry_offset = 0;
+
+ /* construct reply */
+ smbsrv_setup_reply(req, 1, 0);
+ SSVAL(req->out.vwv, VWV(0), 0);
+ req_append_var_block(req, NULL, 0);
+
+ if (resume_key_length != 0) {
+ union smb_search_next *sn;
+
+ if (resume_key_length != 21 ||
+ req_data_oob(&req->in.bufinfo, p, 21) ||
+ level == RAW_SEARCH_FUNIQUE) {
+ smbsrv_send_error(req, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+
+ /* do a search next operation */
+ SMBSRV_TALLOC_IO_PTR(sn, union smb_search_next);
+ SMBSRV_SETUP_NTVFS_REQUEST(reply_search_next_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ sn->search_next.in.id.reserved = CVAL(p, 0);
+ memcpy(sn->search_next.in.id.name, p+1, 11);
+ sn->search_next.in.id.handle = CVAL(p, 12);
+ sn->search_next.in.id.server_cookie = IVAL(p, 13);
+ sn->search_next.in.id.client_cookie = IVAL(p, 17);
+
+ sn->search_next.level = level;
+ sn->search_next.data_level = RAW_SEARCH_DATA_SEARCH;
+ sn->search_next.in.max_count = SVAL(req->in.vwv, VWV(0));
+ sn->search_next.in.search_attrib = SVAL(req->in.vwv, VWV(1));
+
+ /* call backend */
+ SMBSRV_CALL_NTVFS_BACKEND(ntvfs_search_next(req->ntvfs, sn, state, find_callback));
+ } else {
+ SMBSRV_SETUP_NTVFS_REQUEST(reply_search_first_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ /* do a search first operation */
+ sf->search_first.level = level;
+ sf->search_first.data_level = RAW_SEARCH_DATA_SEARCH;
+ sf->search_first.in.search_attrib = SVAL(req->in.vwv, VWV(1));
+ sf->search_first.in.max_count = SVAL(req->in.vwv, VWV(0));
+
+ SMBSRV_CALL_NTVFS_BACKEND(ntvfs_search_first(req->ntvfs, sf, state, find_callback));
+ }
+}
+
+
+/****************************************************************************
+ Reply to a fclose (async reply)
+****************************************************************************/
+static void reply_fclose_send(struct ntvfs_request *ntvfs)
+{
+ struct smbsrv_request *req;
+
+ SMBSRV_CHECK_ASYNC_STATUS_SIMPLE;
+
+ /* construct reply */
+ smbsrv_setup_reply(req, 1, 0);
+
+ SSVAL(req->out.vwv, VWV(0), 0);
+
+ smbsrv_send_reply(req);
+}
+
+
+/****************************************************************************
+ Reply to fclose (stop directory search).
+****************************************************************************/
+void smbsrv_reply_fclose(struct smbsrv_request *req)
+{
+ union smb_search_close *sc;
+ uint16_t resume_key_length;
+ uint8_t *p;
+ const char *pattern;
+
+ /* parse request */
+ if (req->in.wct != 2) {
+ smbsrv_send_error(req, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+
+ SMBSRV_TALLOC_IO_PTR(sc, union smb_search_close);
+ SMBSRV_SETUP_NTVFS_REQUEST(reply_fclose_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ p = req->in.data;
+ p += req_pull_ascii4(&req->in.bufinfo, &pattern, p, STR_TERMINATE);
+ if (pattern && *pattern) {
+ smbsrv_send_error(req, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+
+ if (req_data_oob(&req->in.bufinfo, p, 3)) {
+ smbsrv_send_error(req, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+ if (*p != 5) {
+ smbsrv_send_error(req, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+ resume_key_length = SVAL(p, 1);
+ p += 3;
+
+ if (resume_key_length != 21) {
+ smbsrv_send_error(req, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+
+ if (req_data_oob(&req->in.bufinfo, p, 21)) {
+ smbsrv_send_error(req, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+
+ sc->fclose.level = RAW_FINDCLOSE_FCLOSE;
+ sc->fclose.in.max_count = SVAL(req->in.vwv, VWV(0));
+ sc->fclose.in.search_attrib = SVAL(req->in.vwv, VWV(1));
+ sc->fclose.in.id.reserved = CVAL(p, 0);
+ memcpy(sc->fclose.in.id.name, p+1, 11);
+ sc->fclose.in.id.handle = CVAL(p, 12);
+ sc->fclose.in.id.server_cookie = IVAL(p, 13);
+ sc->fclose.in.id.client_cookie = IVAL(p, 17);
+
+ SMBSRV_CALL_NTVFS_BACKEND(ntvfs_search_close(req->ntvfs, sc));
+
+}
diff --git a/source4/smb_server/smb/service.c b/source4/smb_server/smb/service.c
new file mode 100644
index 0000000..eb1874c
--- /dev/null
+++ b/source4/smb_server/smb/service.c
@@ -0,0 +1,202 @@
+/*
+ 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 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "smb_server/smb_server.h"
+#include "samba/service_stream.h"
+#include "ntvfs/ntvfs.h"
+#include "param/param.h"
+
+#undef strcasecmp
+
+/****************************************************************************
+ Make a connection, given the snum to connect to, and the vuser of the
+ connecting user if appropriate.
+ Does note invoke the NTVFS connection hook
+****************************************************************************/
+static NTSTATUS make_connection_scfg(struct smbsrv_request *req,
+ struct share_config *scfg,
+ enum ntvfs_type type,
+ DATA_BLOB password,
+ const char *dev)
+{
+ struct smbsrv_tcon *tcon;
+ NTSTATUS status;
+ uint64_t ntvfs_caps = 0;
+
+ tcon = smbsrv_smb_tcon_new(req->smb_conn, scfg->name);
+ if (!tcon) {
+ DEBUG(0,("Couldn't find free connection.\n"));
+ return NT_STATUS_INSUFFICIENT_RESOURCES;
+ }
+ req->tcon = tcon;
+
+ if (req->smb_conn->negotiate.client_caps & CAP_LEVEL_II_OPLOCKS) {
+ ntvfs_caps |= NTVFS_CLIENT_CAP_LEVEL_II_OPLOCKS;
+ }
+
+ /* init ntvfs function pointers */
+ status = ntvfs_init_connection(tcon, scfg, type,
+ req->smb_conn->negotiate.protocol,
+ ntvfs_caps,
+ req->smb_conn->connection->event.ctx,
+ req->smb_conn->connection->msg_ctx,
+ req->smb_conn->lp_ctx,
+ req->smb_conn->connection->server_id,
+ &tcon->ntvfs);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("make_connection_scfg: connection failed for service %s\n",
+ scfg->name));
+ goto failed;
+ }
+
+ status = ntvfs_set_oplock_handler(tcon->ntvfs, smbsrv_send_oplock_break, tcon);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("make_connection: NTVFS failed to set the oplock handler!\n"));
+ goto failed;
+ }
+
+ status = ntvfs_set_addresses(tcon->ntvfs,
+ req->smb_conn->connection->local_address,
+ req->smb_conn->connection->remote_address);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("make_connection: NTVFS failed to set the addresses!\n"));
+ goto failed;
+ }
+
+ status = ntvfs_set_handle_callbacks(tcon->ntvfs,
+ smbsrv_handle_create_new,
+ smbsrv_handle_make_valid,
+ smbsrv_handle_destroy,
+ smbsrv_handle_search_by_wire_key,
+ smbsrv_handle_get_wire_key,
+ tcon);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("make_connection: NTVFS failed to set the handle callbacks!\n"));
+ goto failed;
+ }
+
+ return NT_STATUS_OK;
+
+failed:
+ req->tcon = NULL;
+ talloc_free(tcon);
+ return status;
+}
+
+/****************************************************************************
+ Make a connection to a service.
+ *
+ * @param service
+****************************************************************************/
+static NTSTATUS make_connection(struct smbsrv_request *req,
+ const char *service, DATA_BLOB password,
+ const char *dev)
+{
+ NTSTATUS status;
+ enum ntvfs_type type;
+ const char *type_str;
+ struct share_config *scfg;
+ char *sharetype;
+
+ /* the service might be of the form \\SERVER\SHARE. Should we put
+ the server name we get from this somewhere? */
+ if (strncmp(service, "\\\\", 2) == 0) {
+ char *p = strchr(service+2, '\\');
+ if (p) {
+ service = p + 1;
+ }
+ }
+
+ status = share_get_config(req, req->smb_conn->share_context, service, &scfg);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("make_connection: couldn't find service %s: %s\n", service, nt_errstr(status)));
+ return NT_STATUS_BAD_NETWORK_NAME;
+ }
+
+ /* TODO: check the password, when it's share level security! */
+
+ if (!socket_check_access(req->smb_conn->connection->socket,
+ scfg->name,
+ share_string_list_option(req, scfg, SHARE_HOSTS_ALLOW),
+ share_string_list_option(req, scfg, SHARE_HOSTS_DENY))) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ /* work out what sort of connection this is */
+ sharetype = share_string_option(req, scfg, "type", "DISK");
+ if (sharetype && strcmp(sharetype, "IPC") == 0) {
+ type = NTVFS_IPC;
+ type_str = "IPC";
+ } else if (sharetype && strcmp(sharetype, "PRINTER") == 0) {
+ type = NTVFS_PRINT;
+ type_str = "LPT:";
+ } else {
+ type = NTVFS_DISK;
+ type_str = "A:";
+ }
+ TALLOC_FREE(sharetype);
+
+ if (strcmp(dev, "?????") != 0 && strcasecmp(type_str, dev) != 0) {
+ /* the client gave us the wrong device type */
+ return NT_STATUS_BAD_DEVICE_TYPE;
+ }
+
+ return make_connection_scfg(req, scfg, type, password, dev);
+}
+
+/*
+ backend for tree connect call, in preparation for calling ntvfs_connect()
+*/
+NTSTATUS smbsrv_tcon_backend(struct smbsrv_request *req, union smb_tcon *con)
+{
+ NTSTATUS status;
+
+ if (con->generic.level == RAW_TCON_TCON) {
+ DATA_BLOB password;
+ password = data_blob_string_const(con->tcon.in.password);
+
+ 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;
+ }
+
+ /* TODO: take a look at tconx.in.flags! */
+
+ 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.options = SMB_SUPPORT_SEARCH_BITS | (share_int_option(req->tcon->ntvfs->config, SHARE_CSC_POLICY, SHARE_CSC_POLICY_DEFAULT) << 2);
+ if (share_bool_option(req->tcon->ntvfs->config, SHARE_MSDFS_ROOT, SHARE_MSDFS_ROOT_DEFAULT) && lpcfg_host_msdfs(req->smb_conn->lp_ctx)) {
+ 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 0000000..07a7e7e
--- /dev/null
+++ b/source4/smb_server/smb/sesssetup.c
@@ -0,0 +1,644 @@
+
+/*
+ Unix SMB/CIFS implementation.
+ handle SMBsessionsetup
+ Copyright (C) Andrew Tridgell 1998-2001
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2001-2005
+ Copyright (C) Jim McDonough 2002
+ Copyright (C) Luke Howard 2003
+ Copyright (C) Stefan Metzmacher 2005
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include <tevent.h>
+#include "version.h"
+#include "auth/gensec/gensec.h"
+#include "auth/auth.h"
+#include "smb_server/smb_server.h"
+#include "samba/service_stream.h"
+#include "param/param.h"
+#include "../lib/tsocket/tsocket.h"
+#include "lib/stream/packet.h"
+
+struct sesssetup_context {
+ struct auth4_context *auth_context;
+ struct smbsrv_request *req;
+};
+
+/*
+ * Log the SMB authentication, as by not calling GENSEC we won't log
+ * it during the gensec_session_info().
+ */
+void smbsrv_not_spengo_sesssetup_authz_log(struct smbsrv_request *req,
+ struct auth_session_info *session_info)
+{
+ struct tsocket_address *local_address;
+ struct tsocket_address *remote_address;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ remote_address = socket_get_remote_addr(req->smb_conn->connection->socket,
+ frame);
+ local_address = socket_get_local_addr(req->smb_conn->connection->socket,
+ frame);
+
+ log_successful_authz_event(req->smb_conn->connection->msg_ctx,
+ req->smb_conn->lp_ctx,
+ remote_address,
+ local_address,
+ "SMB",
+ "bare-NTLM",
+ AUTHZ_TRANSPORT_PROTECTION_SMB,
+ session_info);
+
+ talloc_free(frame);
+ return;
+}
+
+
+/*
+ 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",
+ lpcfg_workgroup(req->smb_conn->lp_ctx));
+}
+
+static void smbsrv_sesssetup_backend_send(struct smbsrv_request *req,
+ union smb_sesssetup *sess,
+ NTSTATUS status)
+{
+ if (NT_STATUS_IS_OK(status)) {
+ req->smb_conn->negotiate.done_sesssetup = true;
+ /* we need to keep the session long term */
+ req->session = talloc_steal(req->smb_conn, req->session);
+ }
+ smbsrv_reply_sesssetup_send(req, sess, status);
+}
+
+static void sesssetup_old_send(struct tevent_req *subreq)
+{
+ struct sesssetup_context *state = tevent_req_callback_data(subreq, struct sesssetup_context);
+ struct smbsrv_request *req = state->req;
+
+ union smb_sesssetup *sess = talloc_get_type(req->io_ptr, union smb_sesssetup);
+ struct auth_user_info_dc *user_info_dc = NULL;
+ struct auth_session_info *session_info;
+ struct smbsrv_session *smb_sess;
+ NTSTATUS status;
+ uint8_t authoritative = 1;
+ uint32_t flags;
+
+ status = auth_check_password_recv(subreq, req, &user_info_dc,
+ &authoritative);
+ TALLOC_FREE(subreq);
+ if (!NT_STATUS_IS_OK(status)) goto failed;
+
+ flags = AUTH_SESSION_INFO_DEFAULT_GROUPS;
+ if (user_info_dc->info->authenticated) {
+ flags |= AUTH_SESSION_INFO_AUTHENTICATED;
+ }
+ /* This references user_info_dc into session_info */
+ status = req->smb_conn->negotiate.auth_context->generate_session_info(req->smb_conn->negotiate.auth_context,
+ req,
+ user_info_dc, sess->old.in.user,
+ flags, &session_info);
+ if (!NT_STATUS_IS_OK(status)) goto failed;
+
+ /* allocate a new session */
+ smb_sess = smbsrv_session_new(req->smb_conn, req, NULL);
+ if (!smb_sess) {
+ status = NT_STATUS_INSUFFICIENT_RESOURCES;
+ goto failed;
+ }
+
+ smbsrv_not_spengo_sesssetup_authz_log(req, session_info);
+
+ /* Ensure this is marked as a 'real' vuid, not one
+ * simply valid for the session setup leg */
+ status = smbsrv_session_sesssetup_finished(smb_sess, session_info);
+ if (!NT_STATUS_IS_OK(status)) goto failed;
+
+ /* To correctly process any AndX packet (like a tree connect)
+ * we need to fill in the session on the request here */
+ req->session = smb_sess;
+ sess->old.out.vuid = smb_sess->vuid;
+
+failed:
+ status = nt_status_squash(status);
+ smbsrv_sesssetup_backend_send(req, sess, status);
+}
+
+/*
+ handler for old style session setup
+*/
+static void sesssetup_old(struct smbsrv_request *req, union smb_sesssetup *sess)
+{
+ struct auth_usersupplied_info *user_info = NULL;
+ struct tsocket_address *remote_address, *local_address;
+ const char *remote_machine = NULL;
+ struct tevent_req *subreq;
+ struct sesssetup_context *state;
+
+ sess->old.out.vuid = 0;
+ sess->old.out.action = 0;
+
+ sesssetup_common_strings(req,
+ &sess->old.out.os,
+ &sess->old.out.lanman,
+ &sess->old.out.domain);
+
+ if (!req->smb_conn->negotiate.done_sesssetup) {
+ req->smb_conn->negotiate.max_send = sess->old.in.bufsize;
+ }
+
+ if (req->smb_conn->negotiate.calling_name) {
+ remote_machine = req->smb_conn->negotiate.calling_name->name;
+ }
+
+ remote_address = socket_get_remote_addr(req->smb_conn->connection->socket, req);
+ if (!remote_address) goto nomem;
+
+ if (!remote_machine) {
+ remote_machine = tsocket_address_inet_addr_string(remote_address, req);
+ if (!remote_machine) goto nomem;
+ }
+
+ local_address = socket_get_local_addr(req->smb_conn->connection->socket, req);
+ if (!local_address) goto nomem;
+
+ user_info = talloc_zero(req, struct auth_usersupplied_info);
+ if (!user_info) goto nomem;
+
+ user_info->service_description = "SMB";
+
+ user_info->logon_parameters = 0;
+ user_info->flags = 0;
+ user_info->client.account_name = sess->old.in.user;
+ user_info->client.domain_name = sess->old.in.domain;
+ user_info->workstation_name = remote_machine;
+
+ user_info->remote_host = talloc_steal(user_info, remote_address);
+ user_info->local_host = talloc_steal(user_info, local_address);
+
+ user_info->password_state = AUTH_PASSWORD_RESPONSE;
+ user_info->password.response.lanman = sess->old.in.password;
+ user_info->password.response.lanman.data = talloc_steal(user_info, sess->old.in.password.data);
+ user_info->password.response.nt = data_blob(NULL, 0);
+
+ state = talloc(req, struct sesssetup_context);
+ if (!state) goto nomem;
+
+ if (req->smb_conn->negotiate.auth_context) {
+ state->auth_context = req->smb_conn->negotiate.auth_context;
+ } else {
+ /* TODO: should we use just "anonymous" here? */
+ NTSTATUS status = auth_context_create(state,
+ req->smb_conn->connection->event.ctx,
+ req->smb_conn->connection->msg_ctx,
+ req->smb_conn->lp_ctx,
+ &state->auth_context);
+ if (!NT_STATUS_IS_OK(status)) {
+ smbsrv_sesssetup_backend_send(req, sess, status);
+ return;
+ }
+ }
+
+ state->req = req;
+
+ subreq = auth_check_password_send(state,
+ req->smb_conn->connection->event.ctx,
+ req->smb_conn->negotiate.auth_context,
+ user_info);
+ if (!subreq) goto nomem;
+ tevent_req_set_callback(subreq, sesssetup_old_send, state);
+ return;
+
+nomem:
+ smbsrv_sesssetup_backend_send(req, sess, NT_STATUS_NO_MEMORY);
+}
+
+static void sesssetup_nt1_send(struct tevent_req *subreq)
+{
+ struct sesssetup_context *state = tevent_req_callback_data(subreq, struct sesssetup_context);
+ struct smbsrv_request *req = state->req;
+ union smb_sesssetup *sess = talloc_get_type(req->io_ptr, union smb_sesssetup);
+ struct auth_user_info_dc *user_info_dc = NULL;
+ struct auth_session_info *session_info;
+ struct smbsrv_session *smb_sess;
+ uint8_t authoritative = 1;
+ uint32_t flags;
+ NTSTATUS status;
+
+ status = auth_check_password_recv(subreq, req, &user_info_dc,
+ &authoritative);
+ TALLOC_FREE(subreq);
+ if (!NT_STATUS_IS_OK(status)) goto failed;
+
+ flags = AUTH_SESSION_INFO_DEFAULT_GROUPS;
+ if (user_info_dc->info->authenticated) {
+ flags |= AUTH_SESSION_INFO_AUTHENTICATED;
+ }
+ /* This references user_info_dc into session_info */
+ status = state->auth_context->generate_session_info(state->auth_context,
+ req,
+ user_info_dc,
+ sess->nt1.in.user,
+ flags,
+ &session_info);
+ if (!NT_STATUS_IS_OK(status)) goto failed;
+
+ /* allocate a new session */
+ smb_sess = smbsrv_session_new(req->smb_conn, req, NULL);
+ if (!smb_sess) {
+ status = NT_STATUS_INSUFFICIENT_RESOURCES;
+ goto failed;
+ }
+
+ smbsrv_not_spengo_sesssetup_authz_log(req, session_info);
+
+ /* Ensure this is marked as a 'real' vuid, not one
+ * simply valid for the session setup leg */
+ status = smbsrv_session_sesssetup_finished(smb_sess, session_info);
+ if (!NT_STATUS_IS_OK(status)) goto failed;
+
+ /* To correctly process any AndX packet (like a tree connect)
+ * we need to fill in the session on the request here */
+ req->session = smb_sess;
+ sess->nt1.out.vuid = smb_sess->vuid;
+
+ if (!smbsrv_setup_signing(req->smb_conn, &session_info->session_key, &sess->nt1.in.password2)) {
+ /* Already signing, or disabled */
+ goto done;
+ }
+
+done:
+ status = NT_STATUS_OK;
+failed:
+ status = nt_status_squash(status);
+ smbsrv_sesssetup_backend_send(req, sess, status);
+}
+
+/*
+ handler for NT1 style session setup
+*/
+static void sesssetup_nt1(struct smbsrv_request *req, union smb_sesssetup *sess)
+{
+ NTSTATUS status;
+ struct auth_usersupplied_info *user_info = NULL;
+ struct tsocket_address *remote_address, *local_address;
+ const char *remote_machine = NULL;
+ struct tevent_req *subreq;
+ struct sesssetup_context *state;
+ bool allow_raw = lpcfg_raw_ntlmv2_auth(req->smb_conn->lp_ctx);
+
+ sess->nt1.out.vuid = 0;
+ sess->nt1.out.action = 0;
+
+ sesssetup_common_strings(req,
+ &sess->nt1.out.os,
+ &sess->nt1.out.lanman,
+ &sess->nt1.out.domain);
+
+ if (!req->smb_conn->negotiate.done_sesssetup) {
+ req->smb_conn->negotiate.max_send = sess->nt1.in.bufsize;
+ req->smb_conn->negotiate.client_caps = sess->nt1.in.capabilities;
+ }
+
+ state = talloc(req, struct sesssetup_context);
+ if (!state) goto nomem;
+
+ state->req = req;
+
+ if (req->smb_conn->negotiate.oid) {
+ if (sess->nt1.in.user && *sess->nt1.in.user) {
+ /* We can't accept a normal login, because we
+ * don't have a challenge */
+ status = NT_STATUS_LOGON_FAILURE;
+ goto failed;
+ }
+
+ /* TODO: should we use just "anonymous" here? */
+ status = auth_context_create(state,
+ req->smb_conn->connection->event.ctx,
+ req->smb_conn->connection->msg_ctx,
+ req->smb_conn->lp_ctx,
+ &state->auth_context);
+ if (!NT_STATUS_IS_OK(status)) goto failed;
+ } else if (req->smb_conn->negotiate.auth_context) {
+ state->auth_context = req->smb_conn->negotiate.auth_context;
+ } else {
+ /* TODO: should we use just "anonymous" here? */
+ status = auth_context_create(state,
+ req->smb_conn->connection->event.ctx,
+ req->smb_conn->connection->msg_ctx,
+ req->smb_conn->lp_ctx,
+ &state->auth_context);
+ if (!NT_STATUS_IS_OK(status)) goto failed;
+ }
+
+ if (req->smb_conn->negotiate.calling_name) {
+ remote_machine = req->smb_conn->negotiate.calling_name->name;
+ }
+
+ remote_address = socket_get_remote_addr(req->smb_conn->connection->socket, req);
+ if (!remote_address) goto nomem;
+
+ if (!remote_machine) {
+ remote_machine = tsocket_address_inet_addr_string(remote_address, req);
+ if (!remote_machine) goto nomem;
+ }
+
+ local_address = socket_get_local_addr(req->smb_conn->connection->socket, req);
+ if (!local_address) goto nomem;
+
+ user_info = talloc_zero(req, struct auth_usersupplied_info);
+ if (!user_info) goto nomem;
+
+ user_info->service_description = "SMB";
+ user_info->auth_description = "bare-NTLM";
+
+ user_info->logon_parameters = 0;
+ user_info->flags = 0;
+ user_info->client.account_name = sess->nt1.in.user;
+ user_info->client.domain_name = sess->nt1.in.domain;
+ user_info->workstation_name = remote_machine;
+ user_info->remote_host = talloc_steal(user_info, remote_address);
+ user_info->local_host = talloc_steal(user_info, local_address);
+
+ user_info->password_state = AUTH_PASSWORD_RESPONSE;
+ user_info->password.response.lanman = sess->nt1.in.password1;
+ user_info->password.response.lanman.data = talloc_steal(user_info, sess->nt1.in.password1.data);
+ user_info->password.response.nt = sess->nt1.in.password2;
+ user_info->password.response.nt.data = talloc_steal(user_info, sess->nt1.in.password2.data);
+
+ if (!allow_raw && user_info->password.response.nt.length >= 48) {
+ /*
+ * NTLMv2_RESPONSE has at least 48 bytes
+ * and should only be supported via NTLMSSP.
+ */
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto failed;
+ }
+
+ subreq = auth_check_password_send(state,
+ req->smb_conn->connection->event.ctx,
+ state->auth_context,
+ user_info);
+ if (!subreq) goto nomem;
+ tevent_req_set_callback(subreq, sesssetup_nt1_send, state);
+
+ return;
+
+nomem:
+ status = NT_STATUS_NO_MEMORY;
+failed:
+ status = nt_status_squash(status);
+ smbsrv_sesssetup_backend_send(req, sess, status);
+}
+
+struct sesssetup_spnego_state {
+ struct smbsrv_request *req;
+ union smb_sesssetup *sess;
+ struct smbsrv_session *smb_sess;
+};
+
+static void sesssetup_spnego_send(struct tevent_req *subreq)
+{
+ struct sesssetup_spnego_state *s = tevent_req_callback_data(subreq,
+ struct sesssetup_spnego_state);
+ struct smbsrv_request *req = s->req;
+ union smb_sesssetup *sess = s->sess;
+ struct smbsrv_session *smb_sess = s->smb_sess;
+ struct auth_session_info *session_info = NULL;
+ NTSTATUS status;
+ NTSTATUS skey_status;
+ DATA_BLOB session_key;
+
+ status = gensec_update_recv(subreq, req, &sess->spnego.out.secblob);
+ packet_recv_enable(req->smb_conn->packet);
+ TALLOC_FREE(subreq);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ goto done;
+ } else if (!NT_STATUS_IS_OK(status)) {
+ goto failed;
+ }
+
+ status = gensec_session_info(smb_sess->gensec_ctx, smb_sess, &session_info);
+ if (!NT_STATUS_IS_OK(status)) goto failed;
+
+ /* The session_key is only needed until the end of the smbsrv_setup_signing() call */
+ skey_status = gensec_session_key(smb_sess->gensec_ctx, req, &session_key);
+ if (NT_STATUS_IS_OK(skey_status)) {
+ smbsrv_setup_signing(req->smb_conn, &session_key, NULL);
+ }
+
+ /* Ensure this is marked as a 'real' vuid, not one
+ * simply valid for the session setup leg */
+ status = smbsrv_session_sesssetup_finished(smb_sess, session_info);
+ if (!NT_STATUS_IS_OK(status)) goto failed;
+
+ req->session = smb_sess;
+
+done:
+ sess->spnego.out.vuid = smb_sess->vuid;
+failed:
+ status = nt_status_squash(status);
+ smbsrv_sesssetup_backend_send(req, sess, status);
+ if (!NT_STATUS_IS_OK(status) &&
+ !NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ talloc_free(smb_sess);
+ }
+}
+
+/*
+ handler for SPNEGO style session setup
+*/
+static void sesssetup_spnego(struct smbsrv_request *req, union smb_sesssetup *sess)
+{
+ NTSTATUS status;
+ struct smbsrv_session *smb_sess = NULL;
+ bool is_smb_sess_new = false;
+ struct sesssetup_spnego_state *s = NULL;
+ uint16_t vuid;
+ struct tevent_req *subreq;
+
+ sess->spnego.out.vuid = 0;
+ sess->spnego.out.action = 0;
+
+ sesssetup_common_strings(req,
+ &sess->spnego.out.os,
+ &sess->spnego.out.lanman,
+ &sess->spnego.out.workgroup);
+
+ if (!req->smb_conn->negotiate.done_sesssetup) {
+ req->smb_conn->negotiate.max_send = sess->spnego.in.bufsize;
+ req->smb_conn->negotiate.client_caps = sess->spnego.in.capabilities;
+ }
+
+ vuid = SVAL(req->in.hdr,HDR_UID);
+
+ /* lookup an existing session */
+ if (vuid == 0) {
+ struct gensec_security *gensec_ctx;
+ struct tsocket_address *remote_address, *local_address;
+ status = samba_server_gensec_start(req,
+ req->smb_conn->connection->event.ctx,
+ req->smb_conn->connection->msg_ctx,
+ req->smb_conn->lp_ctx,
+ req->smb_conn->negotiate.server_credentials,
+ "cifs",
+ &gensec_ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Failed to start GENSEC server code: %s\n", nt_errstr(status)));
+ goto failed;
+ }
+
+ gensec_want_feature(gensec_ctx, GENSEC_FEATURE_SESSION_KEY);
+ gensec_want_feature(gensec_ctx, GENSEC_FEATURE_SMB_TRANSPORT);
+
+ remote_address = socket_get_remote_addr(req->smb_conn->connection->socket,
+ req);
+ if (!remote_address) {
+ status = NT_STATUS_INTERNAL_ERROR;
+ DBG_ERR("Failed to obtain remote address");
+ goto failed;
+ }
+
+ status = gensec_set_remote_address(gensec_ctx,
+ remote_address);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_ERR("Failed to set remote address");
+ goto failed;
+ }
+
+ local_address = socket_get_local_addr(req->smb_conn->connection->socket,
+ req);
+ if (!local_address) {
+ status = NT_STATUS_INTERNAL_ERROR;
+ DBG_ERR("Failed to obtain local address");
+ goto failed;
+ }
+
+ status = gensec_set_local_address(gensec_ctx,
+ local_address);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_ERR("Failed to set local address");
+ goto failed;
+ }
+
+ status = gensec_set_target_service_description(gensec_ctx,
+ "SMB");
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_ERR("Failed to set service description");
+ goto failed;
+ }
+
+ status = gensec_start_mech_by_oid(gensec_ctx, req->smb_conn->negotiate.oid);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Failed to start GENSEC %s server code: %s\n",
+ gensec_get_name_by_oid(gensec_ctx, req->smb_conn->negotiate.oid), nt_errstr(status)));
+ goto failed;
+ }
+
+ /* allocate a new session */
+ smb_sess = smbsrv_session_new(req->smb_conn, req->smb_conn, gensec_ctx);
+ if (!smb_sess) {
+ status = NT_STATUS_INSUFFICIENT_RESOURCES;
+ goto failed;
+ }
+ is_smb_sess_new = true;
+ } else {
+ smb_sess = smbsrv_session_find_sesssetup(req->smb_conn, vuid);
+ }
+
+ if (!smb_sess) {
+ status = NT_STATUS_DOS(ERRSRV, ERRbaduid);
+ goto failed;
+ }
+
+ if (smb_sess->session_info) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto failed;
+ }
+
+ if (!smb_sess->gensec_ctx) {
+ status = NT_STATUS_INTERNAL_ERROR;
+ DEBUG(1, ("Internal ERROR: no gensec_ctx on session: %s\n", nt_errstr(status)));
+ goto failed;
+ }
+
+ s = talloc(req, struct sesssetup_spnego_state);
+ if (!s) goto nomem;
+ s->req = req;
+ s->sess = sess;
+ s->smb_sess = smb_sess;
+
+ subreq = gensec_update_send(s,
+ req->smb_conn->connection->event.ctx,
+ smb_sess->gensec_ctx,
+ sess->spnego.in.secblob);
+ if (!subreq) {
+ goto nomem;
+ }
+ /* disable receipt of more packets on this socket until we've
+ finished with the session setup. This avoids a problem with
+ crashes if we get EOF on the socket while processing a session
+ setup */
+ packet_recv_disable(req->smb_conn->packet);
+ tevent_req_set_callback(subreq, sesssetup_spnego_send, s);
+
+ return;
+
+nomem:
+ status = NT_STATUS_NO_MEMORY;
+failed:
+ if (is_smb_sess_new) {
+ talloc_free(smb_sess);
+ }
+ status = nt_status_squash(status);
+ smbsrv_sesssetup_backend_send(req, sess, status);
+}
+
+/*
+ backend for sessionsetup call - this takes all 3 variants of the call
+*/
+void smbsrv_sesssetup_backend(struct smbsrv_request *req,
+ union smb_sesssetup *sess)
+{
+ switch (sess->old.level) {
+ case RAW_SESSSETUP_OLD:
+ sesssetup_old(req, sess);
+ return;
+
+ case RAW_SESSSETUP_NT1:
+ sesssetup_nt1(req, sess);
+ return;
+
+ case RAW_SESSSETUP_SPNEGO:
+ sesssetup_spnego(req, sess);
+ return;
+
+ case RAW_SESSSETUP_SMB2:
+ break;
+ }
+
+ smbsrv_sesssetup_backend_send(req, sess, NT_STATUS_INVALID_LEVEL);
+}
diff --git a/source4/smb_server/smb/signing.c b/source4/smb_server/smb/signing.c
new file mode 100644
index 0000000..3fe7cff
--- /dev/null
+++ b/source4/smb_server/smb/signing.c
@@ -0,0 +1,147 @@
+/*
+ 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 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "smb_server/smb_server.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "param/param.h"
+
+
+/*
+ sign an outgoing packet
+*/
+void smbsrv_sign_packet(struct smbsrv_request *req)
+{
+#if 0
+ /* enable this when packet signing is preventing you working out why valgrind
+ says that data is uninitialised */
+ file_save("pkt.dat", req->out.buffer, req->out.size);
+#endif
+
+ switch (req->smb_conn->signing.signing_state) {
+ case SMB_SIGNING_ENGINE_OFF:
+ break;
+
+ case SMB_SIGNING_ENGINE_BSRSPYL:
+ /* mark the packet as signed - BEFORE we sign it...*/
+ mark_packet_signed(&req->out);
+
+ /* I wonder what BSRSPYL stands for - but this is what MS
+ actually sends! */
+ memcpy((req->out.hdr + HDR_SS_FIELD), "BSRSPYL ", 8);
+ break;
+
+ case SMB_SIGNING_ENGINE_ON:
+
+ sign_outgoing_message(&req->out,
+ &req->smb_conn->signing.mac_key,
+ req->seq_num+1);
+ break;
+ }
+ return;
+}
+
+
+
+/*
+ setup the signing key for a connection. Called after authentication succeeds
+ in a session setup
+*/
+bool smbsrv_setup_signing(struct smbsrv_connection *smb_conn,
+ DATA_BLOB *session_key,
+ DATA_BLOB *response)
+{
+ if (!set_smb_signing_common(&smb_conn->signing)) {
+ return false;
+ }
+ return smbcli_simple_set_signing(smb_conn,
+ &smb_conn->signing, session_key, response);
+}
+
+bool smbsrv_init_signing(struct smbsrv_connection *smb_conn)
+{
+ smb_conn->signing.mac_key = data_blob(NULL, 0);
+ if (!smbcli_set_signing_off(&smb_conn->signing)) {
+ return false;
+ }
+
+ smb_conn->signing.allow_smb_signing
+ = lpcfg_server_signing_allowed(smb_conn->lp_ctx,
+ &smb_conn->signing.mandatory_signing);
+ return true;
+}
+
+/*
+ allocate a sequence number to a request
+*/
+static void req_signing_alloc_seq_num(struct smbsrv_request *req)
+{
+ req->seq_num = req->smb_conn->signing.next_seq_num;
+
+ if (req->smb_conn->signing.signing_state != SMB_SIGNING_ENGINE_OFF) {
+ req->smb_conn->signing.next_seq_num += 2;
+ }
+}
+
+/*
+ called for requests that do not produce a reply of their own
+*/
+void smbsrv_signing_no_reply(struct smbsrv_request *req)
+{
+ if (req->smb_conn->signing.signing_state != SMB_SIGNING_ENGINE_OFF) {
+ req->smb_conn->signing.next_seq_num--;
+ }
+}
+
+/***********************************************************
+ SMB signing - Simple implementation - check a MAC sent by client
+************************************************************/
+/**
+ * Check a packet supplied by the server.
+ * @return false if we had an established signing connection
+ * which had a back checksum, true otherwise
+ */
+bool smbsrv_signing_check_incoming(struct smbsrv_request *req)
+{
+ bool good;
+
+ req_signing_alloc_seq_num(req);
+
+ switch (req->smb_conn->signing.signing_state)
+ {
+ case SMB_SIGNING_ENGINE_OFF:
+ return true;
+ case SMB_SIGNING_ENGINE_BSRSPYL:
+ case SMB_SIGNING_ENGINE_ON:
+ {
+ if (req->in.size < (HDR_SS_FIELD + 8)) {
+ return false;
+ } else {
+ good = check_signed_incoming_message(&req->in,
+ &req->smb_conn->signing.mac_key,
+ req->seq_num);
+
+ return signing_good(&req->smb_conn->signing,
+ req->seq_num+1, good);
+ }
+ }
+ }
+ return false;
+}
diff --git a/source4/smb_server/smb/srvtime.c b/source4/smb_server/smb/srvtime.c
new file mode 100644
index 0000000..7aee907
--- /dev/null
+++ b/source4/smb_server/smb/srvtime.c
@@ -0,0 +1,82 @@
+/*
+ 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 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "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 0000000..fc357ed
--- /dev/null
+++ b/source4/smb_server/smb/trans2.c
@@ -0,0 +1,1562 @@
+/*
+ Unix SMB/CIFS implementation.
+ transaction2 handling
+ Copyright (C) Andrew Tridgell 2003
+ Copyright Matthieu Patou <mat@matws.net> 2010-2011
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+/*
+ This file handles the parsing of transact2 requests
+*/
+
+#include "includes.h"
+#include "samba/service_stream.h"
+#include "smb_server/smb_server.h"
+#include "ntvfs/ntvfs.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "librpc/gen_ndr/dfsblobs.h"
+#include "librpc/gen_ndr/ndr_dfsblobs.h"
+#include "dsdb/samdb/samdb.h"
+#include "auth/session.h"
+#include "param/param.h"
+#include "lib/tsocket/tsocket.h"
+#include "dfs_server/dfs_server_ad.h"
+
+#define MAX_DFS_RESPONSE 56*1024 /* 56 Kb */
+
+#define TRANS2_CHECK_ASYNC_STATUS_SIMPLE do { \
+ if (!NT_STATUS_IS_OK(req->ntvfs->async_states->status)) { \
+ trans2_setup_reply(trans, 0, 0, 0);\
+ return req->ntvfs->async_states->status; \
+ } \
+} while (0)
+#define TRANS2_CHECK_ASYNC_STATUS(ptr, type) do { \
+ TRANS2_CHECK_ASYNC_STATUS_SIMPLE; \
+ ptr = talloc_get_type(op->op_info, type); \
+} while (0)
+#define TRANS2_CHECK(cmd) do { \
+ NTSTATUS _status; \
+ _status = cmd; \
+ NT_STATUS_NOT_OK_RETURN(_status); \
+} while (0)
+
+/*
+ hold the state of a nttrans op while in progress. Needed to allow for async backend
+ functions.
+*/
+struct trans_op {
+ struct smbsrv_request *req;
+ struct smb_trans2 *trans;
+ uint8_t command;
+ NTSTATUS (*send_fn)(struct trans_op *);
+ void *op_info;
+};
+
+#define CHECK_MIN_BLOB_SIZE(blob, size) do { \
+ if ((blob)->length < (size)) { \
+ return NT_STATUS_INFO_LENGTH_MISMATCH; \
+ }} while (0)
+
+/* setup a trans2 reply, given the data and params sizes */
+static NTSTATUS trans2_setup_reply(struct smb_trans2 *trans,
+ uint16_t param_size, uint16_t data_size,
+ uint8_t setup_count)
+{
+ trans->out.setup_count = setup_count;
+ if (setup_count > 0) {
+ trans->out.setup = talloc_zero_array(trans, uint16_t, setup_count);
+ NT_STATUS_HAVE_NO_MEMORY(trans->out.setup);
+ }
+ trans->out.params = data_blob_talloc(trans, NULL, param_size);
+ if (param_size > 0) NT_STATUS_HAVE_NO_MEMORY(trans->out.params.data);
+
+ trans->out.data = data_blob_talloc(trans, NULL, data_size);
+ if (data_size > 0) NT_STATUS_HAVE_NO_MEMORY(trans->out.data.data);
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS trans2_push_fsinfo(struct smbsrv_connection *smb_conn,
+ TALLOC_CTX *mem_ctx,
+ DATA_BLOB *blob,
+ union smb_fsinfo *fsinfo,
+ int default_str_flags)
+{
+ enum smb_fsinfo_level passthru_level;
+
+ switch (fsinfo->generic.level) {
+ case RAW_QFS_ALLOCATION:
+ TRANS2_CHECK(smbsrv_blob_grow_data(mem_ctx, blob, 18));
+
+ SIVAL(blob->data, 0, fsinfo->allocation.out.fs_id);
+ SIVAL(blob->data, 4, fsinfo->allocation.out.sectors_per_unit);
+ SIVAL(blob->data, 8, fsinfo->allocation.out.total_alloc_units);
+ SIVAL(blob->data, 12, fsinfo->allocation.out.avail_alloc_units);
+ SSVAL(blob->data, 16, fsinfo->allocation.out.bytes_per_sector);
+
+ return NT_STATUS_OK;
+
+ case RAW_QFS_VOLUME:
+ TRANS2_CHECK(smbsrv_blob_grow_data(mem_ctx, blob, 5));
+
+ SIVAL(blob->data, 0, fsinfo->volume.out.serial_number);
+ /* w2k3 implements this incorrectly for unicode - it
+ * leaves the last byte off the string */
+ TRANS2_CHECK(smbsrv_blob_append_string(mem_ctx, blob,
+ fsinfo->volume.out.volume_name.s,
+ 4, default_str_flags,
+ STR_LEN8BIT|STR_NOALIGN));
+
+ return NT_STATUS_OK;
+
+ case RAW_QFS_VOLUME_INFO:
+ passthru_level = RAW_QFS_VOLUME_INFORMATION;
+ break;
+
+ case RAW_QFS_SIZE_INFO:
+ passthru_level = RAW_QFS_SIZE_INFORMATION;
+ break;
+
+ case RAW_QFS_DEVICE_INFO:
+ passthru_level = RAW_QFS_DEVICE_INFORMATION;
+ break;
+
+ case RAW_QFS_ATTRIBUTE_INFO:
+ passthru_level = RAW_QFS_ATTRIBUTE_INFORMATION;
+ break;
+
+ default:
+ passthru_level = fsinfo->generic.level;
+ break;
+ }
+
+ return smbsrv_push_passthru_fsinfo(mem_ctx, blob,
+ passthru_level, fsinfo,
+ default_str_flags);
+}
+
+/*
+ trans2 qfsinfo implementation send
+*/
+static NTSTATUS trans2_qfsinfo_send(struct trans_op *op)
+{
+ struct smbsrv_request *req = op->req;
+ struct smb_trans2 *trans = op->trans;
+ union smb_fsinfo *fsinfo;
+
+ TRANS2_CHECK_ASYNC_STATUS(fsinfo, union smb_fsinfo);
+
+ TRANS2_CHECK(trans2_setup_reply(trans, 0, 0, 0));
+
+ TRANS2_CHECK(trans2_push_fsinfo(req->smb_conn, trans,
+ &trans->out.data, fsinfo,
+ SMBSRV_REQ_DEFAULT_STR_FLAGS(req)));
+
+ return NT_STATUS_OK;
+}
+
+/*
+ trans2 qfsinfo implementation
+*/
+static NTSTATUS trans2_qfsinfo(struct smbsrv_request *req, struct trans_op *op)
+{
+ struct smb_trans2 *trans = op->trans;
+ union smb_fsinfo *fsinfo;
+ uint16_t level;
+
+ /* make sure we got enough parameters */
+ if (trans->in.params.length != 2) {
+ return NT_STATUS_FOOBAR;
+ }
+
+ fsinfo = talloc(op, union smb_fsinfo);
+ NT_STATUS_HAVE_NO_MEMORY(fsinfo);
+
+ level = SVAL(trans->in.params.data, 0);
+
+ /* work out the backend level - we make it 1-1 in the header */
+ fsinfo->generic.level = (enum smb_fsinfo_level)level;
+ if (fsinfo->generic.level >= RAW_QFS_GENERIC) {
+ return NT_STATUS_INVALID_LEVEL;
+ }
+
+ op->op_info = fsinfo;
+ op->send_fn = trans2_qfsinfo_send;
+
+ return ntvfs_fsinfo(req->ntvfs, fsinfo);
+}
+
+
+/*
+ trans2 open implementation send
+*/
+static NTSTATUS trans2_open_send(struct trans_op *op)
+{
+ struct smbsrv_request *req = op->req;
+ struct smb_trans2 *trans = op->trans;
+ union smb_open *io;
+
+ TRANS2_CHECK_ASYNC_STATUS(io, union smb_open);
+
+ TRANS2_CHECK(trans2_setup_reply(trans, 30, 0, 0));
+
+ smbsrv_push_fnum(trans->out.params.data, VWV(0), io->t2open.out.file.ntvfs);
+ 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 NT_STATUS_OK;
+}
+
+/*
+ trans2 open implementation
+*/
+static NTSTATUS trans2_open(struct smbsrv_request *req, struct trans_op *op)
+{
+ struct smb_trans2 *trans = op->trans;
+ union smb_open *io;
+
+ /* make sure we got enough parameters */
+ if (trans->in.params.length < 29) {
+ return NT_STATUS_FOOBAR;
+ }
+
+ io = talloc(op, union smb_open);
+ NT_STATUS_HAVE_NO_MEMORY(io);
+
+ 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;
+
+ smbsrv_blob_pull_string(&req->in.bufinfo, &trans->in.params, 28, &io->t2open.in.fname, 0);
+ if (io->t2open.in.fname == NULL) {
+ return NT_STATUS_FOOBAR;
+ }
+
+ TRANS2_CHECK(ea_pull_list(&trans->in.data, io, &io->t2open.in.num_eas, &io->t2open.in.eas));
+
+ op->op_info = io;
+ op->send_fn = trans2_open_send;
+
+ return ntvfs_open(req->ntvfs, io);
+}
+
+
+/*
+ trans2 simple send
+*/
+static NTSTATUS trans2_simple_send(struct trans_op *op)
+{
+ struct smbsrv_request *req = op->req;
+ struct smb_trans2 *trans = op->trans;
+
+ TRANS2_CHECK_ASYNC_STATUS_SIMPLE;
+
+ TRANS2_CHECK(trans2_setup_reply(trans, 2, 0, 0));
+
+ SSVAL(trans->out.params.data, VWV(0), 0);
+
+ return NT_STATUS_OK;
+}
+
+/*
+ trans2 mkdir implementation
+*/
+static NTSTATUS trans2_mkdir(struct smbsrv_request *req, struct trans_op *op)
+{
+ struct smb_trans2 *trans = op->trans;
+ union smb_mkdir *io;
+
+ /* make sure we got enough parameters */
+ if (trans->in.params.length < 5) {
+ return NT_STATUS_FOOBAR;
+ }
+
+ io = talloc(op, union smb_mkdir);
+ NT_STATUS_HAVE_NO_MEMORY(io);
+
+ io->t2mkdir.level = RAW_MKDIR_T2MKDIR;
+ smbsrv_blob_pull_string(&req->in.bufinfo, &trans->in.params, 4, &io->t2mkdir.in.path, 0);
+ if (io->t2mkdir.in.path == NULL) {
+ return NT_STATUS_FOOBAR;
+ }
+
+ TRANS2_CHECK(ea_pull_list(&trans->in.data, io,
+ &io->t2mkdir.in.num_eas,
+ &io->t2mkdir.in.eas));
+
+ op->op_info = io;
+ op->send_fn = trans2_simple_send;
+
+ return ntvfs_mkdir(req->ntvfs, io);
+}
+
+static NTSTATUS trans2_push_fileinfo(struct smbsrv_connection *smb_conn,
+ TALLOC_CTX *mem_ctx,
+ DATA_BLOB *blob,
+ union smb_fileinfo *st,
+ int default_str_flags)
+{
+ uint32_t list_size;
+ enum smb_fileinfo_level passthru_level;
+
+ switch (st->generic.level) {
+ case RAW_FILEINFO_GENERIC:
+ case RAW_FILEINFO_GETATTR:
+ case RAW_FILEINFO_GETATTRE:
+ case RAW_FILEINFO_SEC_DESC:
+ case RAW_FILEINFO_SMB2_ALL_EAS:
+ case RAW_FILEINFO_SMB2_ALL_INFORMATION:
+ /* handled elsewhere */
+ return NT_STATUS_INVALID_LEVEL;
+
+ case RAW_FILEINFO_UNIX_BASIC:
+ case RAW_FILEINFO_UNIX_LINK:
+ /* not implemented yet */
+ return NT_STATUS_INVALID_LEVEL;
+
+ case RAW_FILEINFO_STANDARD:
+ TRANS2_CHECK(smbsrv_blob_grow_data(mem_ctx, blob, 22));
+
+ srv_push_dos_date2(smb_conn, blob->data, 0, st->standard.out.create_time);
+ srv_push_dos_date2(smb_conn, blob->data, 4, st->standard.out.access_time);
+ srv_push_dos_date2(smb_conn, blob->data, 8, st->standard.out.write_time);
+ SIVAL(blob->data, 12, st->standard.out.size);
+ SIVAL(blob->data, 16, st->standard.out.alloc_size);
+ SSVAL(blob->data, 20, st->standard.out.attrib);
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_EA_SIZE:
+ TRANS2_CHECK(smbsrv_blob_grow_data(mem_ctx, blob, 26));
+
+ srv_push_dos_date2(smb_conn, blob->data, 0, st->ea_size.out.create_time);
+ srv_push_dos_date2(smb_conn, blob->data, 4, st->ea_size.out.access_time);
+ srv_push_dos_date2(smb_conn, blob->data, 8, st->ea_size.out.write_time);
+ SIVAL(blob->data, 12, st->ea_size.out.size);
+ SIVAL(blob->data, 16, st->ea_size.out.alloc_size);
+ SSVAL(blob->data, 20, st->ea_size.out.attrib);
+ SIVAL(blob->data, 22, st->ea_size.out.ea_size);
+ 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_CHECK(smbsrv_blob_grow_data(mem_ctx, blob, list_size));
+
+ ea_put_list(blob->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_CHECK(smbsrv_blob_grow_data(mem_ctx, blob, list_size));
+
+ ea_put_list(blob->data,
+ st->all_eas.out.num_eas, st->all_eas.out.eas);
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_IS_NAME_VALID:
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_BASIC_INFO:
+ passthru_level = RAW_FILEINFO_BASIC_INFORMATION;
+ break;
+
+ case RAW_FILEINFO_STANDARD_INFO:
+ passthru_level = RAW_FILEINFO_STANDARD_INFORMATION;
+ break;
+
+ case RAW_FILEINFO_EA_INFO:
+ passthru_level = RAW_FILEINFO_EA_INFORMATION;
+ break;
+
+ case RAW_FILEINFO_COMPRESSION_INFO:
+ passthru_level = RAW_FILEINFO_COMPRESSION_INFORMATION;
+ break;
+
+ case RAW_FILEINFO_ALL_INFO:
+ passthru_level = RAW_FILEINFO_ALL_INFORMATION;
+ break;
+
+ case RAW_FILEINFO_NAME_INFO:
+ passthru_level = RAW_FILEINFO_NAME_INFORMATION;
+ break;
+
+ case RAW_FILEINFO_ALT_NAME_INFO:
+ passthru_level = RAW_FILEINFO_ALT_NAME_INFORMATION;
+ break;
+
+ case RAW_FILEINFO_STREAM_INFO:
+ passthru_level = RAW_FILEINFO_STREAM_INFORMATION;
+ break;
+
+ default:
+ passthru_level = st->generic.level;
+ break;
+ }
+
+ return smbsrv_push_passthru_fileinfo(mem_ctx, blob,
+ passthru_level, st,
+ default_str_flags);
+}
+
+/*
+ fill in the reply from a qpathinfo or qfileinfo call
+*/
+static NTSTATUS trans2_fileinfo_send(struct trans_op *op)
+{
+ struct smbsrv_request *req = op->req;
+ struct smb_trans2 *trans = op->trans;
+ union smb_fileinfo *st;
+
+ TRANS2_CHECK_ASYNC_STATUS(st, union smb_fileinfo);
+
+ TRANS2_CHECK(trans2_setup_reply(trans, 2, 0, 0));
+ SSVAL(trans->out.params.data, 0, 0);
+
+ TRANS2_CHECK(trans2_push_fileinfo(req->smb_conn, trans,
+ &trans->out.data, st,
+ SMBSRV_REQ_DEFAULT_STR_FLAGS(req)));
+
+ return NT_STATUS_OK;
+}
+
+/*
+ trans2 qpathinfo implementation
+*/
+static NTSTATUS trans2_qpathinfo(struct smbsrv_request *req, struct trans_op *op)
+{
+ struct smb_trans2 *trans = op->trans;
+ union smb_fileinfo *st;
+ uint16_t level;
+
+ /* make sure we got enough parameters */
+ if (trans->in.params.length < 2) {
+ return NT_STATUS_FOOBAR;
+ }
+
+ st = talloc(op, union smb_fileinfo);
+ NT_STATUS_HAVE_NO_MEMORY(st);
+
+ level = SVAL(trans->in.params.data, 0);
+
+ smbsrv_blob_pull_string(&req->in.bufinfo, &trans->in.params, 6, &st->generic.in.file.path, 0);
+ if (st->generic.in.file.path == 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) {
+ TRANS2_CHECK(ea_pull_name_list(&trans->in.data, req,
+ &st->ea_list.in.num_names,
+ &st->ea_list.in.ea_names));
+ }
+
+ op->op_info = st;
+ op->send_fn = trans2_fileinfo_send;
+
+ return ntvfs_qpathinfo(req->ntvfs, st);
+}
+
+
+/*
+ trans2 qpathinfo implementation
+*/
+static NTSTATUS trans2_qfileinfo(struct smbsrv_request *req, struct trans_op *op)
+{
+ struct smb_trans2 *trans = op->trans;
+ union smb_fileinfo *st;
+ uint16_t level;
+ struct ntvfs_handle *h;
+
+ /* make sure we got enough parameters */
+ if (trans->in.params.length < 4) {
+ return NT_STATUS_FOOBAR;
+ }
+
+ st = talloc(op, union smb_fileinfo);
+ NT_STATUS_HAVE_NO_MEMORY(st);
+
+ h = smbsrv_pull_fnum(req, trans->in.params.data, 0);
+ level = SVAL(trans->in.params.data, 2);
+
+ st->generic.in.file.ntvfs = h;
+ /* 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) {
+ TRANS2_CHECK(ea_pull_name_list(&trans->in.data, req,
+ &st->ea_list.in.num_names,
+ &st->ea_list.in.ea_names));
+ }
+
+ op->op_info = st;
+ op->send_fn = trans2_fileinfo_send;
+
+ SMBSRV_CHECK_FILE_HANDLE_NTSTATUS(st->generic.in.file.ntvfs);
+ return ntvfs_qfileinfo(req->ntvfs, st);
+}
+
+
+/*
+ parse a trans2 setfileinfo/setpathinfo data blob
+*/
+static NTSTATUS trans2_parse_sfileinfo(struct smbsrv_request *req,
+ union smb_setfileinfo *st,
+ const DATA_BLOB *blob)
+{
+ enum smb_setfileinfo_level passthru_level;
+
+ 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:
+ passthru_level = SMB_SFILEINFO_BASIC_INFORMATION;
+ break;
+
+ case SMB_SFILEINFO_DISPOSITION_INFO:
+ case SMB_SFILEINFO_DISPOSITION_INFORMATION:
+ passthru_level = SMB_SFILEINFO_DISPOSITION_INFORMATION;
+ break;
+
+ case SMB_SFILEINFO_ALLOCATION_INFO:
+ case SMB_SFILEINFO_ALLOCATION_INFORMATION:
+ passthru_level = SMB_SFILEINFO_ALLOCATION_INFORMATION;
+ break;
+
+ case RAW_SFILEINFO_END_OF_FILE_INFO:
+ case RAW_SFILEINFO_END_OF_FILE_INFORMATION:
+ passthru_level = RAW_SFILEINFO_END_OF_FILE_INFORMATION;
+ break;
+
+ case RAW_SFILEINFO_RENAME_INFORMATION:
+ case RAW_SFILEINFO_POSITION_INFORMATION:
+ case RAW_SFILEINFO_MODE_INFORMATION:
+ passthru_level = st->generic.level;
+ break;
+
+ case RAW_SFILEINFO_UNIX_BASIC:
+ case RAW_SFILEINFO_UNIX_LINK:
+ case RAW_SFILEINFO_UNIX_HLINK:
+ case RAW_SFILEINFO_PIPE_INFORMATION:
+ case RAW_SFILEINFO_VALID_DATA_INFORMATION:
+ case RAW_SFILEINFO_SHORT_NAME_INFORMATION:
+ case RAW_SFILEINFO_1025:
+ case RAW_SFILEINFO_1027:
+ case RAW_SFILEINFO_1029:
+ case RAW_SFILEINFO_1030:
+ case RAW_SFILEINFO_1031:
+ case RAW_SFILEINFO_1032:
+ case RAW_SFILEINFO_1036:
+ case RAW_SFILEINFO_1041:
+ case RAW_SFILEINFO_1042:
+ case RAW_SFILEINFO_1043:
+ case RAW_SFILEINFO_1044:
+ return NT_STATUS_INVALID_LEVEL;
+
+ default:
+ /* we need a default here to cope with invalid values on the wire */
+ return NT_STATUS_INVALID_LEVEL;
+ }
+
+ return smbsrv_pull_passthru_sfileinfo(st, passthru_level, st,
+ blob, SMBSRV_REQ_DEFAULT_STR_FLAGS(req),
+ &req->in.bufinfo);
+}
+
+/*
+ trans2 setfileinfo implementation
+*/
+static NTSTATUS trans2_setfileinfo(struct smbsrv_request *req, struct trans_op *op)
+{
+ struct smb_trans2 *trans = op->trans;
+ union smb_setfileinfo *st;
+ uint16_t level;
+ struct ntvfs_handle *h;
+
+ /* make sure we got enough parameters */
+ if (trans->in.params.length < 4) {
+ return NT_STATUS_FOOBAR;
+ }
+
+ st = talloc(op, union smb_setfileinfo);
+ NT_STATUS_HAVE_NO_MEMORY(st);
+
+ h = smbsrv_pull_fnum(req, trans->in.params.data, 0);
+ level = SVAL(trans->in.params.data, 2);
+
+ st->generic.in.file.ntvfs = h;
+ /* work out the backend level - we make it 1-1 in the header */
+ st->generic.level = (enum smb_setfileinfo_level)level;
+ if (st->generic.level >= RAW_SFILEINFO_GENERIC) {
+ return NT_STATUS_INVALID_LEVEL;
+ }
+
+ TRANS2_CHECK(trans2_parse_sfileinfo(req, st, &trans->in.data));
+
+ op->op_info = st;
+ op->send_fn = trans2_simple_send;
+
+ SMBSRV_CHECK_FILE_HANDLE_NTSTATUS(st->generic.in.file.ntvfs);
+ return ntvfs_setfileinfo(req->ntvfs, st);
+}
+
+/*
+ trans2 setpathinfo implementation
+*/
+static NTSTATUS trans2_setpathinfo(struct smbsrv_request *req, struct trans_op *op)
+{
+ struct smb_trans2 *trans = op->trans;
+ union smb_setfileinfo *st;
+ uint16_t level;
+
+ /* make sure we got enough parameters */
+ if (trans->in.params.length < 4) {
+ return NT_STATUS_FOOBAR;
+ }
+
+ st = talloc(op, union smb_setfileinfo);
+ NT_STATUS_HAVE_NO_MEMORY(st);
+
+ level = SVAL(trans->in.params.data, 0);
+
+ smbsrv_blob_pull_string(&req->in.bufinfo, &trans->in.params, 6, &st->generic.in.file.path, 0);
+ if (st->generic.in.file.path == NULL) {
+ return NT_STATUS_FOOBAR;
+ }
+
+ /* work out the backend level - we make it 1-1 in the header */
+ st->generic.level = (enum smb_setfileinfo_level)level;
+ if (st->generic.level >= RAW_SFILEINFO_GENERIC) {
+ return NT_STATUS_INVALID_LEVEL;
+ }
+
+ TRANS2_CHECK(trans2_parse_sfileinfo(req, st, &trans->in.data));
+
+ op->op_info = st;
+ op->send_fn = trans2_simple_send;
+
+ return ntvfs_setpathinfo(req->ntvfs, st);
+}
+
+
+/* a structure to encapsulate the state information about an in-progress ffirst/fnext operation */
+struct find_state {
+ struct trans_op *op;
+ void *search;
+ enum smb_search_data_level data_level;
+ uint16_t last_entry_offset;
+ uint16_t flags;
+};
+
+/*
+ fill a single entry in a trans2 find reply
+*/
+static NTSTATUS find_fill_info(struct find_state *state,
+ const union smb_search_data *file)
+{
+ struct smbsrv_request *req = state->op->req;
+ struct smb_trans2 *trans = state->op->trans;
+ uint8_t *data;
+ unsigned int ofs = trans->out.data.length;
+ uint32_t ea_size;
+
+ switch (state->data_level) {
+ case RAW_SEARCH_DATA_GENERIC:
+ case RAW_SEARCH_DATA_SEARCH:
+ /* handled elsewhere */
+ return NT_STATUS_INVALID_LEVEL;
+
+ case RAW_SEARCH_DATA_STANDARD:
+ if (state->flags & FLAG_TRANS2_FIND_REQUIRE_RESUME) {
+ TRANS2_CHECK(smbsrv_blob_grow_data(trans, &trans->out.data, ofs + 27));
+ SIVAL(trans->out.data.data, ofs, file->standard.resume_key);
+ ofs += 4;
+ } else {
+ TRANS2_CHECK(smbsrv_blob_grow_data(trans, &trans->out.data, 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_CHECK(smbsrv_blob_append_string(trans, &trans->out.data, file->standard.name.s,
+ ofs + 22, SMBSRV_REQ_DEFAULT_STR_FLAGS(req),
+ STR_LEN8BIT | STR_TERMINATE | STR_LEN_NOTERM));
+ break;
+
+ case RAW_SEARCH_DATA_EA_SIZE:
+ if (state->flags & FLAG_TRANS2_FIND_REQUIRE_RESUME) {
+ TRANS2_CHECK(smbsrv_blob_grow_data(trans, &trans->out.data, ofs + 31));
+ SIVAL(trans->out.data.data, ofs, file->ea_size.resume_key);
+ ofs += 4;
+ } else {
+ TRANS2_CHECK(smbsrv_blob_grow_data(trans, &trans->out.data, 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_CHECK(smbsrv_blob_append_string(trans, &trans->out.data, file->ea_size.name.s,
+ ofs + 26, SMBSRV_REQ_DEFAULT_STR_FLAGS(req),
+ STR_LEN8BIT | STR_NOALIGN));
+ TRANS2_CHECK(smbsrv_blob_fill_data(trans, &trans->out.data, trans->out.data.length + 1));
+ break;
+
+ case RAW_SEARCH_DATA_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) {
+ TRANS2_CHECK(smbsrv_blob_grow_data(trans, &trans->out.data, ofs + 27 + ea_size));
+ SIVAL(trans->out.data.data, ofs, file->ea_list.resume_key);
+ ofs += 4;
+ } else {
+ TRANS2_CHECK(smbsrv_blob_grow_data(trans, &trans->out.data, ofs + 23 + ea_size));
+ }
+ 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_CHECK(smbsrv_blob_append_string(trans, &trans->out.data, file->ea_list.name.s,
+ ofs + 22 + ea_size, SMBSRV_REQ_DEFAULT_STR_FLAGS(req),
+ STR_LEN8BIT | STR_NOALIGN));
+ TRANS2_CHECK(smbsrv_blob_fill_data(trans, &trans->out.data, trans->out.data.length + 1));
+ break;
+
+ case RAW_SEARCH_DATA_DIRECTORY_INFO:
+ case RAW_SEARCH_DATA_FULL_DIRECTORY_INFO:
+ case RAW_SEARCH_DATA_NAME_INFO:
+ case RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO:
+ case RAW_SEARCH_DATA_ID_FULL_DIRECTORY_INFO:
+ case RAW_SEARCH_DATA_ID_BOTH_DIRECTORY_INFO:
+ return smbsrv_push_passthru_search(trans, &trans->out.data, state->data_level, file,
+ SMBSRV_REQ_DEFAULT_STR_FLAGS(req));
+
+ case RAW_SEARCH_DATA_UNIX_INFO:
+ case RAW_SEARCH_DATA_UNIX_INFO2:
+ return NT_STATUS_INVALID_LEVEL;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/* callback function for trans2 findfirst/findnext */
+static bool find_callback(void *private_data, const union smb_search_data *file)
+{
+ struct find_state *state = talloc_get_type(private_data, struct find_state);
+ struct smb_trans2 *trans = state->op->trans;
+ unsigned int old_length;
+
+ old_length = trans->out.data.length;
+
+ if (!NT_STATUS_IS_OK(find_fill_info(state, file)) ||
+ trans->out.data.length > trans->in.max_data) {
+ /* restore the old length and tell the backend to stop */
+ smbsrv_blob_grow_data(trans, &trans->out.data, old_length);
+ return false;
+ }
+
+ state->last_entry_offset = old_length;
+ return true;
+}
+
+/*
+ trans2 findfirst send
+ */
+static NTSTATUS trans2_findfirst_send(struct trans_op *op)
+{
+ struct smbsrv_request *req = op->req;
+ struct smb_trans2 *trans = op->trans;
+ union smb_search_first *search;
+ struct find_state *state;
+ uint8_t *param;
+
+ TRANS2_CHECK_ASYNC_STATUS(state, struct find_state);
+ search = talloc_get_type(state->search, union smb_search_first);
+
+ /* 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 getdfsreferral implementation
+*/
+static NTSTATUS trans2_getdfsreferral(struct smbsrv_request *req,
+ struct trans_op *op)
+{
+ enum ndr_err_code ndr_err;
+ struct smb_trans2 *trans = op->trans;
+ struct ldb_context *ldb;
+ struct loadparm_context *lp_ctx;
+ NTSTATUS status;
+ struct dfs_GetDFSReferral *r;
+ DATA_BLOB outblob = data_blob_null;
+ uint16_t nb_referrals = 0;
+
+ lp_ctx = req->tcon->ntvfs->lp_ctx;
+ if (!lpcfg_host_msdfs(lp_ctx)) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ r = talloc_zero(req, struct dfs_GetDFSReferral);
+ NT_STATUS_HAVE_NO_MEMORY(r);
+
+ ldb = samdb_connect(r,
+ req->tcon->ntvfs->event_ctx,
+ lp_ctx,
+ system_session(lp_ctx),
+ NULL,
+ 0);
+ if (ldb == NULL) {
+ DEBUG(2,(__location__ ": Failed to open samdb\n"));
+ talloc_free(r);
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ ndr_err = ndr_pull_struct_blob(&trans->in.params, r,
+ &r->in.req,
+ (ndr_pull_flags_fn_t)ndr_pull_dfs_GetDFSReferral_in);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ status = ndr_map_error2ntstatus(ndr_err);
+ DEBUG(2,(__location__ ": Failed to parse GetDFSReferral_in - %s\n",
+ nt_errstr(status)));
+ talloc_free(r);
+ return status;
+ }
+
+ DEBUG(8, ("Requested DFS name: %s length: %u\n",
+ r->in.req.servername,
+ (unsigned int)strlen_m(r->in.req.servername)*2));
+
+ status = dfs_server_ad_get_referrals(lp_ctx, ldb,
+ req->smb_conn->connection->remote_address, r);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(r);
+ return status;
+ }
+
+ ndr_err = ndr_push_struct_blob(&outblob, trans,
+ r->out.resp,
+ (ndr_push_flags_fn_t)ndr_push_dfs_referral_resp);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ DEBUG(2,(__location__ ":NDR marchalling of domain deferral response failed\n"));
+ talloc_free(r);
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ nb_referrals = r->out.resp->nb_referrals;
+
+ if (outblob.length > trans->in.max_data) {
+ bool ok = false;
+
+ DEBUG(3, ("Blob is too big for the output buffer "
+ "size %u max %u\n",
+ (unsigned int)outblob.length, trans->in.max_data));
+
+ if (trans->in.max_data != MAX_DFS_RESPONSE) {
+ /* As specified in MS-DFSC.pdf 3.3.5.2 */
+ talloc_free(r);
+ return STATUS_BUFFER_OVERFLOW;
+ }
+
+ /*
+ * The answer is too big, so let's remove some answers
+ */
+ while (!ok && r->out.resp->nb_referrals > 2) {
+ data_blob_free(&outblob);
+
+ /*
+ * Let's scrap the last referral (for now)
+ */
+ r->out.resp->nb_referrals -= 1;
+
+ ndr_err = ndr_push_struct_blob(&outblob, trans,
+ r->out.resp,
+ (ndr_push_flags_fn_t)ndr_push_dfs_referral_resp);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ talloc_free(r);
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ if (outblob.length <= MAX_DFS_RESPONSE) {
+ DEBUG(10,("DFS: managed to reduce the size of referral initial"
+ "number of referral %d, actual count: %d",
+ nb_referrals, r->out.resp->nb_referrals));
+ ok = true;
+ break;
+ }
+ }
+
+ if (!ok && r->out.resp->nb_referrals <= 2) {
+ DEBUG(8, (__location__ "; Not able to fit the domain and realm in DFS a "
+ " 56K buffer, something must be broken"));
+ talloc_free(r);
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+ }
+
+ TRANS2_CHECK(trans2_setup_reply(trans, 0, outblob.length, 0));
+
+ trans->out.data = outblob;
+ talloc_free(r);
+ return NT_STATUS_OK;
+}
+
+/*
+ trans2 findfirst implementation
+*/
+static NTSTATUS trans2_findfirst(struct smbsrv_request *req, struct trans_op *op)
+{
+ struct smb_trans2 *trans = op->trans;
+ union smb_search_first *search;
+ uint16_t level;
+ struct find_state *state;
+
+ /* make sure we got all the parameters */
+ if (trans->in.params.length < 14) {
+ return NT_STATUS_FOOBAR;
+ }
+
+ search = talloc(op, union smb_search_first);
+ NT_STATUS_HAVE_NO_MEMORY(search);
+
+ 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);
+
+ smbsrv_blob_pull_string(&req->in.bufinfo, &trans->in.params, 12, &search->t2ffirst.in.pattern, 0);
+ if (search->t2ffirst.in.pattern == NULL) {
+ return NT_STATUS_FOOBAR;
+ }
+
+ search->t2ffirst.level = RAW_SEARCH_TRANS2;
+ search->t2ffirst.data_level = (enum smb_search_data_level)level;
+ if (search->t2ffirst.data_level >= RAW_SEARCH_DATA_GENERIC) {
+ return NT_STATUS_INVALID_LEVEL;
+ }
+
+ if (search->t2ffirst.data_level == RAW_SEARCH_DATA_EA_LIST) {
+ TRANS2_CHECK(ea_pull_name_list(&trans->in.data, req,
+ &search->t2ffirst.in.num_names,
+ &search->t2ffirst.in.ea_names));
+ }
+
+ /* setup the private state structure that the backend will
+ give us in the callback */
+ state = talloc(op, struct find_state);
+ NT_STATUS_HAVE_NO_MEMORY(state);
+ state->op = op;
+ state->search = search;
+ state->data_level = search->t2ffirst.data_level;
+ state->last_entry_offset= 0;
+ state->flags = search->t2ffirst.in.flags;
+
+ /* setup for just a header in the reply */
+ TRANS2_CHECK(trans2_setup_reply(trans, 10, 0, 0));
+
+ op->op_info = state;
+ op->send_fn = trans2_findfirst_send;
+
+ return ntvfs_search_first(req->ntvfs, search, state, find_callback);
+}
+
+
+/*
+ trans2 findnext send
+*/
+static NTSTATUS trans2_findnext_send(struct trans_op *op)
+{
+ struct smbsrv_request *req = op->req;
+ struct smb_trans2 *trans = op->trans;
+ union smb_search_next *search;
+ struct find_state *state;
+ uint8_t *param;
+
+ TRANS2_CHECK_ASYNC_STATUS(state, struct find_state);
+ search = talloc_get_type(state->search, union smb_search_next);
+
+ /* 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;
+}
+
+
+/*
+ trans2 findnext implementation
+*/
+static NTSTATUS trans2_findnext(struct smbsrv_request *req, struct trans_op *op)
+{
+ struct smb_trans2 *trans = op->trans;
+ union smb_search_next *search;
+ uint16_t level;
+ struct find_state *state;
+
+ /* make sure we got all the parameters */
+ if (trans->in.params.length < 12) {
+ return NT_STATUS_FOOBAR;
+ }
+
+ search = talloc(op, union smb_search_next);
+ NT_STATUS_HAVE_NO_MEMORY(search);
+
+ 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);
+
+ smbsrv_blob_pull_string(&req->in.bufinfo, &trans->in.params, 12, &search->t2fnext.in.last_name, 0);
+ if (search->t2fnext.in.last_name == NULL) {
+ return NT_STATUS_FOOBAR;
+ }
+
+ search->t2fnext.level = RAW_SEARCH_TRANS2;
+ search->t2fnext.data_level = (enum smb_search_data_level)level;
+ if (search->t2fnext.data_level >= RAW_SEARCH_DATA_GENERIC) {
+ return NT_STATUS_INVALID_LEVEL;
+ }
+
+ if (search->t2fnext.data_level == RAW_SEARCH_DATA_EA_LIST) {
+ TRANS2_CHECK(ea_pull_name_list(&trans->in.data, req,
+ &search->t2fnext.in.num_names,
+ &search->t2fnext.in.ea_names));
+ }
+
+ /* setup the private state structure that the backend will give us in the callback */
+ state = talloc(op, struct find_state);
+ NT_STATUS_HAVE_NO_MEMORY(state);
+ state->op = op;
+ state->search = search;
+ state->data_level = search->t2fnext.data_level;
+ state->last_entry_offset= 0;
+ state->flags = search->t2fnext.in.flags;
+
+ /* setup for just a header in the reply */
+ TRANS2_CHECK(trans2_setup_reply(trans, 8, 0, 0));
+
+ op->op_info = state;
+ op->send_fn = trans2_findnext_send;
+
+ return ntvfs_search_next(req->ntvfs, search, state, find_callback);
+}
+
+
+/*
+ backend for trans2 requests
+*/
+static NTSTATUS trans2_backend(struct smbsrv_request *req, struct trans_op *op)
+{
+ struct smb_trans2 *trans = op->trans;
+ NTSTATUS status;
+
+ /* direct trans2 pass thru */
+ status = ntvfs_trans2(req->ntvfs, 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_GET_DFS_REFERRAL:
+ return trans2_getdfsreferral(req, op);
+ case TRANSACT2_FINDFIRST:
+ return trans2_findfirst(req, op);
+ case TRANSACT2_FINDNEXT:
+ return trans2_findnext(req, op);
+ case TRANSACT2_QPATHINFO:
+ return trans2_qpathinfo(req, op);
+ case TRANSACT2_QFILEINFO:
+ return trans2_qfileinfo(req, op);
+ case TRANSACT2_SETFILEINFO:
+ return trans2_setfileinfo(req, op);
+ case TRANSACT2_SETPATHINFO:
+ return trans2_setpathinfo(req, op);
+ case TRANSACT2_QFSINFO:
+ return trans2_qfsinfo(req, op);
+ case TRANSACT2_OPEN:
+ return trans2_open(req, op);
+ case TRANSACT2_MKDIR:
+ return trans2_mkdir(req, op);
+ }
+
+ /* an unknown trans2 command */
+ return NT_STATUS_FOOBAR;
+}
+
+int smbsrv_trans_partial_destructor(struct smbsrv_trans_partial *tp)
+{
+ DLIST_REMOVE(tp->req->smb_conn->trans_partial, tp);
+ return 0;
+}
+
+
+/*
+ send a continue request
+*/
+static void reply_trans_continue(struct smbsrv_request *req, uint8_t command,
+ struct smb_trans2 *trans)
+{
+ struct smbsrv_request *req2;
+ 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) {
+ smbsrv_send_error(req, NT_STATUS_INSUFFICIENT_RESOURCES);
+ return;
+ }
+
+ tp = talloc(req, struct smbsrv_trans_partial);
+
+ tp->req = req;
+ tp->u.trans = trans;
+ tp->command = command;
+
+ DLIST_ADD(req->smb_conn->trans_partial, tp);
+ talloc_set_destructor(tp, smbsrv_trans_partial_destructor);
+
+ req2 = smbsrv_setup_secondary_request(req);
+
+ /* send a 'please continue' reply */
+ smbsrv_setup_reply(req2, 0, 0);
+ smbsrv_send_reply(req2);
+}
+
+
+/*
+ answer a reconstructed trans request
+*/
+static void reply_trans_send(struct ntvfs_request *ntvfs)
+{
+ struct smbsrv_request *req;
+ struct trans_op *op;
+ struct smb_trans2 *trans;
+ uint16_t params_left, data_left;
+ uint8_t *params, *data;
+ int i;
+
+ SMBSRV_CHECK_ASYNC_STATUS_ERR(op, struct trans_op);
+ trans = op->trans;
+
+ /* if this function needs work to form the nttrans reply buffer, then
+ call that now */
+ if (op->send_fn != NULL) {
+ NTSTATUS status;
+ status = op->send_fn(op);
+ if (!NT_STATUS_IS_OK(status)) {
+ smbsrv_send_error(req, status);
+ return;
+ }
+ }
+
+ params_left = trans->out.params.length;
+ data_left = trans->out.data.length;
+ params = trans->out.params.data;
+ data = trans->out.data.data;
+
+ smbsrv_setup_reply(req, 10 + trans->out.setup_count, 0);
+
+ if (!NT_STATUS_IS_OK(req->ntvfs->async_states->status)) {
+ smbsrv_setup_error(req, req->ntvfs->async_states->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;
+ unsigned int 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 = smbsrv_setup_secondary_request(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));
+
+ SCVAL(this_req->out.vwv, VWV(9), trans->out.setup_count);
+ SCVAL(this_req->out.vwv, VWV(9)+1, 0); /* reserved */
+ for (i=0;i<trans->out.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;
+
+ smbsrv_send_reply(this_req);
+ } while (params_left != 0 || data_left != 0);
+}
+
+
+/*
+ answer a reconstructed trans request
+*/
+static void reply_trans_complete(struct smbsrv_request *req, uint8_t command,
+ struct smb_trans2 *trans)
+{
+ struct trans_op *op;
+
+ SMBSRV_TALLOC_IO_PTR(op, struct trans_op);
+ SMBSRV_SETUP_NTVFS_REQUEST(reply_trans_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ op->req = req;
+ op->trans = trans;
+ op->command = command;
+ op->op_info = NULL;
+ op->send_fn = NULL;
+
+ /* its a full request, give it to the backend */
+ if (command == SMBtrans) {
+ SMBSRV_CALL_NTVFS_BACKEND(ntvfs_trans(req->ntvfs, trans));
+ return;
+ } else {
+ SMBSRV_CALL_NTVFS_BACKEND(trans2_backend(req, op));
+ return;
+ }
+}
+
+/*
+ Reply to an SMBtrans or SMBtrans2 request
+*/
+static 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;
+
+ /* parse request */
+ if (req->in.wct < 14) {
+ smbsrv_send_error(req, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+
+ trans = talloc(req, struct smb_trans2);
+ if (trans == NULL) {
+ smbsrv_send_error(req, NT_STATUS_NO_MEMORY);
+ 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) {
+ smbsrv_send_error(req, NT_STATUS_DOS(ERRSRV, ERRerror));
+ return;
+ }
+
+ /* parse out the setup words */
+ trans->in.setup = talloc_array(trans, uint16_t, trans->in.setup_count);
+ if (trans->in.setup_count && !trans->in.setup) {
+ smbsrv_send_error(req, NT_STATUS_NO_MEMORY);
+ return;
+ }
+ for (i=0;i<trans->in.setup_count;i++) {
+ trans->in.setup[i] = SVAL(req->in.vwv, VWV(14+i));
+ }
+
+ if (command == SMBtrans) {
+ req_pull_string(&req->in.bufinfo, &trans->in.trans_name, req->in.data, -1, STR_TERMINATE);
+ }
+
+ if (!req_pull_blob(&req->in.bufinfo, req->in.hdr + param_ofs, param_count, &trans->in.params) ||
+ !req_pull_blob(&req->in.bufinfo, req->in.hdr + data_ofs, data_count, &trans->in.data)) {
+ smbsrv_send_error(req, NT_STATUS_FOOBAR);
+ return;
+ }
+
+ /* is it a partial request? if so, then send a 'send more' message */
+ if (param_total > param_count || data_total > data_count) {
+ 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;
+ uint8_t wct;
+
+ if (command == SMBtrans2) {
+ wct = 9;
+ } else {
+ wct = 8;
+ }
+
+ /* parse request */
+ if (req->in.wct != wct) {
+ /*
+ * TODO: add some error code tests
+ * w2k3 returns NT_STATUS_DOS(ERRSRV, ERRerror) here
+ */
+ smbsrv_send_error(req, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+
+ 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)) {
+/* TODO: check the VUID, PID and TID too? */
+ break;
+ }
+ }
+
+ if (tp == NULL) {
+ smbsrv_send_error(req, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+
+ trans = tp->u.trans;
+
+ 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->in.bufinfo, req->in.hdr + param_ofs, param_count, &params) ||
+ !req_pull_blob(&req->in.bufinfo, req->in.hdr + data_ofs, data_count, &data)) {
+ smbsrv_send_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)) {
+ smbsrv_send_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) {
+ smbsrv_send_error(tp->req, NT_STATUS_NO_MEMORY);
+ return;
+ }
+ 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) {
+ smbsrv_send_error(tp->req, NT_STATUS_NO_MEMORY);
+ return;
+ }
+ 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 */
+ req = tp->req;
+ talloc_free(tp);
+ reply_trans_complete(req, command, trans);
+ }
+ return;
+}
+
+
+/*
+ Reply to an SMBtrans2
+*/
+void smbsrv_reply_trans2(struct smbsrv_request *req)
+{
+ reply_trans_generic(req, SMBtrans2);
+}
+
+/*
+ Reply to an SMBtrans
+*/
+void smbsrv_reply_trans(struct smbsrv_request *req)
+{
+ reply_trans_generic(req, SMBtrans);
+}
+
+/*
+ Reply to an SMBtranss request
+*/
+void smbsrv_reply_transs(struct smbsrv_request *req)
+{
+ reply_transs_generic(req, SMBtrans);
+}
+
+/*
+ Reply to an SMBtranss2 request
+*/
+void smbsrv_reply_transs2(struct smbsrv_request *req)
+{
+ reply_transs_generic(req, SMBtrans2);
+}
diff --git a/source4/smb_server/smb/wscript_build b/source4/smb_server/smb/wscript_build
new file mode 100644
index 0000000..3e3df21
--- /dev/null
+++ b/source4/smb_server/smb/wscript_build
@@ -0,0 +1,10 @@
+#!/usr/bin/env python
+
+bld.SAMBA_SUBSYSTEM('SMB_PROTOCOL',
+ source='receive.c negprot.c nttrans.c reply.c request.c search.c service.c sesssetup.c srvtime.c trans2.c signing.c',
+ autoproto='smb_proto.h',
+ deps='dfs_server_ad',
+ public_deps='ntvfs LIBPACKET samba-credentials samba_server_gensec',
+ enabled=bld.CONFIG_SET('WITH_NTVFS_FILESERVER')
+ )
+
diff --git a/source4/smb_server/smb2/fileinfo.c b/source4/smb_server/smb2/fileinfo.c
new file mode 100644
index 0000000..a90c41a
--- /dev/null
+++ b/source4/smb_server/smb2/fileinfo.c
@@ -0,0 +1,377 @@
+/*
+ Unix SMB2 implementation.
+
+ Copyright (C) Stefan Metzmacher 2006
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+#include "smb_server/smb_server.h"
+#include "smb_server/smb2/smb2_server.h"
+#include "ntvfs/ntvfs.h"
+#include "librpc/gen_ndr/ndr_security.h"
+
+struct smb2srv_getinfo_op {
+ struct smb2srv_request *req;
+ struct smb2_getinfo *info;
+ void *io_ptr;
+ NTSTATUS (*send_fn)(struct smb2srv_getinfo_op *op);
+};
+
+static void smb2srv_getinfo_send(struct ntvfs_request *ntvfs)
+{
+ struct smb2srv_getinfo_op *op;
+ struct smb2srv_request *req;
+
+ /*
+ * SMB2 uses NT_STATUS_INVALID_INFO_CLASS
+ * so we need to translated it here
+ */
+ if (NT_STATUS_EQUAL(NT_STATUS_INVALID_LEVEL, ntvfs->async_states->status)) {
+ ntvfs->async_states->status = NT_STATUS_INVALID_INFO_CLASS;
+ }
+
+ SMB2SRV_CHECK_ASYNC_STATUS(op, struct smb2srv_getinfo_op);
+
+ ZERO_STRUCT(op->info->out);
+ if (op->send_fn) {
+ SMB2SRV_CHECK(op->send_fn(op));
+ }
+
+ if (op->info->in.output_buffer_length < op->info->out.blob.length) {
+ smb2srv_send_error(req, NT_STATUS_INFO_LENGTH_MISMATCH);
+ return;
+ }
+
+ SMB2SRV_CHECK(smb2srv_setup_reply(req, 0x08, true, op->info->out.blob.length));
+
+ SMB2SRV_CHECK(smb2_push_o16s32_blob(&req->out, 0x02, op->info->out.blob));
+ SSVAL(req->out.body, 0x06, 0);
+
+ smb2srv_send_reply(req);
+}
+
+static NTSTATUS smb2srv_getinfo_file_send(struct smb2srv_getinfo_op *op)
+{
+ union smb_fileinfo *io = talloc_get_type(op->io_ptr, union smb_fileinfo);
+ NTSTATUS status;
+
+ status = smbsrv_push_passthru_fileinfo(op->req,
+ &op->info->out.blob,
+ io->generic.level, io,
+ STR_UNICODE);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS smb2srv_getinfo_file(struct smb2srv_getinfo_op *op, uint8_t smb2_level)
+{
+ union smb_fileinfo *io;
+ uint16_t level;
+
+ io = talloc(op, union smb_fileinfo);
+ NT_STATUS_HAVE_NO_MEMORY(io);
+
+ level = op->info->in.info_type | (op->info->in.info_class << 8);
+ switch (level) {
+ case RAW_FILEINFO_SMB2_ALL_EAS:
+ io->all_eas.level = level;
+ io->all_eas.in.file.ntvfs = op->info->in.file.ntvfs;
+ io->all_eas.in.continue_flags = op->info->in.getinfo_flags;
+ break;
+
+ case RAW_FILEINFO_SMB2_ALL_INFORMATION:
+ io->all_info2.level = level;
+ io->all_info2.in.file.ntvfs = op->info->in.file.ntvfs;
+ break;
+
+ default:
+ /* the rest directly maps to the passthru levels */
+ io->generic.level = smb2_level + 1000;
+ io->generic.in.file.ntvfs = op->info->in.file.ntvfs;
+ break;
+ }
+
+ op->io_ptr = io;
+ op->send_fn = smb2srv_getinfo_file_send;
+
+ return ntvfs_qfileinfo(op->req->ntvfs, io);
+}
+
+static NTSTATUS smb2srv_getinfo_fs_send(struct smb2srv_getinfo_op *op)
+{
+ union smb_fsinfo *io = talloc_get_type(op->io_ptr, union smb_fsinfo);
+ NTSTATUS status;
+
+ status = smbsrv_push_passthru_fsinfo(op->req,
+ &op->info->out.blob,
+ io->generic.level, io,
+ STR_UNICODE);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS smb2srv_getinfo_fs(struct smb2srv_getinfo_op *op, uint8_t smb2_level)
+{
+ union smb_fsinfo *io;
+
+ io = talloc(op, union smb_fsinfo);
+ NT_STATUS_HAVE_NO_MEMORY(io);
+
+ /* the rest directly maps to the passthru levels */
+ io->generic.level = smb2_level + 1000;
+
+ /* TODO: allow qfsinfo only the share root directory handle */
+
+ op->io_ptr = io;
+ op->send_fn = smb2srv_getinfo_fs_send;
+
+ return ntvfs_fsinfo(op->req->ntvfs, io);
+}
+
+static NTSTATUS smb2srv_getinfo_security_send(struct smb2srv_getinfo_op *op)
+{
+ union smb_fileinfo *io = talloc_get_type(op->io_ptr, union smb_fileinfo);
+ enum ndr_err_code ndr_err;
+
+ ndr_err = ndr_push_struct_blob(&op->info->out.blob, op->req,
+ io->query_secdesc.out.sd,
+ (ndr_push_flags_fn_t)ndr_push_security_descriptor);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return ndr_map_error2ntstatus(ndr_err);
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS smb2srv_getinfo_security(struct smb2srv_getinfo_op *op, uint8_t smb2_level)
+{
+ union smb_fileinfo *io;
+
+ switch (smb2_level) {
+ case 0x00:
+ io = talloc(op, union smb_fileinfo);
+ NT_STATUS_HAVE_NO_MEMORY(io);
+
+ io->query_secdesc.level = RAW_FILEINFO_SEC_DESC;
+ io->query_secdesc.in.file.ntvfs = op->info->in.file.ntvfs;
+ io->query_secdesc.in.secinfo_flags = op->info->in.additional_information;
+
+ op->io_ptr = io;
+ op->send_fn = smb2srv_getinfo_security_send;
+
+ return ntvfs_qfileinfo(op->req->ntvfs, io);
+ }
+
+ return NT_STATUS_INVALID_PARAMETER;
+}
+
+static NTSTATUS smb2srv_getinfo_backend(struct smb2srv_getinfo_op *op)
+{
+ switch (op->info->in.info_type) {
+ case SMB2_0_INFO_FILE:
+ return smb2srv_getinfo_file(op, op->info->in.info_class);
+
+ case SMB2_0_INFO_FILESYSTEM:
+ return smb2srv_getinfo_fs(op, op->info->in.info_class);
+
+ case SMB2_0_INFO_SECURITY:
+ return smb2srv_getinfo_security(op, op->info->in.info_class);
+
+ case SMB2_0_INFO_QUOTA:
+ return NT_STATUS_NOT_SUPPORTED;
+ }
+
+ return NT_STATUS_INVALID_PARAMETER;
+}
+
+void smb2srv_getinfo_recv(struct smb2srv_request *req)
+{
+ struct smb2_getinfo *info;
+ struct smb2srv_getinfo_op *op;
+
+ SMB2SRV_CHECK_BODY_SIZE(req, 0x28, true);
+ SMB2SRV_TALLOC_IO_PTR(info, struct smb2_getinfo);
+ /* this overwrites req->io_ptr !*/
+ SMB2SRV_TALLOC_IO_PTR(op, struct smb2srv_getinfo_op);
+ op->req = req;
+ op->info = info;
+ op->io_ptr = NULL;
+ op->send_fn = NULL;
+ SMB2SRV_SETUP_NTVFS_REQUEST(smb2srv_getinfo_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ info->in.info_type = CVAL(req->in.body, 0x02);
+ info->in.info_class = CVAL(req->in.body, 0x03);
+ info->in.output_buffer_length = IVAL(req->in.body, 0x04);
+ info->in.reserved = IVAL(req->in.body, 0x0C);
+ info->in.additional_information = IVAL(req->in.body, 0x10);
+ info->in.getinfo_flags = IVAL(req->in.body, 0x14);
+ info->in.file.ntvfs = smb2srv_pull_handle(req, req->in.body, 0x18);
+ SMB2SRV_CHECK(smb2_pull_o16As32_blob(&req->in, op,
+ req->in.body+0x08, &info->in.input_buffer));
+
+ SMB2SRV_CHECK_FILE_HANDLE(info->in.file.ntvfs);
+ SMB2SRV_CALL_NTVFS_BACKEND(smb2srv_getinfo_backend(op));
+}
+
+struct smb2srv_setinfo_op {
+ struct smb2srv_request *req;
+ struct smb2_setinfo *info;
+};
+
+static void smb2srv_setinfo_send(struct ntvfs_request *ntvfs)
+{
+ struct smb2srv_setinfo_op *op;
+ struct smb2srv_request *req;
+
+ /*
+ * SMB2 uses NT_STATUS_INVALID_INFO_CLASS
+ * so we need to translated it here
+ */
+ if (NT_STATUS_EQUAL(NT_STATUS_INVALID_LEVEL, ntvfs->async_states->status)) {
+ ntvfs->async_states->status = NT_STATUS_INVALID_INFO_CLASS;
+ }
+
+ SMB2SRV_CHECK_ASYNC_STATUS(op, struct smb2srv_setinfo_op);
+
+ SMB2SRV_CHECK(smb2srv_setup_reply(op->req, 0x02, false, 0));
+
+ smb2srv_send_reply(req);
+}
+
+static NTSTATUS smb2srv_setinfo_file(struct smb2srv_setinfo_op *op, uint8_t smb2_level)
+{
+ union smb_setfileinfo *io;
+ NTSTATUS status;
+
+ io = talloc(op, union smb_setfileinfo);
+ NT_STATUS_HAVE_NO_MEMORY(io);
+
+ /* the levels directly map to the passthru levels */
+ io->generic.level = smb2_level + 1000;
+ io->generic.in.file.ntvfs = op->info->in.file.ntvfs;
+
+ /* handle cases that don't map directly */
+ if (io->generic.level == RAW_SFILEINFO_RENAME_INFORMATION) {
+ io->generic.level = RAW_SFILEINFO_RENAME_INFORMATION_SMB2;
+ }
+
+ status = smbsrv_pull_passthru_sfileinfo(io, io->generic.level, io,
+ &op->info->in.blob,
+ STR_UNICODE, &op->req->in.bufinfo);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ return ntvfs_setfileinfo(op->req->ntvfs, io);
+}
+
+static NTSTATUS smb2srv_setinfo_fs(struct smb2srv_setinfo_op *op, uint8_t smb2_level)
+{
+ switch (smb2_level) {
+ case 0x02:
+ return NT_STATUS_NOT_IMPLEMENTED;
+
+ case 0x06:
+ return NT_STATUS_ACCESS_DENIED;
+
+ case 0x08:
+ return NT_STATUS_ACCESS_DENIED;
+
+ case 0x0A:
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ return NT_STATUS_INVALID_INFO_CLASS;
+}
+
+static NTSTATUS smb2srv_setinfo_security(struct smb2srv_setinfo_op *op, uint8_t smb2_level)
+{
+ union smb_setfileinfo *io;
+ enum ndr_err_code ndr_err;
+
+ switch (smb2_level) {
+ case 0x00:
+ io = talloc(op, union smb_setfileinfo);
+ NT_STATUS_HAVE_NO_MEMORY(io);
+
+ io->set_secdesc.level = RAW_SFILEINFO_SEC_DESC;
+ io->set_secdesc.in.file.ntvfs = op->info->in.file.ntvfs;
+ io->set_secdesc.in.secinfo_flags = op->info->in.flags;
+
+ io->set_secdesc.in.sd = talloc(io, struct security_descriptor);
+ NT_STATUS_HAVE_NO_MEMORY(io->set_secdesc.in.sd);
+
+ ndr_err = ndr_pull_struct_blob(&op->info->in.blob, io,
+ io->set_secdesc.in.sd,
+ (ndr_pull_flags_fn_t)ndr_pull_security_descriptor);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return ndr_map_error2ntstatus(ndr_err);
+ }
+
+ return ntvfs_setfileinfo(op->req->ntvfs, io);
+ }
+
+ return NT_STATUS_INVALID_INFO_CLASS;
+}
+
+static NTSTATUS smb2srv_setinfo_backend(struct smb2srv_setinfo_op *op)
+{
+ uint8_t smb2_class;
+ uint8_t smb2_level;
+
+ smb2_class = 0xFF & op->info->in.level;
+ smb2_level = 0xFF & (op->info->in.level>>8);
+
+ switch (smb2_class) {
+ case SMB2_0_INFO_FILE:
+ return smb2srv_setinfo_file(op, smb2_level);
+
+ case SMB2_0_INFO_FILESYSTEM:
+ return smb2srv_setinfo_fs(op, smb2_level);
+
+ case SMB2_0_INFO_SECURITY:
+ return smb2srv_setinfo_security(op, smb2_level);
+
+ case SMB2_0_INFO_QUOTA:
+ return NT_STATUS_NOT_SUPPORTED;
+ }
+
+ return NT_STATUS_INVALID_PARAMETER;
+}
+
+void smb2srv_setinfo_recv(struct smb2srv_request *req)
+{
+ struct smb2_setinfo *info;
+ struct smb2srv_setinfo_op *op;
+
+ SMB2SRV_CHECK_BODY_SIZE(req, 0x20, true);
+ SMB2SRV_TALLOC_IO_PTR(info, struct smb2_setinfo);
+ /* this overwrites req->io_ptr !*/
+ SMB2SRV_TALLOC_IO_PTR(op, struct smb2srv_setinfo_op);
+ op->req = req;
+ op->info = info;
+ SMB2SRV_SETUP_NTVFS_REQUEST(smb2srv_setinfo_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ info->in.level = SVAL(req->in.body, 0x02);
+ SMB2SRV_CHECK(smb2_pull_s32o16_blob(&req->in, info, req->in.body+0x04, &info->in.blob));
+ info->in.flags = IVAL(req->in.body, 0x0C);
+ info->in.file.ntvfs = smb2srv_pull_handle(req, req->in.body, 0x10);
+
+ SMB2SRV_CHECK_FILE_HANDLE(info->in.file.ntvfs);
+ SMB2SRV_CALL_NTVFS_BACKEND(smb2srv_setinfo_backend(op));
+}
diff --git a/source4/smb_server/smb2/fileio.c b/source4/smb_server/smb2/fileio.c
new file mode 100644
index 0000000..4153a42
--- /dev/null
+++ b/source4/smb_server/smb2/fileio.c
@@ -0,0 +1,547 @@
+/*
+ Unix SMB2 implementation.
+
+ Copyright (C) Stefan Metzmacher 2005
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+#include "smb_server/smb_server.h"
+#include "smb_server/smb2/smb2_server.h"
+#include "ntvfs/ntvfs.h"
+#include "libcli/raw/raw_proto.h"
+#include "librpc/gen_ndr/ndr_security.h"
+
+static void smb2srv_create_send(struct ntvfs_request *ntvfs)
+{
+ struct smb2srv_request *req;
+ union smb_open *io;
+ DATA_BLOB blob;
+
+ SMB2SRV_CHECK_ASYNC_STATUS(io, union smb_open);
+
+ /* setup the blobs we should give in the reply */
+ if (io->smb2.out.maximal_access != 0) {
+ uint32_t data[2];
+ SIVAL(data, 0, 0);
+ SIVAL(data, 4, io->smb2.out.maximal_access);
+ SMB2SRV_CHECK(smb2_create_blob_add(req, &io->smb2.out.blobs,
+ SMB2_CREATE_TAG_MXAC,
+ data_blob_const(data, 8)));
+ }
+
+ if (IVAL(io->smb2.out.on_disk_id, 0) != 0) {
+ SMB2SRV_CHECK(smb2_create_blob_add(req, &io->smb2.out.blobs,
+ SMB2_CREATE_TAG_QFID,
+ data_blob_const(io->smb2.out.on_disk_id, 32)));
+ }
+
+ SMB2SRV_CHECK(smb2_create_blob_push(req, &blob, io->smb2.out.blobs));
+ SMB2SRV_CHECK(smb2srv_setup_reply(req, 0x58, true, blob.length));
+
+ SCVAL(req->out.body, 0x02, io->smb2.out.oplock_level);
+ SCVAL(req->out.body, 0x03, io->smb2.out.reserved);
+ SIVAL(req->out.body, 0x04, io->smb2.out.create_action);
+ SBVAL(req->out.body, 0x08, io->smb2.out.create_time);
+ SBVAL(req->out.body, 0x10, io->smb2.out.access_time);
+ SBVAL(req->out.body, 0x18, io->smb2.out.write_time);
+ SBVAL(req->out.body, 0x20, io->smb2.out.change_time);
+ SBVAL(req->out.body, 0x28, io->smb2.out.alloc_size);
+ SBVAL(req->out.body, 0x30, io->smb2.out.size);
+ SIVAL(req->out.body, 0x38, io->smb2.out.file_attr);
+ SIVAL(req->out.body, 0x3C, io->smb2.out.reserved2);
+ smb2srv_push_handle(req->out.body, 0x40, io->smb2.out.file.ntvfs);
+ SMB2SRV_CHECK(smb2_push_o32s32_blob(&req->out, 0x50, blob));
+
+ /* also setup the chained file handle */
+ req->chained_file_handle = req->_chained_file_handle;
+ smb2srv_push_handle(req->chained_file_handle, 0, io->smb2.out.file.ntvfs);
+
+ smb2srv_send_reply(req);
+}
+
+void smb2srv_create_recv(struct smb2srv_request *req)
+{
+ union smb_open *io;
+ DATA_BLOB blob;
+ int i;
+
+ SMB2SRV_CHECK_BODY_SIZE(req, 0x38, true);
+ SMB2SRV_TALLOC_IO_PTR(io, union smb_open);
+ SMB2SRV_SETUP_NTVFS_REQUEST(smb2srv_create_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ ZERO_STRUCT(io->smb2.in);
+ io->smb2.level = RAW_OPEN_SMB2;
+ io->smb2.in.security_flags = CVAL(req->in.body, 0x02);
+ io->smb2.in.oplock_level = CVAL(req->in.body, 0x03);
+ io->smb2.in.impersonation_level = IVAL(req->in.body, 0x04);
+ io->smb2.in.create_flags = BVAL(req->in.body, 0x08);
+ io->smb2.in.reserved = BVAL(req->in.body, 0x10);
+ io->smb2.in.desired_access = IVAL(req->in.body, 0x18);
+ io->smb2.in.file_attributes = IVAL(req->in.body, 0x1C);
+ io->smb2.in.share_access = IVAL(req->in.body, 0x20);
+ io->smb2.in.create_disposition = IVAL(req->in.body, 0x24);
+ io->smb2.in.create_options = IVAL(req->in.body, 0x28);
+ SMB2SRV_CHECK(smb2_pull_o16s16_string(&req->in, io, req->in.body+0x2C, &io->smb2.in.fname));
+ SMB2SRV_CHECK(smb2_pull_o32s32_blob(&req->in, io, req->in.body+0x30, &blob));
+ SMB2SRV_CHECK(smb2_create_blob_parse(io, blob, &io->smb2.in.blobs));
+
+ /* interpret the parsed tags that a server needs to respond to */
+ for (i=0;i<io->smb2.in.blobs.num_blobs;i++) {
+ if (strcmp(io->smb2.in.blobs.blobs[i].tag, SMB2_CREATE_TAG_EXTA) == 0) {
+ SMB2SRV_CHECK(ea_pull_list_chained(&io->smb2.in.blobs.blobs[i].data, io,
+ &io->smb2.in.eas.num_eas,
+ &io->smb2.in.eas.eas));
+ }
+ if (strcmp(io->smb2.in.blobs.blobs[i].tag, SMB2_CREATE_TAG_SECD) == 0) {
+ enum ndr_err_code ndr_err;
+ io->smb2.in.sec_desc = talloc(io, struct security_descriptor);
+ if (io->smb2.in.sec_desc == NULL) {
+ smb2srv_send_error(req, NT_STATUS_NO_MEMORY);
+ return;
+ }
+ ndr_err = ndr_pull_struct_blob(&io->smb2.in.blobs.blobs[i].data, io,
+ io->smb2.in.sec_desc,
+ (ndr_pull_flags_fn_t)ndr_pull_security_descriptor);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ smb2srv_send_error(req, ndr_map_error2ntstatus(ndr_err));
+ return;
+ }
+ }
+ if (strcmp(io->smb2.in.blobs.blobs[i].tag, SMB2_CREATE_TAG_DHNQ) == 0) {
+ io->smb2.in.durable_open = true;
+ }
+ if (strcmp(io->smb2.in.blobs.blobs[i].tag, SMB2_CREATE_TAG_DHNC) == 0) {
+ if (io->smb2.in.blobs.blobs[i].data.length != 16) {
+ smb2srv_send_error(req, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+ io->smb2.in.durable_handle = talloc(io, struct smb2_handle);
+ if (io->smb2.in.durable_handle == NULL) {
+ smb2srv_send_error(req, NT_STATUS_NO_MEMORY);
+ return;
+ }
+ smb2_pull_handle(io->smb2.in.blobs.blobs[i].data.data, io->smb2.in.durable_handle);
+ }
+ if (strcmp(io->smb2.in.blobs.blobs[i].tag, SMB2_CREATE_TAG_ALSI) == 0) {
+ if (io->smb2.in.blobs.blobs[i].data.length != 8) {
+ smb2srv_send_error(req, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+ io->smb2.in.alloc_size = BVAL(io->smb2.in.blobs.blobs[i].data.data, 0);
+ }
+ if (strcmp(io->smb2.in.blobs.blobs[i].tag, SMB2_CREATE_TAG_MXAC) == 0) {
+ io->smb2.in.query_maximal_access = true;
+ }
+ if (strcmp(io->smb2.in.blobs.blobs[i].tag, SMB2_CREATE_TAG_TWRP) == 0) {
+ if (io->smb2.in.blobs.blobs[i].data.length != 8) {
+ smb2srv_send_error(req, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+ io->smb2.in.timewarp = BVAL(io->smb2.in.blobs.blobs[i].data.data, 0);
+ }
+ if (strcmp(io->smb2.in.blobs.blobs[i].tag, SMB2_CREATE_TAG_QFID) == 0) {
+ io->smb2.in.query_on_disk_id = true;
+ }
+ }
+
+ /* the VFS backend does not yet handle NULL filenames */
+ if (io->smb2.in.fname == NULL) {
+ io->smb2.in.fname = "";
+ }
+
+ SMB2SRV_CALL_NTVFS_BACKEND(ntvfs_open(req->ntvfs, io));
+}
+
+static void smb2srv_close_send(struct ntvfs_request *ntvfs)
+{
+ struct smb2srv_request *req;
+ union smb_close *io;
+
+ SMB2SRV_CHECK_ASYNC_STATUS(io, union smb_close);
+ SMB2SRV_CHECK(smb2srv_setup_reply(req, 0x3C, false, 0));
+
+ SSVAL(req->out.body, 0x02, io->smb2.out.flags);
+ SIVAL(req->out.body, 0x04, io->smb2.out._pad);
+ SBVAL(req->out.body, 0x08, io->smb2.out.create_time);
+ SBVAL(req->out.body, 0x10, io->smb2.out.access_time);
+ SBVAL(req->out.body, 0x18, io->smb2.out.write_time);
+ SBVAL(req->out.body, 0x20, io->smb2.out.change_time);
+ SBVAL(req->out.body, 0x28, io->smb2.out.alloc_size);
+ SBVAL(req->out.body, 0x30, io->smb2.out.size);
+ SIVAL(req->out.body, 0x38, io->smb2.out.file_attr);
+
+ /* also destroy the chained file handle */
+ req->chained_file_handle = NULL;
+ memset(req->_chained_file_handle, 0, sizeof(req->_chained_file_handle));
+
+ smb2srv_send_reply(req);
+}
+
+void smb2srv_close_recv(struct smb2srv_request *req)
+{
+ union smb_close *io;
+
+ SMB2SRV_CHECK_BODY_SIZE(req, 0x18, false);
+ SMB2SRV_TALLOC_IO_PTR(io, union smb_close);
+ SMB2SRV_SETUP_NTVFS_REQUEST(smb2srv_close_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ io->smb2.level = RAW_CLOSE_SMB2;
+ io->smb2.in.flags = SVAL(req->in.body, 0x02);
+ io->smb2.in._pad = IVAL(req->in.body, 0x04);
+ io->smb2.in.file.ntvfs = smb2srv_pull_handle(req, req->in.body, 0x08);
+
+ SMB2SRV_CHECK_FILE_HANDLE(io->smb2.in.file.ntvfs);
+ SMB2SRV_CALL_NTVFS_BACKEND(ntvfs_close(req->ntvfs, io));
+}
+
+static void smb2srv_flush_send(struct ntvfs_request *ntvfs)
+{
+ struct smb2srv_request *req;
+ union smb_flush *io;
+
+ SMB2SRV_CHECK_ASYNC_STATUS(io, union smb_flush);
+ SMB2SRV_CHECK(smb2srv_setup_reply(req, 0x04, false, 0));
+
+ SSVAL(req->out.body, 0x02, io->smb2.out.reserved);
+
+ smb2srv_send_reply(req);
+}
+
+void smb2srv_flush_recv(struct smb2srv_request *req)
+{
+ union smb_flush *io;
+
+ SMB2SRV_CHECK_BODY_SIZE(req, 0x18, false);
+ SMB2SRV_TALLOC_IO_PTR(io, union smb_flush);
+ SMB2SRV_SETUP_NTVFS_REQUEST(smb2srv_flush_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ io->smb2.level = RAW_FLUSH_SMB2;
+ io->smb2.in.reserved1 = SVAL(req->in.body, 0x02);
+ io->smb2.in.reserved2 = IVAL(req->in.body, 0x04);
+ io->smb2.in.file.ntvfs = smb2srv_pull_handle(req, req->in.body, 0x08);
+
+ SMB2SRV_CHECK_FILE_HANDLE(io->smb2.in.file.ntvfs);
+ SMB2SRV_CALL_NTVFS_BACKEND(ntvfs_flush(req->ntvfs, io));
+}
+
+static void smb2srv_read_send(struct ntvfs_request *ntvfs)
+{
+ struct smb2srv_request *req;
+ union smb_read *io;
+
+ SMB2SRV_CHECK_ASYNC_STATUS(io, union smb_read);
+ SMB2SRV_CHECK(smb2srv_setup_reply(req, 0x10, true, io->smb2.out.data.length));
+
+ /* TODO: avoid the memcpy */
+ SMB2SRV_CHECK(smb2_push_o16s32_blob(&req->out, 0x02, io->smb2.out.data));
+ SIVAL(req->out.body, 0x08, io->smb2.out.remaining);
+ SIVAL(req->out.body, 0x0C, io->smb2.out.reserved);
+
+ smb2srv_send_reply(req);
+}
+
+void smb2srv_read_recv(struct smb2srv_request *req)
+{
+ union smb_read *io;
+
+ SMB2SRV_CHECK_BODY_SIZE(req, 0x30, true);
+
+ /* MS-SMB2 2.2.19 read must have a single byte of zero */
+ if (req->in.body_size - req->in.body_fixed < 1) {
+ smb2srv_send_error(req, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+ SMB2SRV_TALLOC_IO_PTR(io, union smb_read);
+ SMB2SRV_SETUP_NTVFS_REQUEST(smb2srv_read_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ io->smb2.level = RAW_READ_SMB2;
+ io->smb2.in._pad = SVAL(req->in.body, 0x02);
+ io->smb2.in.length = IVAL(req->in.body, 0x04);
+ io->smb2.in.offset = BVAL(req->in.body, 0x08);
+ io->smb2.in.file.ntvfs = smb2srv_pull_handle(req, req->in.body, 0x10);
+ io->smb2.in.min_count = IVAL(req->in.body, 0x20);
+ io->smb2.in.channel = IVAL(req->in.body, 0x24);
+ io->smb2.in.remaining = IVAL(req->in.body, 0x28);
+ io->smb2.in.channel_offset = SVAL(req->in.body, 0x2C);
+ io->smb2.in.channel_length = SVAL(req->in.body, 0x2E);
+
+ SMB2SRV_CHECK_FILE_HANDLE(io->smb2.in.file.ntvfs);
+
+ /* preallocate the buffer for the backends */
+ io->smb2.out.data = data_blob_talloc(io, NULL, io->smb2.in.length);
+ if (io->smb2.out.data.length != io->smb2.in.length) {
+ SMB2SRV_CHECK(NT_STATUS_NO_MEMORY);
+ }
+
+ SMB2SRV_CALL_NTVFS_BACKEND(ntvfs_read(req->ntvfs, io));
+}
+
+static void smb2srv_write_send(struct ntvfs_request *ntvfs)
+{
+ struct smb2srv_request *req;
+ union smb_write *io;
+
+ SMB2SRV_CHECK_ASYNC_STATUS(io, union smb_write);
+ SMB2SRV_CHECK(smb2srv_setup_reply(req, 0x10, true, 0));
+
+ SSVAL(req->out.body, 0x02, io->smb2.out._pad);
+ SIVAL(req->out.body, 0x04, io->smb2.out.nwritten);
+ SBVAL(req->out.body, 0x08, io->smb2.out.unknown1);
+
+ smb2srv_send_reply(req);
+}
+
+void smb2srv_write_recv(struct smb2srv_request *req)
+{
+ union smb_write *io;
+
+ SMB2SRV_CHECK_BODY_SIZE(req, 0x30, true);
+ SMB2SRV_TALLOC_IO_PTR(io, union smb_write);
+ SMB2SRV_SETUP_NTVFS_REQUEST(smb2srv_write_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ /* TODO: avoid the memcpy */
+ io->smb2.level = RAW_WRITE_SMB2;
+ SMB2SRV_CHECK(smb2_pull_o16s32_blob(&req->in, io, req->in.body+0x02, &io->smb2.in.data));
+ io->smb2.in.offset = BVAL(req->in.body, 0x08);
+ io->smb2.in.file.ntvfs = smb2srv_pull_handle(req, req->in.body, 0x10);
+ io->smb2.in.unknown1 = BVAL(req->in.body, 0x20);
+ io->smb2.in.unknown2 = BVAL(req->in.body, 0x28);
+
+ SMB2SRV_CHECK_FILE_HANDLE(io->smb2.in.file.ntvfs);
+ SMB2SRV_CALL_NTVFS_BACKEND(ntvfs_write(req->ntvfs, io));
+}
+
+static void smb2srv_lock_send(struct ntvfs_request *ntvfs)
+{
+ struct smb2srv_request *req;
+ union smb_lock *io;
+
+ SMB2SRV_CHECK_ASYNC_STATUS_ERR(io, union smb_lock);
+ SMB2SRV_CHECK(smb2srv_setup_reply(req, 0x04, false, 0));
+
+ SSVAL(req->out.body, 0x02, io->smb2.out.reserved);
+
+ smb2srv_send_reply(req);
+}
+
+void smb2srv_lock_recv(struct smb2srv_request *req)
+{
+ union smb_lock *io;
+ int i;
+
+ SMB2SRV_CHECK_BODY_SIZE(req, 0x30, false);
+ SMB2SRV_TALLOC_IO_PTR(io, union smb_lock);
+ SMB2SRV_SETUP_NTVFS_REQUEST(smb2srv_lock_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ io->smb2.level = RAW_LOCK_SMB2;
+ io->smb2.in.lock_count = SVAL(req->in.body, 0x02);
+ io->smb2.in.lock_sequence = IVAL(req->in.body, 0x04);
+ io->smb2.in.file.ntvfs = smb2srv_pull_handle(req, req->in.body, 0x08);
+ if (req->in.body_size < 24 + 24*(uint64_t)io->smb2.in.lock_count) {
+ DEBUG(0,("%s: lock buffer too small\n", __location__));
+ smb2srv_send_error(req, NT_STATUS_FOOBAR);
+ return;
+ }
+ io->smb2.in.locks = talloc_array(io, struct smb2_lock_element,
+ io->smb2.in.lock_count);
+ if (io->smb2.in.locks == NULL) {
+ smb2srv_send_error(req, NT_STATUS_NO_MEMORY);
+ return;
+ }
+
+ for (i=0;i<io->smb2.in.lock_count;i++) {
+ io->smb2.in.locks[i].offset = BVAL(req->in.body, 24 + i*24);
+ io->smb2.in.locks[i].length = BVAL(req->in.body, 32 + i*24);
+ io->smb2.in.locks[i].flags = IVAL(req->in.body, 40 + i*24);
+ io->smb2.in.locks[i].reserved = IVAL(req->in.body, 44 + i*24);
+ }
+
+ SMB2SRV_CHECK_FILE_HANDLE(io->smb2.in.file.ntvfs);
+ SMB2SRV_CALL_NTVFS_BACKEND(ntvfs_lock(req->ntvfs, io));
+}
+
+static void smb2srv_ioctl_send(struct ntvfs_request *ntvfs)
+{
+ struct smb2srv_request *req;
+ union smb_ioctl *io;
+
+ SMB2SRV_CHECK_ASYNC_STATUS_ERR(io, union smb_ioctl);
+ SMB2SRV_CHECK(smb2srv_setup_reply(req, 0x30, true, 0));
+
+ SSVAL(req->out.body, 0x02, io->smb2.out.reserved);
+ SIVAL(req->out.body, 0x04, io->smb2.out.function);
+ if (io->smb2.level == RAW_IOCTL_SMB2_NO_HANDLE) {
+ struct smb2_handle h;
+ h.data[0] = UINT64_MAX;
+ h.data[1] = UINT64_MAX;
+ smb2_push_handle(req->out.body + 0x08, &h);
+ } else {
+ smb2srv_push_handle(req->out.body, 0x08,io->smb2.in.file.ntvfs);
+ }
+ SMB2SRV_CHECK(smb2_push_o32s32_blob(&req->out, 0x18, io->smb2.out.in));
+ SMB2SRV_CHECK(smb2_push_o32s32_blob(&req->out, 0x20, io->smb2.out.out));
+ SIVAL(req->out.body, 0x28, io->smb2.out.flags);
+ SIVAL(req->out.body, 0x2C, io->smb2.out.reserved2);
+
+ smb2srv_send_reply(req);
+}
+
+void smb2srv_ioctl_recv(struct smb2srv_request *req)
+{
+ union smb_ioctl *io;
+ struct smb2_handle h;
+
+ SMB2SRV_CHECK_BODY_SIZE(req, 0x38, true);
+ SMB2SRV_TALLOC_IO_PTR(io, union smb_ioctl);
+ SMB2SRV_SETUP_NTVFS_REQUEST(smb2srv_ioctl_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ /* TODO: avoid the memcpy */
+ io->smb2.in.reserved = SVAL(req->in.body, 0x02);
+ io->smb2.in.function = IVAL(req->in.body, 0x04);
+ /* file handle ... */
+ SMB2SRV_CHECK(smb2_pull_o32s32_blob(&req->in, io, req->in.body+0x18, &io->smb2.in.out));
+ io->smb2.in.max_input_response = IVAL(req->in.body, 0x20);
+ SMB2SRV_CHECK(smb2_pull_o32s32_blob(&req->in, io, req->in.body+0x24, &io->smb2.in.in));
+ io->smb2.in.max_output_response = IVAL(req->in.body, 0x2C);
+ io->smb2.in.flags = IVAL(req->in.body, 0x30);
+ io->smb2.in.reserved2 = IVAL(req->in.body, 0x34);
+
+ smb2_pull_handle(req->in.body + 0x08, &h);
+ if (h.data[0] == UINT64_MAX && h.data[1] == UINT64_MAX) {
+ io->smb2.level = RAW_IOCTL_SMB2_NO_HANDLE;
+ } else {
+ io->smb2.level = RAW_IOCTL_SMB2;
+ io->smb2.in.file.ntvfs = smb2srv_pull_handle(req, req->in.body, 0x08);
+ SMB2SRV_CHECK_FILE_HANDLE(io->smb2.in.file.ntvfs);
+ }
+
+ SMB2SRV_CALL_NTVFS_BACKEND(ntvfs_ioctl(req->ntvfs, io));
+}
+
+static void smb2srv_notify_send(struct ntvfs_request *ntvfs)
+{
+ struct smb2srv_request *req;
+ union smb_notify *io;
+ size_t size = 0;
+ int i;
+ uint8_t *p;
+ DATA_BLOB blob = data_blob(NULL, 0);
+
+ SMB2SRV_CHECK_ASYNC_STATUS(io, union smb_notify);
+ SMB2SRV_CHECK(smb2srv_setup_reply(req, 0x08, true, 0));
+
+#define MAX_BYTES_PER_CHAR 3
+
+ /* work out how big the reply buffer could be */
+ for (i=0;i<io->smb2.out.num_changes;i++) {
+ size += 12 + 3 + (1+strlen(io->smb2.out.changes[i].name.s)) * MAX_BYTES_PER_CHAR;
+ }
+
+ blob = data_blob_talloc(req, NULL, size);
+ if (size > 0 && !blob.data) {
+ SMB2SRV_CHECK(NT_STATUS_NO_MEMORY);
+ }
+
+ p = blob.data;
+
+ /* construct the changes buffer */
+ for (i=0;i<io->smb2.out.num_changes;i++) {
+ uint32_t ofs;
+ ssize_t len;
+
+ SIVAL(p, 4, io->smb2.out.changes[i].action);
+ len = push_string(p + 12, io->smb2.out.changes[i].name.s,
+ blob.length - (p+12 - blob.data), STR_UNICODE);
+ SIVAL(p, 8, len);
+
+ ofs = len + 12;
+
+ if (ofs & 3) {
+ int pad = 4 - (ofs & 3);
+ memset(p+ofs, 0, pad);
+ ofs += pad;
+ }
+
+ if (i == io->smb2.out.num_changes-1) {
+ SIVAL(p, 0, 0);
+ } else {
+ SIVAL(p, 0, ofs);
+ }
+
+ p += ofs;
+ }
+
+ blob.length = p - blob.data;
+
+ SMB2SRV_CHECK(smb2_push_o16s32_blob(&req->out, 0x02, blob));
+
+ smb2srv_send_reply(req);
+}
+
+void smb2srv_notify_recv(struct smb2srv_request *req)
+{
+ union smb_notify *io;
+
+ SMB2SRV_CHECK_BODY_SIZE(req, 0x20, false);
+ SMB2SRV_TALLOC_IO_PTR(io, union smb_notify);
+ SMB2SRV_SETUP_NTVFS_REQUEST(smb2srv_notify_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ io->smb2.level = RAW_NOTIFY_SMB2;
+ io->smb2.in.recursive = SVAL(req->in.body, 0x02);
+ io->smb2.in.buffer_size = IVAL(req->in.body, 0x04);
+ io->smb2.in.file.ntvfs = smb2srv_pull_handle(req, req->in.body, 0x08);
+ io->smb2.in.completion_filter = IVAL(req->in.body, 0x18);
+ io->smb2.in.unknown = BVAL(req->in.body, 0x1C);
+
+ SMB2SRV_CHECK_FILE_HANDLE(io->smb2.in.file.ntvfs);
+ SMB2SRV_CALL_NTVFS_BACKEND(ntvfs_notify(req->ntvfs, io));
+}
+
+static void smb2srv_break_send(struct ntvfs_request *ntvfs)
+{
+ struct smb2srv_request *req;
+ union smb_lock *io;
+
+ SMB2SRV_CHECK_ASYNC_STATUS_ERR(io, union smb_lock);
+ SMB2SRV_CHECK(smb2srv_setup_reply(req, 0x18, false, 0));
+
+ SCVAL(req->out.body, 0x02, io->smb2_break.out.oplock_level);
+ SCVAL(req->out.body, 0x03, io->smb2_break.out.reserved);
+ SIVAL(req->out.body, 0x04, io->smb2_break.out.reserved2);
+ smb2srv_push_handle(req->out.body, 0x08,io->smb2_break.out.file.ntvfs);
+
+ smb2srv_send_reply(req);
+}
+
+void smb2srv_break_recv(struct smb2srv_request *req)
+{
+ union smb_lock *io;
+
+ SMB2SRV_CHECK_BODY_SIZE(req, 0x18, false);
+ SMB2SRV_TALLOC_IO_PTR(io, union smb_lock);
+ SMB2SRV_SETUP_NTVFS_REQUEST(smb2srv_break_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ io->smb2_break.level = RAW_LOCK_SMB2_BREAK;
+ io->smb2_break.in.oplock_level = CVAL(req->in.body, 0x02);
+ io->smb2_break.in.reserved = CVAL(req->in.body, 0x03);
+ io->smb2_break.in.reserved2 = IVAL(req->in.body, 0x04);
+ io->smb2_break.in.file.ntvfs = smb2srv_pull_handle(req, req->in.body, 0x08);
+
+ SMB2SRV_CHECK_FILE_HANDLE(io->smb2_break.in.file.ntvfs);
+ SMB2SRV_CALL_NTVFS_BACKEND(ntvfs_lock(req->ntvfs, io));
+}
diff --git a/source4/smb_server/smb2/find.c b/source4/smb_server/smb2/find.c
new file mode 100644
index 0000000..17f09e1
--- /dev/null
+++ b/source4/smb_server/smb2/find.c
@@ -0,0 +1,167 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB2 Find
+ Copyright (C) Andrew Tridgell 2003
+ Copyright (c) Stefan Metzmacher 2006
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+/*
+ This file handles the parsing of transact2 requests
+*/
+
+#include "includes.h"
+#include "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+#include "smb_server/smb_server.h"
+#include "smb_server/smb2/smb2_server.h"
+#include "ntvfs/ntvfs.h"
+
+
+/* a structure to encapsulate the state information about an in-progress ffirst/fnext operation */
+struct smb2srv_find_state {
+ struct smb2srv_request *req;
+ struct smb2_find *info;
+ union smb_search_first *ff;
+ union smb_search_next *fn;
+ uint32_t last_entry_offset;
+};
+
+/* callback function for SMB2 Find */
+static bool smb2srv_find_callback(void *private_data, const union smb_search_data *file)
+{
+ struct smb2srv_find_state *state = talloc_get_type(private_data, struct smb2srv_find_state);
+ struct smb2_find *info = state->info;
+ uint32_t old_length;
+ NTSTATUS status;
+
+ old_length = info->out.blob.length;
+
+ status = smbsrv_push_passthru_search(state, &info->out.blob, info->data_level, file, STR_UNICODE);
+ if (!NT_STATUS_IS_OK(status) ||
+ info->out.blob.length > info->in.max_response_size) {
+ /* restore the old length and tell the backend to stop */
+ smbsrv_blob_grow_data(state, &info->out.blob, old_length);
+ return false;
+ }
+
+ state->last_entry_offset = old_length;
+
+ return true;
+}
+
+static void smb2srv_find_send(struct ntvfs_request *ntvfs)
+{
+ struct smb2srv_request *req;
+ struct smb2srv_find_state *state;
+
+ SMB2SRV_CHECK_ASYNC_STATUS(state, struct smb2srv_find_state);
+ SMB2SRV_CHECK(smb2srv_setup_reply(req, 0x08, true, state->info->out.blob.length));
+
+ if (state->info->out.blob.length > 0) {
+ SIVAL(state->info->out.blob.data + state->last_entry_offset, 0, 0);
+ }
+
+ SMB2SRV_CHECK(smb2_push_o16s32_blob(&req->out, 0x02, state->info->out.blob));
+
+ smb2srv_send_reply(req);
+}
+
+static NTSTATUS smb2srv_find_backend(struct smb2srv_find_state *state)
+{
+ struct smb2_find *info = state->info;
+
+ switch (info->in.level) {
+ case SMB2_FIND_DIRECTORY_INFO:
+ info->data_level = RAW_SEARCH_DATA_DIRECTORY_INFO;
+ break;
+
+ case SMB2_FIND_FULL_DIRECTORY_INFO:
+ info->data_level = RAW_SEARCH_DATA_FULL_DIRECTORY_INFO;
+ break;
+
+ case SMB2_FIND_BOTH_DIRECTORY_INFO:
+ info->data_level = RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO;
+ break;
+
+ case SMB2_FIND_NAME_INFO:
+ info->data_level = RAW_SEARCH_DATA_NAME_INFO;
+ break;
+
+ case SMB2_FIND_ID_BOTH_DIRECTORY_INFO:
+ info->data_level = RAW_SEARCH_DATA_ID_BOTH_DIRECTORY_INFO;
+ break;
+
+ case SMB2_FIND_ID_FULL_DIRECTORY_INFO:
+ info->data_level = RAW_SEARCH_DATA_ID_FULL_DIRECTORY_INFO;
+ break;
+
+ default:
+ return NT_STATUS_FOOBAR;
+ }
+
+ if (info->in.continue_flags & SMB2_CONTINUE_FLAG_REOPEN) {
+ state->ff = talloc(state, union smb_search_first);
+ NT_STATUS_HAVE_NO_MEMORY(state->ff);
+
+ state->ff->smb2 = *info;
+ state->info = &state->ff->smb2;
+ ZERO_STRUCT(state->ff->smb2.out);
+
+ return ntvfs_search_first(state->req->ntvfs, state->ff, state, smb2srv_find_callback);
+ } else {
+ state->fn = talloc(state, union smb_search_next);
+ NT_STATUS_HAVE_NO_MEMORY(state->fn);
+
+ state->fn->smb2 = *info;
+ state->info = &state->fn->smb2;
+ ZERO_STRUCT(state->fn->smb2.out);
+
+ return ntvfs_search_next(state->req->ntvfs, state->fn, state, smb2srv_find_callback);
+ }
+}
+
+void smb2srv_find_recv(struct smb2srv_request *req)
+{
+ struct smb2srv_find_state *state;
+ struct smb2_find *info;
+
+ SMB2SRV_CHECK_BODY_SIZE(req, 0x20, true);
+ SMB2SRV_TALLOC_IO_PTR(info, struct smb2_find);
+ /* this overwrites req->io_ptr !*/
+ SMB2SRV_TALLOC_IO_PTR(state, struct smb2srv_find_state);
+ state->req = req;
+ state->info = info;
+ state->ff = NULL;
+ state->fn = NULL;
+ state->last_entry_offset= 0;
+ SMB2SRV_SETUP_NTVFS_REQUEST(smb2srv_find_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ info->level = RAW_SEARCH_SMB2;
+ info->data_level = RAW_SEARCH_DATA_GENERIC;/* will be overwritten later */
+ info->in.level = CVAL(req->in.body, 0x02);
+ info->in.continue_flags = CVAL(req->in.body, 0x03);
+ info->in.file_index = IVAL(req->in.body, 0x04);
+ info->in.file.ntvfs = smb2srv_pull_handle(req, req->in.body, 0x08);
+ SMB2SRV_CHECK(smb2_pull_o16s16_string(&req->in, info, req->in.body+0x18, &info->in.pattern));
+ info->in.max_response_size = IVAL(req->in.body, 0x1C);
+
+ /* the VFS backend does not yet handle NULL patterns */
+ if (info->in.pattern == NULL) {
+ info->in.pattern = "";
+ }
+
+ SMB2SRV_CHECK_FILE_HANDLE(info->in.file.ntvfs);
+ SMB2SRV_CALL_NTVFS_BACKEND(smb2srv_find_backend(state));
+}
diff --git a/source4/smb_server/smb2/keepalive.c b/source4/smb_server/smb2/keepalive.c
new file mode 100644
index 0000000..cd53778
--- /dev/null
+++ b/source4/smb_server/smb2/keepalive.c
@@ -0,0 +1,71 @@
+/*
+ Unix SMB2 implementation.
+
+ Copyright (C) Stefan Metzmacher 2005
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/smb2/smb2.h"
+#include "smb_server/smb_server.h"
+#include "smb_server/smb2/smb2_server.h"
+
+static NTSTATUS smb2srv_keepalive_backend(struct smb2srv_request *req)
+{
+ /* TODO: maybe update some flags on the connection struct */
+ return NT_STATUS_OK;
+}
+
+static void smb2srv_keepalive_send(struct smb2srv_request *req)
+{
+ NTSTATUS status;
+
+ if (NT_STATUS_IS_ERR(req->status)) {
+ smb2srv_send_error(req, req->status);
+ return;
+ }
+
+ status = smb2srv_setup_reply(req, 0x04, false, 0);
+ if (!NT_STATUS_IS_OK(status)) {
+ smbsrv_terminate_connection(req->smb_conn, nt_errstr(status));
+ talloc_free(req);
+ return;
+ }
+
+ SSVAL(req->out.body, 0x02, 0);
+
+ smb2srv_send_reply(req);
+}
+
+void smb2srv_keepalive_recv(struct smb2srv_request *req)
+{
+ if (req->in.body_size != 0x04) {
+ smb2srv_send_error(req, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+
+ if (SVAL(req->in.body, 0x00) != 0x04) {
+ smb2srv_send_error(req, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+
+ req->status = smb2srv_keepalive_backend(req);
+
+ if (req->control_flags & SMB2SRV_REQ_CTRL_FLAG_NOT_REPLY) {
+ talloc_free(req);
+ return;
+ }
+ smb2srv_keepalive_send(req);
+}
diff --git a/source4/smb_server/smb2/negprot.c b/source4/smb_server/smb2/negprot.c
new file mode 100644
index 0000000..048d7b4
--- /dev/null
+++ b/source4/smb_server/smb2/negprot.c
@@ -0,0 +1,327 @@
+/*
+ Unix SMB2 implementation.
+
+ Copyright (C) Andrew Bartlett 2001-2005
+ Copyright (C) Stefan Metzmacher 2005
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "auth/credentials/credentials.h"
+#include "auth/auth.h"
+#include "auth/gensec/gensec.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+#include "smb_server/smb_server.h"
+#include "smb_server/smb2/smb2_server.h"
+#include "samba/service_stream.h"
+#include "param/param.h"
+
+static NTSTATUS smb2srv_negprot_secblob(struct smb2srv_request *req, DATA_BLOB *_blob)
+{
+ struct gensec_security *gensec_security;
+ DATA_BLOB null_data_blob = data_blob(NULL, 0);
+ DATA_BLOB blob;
+ NTSTATUS nt_status;
+ struct cli_credentials *server_credentials;
+
+ server_credentials =
+ cli_credentials_init_server(req, req->smb_conn->lp_ctx);
+ if (server_credentials == NULL) {
+ DBG_DEBUG("Failed to obtain server credentials, "
+ "perhaps a standalone server?\n");
+ /*
+ * Create anon server credentials for for the
+ * spoolss.notify test.
+ */
+ server_credentials = cli_credentials_init_anon(req);
+ if (server_credentials == NULL) {
+ smbsrv_terminate_connection(req->smb_conn,
+ "Failed to init server credentials\n");
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ req->smb_conn->negotiate.server_credentials = talloc_steal(req->smb_conn, server_credentials);
+
+ nt_status = samba_server_gensec_start(req,
+ req->smb_conn->connection->event.ctx,
+ req->smb_conn->connection->msg_ctx,
+ req->smb_conn->lp_ctx,
+ server_credentials,
+ "cifs",
+ &gensec_security);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(0, ("Failed to start GENSEC: %s\n", nt_errstr(nt_status)));
+ smbsrv_terminate_connection(req->smb_conn, "Failed to start GENSEC\n");
+ return nt_status;
+ }
+
+ gensec_set_target_service(gensec_security, "cifs");
+
+ gensec_set_credentials(gensec_security, server_credentials);
+
+ nt_status = gensec_start_mech_by_oid(gensec_security, GENSEC_OID_SPNEGO);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(0, ("Failed to start SPNEGO: %s\n", nt_errstr(nt_status)));
+ smbsrv_terminate_connection(req->smb_conn, "Failed to start SPNEGO\n");
+ return nt_status;
+ }
+
+ nt_status = gensec_update(gensec_security, req,
+ null_data_blob, &blob);
+ if (!NT_STATUS_IS_OK(nt_status) && !NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ DEBUG(0, ("Failed to get SPNEGO to give us the first token: %s\n", nt_errstr(nt_status)));
+ smbsrv_terminate_connection(req->smb_conn, "Failed to start SPNEGO - no first token\n");
+ return nt_status;
+ }
+
+ *_blob = blob;
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS smb2srv_negprot_backend(struct smb2srv_request *req, struct smb2_negprot *io)
+{
+ NTSTATUS status;
+ struct timeval current_time;
+ struct timeval boot_time;
+ uint16_t i;
+ uint16_t dialect = 0;
+ enum smb_signing_setting signing_setting;
+ struct loadparm_context *lp_ctx = req->smb_conn->lp_ctx;
+
+ /* we only do one dialect for now */
+ if (io->in.dialect_count < 1) {
+ return NT_STATUS_NOT_SUPPORTED;
+ }
+ for (i=0; i < io->in.dialect_count; i++) {
+ dialect = io->in.dialects[i];
+ if (dialect == SMB2_DIALECT_REVISION_202) {
+ break;
+ }
+ }
+ if (dialect != SMB2_DIALECT_REVISION_202) {
+ DEBUG(0,("Got unexpected SMB2 dialect %u\n", dialect));
+ return NT_STATUS_NOT_SUPPORTED;
+ }
+
+ req->smb_conn->negotiate.protocol = PROTOCOL_SMB2_02;
+
+ current_time = timeval_current(); /* TODO: handle timezone?! */
+ boot_time = timeval_current(); /* TODO: fix me */
+
+ ZERO_STRUCT(io->out);
+
+ signing_setting = lpcfg_server_signing(lp_ctx);
+ if (signing_setting == SMB_SIGNING_DEFAULT) {
+ /*
+ * If we are a domain controller, SMB signing is
+ * really important, as it can prevent a number of
+ * attacks on communications between us and the
+ * clients
+ *
+ * However, it really sucks (no sendfile, CPU
+ * overhead) performance-wise when used on a
+ * file server, so disable it by default
+ * on non-DCs
+ */
+
+ if (lpcfg_server_role(lp_ctx) >= ROLE_ACTIVE_DIRECTORY_DC) {
+ signing_setting = SMB_SIGNING_REQUIRED;
+ } else {
+ signing_setting = SMB_SIGNING_OFF;
+ }
+ }
+
+ switch (signing_setting) {
+ case SMB_SIGNING_DEFAULT:
+ case SMB_SIGNING_IPC_DEFAULT:
+ smb_panic(__location__);
+ break;
+ case SMB_SIGNING_OFF:
+ io->out.security_mode = 0;
+ break;
+ case SMB_SIGNING_DESIRED:
+ case SMB_SIGNING_IF_REQUIRED:
+ io->out.security_mode = SMB2_NEGOTIATE_SIGNING_ENABLED;
+ break;
+ case SMB_SIGNING_REQUIRED:
+ io->out.security_mode = SMB2_NEGOTIATE_SIGNING_ENABLED | SMB2_NEGOTIATE_SIGNING_REQUIRED;
+ /* force signing on immediately */
+ req->smb_conn->smb2_signing_required = true;
+ break;
+ }
+ io->out.dialect_revision = dialect;
+ io->out.capabilities = 0;
+ io->out.max_transact_size = lpcfg_parm_ulong(req->smb_conn->lp_ctx, NULL,
+ "smb2", "max transaction size", 0x10000);
+ io->out.max_read_size = lpcfg_parm_ulong(req->smb_conn->lp_ctx, NULL,
+ "smb2", "max read size", 0x10000);
+ io->out.max_write_size = lpcfg_parm_ulong(req->smb_conn->lp_ctx, NULL,
+ "smb2", "max write size", 0x10000);
+ io->out.system_time = timeval_to_nttime(&current_time);
+ io->out.server_start_time = timeval_to_nttime(&boot_time);
+ io->out.reserved2 = 0;
+ status = smb2srv_negprot_secblob(req, &io->out.secblob);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ return NT_STATUS_OK;
+}
+
+static void smb2srv_negprot_send(struct smb2srv_request *req, struct smb2_negprot *io)
+{
+ NTSTATUS status;
+
+ if (NT_STATUS_IS_ERR(req->status)) {
+ smb2srv_send_error(req, req->status); /* TODO: is this correct? */
+ return;
+ }
+
+ status = smb2srv_setup_reply(req, 0x40, true, io->out.secblob.length);
+ if (!NT_STATUS_IS_OK(status)) {
+ smbsrv_terminate_connection(req->smb_conn, nt_errstr(status));
+ talloc_free(req);
+ return;
+ }
+
+ SSVAL(req->out.body, 0x02, io->out.security_mode);
+ SIVAL(req->out.body, 0x04, io->out.dialect_revision);
+ SIVAL(req->out.body, 0x06, io->out.reserved);
+ status = smbcli_push_guid(req->out.body, 0x08, &io->out.server_guid);
+ if (!NT_STATUS_IS_OK(status)) {
+ smbsrv_terminate_connection(req->smb_conn, nt_errstr(status));
+ talloc_free(req);
+ return;
+ }
+ SIVAL(req->out.body, 0x18, io->out.capabilities);
+ SIVAL(req->out.body, 0x1C, io->out.max_transact_size);
+ SIVAL(req->out.body, 0x20, io->out.max_read_size);
+ SIVAL(req->out.body, 0x24, io->out.max_write_size);
+ push_nttime(req->out.body, 0x28, io->out.system_time);
+ push_nttime(req->out.body, 0x30, io->out.server_start_time);
+ SIVAL(req->out.body, 0x3C, io->out.reserved2);
+ status = smb2_push_o16s16_blob(&req->out, 0x38, io->out.secblob);
+ if (!NT_STATUS_IS_OK(status)) {
+ smbsrv_terminate_connection(req->smb_conn, nt_errstr(status));
+ talloc_free(req);
+ return;
+ }
+
+ smb2srv_send_reply(req);
+}
+
+void smb2srv_negprot_recv(struct smb2srv_request *req)
+{
+ struct smb2_negprot *io;
+ int i;
+
+ if (req->in.body_size < 0x26) {
+ smbsrv_terminate_connection(req->smb_conn, "Bad body size in SMB2 negprot");
+ return;
+ }
+
+ io = talloc(req, struct smb2_negprot);
+ if (!io) {
+ smbsrv_terminate_connection(req->smb_conn, nt_errstr(NT_STATUS_NO_MEMORY));
+ talloc_free(req);
+ return;
+ }
+
+ io->in.dialect_count = SVAL(req->in.body, 0x02);
+ io->in.security_mode = SVAL(req->in.body, 0x04);
+ io->in.reserved = SVAL(req->in.body, 0x06);
+ io->in.capabilities = IVAL(req->in.body, 0x08);
+ req->status = smbcli_pull_guid(req->in.body, 0xC, &io->in.client_guid);
+ if (!NT_STATUS_IS_OK(req->status)) {
+ smbsrv_terminate_connection(req->smb_conn, "Bad GUID in SMB2 negprot");
+ talloc_free(req);
+ return;
+ }
+ io->in.start_time = smbcli_pull_nttime(req->in.body, 0x1C);
+
+ io->in.dialects = talloc_array(req, uint16_t, io->in.dialect_count);
+ if (io->in.dialects == NULL) {
+ smbsrv_terminate_connection(req->smb_conn, nt_errstr(NT_STATUS_NO_MEMORY));
+ talloc_free(req);
+ return;
+ }
+ for (i=0;i<io->in.dialect_count;i++) {
+ io->in.dialects[i] = SVAL(req->in.body, 0x24+i*2);
+ }
+
+ req->status = smb2srv_negprot_backend(req, io);
+
+ if (req->control_flags & SMB2SRV_REQ_CTRL_FLAG_NOT_REPLY) {
+ talloc_free(req);
+ return;
+ }
+ smb2srv_negprot_send(req, io);
+}
+
+/*
+ * reply to a SMB negprot request with dialect "SMB 2.002"
+ */
+void smb2srv_reply_smb_negprot(struct smbsrv_request *smb_req)
+{
+ struct smb2srv_request *req;
+ uint32_t body_fixed_size = 0x26;
+
+ req = talloc_zero(smb_req->smb_conn, struct smb2srv_request);
+ if (!req) goto nomem;
+ req->smb_conn = smb_req->smb_conn;
+ req->request_time = smb_req->request_time;
+ talloc_steal(req, smb_req);
+
+ req->in.size = NBT_HDR_SIZE+SMB2_HDR_BODY+body_fixed_size;
+ req->in.allocated = req->in.size;
+ req->in.buffer = talloc_array(req, uint8_t, req->in.allocated);
+ if (!req->in.buffer) goto nomem;
+ req->in.hdr = req->in.buffer + NBT_HDR_SIZE;
+ req->in.body = req->in.hdr + SMB2_HDR_BODY;
+ req->in.body_size = body_fixed_size;
+ req->in.dynamic = NULL;
+
+ smb2srv_setup_bufinfo(req);
+
+ SIVAL(req->in.hdr, 0, SMB2_MAGIC);
+ SSVAL(req->in.hdr, SMB2_HDR_LENGTH, SMB2_HDR_BODY);
+ SSVAL(req->in.hdr, SMB2_HDR_EPOCH, 0);
+ SIVAL(req->in.hdr, SMB2_HDR_STATUS, 0);
+ SSVAL(req->in.hdr, SMB2_HDR_OPCODE, SMB2_OP_NEGPROT);
+ SSVAL(req->in.hdr, SMB2_HDR_CREDIT, 0);
+ SIVAL(req->in.hdr, SMB2_HDR_FLAGS, 0);
+ SIVAL(req->in.hdr, SMB2_HDR_NEXT_COMMAND, 0);
+ SBVAL(req->in.hdr, SMB2_HDR_MESSAGE_ID, 0);
+ SIVAL(req->in.hdr, SMB2_HDR_PID, 0);
+ SIVAL(req->in.hdr, SMB2_HDR_TID, 0);
+ SBVAL(req->in.hdr, SMB2_HDR_SESSION_ID, 0);
+ memset(req->in.hdr+SMB2_HDR_SIGNATURE, 0, 16);
+
+ /* this seems to be a bug, they use 0x24 but the length is 0x26 */
+ SSVAL(req->in.body, 0x00, 0x24);
+
+ SSVAL(req->in.body, 0x02, 1);
+ memset(req->in.body+0x04, 0, 32);
+ SSVAL(req->in.body, 0x24, SMB2_DIALECT_REVISION_202);
+
+ smb2srv_negprot_recv(req);
+ return;
+nomem:
+ smbsrv_terminate_connection(smb_req->smb_conn, nt_errstr(NT_STATUS_NO_MEMORY));
+ talloc_free(req);
+ return;
+}
diff --git a/source4/smb_server/smb2/receive.c b/source4/smb_server/smb2/receive.c
new file mode 100644
index 0000000..c4109bf
--- /dev/null
+++ b/source4/smb_server/smb2/receive.c
@@ -0,0 +1,710 @@
+/*
+ Unix SMB2 implementation.
+
+ Copyright (C) Andrew Tridgell 2005
+ Copyright (C) Stefan Metzmacher 2005
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "system/time.h"
+#include "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+#include "smb_server/smb_server.h"
+#include "smb_server/smb2/smb2_server.h"
+#include "samba/service_stream.h"
+#include "lib/stream/packet.h"
+#include "ntvfs/ntvfs.h"
+#include "param/param.h"
+#include "auth/auth.h"
+
+
+/* fill in the bufinfo */
+void smb2srv_setup_bufinfo(struct smb2srv_request *req)
+{
+ req->in.bufinfo.mem_ctx = req;
+ req->in.bufinfo.flags = BUFINFO_FLAG_UNICODE | BUFINFO_FLAG_SMB2;
+ req->in.bufinfo.align_base = req->in.buffer;
+ if (req->in.dynamic) {
+ req->in.bufinfo.data = req->in.dynamic;
+ req->in.bufinfo.data_size = req->in.body_size - req->in.body_fixed;
+ } else {
+ req->in.bufinfo.data = NULL;
+ req->in.bufinfo.data_size = 0;
+ }
+}
+
+static int smb2srv_request_destructor(struct smb2srv_request *req)
+{
+ DLIST_REMOVE(req->smb_conn->requests2.list, req);
+ if (req->pending_id) {
+ idr_remove(req->smb_conn->requests2.idtree_req, req->pending_id);
+ }
+ return 0;
+}
+
+static int smb2srv_request_deny_destructor(struct smb2srv_request *req)
+{
+ return -1;
+}
+
+struct smb2srv_request *smb2srv_init_request(struct smbsrv_connection *smb_conn)
+{
+ struct smb2srv_request *req;
+
+ req = talloc_zero(smb_conn, struct smb2srv_request);
+ if (!req) return NULL;
+
+ req->smb_conn = smb_conn;
+
+ req->chained_session_id = UINT64_MAX;
+ req->chained_tree_id = UINT32_MAX;
+
+ talloc_set_destructor(req, smb2srv_request_destructor);
+
+ return req;
+}
+
+NTSTATUS smb2srv_setup_reply(struct smb2srv_request *req, uint16_t body_fixed_size,
+ bool body_dynamic_present, uint32_t body_dynamic_size)
+{
+ uint32_t flags = IVAL(req->in.hdr, SMB2_HDR_FLAGS);
+ uint32_t pid = IVAL(req->in.hdr, SMB2_HDR_PID);
+ uint32_t tid = IVAL(req->in.hdr, SMB2_HDR_TID);
+ uint16_t credits = SVAL(req->in.hdr, SMB2_HDR_CREDIT);
+
+ if (credits == 0) {
+ credits = 1;
+ }
+
+ flags |= SMB2_HDR_FLAG_REDIRECT;
+
+ if (req->pending_id) {
+ flags |= SMB2_HDR_FLAG_ASYNC;
+ pid = req->pending_id;
+ tid = 0;
+ credits = 0;
+ }
+
+ if (body_dynamic_present) {
+ if (body_dynamic_size == 0) {
+ body_dynamic_size = 1;
+ }
+ } else {
+ body_dynamic_size = 0;
+ }
+
+ req->out.size = SMB2_HDR_BODY+NBT_HDR_SIZE+body_fixed_size;
+
+ req->out.allocated = req->out.size + body_dynamic_size;
+ req->out.buffer = talloc_array(req, uint8_t,
+ req->out.allocated);
+ NT_STATUS_HAVE_NO_MEMORY(req->out.buffer);
+
+ req->out.hdr = req->out.buffer + NBT_HDR_SIZE;
+ req->out.body = req->out.hdr + SMB2_HDR_BODY;
+ req->out.body_fixed = body_fixed_size;
+ req->out.body_size = body_fixed_size;
+ req->out.dynamic = (body_dynamic_size ? req->out.body + body_fixed_size : NULL);
+
+ SIVAL(req->out.hdr, 0, SMB2_MAGIC);
+ SSVAL(req->out.hdr, SMB2_HDR_LENGTH, SMB2_HDR_BODY);
+ SSVAL(req->out.hdr, SMB2_HDR_CREDIT_CHARGE,
+ SVAL(req->in.hdr, SMB2_HDR_CREDIT_CHARGE));
+ SIVAL(req->out.hdr, SMB2_HDR_STATUS, NT_STATUS_V(req->status));
+ SSVAL(req->out.hdr, SMB2_HDR_OPCODE, SVAL(req->in.hdr, SMB2_HDR_OPCODE));
+ SSVAL(req->out.hdr, SMB2_HDR_CREDIT, credits);
+ SIVAL(req->out.hdr, SMB2_HDR_FLAGS, flags);
+ SIVAL(req->out.hdr, SMB2_HDR_NEXT_COMMAND, 0);
+ SBVAL(req->out.hdr, SMB2_HDR_MESSAGE_ID, req->seqnum);
+ SIVAL(req->out.hdr, SMB2_HDR_PID, pid);
+ SIVAL(req->out.hdr, SMB2_HDR_TID, tid);
+ SBVAL(req->out.hdr, SMB2_HDR_SESSION_ID, BVAL(req->in.hdr, SMB2_HDR_SESSION_ID));
+ memcpy(req->out.hdr+SMB2_HDR_SIGNATURE,
+ req->in.hdr+SMB2_HDR_SIGNATURE, 16);
+
+ /* set the length of the fixed body part and +1 if there's a dynamic part also */
+ SSVAL(req->out.body, 0, body_fixed_size + (body_dynamic_size?1:0));
+
+ /*
+ * if we have a dynamic part, make sure the first byte
+ * which is always be part of the packet is initialized
+ */
+ if (body_dynamic_size) {
+ req->out.size += 1;
+ SCVAL(req->out.dynamic, 0, 0);
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS smb2srv_reply(struct smb2srv_request *req);
+
+static void smb2srv_chain_reply(struct smb2srv_request *p_req)
+{
+ NTSTATUS status;
+ struct smbsrv_connection *smb_conn = p_req->smb_conn;
+ struct smb2srv_request *req;
+ uint32_t chain_offset;
+ uint32_t protocol_version;
+ uint16_t buffer_code;
+ uint32_t dynamic_size;
+ uint32_t flags;
+ uint32_t last_hdr_offset;
+
+ last_hdr_offset = p_req->in.hdr - p_req->in.buffer;
+
+ chain_offset = p_req->chain_offset;
+ p_req->chain_offset = 0;
+
+ if (p_req->in.size < (last_hdr_offset + chain_offset + SMB2_MIN_SIZE_NO_BODY)) {
+ DEBUG(2,("Invalid SMB2 chained packet at offset 0x%X from last hdr 0x%X\n",
+ chain_offset, last_hdr_offset));
+ smbsrv_terminate_connection(smb_conn, "Invalid SMB2 chained packet");
+ return;
+ }
+
+ protocol_version = IVAL(p_req->in.buffer, last_hdr_offset + chain_offset);
+ if (protocol_version != SMB2_MAGIC) {
+ DEBUG(2,("Invalid SMB chained packet: protocol prefix: 0x%08X\n",
+ protocol_version));
+ smbsrv_terminate_connection(smb_conn, "NON-SMB2 chained packet");
+ return;
+ }
+
+ req = smb2srv_init_request(smb_conn);
+ if (!req) {
+ smbsrv_terminate_connection(smb_conn, "SMB2 chained packet - no memory");
+ return;
+ }
+
+ talloc_steal(req, p_req);
+
+ req->in.buffer = talloc_steal(req, p_req->in.buffer);
+ req->in.size = p_req->in.size;
+ req->request_time = p_req->request_time;
+ req->in.allocated = req->in.size;
+
+ req->in.hdr = req->in.buffer+ last_hdr_offset + chain_offset;
+ req->in.body = req->in.hdr + SMB2_HDR_BODY;
+ req->in.body_size = req->in.size - (last_hdr_offset+ chain_offset + SMB2_HDR_BODY);
+ req->in.dynamic = NULL;
+
+ req->seqnum = BVAL(req->in.hdr, SMB2_HDR_MESSAGE_ID);
+
+ if (req->in.body_size < 2) {
+ /* error handling for this is different for negprot to
+ other packet types */
+ uint16_t opcode = SVAL(req->in.hdr, SMB2_HDR_OPCODE);
+ if (opcode == SMB2_OP_NEGPROT) {
+ smbsrv_terminate_connection(smb_conn, "Bad body size in SMB2 negprot");
+ return;
+ } else {
+ smb2srv_send_error(req, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+ }
+
+ buffer_code = SVAL(req->in.body, 0);
+ req->in.body_fixed = (buffer_code & ~1);
+ dynamic_size = req->in.body_size - req->in.body_fixed;
+
+ if (dynamic_size != 0 && (buffer_code & 1)) {
+ req->in.dynamic = req->in.body + req->in.body_fixed;
+ if (smb2_oob(&req->in, req->in.dynamic, dynamic_size)) {
+ DEBUG(1,("SMB2 chained request invalid dynamic size 0x%x\n",
+ dynamic_size));
+ smb2srv_send_error(req, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+ }
+
+ smb2srv_setup_bufinfo(req);
+
+ flags = IVAL(req->in.hdr, SMB2_HDR_FLAGS);
+ if (flags & SMB2_HDR_FLAG_CHAINED) {
+ if (p_req->chained_file_handle) {
+ memcpy(req->_chained_file_handle,
+ p_req->_chained_file_handle,
+ sizeof(req->_chained_file_handle));
+ req->chained_file_handle = req->_chained_file_handle;
+ }
+ req->chained_session_id = p_req->chained_session_id;
+ req->chained_tree_id = p_req->chained_tree_id;
+ req->chain_status = p_req->chain_status;
+ }
+
+ /*
+ * TODO: - make sure the length field is 64
+ * - make sure it's a request
+ */
+
+ status = smb2srv_reply(req);
+ if (!NT_STATUS_IS_OK(status)) {
+ smbsrv_terminate_connection(smb_conn, nt_errstr(status));
+ return;
+ }
+}
+
+void smb2srv_send_reply(struct smb2srv_request *req)
+{
+ DATA_BLOB blob;
+ NTSTATUS status;
+
+ if (req->smb_conn->connection->event.fde == NULL) {
+ /* the socket has been destroyed - no point trying to send a reply! */
+ talloc_free(req);
+ return;
+ }
+
+ if (req->out.size > NBT_HDR_SIZE) {
+ _smb_setlen_tcp(req->out.buffer, req->out.size - NBT_HDR_SIZE);
+ }
+
+ /* if signing is active on the session then sign the packet */
+ if (req->is_signed) {
+ status = smb2_sign_message(&req->out,
+ req->session->session_info->session_key);
+ if (!NT_STATUS_IS_OK(status)) {
+ smbsrv_terminate_connection(req->smb_conn, nt_errstr(status));
+ return;
+ }
+ }
+
+
+ blob = data_blob_const(req->out.buffer, req->out.size);
+ status = packet_send(req->smb_conn->packet, blob);
+ if (!NT_STATUS_IS_OK(status)) {
+ smbsrv_terminate_connection(req->smb_conn, nt_errstr(status));
+ return;
+ }
+ if (req->chain_offset) {
+ smb2srv_chain_reply(req);
+ return;
+ }
+ talloc_free(req);
+}
+
+void smb2srv_send_error(struct smb2srv_request *req, NTSTATUS error)
+{
+ NTSTATUS status;
+
+ if (req->smb_conn->connection->event.fde == NULL) {
+ /* the socket has been destroyed - no point trying to send an error! */
+ talloc_free(req);
+ return;
+ }
+
+ status = smb2srv_setup_reply(req, 8, true, 0);
+ if (!NT_STATUS_IS_OK(status)) {
+ smbsrv_terminate_connection(req->smb_conn, nt_errstr(status));
+ talloc_free(req);
+ return;
+ }
+
+ SIVAL(req->out.hdr, SMB2_HDR_STATUS, NT_STATUS_V(error));
+
+ SSVAL(req->out.body, 0x02, 0);
+ SIVAL(req->out.body, 0x04, 0);
+
+ req->chain_status = NT_STATUS_INVALID_PARAMETER;
+
+ smb2srv_send_reply(req);
+}
+
+static NTSTATUS smb2srv_reply(struct smb2srv_request *req)
+{
+ uint16_t opcode;
+ uint32_t tid;
+ uint64_t uid;
+ uint32_t flags;
+
+ if (SVAL(req->in.hdr, SMB2_HDR_LENGTH) != SMB2_HDR_BODY) {
+ smbsrv_terminate_connection(req->smb_conn, "Invalid SMB2 header length");
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ opcode = SVAL(req->in.hdr, SMB2_HDR_OPCODE);
+ req->chain_offset = IVAL(req->in.hdr, SMB2_HDR_NEXT_COMMAND);
+ req->seqnum = BVAL(req->in.hdr, SMB2_HDR_MESSAGE_ID);
+ tid = IVAL(req->in.hdr, SMB2_HDR_TID);
+ uid = BVAL(req->in.hdr, SMB2_HDR_SESSION_ID);
+ flags = IVAL(req->in.hdr, SMB2_HDR_FLAGS);
+
+ if (opcode != SMB2_OP_CANCEL &&
+ req->smb_conn->highest_smb2_seqnum != 0 &&
+ req->seqnum <= req->smb_conn->highest_smb2_seqnum) {
+ smbsrv_terminate_connection(req->smb_conn, "Invalid SMB2 sequence number");
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ if (opcode != SMB2_OP_CANCEL) {
+ req->smb_conn->highest_smb2_seqnum = req->seqnum;
+ }
+
+ if (flags & SMB2_HDR_FLAG_CHAINED) {
+ uid = req->chained_session_id;
+ tid = req->chained_tree_id;
+ }
+
+ req->session = smbsrv_session_find(req->smb_conn, uid, req->request_time);
+ req->tcon = smbsrv_smb2_tcon_find(req->session, tid, req->request_time);
+
+ req->chained_session_id = uid;
+ req->chained_tree_id = tid;
+
+ errno = 0;
+
+ /* supporting signing is mandatory in SMB2, and is per-packet. So we
+ should check the signature on any incoming packet that is signed, and
+ should give a signed reply to any signed request */
+ if (flags & SMB2_HDR_FLAG_SIGNED) {
+ NTSTATUS status;
+
+ if (!req->session) goto nosession;
+
+ req->is_signed = true;
+ status = smb2_check_signature(&req->in,
+ req->session->session_info->session_key);
+ if (!NT_STATUS_IS_OK(status)) {
+ smb2srv_send_error(req, status);
+ return NT_STATUS_OK;
+ }
+ } else if (req->session && req->session->smb2_signing.active) {
+ /* we require signing and this request was not signed */
+ smb2srv_send_error(req, NT_STATUS_ACCESS_DENIED);
+ return NT_STATUS_OK;
+ }
+
+ if (!NT_STATUS_IS_OK(req->chain_status)) {
+ smb2srv_send_error(req, req->chain_status);
+ return NT_STATUS_OK;
+ }
+
+ switch (opcode) {
+ case SMB2_OP_NEGPROT:
+ smb2srv_negprot_recv(req);
+ return NT_STATUS_OK;
+ case SMB2_OP_SESSSETUP:
+ smb2srv_sesssetup_recv(req);
+ return NT_STATUS_OK;
+ case SMB2_OP_LOGOFF:
+ if (!req->session) goto nosession;
+ smb2srv_logoff_recv(req);
+ return NT_STATUS_OK;
+ case SMB2_OP_TCON:
+ if (!req->session) goto nosession;
+ smb2srv_tcon_recv(req);
+ return NT_STATUS_OK;
+ case SMB2_OP_TDIS:
+ if (!req->session) goto nosession;
+ if (!req->tcon) goto notcon;
+ smb2srv_tdis_recv(req);
+ return NT_STATUS_OK;
+ case SMB2_OP_CREATE:
+ if (!req->session) goto nosession;
+ if (!req->tcon) goto notcon;
+ smb2srv_create_recv(req);
+ return NT_STATUS_OK;
+ case SMB2_OP_CLOSE:
+ if (!req->session) goto nosession;
+ if (!req->tcon) goto notcon;
+ smb2srv_close_recv(req);
+ return NT_STATUS_OK;
+ case SMB2_OP_FLUSH:
+ if (!req->session) goto nosession;
+ if (!req->tcon) goto notcon;
+ smb2srv_flush_recv(req);
+ return NT_STATUS_OK;
+ case SMB2_OP_READ:
+ if (!req->session) goto nosession;
+ if (!req->tcon) goto notcon;
+ smb2srv_read_recv(req);
+ return NT_STATUS_OK;
+ case SMB2_OP_WRITE:
+ if (!req->session) goto nosession;
+ if (!req->tcon) goto notcon;
+ smb2srv_write_recv(req);
+ return NT_STATUS_OK;
+ case SMB2_OP_LOCK:
+ if (!req->session) goto nosession;
+ if (!req->tcon) goto notcon;
+ smb2srv_lock_recv(req);
+ return NT_STATUS_OK;
+ case SMB2_OP_IOCTL:
+ if (!req->session) goto nosession;
+ if (!req->tcon) goto notcon;
+ smb2srv_ioctl_recv(req);
+ return NT_STATUS_OK;
+ case SMB2_OP_CANCEL:
+ smb2srv_cancel_recv(req);
+ return NT_STATUS_OK;
+ case SMB2_OP_KEEPALIVE:
+ smb2srv_keepalive_recv(req);
+ return NT_STATUS_OK;
+ case SMB2_OP_QUERY_DIRECTORY:
+ if (!req->session) goto nosession;
+ if (!req->tcon) goto notcon;
+ smb2srv_find_recv(req);
+ return NT_STATUS_OK;
+ case SMB2_OP_NOTIFY:
+ if (!req->session) goto nosession;
+ if (!req->tcon) goto notcon;
+ smb2srv_notify_recv(req);
+ return NT_STATUS_OK;
+ case SMB2_OP_GETINFO:
+ if (!req->session) goto nosession;
+ if (!req->tcon) goto notcon;
+ smb2srv_getinfo_recv(req);
+ return NT_STATUS_OK;
+ case SMB2_OP_SETINFO:
+ if (!req->session) goto nosession;
+ if (!req->tcon) goto notcon;
+ smb2srv_setinfo_recv(req);
+ return NT_STATUS_OK;
+ case SMB2_OP_BREAK:
+ if (!req->session) goto nosession;
+ if (!req->tcon) goto notcon;
+ smb2srv_break_recv(req);
+ return NT_STATUS_OK;
+ }
+
+ DEBUG(1,("Invalid SMB2 opcode: 0x%04X\n", opcode));
+ smbsrv_terminate_connection(req->smb_conn, "Invalid SMB2 opcode");
+ return NT_STATUS_OK;
+
+nosession:
+ smb2srv_send_error(req, NT_STATUS_USER_SESSION_DELETED);
+ return NT_STATUS_OK;
+notcon:
+ smb2srv_send_error(req, NT_STATUS_NETWORK_NAME_DELETED);
+ return NT_STATUS_OK;
+}
+
+NTSTATUS smbsrv_recv_smb2_request(void *private_data, DATA_BLOB blob)
+{
+ struct smbsrv_connection *smb_conn = talloc_get_type(private_data, struct smbsrv_connection);
+ struct smb2srv_request *req;
+ struct timeval cur_time = timeval_current();
+ uint32_t protocol_version;
+ uint16_t buffer_code;
+ uint32_t dynamic_size;
+ uint32_t flags;
+
+ smb_conn->statistics.last_request_time = cur_time;
+
+ /* see if its a special NBT packet */
+ if (CVAL(blob.data,0) != 0) {
+ DEBUG(2,("Special NBT packet on SMB2 connection"));
+ smbsrv_terminate_connection(smb_conn, "Special NBT packet on SMB2 connection");
+ return NT_STATUS_OK;
+ }
+
+ if (blob.length < (NBT_HDR_SIZE + SMB2_MIN_SIZE_NO_BODY)) {
+ DEBUG(2,("Invalid SMB2 packet length count %ld\n", (long)blob.length));
+ smbsrv_terminate_connection(smb_conn, "Invalid SMB2 packet");
+ return NT_STATUS_OK;
+ }
+
+ protocol_version = IVAL(blob.data, NBT_HDR_SIZE);
+ if (protocol_version != SMB2_MAGIC) {
+ DEBUG(2,("Invalid SMB packet: protocol prefix: 0x%08X\n",
+ protocol_version));
+ smbsrv_terminate_connection(smb_conn, "NON-SMB2 packet");
+ return NT_STATUS_OK;
+ }
+
+ req = smb2srv_init_request(smb_conn);
+ NT_STATUS_HAVE_NO_MEMORY(req);
+
+ req->in.buffer = talloc_steal(req, blob.data);
+ req->in.size = blob.length;
+ req->request_time = cur_time;
+ req->in.allocated = req->in.size;
+
+ req->in.hdr = req->in.buffer+ NBT_HDR_SIZE;
+ req->in.body = req->in.hdr + SMB2_HDR_BODY;
+ req->in.body_size = req->in.size - (SMB2_HDR_BODY+NBT_HDR_SIZE);
+ req->in.dynamic = NULL;
+
+ req->seqnum = BVAL(req->in.hdr, SMB2_HDR_MESSAGE_ID);
+
+ if (req->in.body_size < 2) {
+ /* error handling for this is different for negprot to
+ other packet types */
+ uint16_t opcode = SVAL(req->in.hdr, SMB2_HDR_OPCODE);
+ if (opcode == SMB2_OP_NEGPROT) {
+ smbsrv_terminate_connection(req->smb_conn, "Bad body size in SMB2 negprot");
+ return NT_STATUS_OK;
+ } else {
+ smb2srv_send_error(req, NT_STATUS_INVALID_PARAMETER);
+ return NT_STATUS_OK;
+ }
+ }
+
+ buffer_code = SVAL(req->in.body, 0);
+ req->in.body_fixed = (buffer_code & ~1);
+ dynamic_size = req->in.body_size - req->in.body_fixed;
+
+ if (dynamic_size != 0 && (buffer_code & 1)) {
+ req->in.dynamic = req->in.body + req->in.body_fixed;
+ if (smb2_oob(&req->in, req->in.dynamic, dynamic_size)) {
+ DEBUG(1,("SMB2 request invalid dynamic size 0x%x\n",
+ dynamic_size));
+ smb2srv_send_error(req, NT_STATUS_INVALID_PARAMETER);
+ return NT_STATUS_OK;
+ }
+ }
+
+ smb2srv_setup_bufinfo(req);
+
+ /*
+ * TODO: - make sure the length field is 64
+ * - make sure it's a request
+ */
+
+ flags = IVAL(req->in.hdr, SMB2_HDR_FLAGS);
+ /* the first request should never have the related flag set */
+ if (flags & SMB2_HDR_FLAG_CHAINED) {
+ req->chain_status = NT_STATUS_INVALID_PARAMETER;
+ }
+
+ return smb2srv_reply(req);
+}
+
+static NTSTATUS smb2srv_init_pending(struct smbsrv_connection *smb_conn)
+{
+ smb_conn->requests2.idtree_req = idr_init(smb_conn);
+ NT_STATUS_HAVE_NO_MEMORY(smb_conn->requests2.idtree_req);
+ smb_conn->requests2.idtree_limit = 0x00FFFFFF & (UINT32_MAX - 1);
+ smb_conn->requests2.list = NULL;
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS smb2srv_queue_pending(struct smb2srv_request *req)
+{
+ NTSTATUS status;
+ bool signing_used = false;
+ int id;
+ uint16_t credits = SVAL(req->in.hdr, SMB2_HDR_CREDIT);
+
+ if (credits == 0) {
+ credits = 1;
+ }
+
+ if (req->pending_id) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ if (req->smb_conn->connection->event.fde == NULL) {
+ /* the socket has been destroyed - no point trying to send an error! */
+ return NT_STATUS_REMOTE_DISCONNECT;
+ }
+
+ id = idr_get_new_above(req->smb_conn->requests2.idtree_req, req,
+ 1, req->smb_conn->requests2.idtree_limit);
+ if (id == -1) {
+ return NT_STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ DLIST_ADD_END(req->smb_conn->requests2.list, req);
+ req->pending_id = id;
+
+ talloc_set_destructor(req, smb2srv_request_deny_destructor);
+
+ status = smb2srv_setup_reply(req, 8, true, 0);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ SIVAL(req->out.hdr, SMB2_HDR_STATUS, NT_STATUS_V(NT_STATUS_PENDING));
+ SSVAL(req->out.hdr, SMB2_HDR_CREDIT, credits);
+
+ SSVAL(req->out.body, 0x02, 0);
+ SIVAL(req->out.body, 0x04, 0);
+
+ /* if the real reply will be signed set the signed flags, but don't sign */
+ if (req->is_signed) {
+ SIVAL(req->out.hdr, SMB2_HDR_FLAGS, IVAL(req->out.hdr, SMB2_HDR_FLAGS) | SMB2_HDR_FLAG_SIGNED);
+ signing_used = req->is_signed;
+ req->is_signed = false;
+ }
+
+ smb2srv_send_reply(req);
+
+ req->is_signed = signing_used;
+
+ talloc_set_destructor(req, smb2srv_request_destructor);
+ return NT_STATUS_OK;
+}
+
+void smb2srv_cancel_recv(struct smb2srv_request *req)
+{
+ uint32_t pending_id;
+ uint32_t flags;
+ void *p;
+ struct smb2srv_request *r;
+
+ if (!req->session) goto done;
+
+ flags = IVAL(req->in.hdr, SMB2_HDR_FLAGS);
+ pending_id = IVAL(req->in.hdr, SMB2_HDR_PID);
+
+ if (!(flags & SMB2_HDR_FLAG_ASYNC)) {
+ /* TODO: what to do here? */
+ goto done;
+ }
+
+ p = idr_find(req->smb_conn->requests2.idtree_req, pending_id);
+ if (!p) goto done;
+
+ r = talloc_get_type(p, struct smb2srv_request);
+ if (!r) goto done;
+
+ if (!r->ntvfs) goto done;
+
+ ntvfs_cancel(r->ntvfs);
+
+done:
+ /* we never generate a reply for a SMB2 Cancel */
+ talloc_free(req);
+}
+
+/*
+ * init the SMB2 protocol related stuff
+ */
+NTSTATUS smbsrv_init_smb2_connection(struct smbsrv_connection *smb_conn)
+{
+ NTSTATUS status;
+
+ /* now initialise a few default values associated with this smb socket */
+ smb_conn->negotiate.max_send = 0xFFFF;
+
+ /* this is the size that w2k uses, and it appears to be important for
+ good performance */
+ smb_conn->negotiate.max_recv = lpcfg_max_xmit(smb_conn->lp_ctx);
+
+ smb_conn->negotiate.zone_offset = get_time_zone(time(NULL));
+
+ smb_conn->config.nt_status_support = true;
+
+ status = smbsrv_init_sessions(smb_conn, UINT64_MAX);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ status = smb2srv_init_pending(smb_conn);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ return NT_STATUS_OK;
+
+}
diff --git a/source4/smb_server/smb2/sesssetup.c b/source4/smb_server/smb2/sesssetup.c
new file mode 100644
index 0000000..a8c4560
--- /dev/null
+++ b/source4/smb_server/smb2/sesssetup.c
@@ -0,0 +1,326 @@
+/*
+ Unix SMB2 implementation.
+
+ Copyright (C) Andrew Bartlett 2001-2005
+ Copyright (C) Stefan Metzmacher 2005
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include <tevent.h>
+#include "auth/gensec/gensec.h"
+#include "auth/auth.h"
+#include "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+#include "smb_server/smb_server.h"
+#include "smb_server/smb2/smb2_server.h"
+#include "samba/service_stream.h"
+#include "lib/stream/packet.h"
+
+static void smb2srv_sesssetup_send(struct smb2srv_request *req, union smb_sesssetup *io)
+{
+ if (NT_STATUS_IS_OK(req->status)) {
+ /* nothing */
+ } else if (NT_STATUS_EQUAL(req->status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ /* nothing */
+ } else {
+ smb2srv_send_error(req, req->status);
+ return;
+ }
+
+ SMB2SRV_CHECK(smb2srv_setup_reply(req, 0x08, true, io->smb2.out.secblob.length));
+
+ SBVAL(req->out.hdr, SMB2_HDR_SESSION_ID, io->smb2.out.uid);
+
+ SSVAL(req->out.body, 0x02, io->smb2.out.session_flags);
+ SMB2SRV_CHECK(smb2_push_o16s16_blob(&req->out, 0x04, io->smb2.out.secblob));
+
+ smb2srv_send_reply(req);
+}
+
+struct smb2srv_sesssetup_callback_ctx {
+ struct smb2srv_request *req;
+ union smb_sesssetup *io;
+ struct smbsrv_session *smb_sess;
+};
+
+static void smb2srv_sesssetup_callback(struct tevent_req *subreq)
+{
+ struct smb2srv_sesssetup_callback_ctx *ctx = tevent_req_callback_data(subreq,
+ struct smb2srv_sesssetup_callback_ctx);
+ struct smb2srv_request *req = ctx->req;
+ union smb_sesssetup *io = ctx->io;
+ struct smbsrv_session *smb_sess = ctx->smb_sess;
+ struct auth_session_info *session_info = NULL;
+ enum security_user_level user_level;
+ NTSTATUS status;
+
+ packet_recv_enable(req->smb_conn->packet);
+
+ status = gensec_update_recv(subreq, req, &io->smb2.out.secblob);
+ TALLOC_FREE(subreq);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ goto done;
+ } else if (!NT_STATUS_IS_OK(status)) {
+ goto failed;
+ }
+
+ status = gensec_session_info(smb_sess->gensec_ctx, smb_sess, &session_info);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto failed;
+ }
+
+ /* Ensure this is marked as a 'real' vuid, not one
+ * simply valid for the session setup leg */
+ status = smbsrv_session_sesssetup_finished(smb_sess, session_info);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto failed;
+ }
+ req->session = smb_sess;
+
+ user_level = security_session_user_level(smb_sess->session_info, NULL);
+ if (user_level >= SECURITY_USER) {
+ if (smb_sess->smb2_signing.required) {
+ /* activate smb2 signing on the session */
+ smb_sess->smb2_signing.active = true;
+ }
+ /* we need to sign the session setup response */
+ req->is_signed = true;
+ }
+
+done:
+ io->smb2.out.uid = smb_sess->vuid;
+failed:
+ req->status = nt_status_squash(status);
+ smb2srv_sesssetup_send(req, io);
+ if (!NT_STATUS_IS_OK(status) && !
+ NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ talloc_free(smb_sess);
+ }
+}
+
+static void smb2srv_sesssetup_backend(struct smb2srv_request *req, union smb_sesssetup *io)
+{
+ NTSTATUS status;
+ struct smb2srv_sesssetup_callback_ctx *callback_ctx;
+ struct smbsrv_session *smb_sess = NULL;
+ uint64_t vuid;
+ struct tevent_req *subreq;
+
+ io->smb2.out.session_flags = 0;
+ io->smb2.out.uid = 0;
+ io->smb2.out.secblob = data_blob(NULL, 0);
+
+ vuid = BVAL(req->in.hdr, SMB2_HDR_SESSION_ID);
+
+ /*
+ * only when we got '0' we should allocate a new session
+ */
+ if (vuid == 0) {
+ struct gensec_security *gensec_ctx;
+ struct tsocket_address *remote_address, *local_address;
+
+ status = samba_server_gensec_start(req,
+ req->smb_conn->connection->event.ctx,
+ req->smb_conn->connection->msg_ctx,
+ req->smb_conn->lp_ctx,
+ req->smb_conn->negotiate.server_credentials,
+ "cifs",
+ &gensec_ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Failed to start GENSEC server code: %s\n", nt_errstr(status)));
+ goto failed;
+ }
+
+ gensec_want_feature(gensec_ctx, GENSEC_FEATURE_SESSION_KEY);
+ gensec_want_feature(gensec_ctx, GENSEC_FEATURE_SMB_TRANSPORT);
+
+ remote_address = socket_get_remote_addr(req->smb_conn->connection->socket,
+ req);
+ if (!remote_address) {
+ status = NT_STATUS_INTERNAL_ERROR;
+ DBG_ERR("Failed to obtain remote address");
+ goto failed;
+ }
+
+ status = gensec_set_remote_address(gensec_ctx,
+ remote_address);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_ERR("Failed to set remote address");
+ goto failed;
+ }
+
+ local_address = socket_get_local_addr(req->smb_conn->connection->socket,
+ req);
+ if (!local_address) {
+ status = NT_STATUS_INTERNAL_ERROR;
+ DBG_ERR("Failed to obtain local address");
+ goto failed;
+ }
+
+ status = gensec_set_local_address(gensec_ctx,
+ local_address);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_ERR("Failed to set local address");
+ goto failed;
+ }
+
+ status = gensec_set_target_service_description(gensec_ctx,
+ "SMB2");
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_ERR("Failed to set service description");
+ goto failed;
+ }
+
+ status = gensec_start_mech_by_oid(gensec_ctx, GENSEC_OID_SPNEGO);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Failed to start GENSEC SPNEGO server code: %s\n", nt_errstr(status)));
+ goto failed;
+ }
+
+ /* allocate a new session */
+ smb_sess = smbsrv_session_new(req->smb_conn, req->smb_conn, gensec_ctx);
+ if (!smb_sess) {
+ status = NT_STATUS_INSUFFICIENT_RESOURCES;
+ goto failed;
+ }
+ status = smbsrv_smb2_init_tcons(smb_sess);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto failed;
+ }
+ } else {
+ /* lookup an existing session */
+ smb_sess = smbsrv_session_find_sesssetup(req->smb_conn, vuid);
+ }
+
+ if (!smb_sess) {
+ status = NT_STATUS_USER_SESSION_DELETED;
+ goto failed;
+ }
+
+ if (smb_sess->session_info) {
+ /* see WSPP test suite - test 11 */
+ status = NT_STATUS_REQUEST_NOT_ACCEPTED;
+ goto failed;
+ }
+
+ if (!smb_sess->gensec_ctx) {
+ status = NT_STATUS_INTERNAL_ERROR;
+ DEBUG(1, ("Internal ERROR: no gensec_ctx on session: %s\n", nt_errstr(status)));
+ goto failed;
+ }
+
+ callback_ctx = talloc(req, struct smb2srv_sesssetup_callback_ctx);
+ if (!callback_ctx) goto nomem;
+ callback_ctx->req = req;
+ callback_ctx->io = io;
+ callback_ctx->smb_sess = smb_sess;
+
+ subreq = gensec_update_send(callback_ctx,
+ req->smb_conn->connection->event.ctx,
+ smb_sess->gensec_ctx,
+ io->smb2.in.secblob);
+ if (!subreq) goto nomem;
+ tevent_req_set_callback(subreq, smb2srv_sesssetup_callback, callback_ctx);
+
+ /* note that we ignore SMB2_NEGOTIATE_SIGNING_ENABLED from the client.
+ This is deliberate as windows does not set it even when it does
+ set SMB2_NEGOTIATE_SIGNING_REQUIRED */
+ if (io->smb2.in.security_mode & SMB2_NEGOTIATE_SIGNING_REQUIRED) {
+ smb_sess->smb2_signing.required = true;
+ }
+
+ /* disable receipt of more packets on this socket until we've
+ finished with the session setup. This avoids a problem with
+ crashes if we get EOF on the socket while processing a session
+ setup */
+ packet_recv_disable(req->smb_conn->packet);
+
+ return;
+nomem:
+ status = NT_STATUS_NO_MEMORY;
+failed:
+ talloc_free(smb_sess);
+ req->status = nt_status_squash(status);
+ smb2srv_sesssetup_send(req, io);
+}
+
+void smb2srv_sesssetup_recv(struct smb2srv_request *req)
+{
+ union smb_sesssetup *io;
+
+ SMB2SRV_CHECK_BODY_SIZE(req, 0x18, true);
+ SMB2SRV_TALLOC_IO_PTR(io, union smb_sesssetup);
+
+ io->smb2.level = RAW_SESSSETUP_SMB2;
+ io->smb2.in.vc_number = CVAL(req->in.body, 0x02);
+ io->smb2.in.security_mode = CVAL(req->in.body, 0x03);
+ io->smb2.in.capabilities = IVAL(req->in.body, 0x04);
+ io->smb2.in.channel = IVAL(req->in.body, 0x08);
+ io->smb2.in.previous_sessionid = BVAL(req->in.body, 0x10);
+ SMB2SRV_CHECK(smb2_pull_o16s16_blob(&req->in, io, req->in.body+0x0C, &io->smb2.in.secblob));
+
+ smb2srv_sesssetup_backend(req, io);
+}
+
+static int smb2srv_cleanup_session_destructor(struct smbsrv_session **session)
+{
+ /* TODO: call ntvfs backends to close file of this session */
+ DEBUG(0,("free session[%p]\n", *session));
+ talloc_free(*session);
+ return 0;
+}
+
+static NTSTATUS smb2srv_logoff_backend(struct smb2srv_request *req)
+{
+ struct smbsrv_session **session_ptr;
+
+ /* we need to destroy the session after sending the reply */
+ session_ptr = talloc(req, struct smbsrv_session *);
+ NT_STATUS_HAVE_NO_MEMORY(session_ptr);
+
+ *session_ptr = req->session;
+ talloc_set_destructor(session_ptr, smb2srv_cleanup_session_destructor);
+
+ return NT_STATUS_OK;
+}
+
+static void smb2srv_logoff_send(struct smb2srv_request *req)
+{
+ if (NT_STATUS_IS_ERR(req->status)) {
+ smb2srv_send_error(req, req->status);
+ return;
+ }
+
+ SMB2SRV_CHECK(smb2srv_setup_reply(req, 0x04, false, 0));
+
+ SSVAL(req->out.body, 0x02, 0);
+
+ smb2srv_send_reply(req);
+}
+
+void smb2srv_logoff_recv(struct smb2srv_request *req)
+{
+ SMB2SRV_CHECK_BODY_SIZE(req, 0x04, false);
+
+ req->status = smb2srv_logoff_backend(req);
+
+ if (req->control_flags & SMB2SRV_REQ_CTRL_FLAG_NOT_REPLY) {
+ talloc_free(req);
+ return;
+ }
+ smb2srv_logoff_send(req);
+}
diff --git a/source4/smb_server/smb2/smb2_server.h b/source4/smb_server/smb2/smb2_server.h
new file mode 100644
index 0000000..5fe12fe
--- /dev/null
+++ b/source4/smb_server/smb2/smb2_server.h
@@ -0,0 +1,192 @@
+/*
+ Unix SMB2 implementation.
+
+ Copyright (C) Stefan Metzmacher 2005
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* the context for a single SMB2 request. This is passed to any request-context
+ functions */
+struct smb2srv_request {
+ /* the smbsrv_connection needs a list of requests queued for send */
+ struct smb2srv_request *next, *prev;
+
+ /* the server_context contains all context specific to this SMB socket */
+ struct smbsrv_connection *smb_conn;
+
+ /* conn is only set for operations that have a valid TID */
+ struct smbsrv_tcon *tcon;
+
+ /* the session context is derived from the vuid */
+ struct smbsrv_session *session;
+
+#define SMB2SRV_REQ_CTRL_FLAG_NOT_REPLY (1<<0)
+ uint32_t control_flags;
+
+ /* the system time when the request arrived */
+ struct timeval request_time;
+
+ /* a pointer to the per request union smb_* io structure */
+ void *io_ptr;
+
+ /* the ntvfs_request */
+ struct ntvfs_request *ntvfs;
+
+ /* Now the SMB2 specific stuff */
+
+ /* the status the backend returned */
+ NTSTATUS status;
+
+ /* for matching request and reply */
+ uint64_t seqnum;
+
+ /* the id that can be used to cancel the request */
+ uint32_t pending_id;
+
+ /* the offset to the next SMB2 Header for chained requests */
+ uint32_t chain_offset;
+
+ /* the status we return for following chained requests */
+ NTSTATUS chain_status;
+
+ /* chained file handle */
+ uint8_t _chained_file_handle[16];
+ uint8_t *chained_file_handle;
+ uint64_t chained_session_id;
+ uint32_t chained_tree_id;
+
+ bool is_signed;
+
+ struct smb2_request_buffer in;
+ struct smb2_request_buffer out;
+};
+
+struct smbsrv_request;
+
+#include "smb_server/smb2/smb2_proto.h"
+
+/* useful way of catching field size errors with file and line number */
+#define SMB2SRV_CHECK_BODY_SIZE(req, size, dynamic) do { \
+ size_t is_size = req->in.body_size; \
+ uint16_t field_size; \
+ uint16_t want_size = ((dynamic)?(size)+1:(size)); \
+ if (is_size < (size)) { \
+ DEBUG(0,("%s: buffer too small 0x%x. Expected 0x%x\n", \
+ __location__, (unsigned)is_size, (unsigned)want_size)); \
+ smb2srv_send_error(req, NT_STATUS_INVALID_PARAMETER); \
+ return; \
+ }\
+ field_size = SVAL(req->in.body, 0); \
+ if (field_size != want_size) { \
+ DEBUG(0,("%s: unexpected fixed body size 0x%x. Expected 0x%x\n", \
+ __location__, (unsigned)field_size, (unsigned)want_size)); \
+ smb2srv_send_error(req, NT_STATUS_INVALID_PARAMETER); \
+ return; \
+ } \
+} while (0)
+
+#define SMB2SRV_CHECK(cmd) do {\
+ NTSTATUS _status; \
+ _status = cmd; \
+ if (!NT_STATUS_IS_OK(_status)) { \
+ smb2srv_send_error(req, _status); \
+ return; \
+ } \
+} while (0)
+
+/* useful wrapper for talloc with NO_MEMORY reply */
+#define SMB2SRV_TALLOC_IO_PTR(ptr, type) do { \
+ ptr = talloc(req, type); \
+ if (!ptr) { \
+ smb2srv_send_error(req, NT_STATUS_NO_MEMORY); \
+ return; \
+ } \
+ req->io_ptr = ptr; \
+} while (0)
+
+#define SMB2SRV_SETUP_NTVFS_REQUEST(send_fn, state) do { \
+ req->ntvfs = ntvfs_request_create(req->tcon->ntvfs, req, \
+ req->session->session_info,\
+ 0, \
+ req->request_time, \
+ req, send_fn, state); \
+ if (!req->ntvfs) { \
+ smb2srv_send_error(req, NT_STATUS_NO_MEMORY); \
+ return; \
+ } \
+ (void)talloc_steal(req->tcon->ntvfs, req); \
+ req->ntvfs->frontend_data.private_data = req; \
+} while (0)
+
+#define SMB2SRV_CHECK_FILE_HANDLE(handle) do { \
+ if (!handle) { \
+ smb2srv_send_error(req, NT_STATUS_FILE_CLOSED); \
+ return; \
+ } \
+} while (0)
+
+/*
+ check if the backend wants to handle the request asynchronously.
+ if it wants it handled synchronously then call the send function
+ immediately
+*/
+#define SMB2SRV_CALL_NTVFS_BACKEND(cmd) do { \
+ req->ntvfs->async_states->status = cmd; \
+ if (req->ntvfs->async_states->state & NTVFS_ASYNC_STATE_ASYNC) { \
+ NTSTATUS _status; \
+ _status = smb2srv_queue_pending(req); \
+ if (!NT_STATUS_IS_OK(_status)) { \
+ ntvfs_cancel(req->ntvfs); \
+ } \
+ } else { \
+ req->ntvfs->async_states->send_fn(req->ntvfs); \
+ } \
+} while (0)
+
+/* check req->ntvfs->async_states->status and if not OK then send an error reply */
+#define SMB2SRV_CHECK_ASYNC_STATUS_ERR_SIMPLE do { \
+ req = talloc_get_type(ntvfs->async_states->private_data, struct smb2srv_request); \
+ if (ntvfs->async_states->state & NTVFS_ASYNC_STATE_CLOSE || NT_STATUS_EQUAL(ntvfs->async_states->status, NT_STATUS_NET_WRITE_FAULT)) { \
+ smbsrv_terminate_connection(req->smb_conn, get_friendly_nt_error_msg (ntvfs->async_states->status)); \
+ talloc_free(req); \
+ return; \
+ } \
+ req->status = ntvfs->async_states->status; \
+ if (NT_STATUS_IS_ERR(ntvfs->async_states->status)) { \
+ smb2srv_send_error(req, ntvfs->async_states->status); \
+ return; \
+ } \
+} while (0)
+#define SMB2SRV_CHECK_ASYNC_STATUS_ERR(ptr, type) do { \
+ SMB2SRV_CHECK_ASYNC_STATUS_ERR_SIMPLE; \
+ ptr = talloc_get_type(req->io_ptr, type); \
+} while (0)
+#define SMB2SRV_CHECK_ASYNC_STATUS_SIMPLE do { \
+ req = talloc_get_type(ntvfs->async_states->private_data, struct smb2srv_request); \
+ if (ntvfs->async_states->state & NTVFS_ASYNC_STATE_CLOSE || NT_STATUS_EQUAL(ntvfs->async_states->status, NT_STATUS_NET_WRITE_FAULT)) { \
+ smbsrv_terminate_connection(req->smb_conn, get_friendly_nt_error_msg (ntvfs->async_states->status)); \
+ talloc_free(req); \
+ return; \
+ } \
+ req->status = ntvfs->async_states->status; \
+ if (!NT_STATUS_IS_OK(ntvfs->async_states->status)) { \
+ smb2srv_send_error(req, ntvfs->async_states->status); \
+ return; \
+ } \
+} while (0)
+#define SMB2SRV_CHECK_ASYNC_STATUS(ptr, type) do { \
+ SMB2SRV_CHECK_ASYNC_STATUS_SIMPLE; \
+ ptr = talloc_get_type(req->io_ptr, type); \
+} while (0)
diff --git a/source4/smb_server/smb2/tcon.c b/source4/smb_server/smb2/tcon.c
new file mode 100644
index 0000000..0c56420
--- /dev/null
+++ b/source4/smb_server/smb2/tcon.c
@@ -0,0 +1,446 @@
+/*
+ Unix SMB2 implementation.
+
+ Copyright (C) Stefan Metzmacher 2005
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+#include "smb_server/smb_server.h"
+#include "smb_server/smb2/smb2_server.h"
+#include "samba/service_stream.h"
+#include "ntvfs/ntvfs.h"
+
+/*
+ send an oplock break request to a client
+*/
+static NTSTATUS smb2srv_send_oplock_break(void *p, struct ntvfs_handle *h, uint8_t level)
+{
+ struct smbsrv_handle *handle = talloc_get_type(h->frontend_data.private_data,
+ struct smbsrv_handle);
+ struct smb2srv_request *req;
+ NTSTATUS status;
+
+ /* setup a dummy request structure */
+ req = smb2srv_init_request(handle->tcon->smb_conn);
+ NT_STATUS_HAVE_NO_MEMORY(req);
+
+ req->in.buffer = talloc_array(req, uint8_t,
+ NBT_HDR_SIZE + SMB2_MIN_SIZE);
+ NT_STATUS_HAVE_NO_MEMORY(req->in.buffer);
+ req->in.size = NBT_HDR_SIZE + SMB2_MIN_SIZE;
+ req->in.allocated = req->in.size;
+
+ req->in.hdr = req->in.buffer+ NBT_HDR_SIZE;
+ req->in.body = req->in.hdr + SMB2_HDR_BODY;
+ req->in.body_size = req->in.size - (SMB2_HDR_BODY+NBT_HDR_SIZE);
+ req->in.dynamic = NULL;
+
+ req->seqnum = UINT64_MAX;
+
+ smb2srv_setup_bufinfo(req);
+
+ SIVAL(req->in.hdr, 0, SMB2_MAGIC);
+ SSVAL(req->in.hdr, SMB2_HDR_LENGTH, SMB2_HDR_BODY);
+ SSVAL(req->in.hdr, SMB2_HDR_EPOCH, 0);
+ SIVAL(req->in.hdr, SMB2_HDR_STATUS, 0);
+ SSVAL(req->in.hdr, SMB2_HDR_OPCODE, SMB2_OP_BREAK);
+ SSVAL(req->in.hdr, SMB2_HDR_CREDIT, 0);
+ SIVAL(req->in.hdr, SMB2_HDR_FLAGS, 0);
+ SIVAL(req->in.hdr, SMB2_HDR_NEXT_COMMAND, 0);
+ SBVAL(req->in.hdr, SMB2_HDR_MESSAGE_ID, 0);
+ SIVAL(req->in.hdr, SMB2_HDR_PID, 0);
+ SIVAL(req->in.hdr, SMB2_HDR_TID, 0);
+ SBVAL(req->in.hdr, SMB2_HDR_SESSION_ID, 0);
+ memset(req->in.hdr+SMB2_HDR_SIGNATURE, 0, 16);
+
+ SSVAL(req->in.body, 0, 2);
+
+ status = smb2srv_setup_reply(req, 0x18, false, 0);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ SSVAL(req->out.hdr, SMB2_HDR_CREDIT, 0x0000);
+
+ SSVAL(req->out.body, 0x02, 0x0001);
+ SIVAL(req->out.body, 0x04, 0x00000000);
+ smb2srv_push_handle(req->out.body, 0x08, h);
+
+ smb2srv_send_reply(req);
+
+ return NT_STATUS_OK;
+}
+
+struct ntvfs_handle *smb2srv_pull_handle(struct smb2srv_request *req, const uint8_t *base, unsigned int offset)
+{
+ struct smbsrv_tcon *tcon;
+ struct smbsrv_handle *handle;
+ uint32_t hid;
+ uint32_t tid;
+ uint64_t uid;
+
+ /*
+ * if there're chained requests used the cached handle
+ *
+ * TODO: check if this also correct when the given handle
+ * isn't all 0xFF.
+ */
+ if (req->chained_file_handle) {
+ base = req->chained_file_handle;
+ offset = 0;
+ }
+
+ hid = IVAL(base, offset);
+ tid = IVAL(base, offset + 4);
+ uid = BVAL(base, offset + 8);
+
+ /* if it's the wildcard handle, don't waste time to search it... */
+ if (hid == UINT32_MAX && tid == UINT32_MAX && uid == UINT64_MAX) {
+ return NULL;
+ }
+
+ /*
+ * if the (v)uid part doesn't match the given session the handle isn't
+ * valid
+ */
+ if (uid != req->session->vuid) {
+ return NULL;
+ }
+
+ /*
+ * the handle can belong to a different tcon
+ * as that TID in the SMB2 header says, but
+ * the request should succeed nevertheless!
+ *
+ * because of this we put the 32 bit TID into the
+ * 128 bit handle, so that we can extract the tcon from the
+ * handle
+ */
+ tcon = req->tcon;
+ if (tid != req->tcon->tid) {
+ tcon = smbsrv_smb2_tcon_find(req->session, tid, req->request_time);
+ if (!tcon) {
+ return NULL;
+ }
+ }
+
+ handle = smbsrv_smb2_handle_find(tcon, hid, req->request_time);
+ if (!handle) {
+ return NULL;
+ }
+
+ /*
+ * as the smb2srv_tcon is a child object of the smb2srv_session
+ * the handle belongs to the correct session!
+ *
+ * Note: no check is needed here for SMB2
+ */
+
+ /*
+ * as the handle may have overwritten the tcon
+ * we need to set it on the request so that the
+ * correct ntvfs context will be used for the ntvfs_*() request
+ *
+ * TODO: check if that's correct for chained requests as well!
+ */
+ req->tcon = tcon;
+ return handle->ntvfs;
+}
+
+void smb2srv_push_handle(uint8_t *base, unsigned int offset, struct ntvfs_handle *ntvfs)
+{
+ struct smbsrv_handle *handle = talloc_get_type(ntvfs->frontend_data.private_data,
+ struct smbsrv_handle);
+
+ /*
+ * the handle is 128 bit on the wire
+ */
+ SIVAL(base, offset, handle->hid);
+ SIVAL(base, offset + 4, handle->tcon->tid);
+ SBVAL(base, offset + 8, handle->session->vuid);
+}
+
+static NTSTATUS smb2srv_handle_create_new(void *private_data, struct ntvfs_request *ntvfs, struct ntvfs_handle **_h)
+{
+ struct smb2srv_request *req = talloc_get_type(ntvfs->frontend_data.private_data,
+ struct smb2srv_request);
+ struct smbsrv_handle *handle;
+ struct ntvfs_handle *h;
+
+ handle = smbsrv_handle_new(req->session, req->tcon, req, req->request_time);
+ if (!handle) return NT_STATUS_INSUFFICIENT_RESOURCES;
+
+ h = talloc_zero(handle, struct ntvfs_handle);
+ if (!h) goto nomem;
+
+ /*
+ * note: we don't set handle->ntvfs yet,
+ * this will be done by smbsrv_handle_make_valid()
+ * this makes sure the handle is invalid for clients
+ * until the ntvfs subsystem has made it valid
+ */
+ h->ctx = ntvfs->ctx;
+ h->session_info = ntvfs->session_info;
+ h->smbpid = ntvfs->smbpid;
+
+ h->frontend_data.private_data = handle;
+
+ *_h = h;
+ return NT_STATUS_OK;
+nomem:
+ talloc_free(handle);
+ return NT_STATUS_NO_MEMORY;
+}
+
+static NTSTATUS smb2srv_handle_make_valid(void *private_data, struct ntvfs_handle *h)
+{
+ struct smbsrv_tcon *tcon = talloc_get_type(private_data, struct smbsrv_tcon);
+ struct smbsrv_handle *handle = talloc_get_type(h->frontend_data.private_data,
+ struct smbsrv_handle);
+ /* this tells the frontend that the handle is valid */
+ handle->ntvfs = h;
+ /* this moves the smbsrv_request to the smbsrv_tcon memory context */
+ talloc_steal(tcon, handle);
+ return NT_STATUS_OK;
+}
+
+static void smb2srv_handle_destroy(void *private_data, struct ntvfs_handle *h)
+{
+ struct smbsrv_handle *handle = talloc_get_type(h->frontend_data.private_data,
+ struct smbsrv_handle);
+ talloc_free(handle);
+}
+
+static struct ntvfs_handle *smb2srv_handle_search_by_wire_key(void *private_data, struct ntvfs_request *ntvfs, const DATA_BLOB *key)
+{
+ return NULL;
+}
+
+static DATA_BLOB smb2srv_handle_get_wire_key(void *private_data, struct ntvfs_handle *handle, TALLOC_CTX *mem_ctx)
+{
+ return data_blob(NULL, 0);
+}
+
+static NTSTATUS smb2srv_tcon_backend(struct smb2srv_request *req, union smb_tcon *io)
+{
+ struct smbsrv_tcon *tcon;
+ NTSTATUS status;
+ enum ntvfs_type type;
+ const char *service = io->smb2.in.path;
+ struct share_config *scfg;
+ char *sharetype;
+ uint64_t ntvfs_caps = 0;
+
+ if (strncmp(service, "\\\\", 2) == 0) {
+ const char *p = strchr(service+2, '\\');
+ if (p) {
+ service = p + 1;
+ }
+ }
+
+ status = share_get_config(req, req->smb_conn->share_context, service, &scfg);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("smb2srv_tcon_backend: couldn't find service %s\n", service));
+ return NT_STATUS_BAD_NETWORK_NAME;
+ }
+
+ if (!socket_check_access(req->smb_conn->connection->socket,
+ scfg->name,
+ share_string_list_option(req, scfg, SHARE_HOSTS_ALLOW),
+ share_string_list_option(req, scfg, SHARE_HOSTS_DENY))) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ /* work out what sort of connection this is */
+ sharetype = share_string_option(req, scfg, SHARE_TYPE, "DISK");
+ if (sharetype && strcmp(sharetype, "IPC") == 0) {
+ type = NTVFS_IPC;
+ } else if (sharetype && strcmp(sharetype, "PRINTER") == 0) {
+ type = NTVFS_PRINT;
+ } else {
+ type = NTVFS_DISK;
+ }
+ TALLOC_FREE(sharetype);
+
+ tcon = smbsrv_smb2_tcon_new(req->session, scfg->name);
+ if (!tcon) {
+ DEBUG(0,("smb2srv_tcon_backend: Couldn't find free connection.\n"));
+ return NT_STATUS_INSUFFICIENT_RESOURCES;
+ }
+ req->tcon = tcon;
+
+ ntvfs_caps = NTVFS_CLIENT_CAP_LEVEL_II_OPLOCKS;
+
+ /* init ntvfs function pointers */
+ status = ntvfs_init_connection(tcon, scfg, type,
+ req->smb_conn->negotiate.protocol,
+ ntvfs_caps,
+ req->smb_conn->connection->event.ctx,
+ req->smb_conn->connection->msg_ctx,
+ req->smb_conn->lp_ctx,
+ req->smb_conn->connection->server_id,
+ &tcon->ntvfs);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("smb2srv_tcon_backend: ntvfs_init_connection failed for service %s\n",
+ scfg->name));
+ goto failed;
+ }
+
+ status = ntvfs_set_oplock_handler(tcon->ntvfs, smb2srv_send_oplock_break, tcon);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("smb2srv_tcon_backend: NTVFS failed to set the oplock handler!\n"));
+ goto failed;
+ }
+
+ status = ntvfs_set_addresses(tcon->ntvfs,
+ req->smb_conn->connection->local_address,
+ req->smb_conn->connection->remote_address);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("smb2srv_tcon_backend: NTVFS failed to set the address!\n"));
+ goto failed;
+ }
+
+ status = ntvfs_set_handle_callbacks(tcon->ntvfs,
+ smb2srv_handle_create_new,
+ smb2srv_handle_make_valid,
+ smb2srv_handle_destroy,
+ smb2srv_handle_search_by_wire_key,
+ smb2srv_handle_get_wire_key,
+ tcon);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("smb2srv_tcon_backend: NTVFS failed to set the handle callbacks!\n"));
+ goto failed;
+ }
+
+ req->ntvfs = ntvfs_request_create(req->tcon->ntvfs, req,
+ req->session->session_info,
+ SVAL(req->in.hdr, SMB2_HDR_PID),
+ req->request_time,
+ req, NULL, 0);
+ if (!req->ntvfs) {
+ status = NT_STATUS_NO_MEMORY;
+ goto failed;
+ }
+
+ io->smb2.out.share_type = (unsigned)type; /* 1 - DISK, 2 - Print, 3 - IPC */
+ io->smb2.out.reserved = 0;
+ io->smb2.out.flags = 0x00000000;
+ io->smb2.out.capabilities = 0;
+ io->smb2.out.access_mask = SEC_RIGHTS_FILE_ALL;
+
+ io->smb2.out.tid = tcon->tid;
+
+ /* Invoke NTVFS connection hook */
+ status = ntvfs_connect(req->ntvfs, io);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("smb2srv_tcon_backend: NTVFS ntvfs_connect() failed: %s!\n", nt_errstr(status)));
+ goto failed;
+ }
+
+ return NT_STATUS_OK;
+
+failed:
+ req->tcon = NULL;
+ talloc_free(tcon);
+ return status;
+}
+
+static void smb2srv_tcon_send(struct smb2srv_request *req, union smb_tcon *io)
+{
+ if (!NT_STATUS_IS_OK(req->status)) {
+ smb2srv_send_error(req, req->status);
+ return;
+ }
+
+ SMB2SRV_CHECK(smb2srv_setup_reply(req, 0x10, false, 0));
+
+ SIVAL(req->out.hdr, SMB2_HDR_TID, io->smb2.out.tid);
+
+ SCVAL(req->out.body, 0x02, io->smb2.out.share_type);
+ SCVAL(req->out.body, 0x03, io->smb2.out.reserved);
+ SIVAL(req->out.body, 0x04, io->smb2.out.flags);
+ SIVAL(req->out.body, 0x08, io->smb2.out.capabilities);
+ SIVAL(req->out.body, 0x0C, io->smb2.out.access_mask);
+
+ smb2srv_send_reply(req);
+}
+
+void smb2srv_tcon_recv(struct smb2srv_request *req)
+{
+ union smb_tcon *io;
+
+ SMB2SRV_CHECK_BODY_SIZE(req, 0x08, true);
+ SMB2SRV_TALLOC_IO_PTR(io, union smb_tcon);
+
+ io->smb2.level = RAW_TCON_SMB2;
+ io->smb2.in.reserved = SVAL(req->in.body, 0x02);
+ SMB2SRV_CHECK(smb2_pull_o16s16_string(&req->in, io, req->in.body+0x04, &io->smb2.in.path));
+
+ /* the VFS backend does not yet handle NULL paths */
+ if (io->smb2.in.path == NULL) {
+ io->smb2.in.path = "";
+ }
+
+ req->status = smb2srv_tcon_backend(req, io);
+
+ if (req->control_flags & SMB2SRV_REQ_CTRL_FLAG_NOT_REPLY) {
+ talloc_free(req);
+ return;
+ }
+ smb2srv_tcon_send(req, io);
+}
+
+static NTSTATUS smb2srv_tdis_backend(struct smb2srv_request *req)
+{
+ /* TODO: call ntvfs backends to close file of this tcon */
+ talloc_free(req->tcon);
+ req->tcon = NULL;
+ return NT_STATUS_OK;
+}
+
+static void smb2srv_tdis_send(struct smb2srv_request *req)
+{
+ NTSTATUS status;
+
+ if (NT_STATUS_IS_ERR(req->status)) {
+ smb2srv_send_error(req, req->status);
+ return;
+ }
+
+ status = smb2srv_setup_reply(req, 0x04, false, 0);
+ if (!NT_STATUS_IS_OK(status)) {
+ smbsrv_terminate_connection(req->smb_conn, nt_errstr(status));
+ talloc_free(req);
+ return;
+ }
+
+ SSVAL(req->out.body, 0x02, 0);
+
+ smb2srv_send_reply(req);
+}
+
+void smb2srv_tdis_recv(struct smb2srv_request *req)
+{
+ SMB2SRV_CHECK_BODY_SIZE(req, 0x04, false);
+
+ req->status = smb2srv_tdis_backend(req);
+
+ if (req->control_flags & SMB2SRV_REQ_CTRL_FLAG_NOT_REPLY) {
+ talloc_free(req);
+ return;
+ }
+ smb2srv_tdis_send(req);
+}
diff --git a/source4/smb_server/smb2/wscript_build b/source4/smb_server/smb2/wscript_build
new file mode 100644
index 0000000..7866ee9
--- /dev/null
+++ b/source4/smb_server/smb2/wscript_build
@@ -0,0 +1,9 @@
+#!/usr/bin/env python
+
+bld.SAMBA_SUBSYSTEM('SMB2_PROTOCOL',
+ source='receive.c negprot.c sesssetup.c tcon.c fileio.c fileinfo.c find.c keepalive.c',
+ autoproto='smb2_proto.h',
+ public_deps='ntvfs LIBPACKET LIBCLI_SMB2 samba_server_gensec NDR_DFSBLOBS',
+ enabled=bld.CONFIG_SET('WITH_NTVFS_FILESERVER')
+ )
+
diff --git a/source4/smb_server/smb_server.c b/source4/smb_server/smb_server.c
new file mode 100644
index 0000000..c45df70
--- /dev/null
+++ b/source4/smb_server/smb_server.c
@@ -0,0 +1,203 @@
+/*
+ Unix SMB/CIFS implementation.
+ process incoming packets - main loop
+ Copyright (C) Andrew Tridgell 2004-2005
+ 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 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "samba/service_task.h"
+#include "samba/service_stream.h"
+#include "samba/service.h"
+#include "smb_server/smb_server.h"
+#include "smb_server/service_smb_proto.h"
+#include "lib/messaging/irpc.h"
+#include "lib/stream/packet.h"
+#include "libcli/smb2/smb2.h"
+#include "smb_server/smb2/smb2_server.h"
+#include "system/network.h"
+#include "lib/socket/netif.h"
+#include "param/share.h"
+#include "dsdb/samdb/samdb.h"
+#include "param/param.h"
+
+static NTSTATUS smbsrv_recv_generic_request(void *private_data, DATA_BLOB blob)
+{
+ NTSTATUS status;
+ struct smbsrv_connection *smb_conn = talloc_get_type(private_data, struct smbsrv_connection);
+ uint32_t protocol_version;
+
+ /* see if its a special NBT packet */
+ if (CVAL(blob.data,0) != 0) {
+ status = smbsrv_init_smb_connection(smb_conn, smb_conn->lp_ctx);
+ NT_STATUS_NOT_OK_RETURN(status);
+ return smbsrv_recv_smb_request(smb_conn, blob);
+ }
+
+ if (blob.length < (NBT_HDR_SIZE + MIN_SMB_SIZE)) {
+ DEBUG(2,("Invalid SMB packet length count %ld\n", (long)blob.length));
+ smbsrv_terminate_connection(smb_conn, "Invalid SMB packet");
+ return NT_STATUS_OK;
+ }
+
+ protocol_version = IVAL(blob.data, NBT_HDR_SIZE);
+
+ switch (protocol_version) {
+ case SMB_MAGIC:
+ status = smbsrv_init_smb_connection(smb_conn, smb_conn->lp_ctx);
+ NT_STATUS_NOT_OK_RETURN(status);
+ packet_set_callback(smb_conn->packet, smbsrv_recv_smb_request);
+ return smbsrv_recv_smb_request(smb_conn, blob);
+ case SMB2_MAGIC:
+ if (lpcfg_server_max_protocol(smb_conn->lp_ctx) < PROTOCOL_SMB2_02) break;
+ status = smbsrv_init_smb2_connection(smb_conn);
+ NT_STATUS_NOT_OK_RETURN(status);
+ packet_set_callback(smb_conn->packet, smbsrv_recv_smb2_request);
+ return smbsrv_recv_smb2_request(smb_conn, blob);
+ }
+
+ DEBUG(2,("Invalid SMB packet: protocol prefix: 0x%08X\n", protocol_version));
+ smbsrv_terminate_connection(smb_conn, "NON-SMB packet");
+ return NT_STATUS_OK;
+}
+
+/*
+ close the socket and shutdown a server_context
+*/
+void smbsrv_terminate_connection(struct smbsrv_connection *smb_conn, const char *reason)
+{
+ stream_terminate_connection(smb_conn->connection, reason);
+}
+
+/*
+ called when a SMB socket becomes readable
+*/
+static void smbsrv_recv(struct stream_connection *conn, uint16_t flags)
+{
+ struct smbsrv_connection *smb_conn = talloc_get_type(conn->private_data,
+ struct smbsrv_connection);
+
+ DEBUG(10,("smbsrv_recv\n"));
+
+ packet_recv(smb_conn->packet);
+}
+
+/*
+ called when a SMB socket becomes writable
+*/
+static void smbsrv_send(struct stream_connection *conn, uint16_t flags)
+{
+ struct smbsrv_connection *smb_conn = talloc_get_type(conn->private_data,
+ struct smbsrv_connection);
+ packet_queue_run(smb_conn->packet);
+}
+
+/*
+ handle socket recv errors
+*/
+static void smbsrv_recv_error(void *private_data, NTSTATUS status)
+{
+ struct smbsrv_connection *smb_conn = talloc_get_type(private_data, struct smbsrv_connection);
+
+ smbsrv_terminate_connection(smb_conn, nt_errstr(status));
+}
+
+/*
+ initialise a server_context from a open socket and register a event handler
+ for reading from that socket
+*/
+static void smbsrv_accept(struct stream_connection *conn)
+{
+ struct smbsrv_connection *smb_conn;
+
+ DEBUG(5,("smbsrv_accept\n"));
+
+ smb_conn = talloc_zero(conn, struct smbsrv_connection);
+ if (!smb_conn) {
+ stream_terminate_connection(conn, "out of memory");
+ return;
+ }
+
+ smb_conn->packet = packet_init(smb_conn);
+ if (!smb_conn->packet) {
+ smbsrv_terminate_connection(smb_conn, "out of memory");
+ return;
+ }
+ packet_set_private(smb_conn->packet, smb_conn);
+ packet_set_socket(smb_conn->packet, conn->socket);
+ packet_set_callback(smb_conn->packet, smbsrv_recv_generic_request);
+ packet_set_full_request(smb_conn->packet, packet_full_request_nbt);
+ packet_set_error_handler(smb_conn->packet, smbsrv_recv_error);
+ packet_set_event_context(smb_conn->packet, conn->event.ctx);
+ packet_set_fde(smb_conn->packet, conn->event.fde);
+ packet_set_serialise(smb_conn->packet);
+ packet_set_initial_read(smb_conn->packet, 4);
+
+ smb_conn->lp_ctx = conn->lp_ctx;
+ smb_conn->connection = conn;
+ conn->private_data = smb_conn;
+
+ smb_conn->statistics.connect_time = timeval_current();
+
+ smbsrv_management_init(smb_conn);
+
+ irpc_add_name(conn->msg_ctx, "smb_server");
+
+ if (!NT_STATUS_IS_OK(share_get_context(smb_conn,
+ smb_conn->lp_ctx,
+ &(smb_conn->share_context)))) {
+ smbsrv_terminate_connection(smb_conn, "share_init failed!");
+ return;
+ }
+}
+
+static const struct stream_server_ops smb_stream_ops = {
+ .name = "smbsrv",
+ .accept_connection = smbsrv_accept,
+ .recv_handler = smbsrv_recv,
+ .send_handler = smbsrv_send,
+};
+
+/*
+ setup a listening socket on all the SMB ports for a particular address
+*/
+_PUBLIC_ NTSTATUS smbsrv_add_socket(TALLOC_CTX *mem_ctx,
+ struct tevent_context *event_context,
+ struct loadparm_context *lp_ctx,
+ const struct model_ops *model_ops,
+ const char *address,
+ void *process_context)
+{
+ const char **ports = lpcfg_smb_ports(lp_ctx);
+ int i;
+ NTSTATUS status;
+
+ for (i=0;ports[i];i++) {
+ uint16_t port = atoi(ports[i]);
+ if (port == 0) continue;
+ status = stream_setup_socket(mem_ctx, event_context, lp_ctx,
+ model_ops, &smb_stream_ops,
+ "ip", address, &port,
+ lpcfg_socket_options(lp_ctx),
+ NULL, process_context);
+ NT_STATUS_NOT_OK_RETURN(status);
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+
diff --git a/source4/smb_server/smb_server.h b/source4/smb_server/smb_server.h
new file mode 100644
index 0000000..5ddfe78
--- /dev/null
+++ b/source4/smb_server/smb_server.h
@@ -0,0 +1,522 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Copyright (C) Andrew Tridgell 2003
+ Copyright (C) James J Myers 2003 <myersjj@samba.org>
+ Copyright (C) Stefan Metzmacher 2004-2005
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "libcli/raw/request.h"
+#include "libcli/raw/interfaces.h"
+#include "lib/socket/socket.h"
+#include "libds/common/roles.h"
+#include "../lib/util/dlinklist.h"
+#include "../librpc/gen_ndr/nbt.h"
+
+struct tevent_context;
+
+/*
+ this header declares the core context structures associated with smb
+ sockets, tree connects, requests etc
+
+ the idea is that we will eventually get rid of all our global
+ variables and instead store our state from structures hanging off
+ these basic elements
+*/
+
+struct smbsrv_tcons_context {
+ /* an id tree used to allocate tids */
+ struct idr_context *idtree_tid;
+
+ /* this is the limit of vuid values for this connection */
+ uint32_t idtree_limit;
+
+ /* list of open tree connects */
+ struct smbsrv_tcon *list;
+};
+
+struct smbsrv_sessions_context {
+ /* an id tree used to allocate vuids */
+ /* this holds info on session vuids that are already
+ * validated for this VC */
+ struct idr_context *idtree_vuid;
+
+ /* this is the limit of vuid values for this connection */
+ uint64_t idtree_limit;
+
+ /* also kept as a link list so it can be enumerated by
+ the management code */
+ struct smbsrv_session *list;
+};
+
+struct smbsrv_handles_context {
+ /* an id tree used to allocate file handles */
+ struct idr_context *idtree_hid;
+
+ /* this is the limit of handle values for this context */
+ uint64_t idtree_limit;
+
+ /* also kept as a link list so it can be enumerated by
+ the management code */
+ struct smbsrv_handle *list;
+};
+
+/* the current user context for a request */
+struct smbsrv_session {
+ struct smbsrv_session *prev, *next;
+
+ struct smbsrv_connection *smb_conn;
+
+ /*
+ * in SMB2 tcons belong to just one session
+ * and not to the whole connection
+ */
+ struct smbsrv_tcons_context smb2_tcons;
+
+ /*
+ * the open file handles for this session,
+ * used for SMBexit, SMBulogoff and SMB2 SessionLogoff
+ */
+ struct smbsrv_handle_session_item *handles;
+
+ /*
+ * an index passed over the wire:
+ * - 16 bit for smb
+ * - 64 bit for smb2
+ */
+ uint64_t vuid;
+
+ struct gensec_security *gensec_ctx;
+
+ struct auth_session_info *session_info;
+
+ struct {
+ bool required;
+ bool active;
+ } smb2_signing;
+
+ /* some statistics for the management tools */
+ struct {
+ /* the time when the session setup started */
+ struct timeval connect_time;
+ /* the time when the session setup was finished */
+ struct timeval auth_time;
+ /* the time when the last request comes in */
+ struct timeval last_request_time;
+ } statistics;
+};
+
+/* we need a forward declaration of the ntvfs_ops strucutre to prevent
+ include recursion */
+struct ntvfs_context;
+
+struct smbsrv_tcon {
+ struct smbsrv_tcon *next, *prev;
+
+ /* the server context that this was created on */
+ struct smbsrv_connection *smb_conn;
+
+ /* the open file handles on this tcon */
+ struct smbsrv_handles_context handles;
+
+ /*
+ * an index passed over the wire:
+ * - 16 bit for smb
+ * - 32 bit for smb2
+ */
+ uint32_t tid; /* an index passed over the wire (the TID) */
+
+ /* the share name */
+ const char *share_name;
+
+ /* the NTVFS context - see source/ntvfs/ for details */
+ struct ntvfs_context *ntvfs;
+
+ /* some stuff to support share level security */
+ struct {
+ /* in share level security we need to fake up a session */
+ struct smbsrv_session *session;
+ } sec_share;
+
+ /* some stuff to support share level security */
+ struct {
+ /* in SMB2 a tcon always belongs to one session */
+ struct smbsrv_session *session;
+ } smb2;
+
+ /* some statistics for the management tools */
+ struct {
+ /* the time when the tree connect started */
+ struct timeval connect_time;
+ /* the time when the last request comes in */
+ struct timeval last_request_time;
+ } statistics;
+};
+
+struct smbsrv_handle {
+ struct smbsrv_handle *next, *prev;
+
+ /* the tcon the handle belongs to */
+ struct smbsrv_tcon *tcon;
+
+ /* the session the handle was opened on */
+ struct smbsrv_session *session;
+
+ /* the smbpid used on the open, used for SMBexit */
+ uint16_t smbpid;
+
+ /*
+ * this is for adding the handle into a linked list
+ * on the smbsrv_session, we can't use *next,*prev
+ * for this because they're used for the linked list on the
+ * smbsrv_tcon
+ */
+ struct smbsrv_handle_session_item {
+ struct smbsrv_handle_session_item *prev, *next;
+ struct smbsrv_handle *handle;
+ } session_item;
+
+ /*
+ * the value passed over the wire
+ * - 16 bit for smb
+ * - 32 bit for smb2
+ * Note: for SMB2 handles are 128 bit
+ * we'll fill them with
+ * - 32 bit HID
+ * - 32 bit TID
+ * - 64 bit VUID
+ */
+ uint32_t hid;
+
+ /*
+ * the ntvfs handle passed to the ntvfs backend
+ */
+ struct ntvfs_handle *ntvfs;
+
+ /* some statistics for the management tools */
+ struct {
+ /* the time when the tree connect started */
+ struct timeval open_time;
+ /* the time when the last request comes in */
+ struct timeval last_use_time;
+ } statistics;
+};
+
+/* a set of flags to control handling of request structures */
+#define SMBSRV_REQ_CONTROL_LARGE (1<<1) /* allow replies larger than max_xmit */
+
+#define SMBSRV_REQ_DEFAULT_STR_FLAGS(req) (((req)->flags2 & FLAGS2_UNICODE_STRINGS) ? STR_UNICODE : STR_ASCII)
+
+/* the context for a single SMB request. This is passed to any request-context
+ functions */
+struct smbsrv_request {
+ /* the smbsrv_connection needs a list of requests queued for send */
+ struct smbsrv_request *next, *prev;
+
+ /* the server_context contains all context specific to this SMB socket */
+ struct smbsrv_connection *smb_conn;
+
+ /* conn is only set for operations that have a valid TID */
+ struct smbsrv_tcon *tcon;
+
+ /* the session context is derived from the vuid */
+ struct smbsrv_session *session;
+
+ /* a set of flags to control usage of the request. See SMBSRV_REQ_CONTROL_* */
+ uint32_t control_flags;
+
+ /* the system time when the request arrived */
+ struct timeval request_time;
+
+ /* a pointer to the per request union smb_* io structure */
+ void *io_ptr;
+
+ /* the ntvfs_request */
+ struct ntvfs_request *ntvfs;
+
+ /* Now the SMB specific stuff */
+
+ /* the flags from the SMB request, in raw form (host byte order) */
+ uint16_t flags2;
+
+ /* this can contain a fnum from an earlier part of a chained
+ * message (such as an SMBOpenX), or -1 */
+ int chained_fnum;
+
+ /* how far through the chain of SMB commands have we gone? */
+ unsigned chain_count;
+
+ /* the sequence number for signing */
+ uint64_t seq_num;
+
+ struct smb_request_buffer in;
+ struct smb_request_buffer out;
+};
+
+/* smb server context structure. This should contain all the state
+ * information associated with a SMB server connection
+ */
+struct smbsrv_connection {
+ /* context that has been negotiated between the client and server */
+ struct {
+ /* have we already done the NBT session establishment? */
+ bool done_nbt_session;
+
+ /* only one negprot per connection is allowed */
+ bool done_negprot;
+
+ /* multiple session setups are allowed, but some parameters are
+ ignored in any but the first */
+ bool done_sesssetup;
+
+ /*
+ * Size of data we can send to client. Set
+ * by the client for all protocols above CORE.
+ * Set by us for CORE protocol.
+ */
+ unsigned max_send; /* init to BUFFER_SIZE */
+
+ /*
+ * Size of the data we can receive. Set by us.
+ * Can be modified by the max xmit parameter.
+ */
+ unsigned max_recv; /* init to BUFFER_SIZE */
+
+ /* the negotiatiated protocol */
+ enum protocol_types protocol;
+
+ /* authentication context for multi-part negprot */
+ struct auth4_context *auth_context;
+
+ /* reference to the kerberos keytab, or machine trust account */
+ struct cli_credentials *server_credentials;
+
+ /* did we tell the client we support encrypted passwords? */
+ bool encrypted_passwords;
+
+ /* Did we choose SPNEGO, or perhaps raw NTLMSSP, or even no extended security at all? */
+ const char *oid;
+
+ /* client capabilities */
+ uint32_t client_caps;
+
+ /* the timezone we sent to the client */
+ int zone_offset;
+
+ /* NBT names only set when done_nbt_session is true */
+ struct nbt_name *called_name;
+ struct nbt_name *calling_name;
+ } negotiate;
+
+ /* the context associated with open tree connects on a smb socket, not for SMB2 */
+ struct smbsrv_tcons_context smb_tcons;
+
+ /* context associated with currently valid session setups */
+ struct smbsrv_sessions_context sessions;
+
+ /*
+ * the server_context holds a linked list of pending requests,
+ * this is used for finding the request structures on ntcancel requests
+ * For SMB only
+ */
+ struct smbsrv_request *requests;
+
+ /*
+ * the server_context holds a linked list of pending requests,
+ * and an idtree for finding the request structures on SMB2 Cancel
+ * For SMB2 only
+ */
+ struct {
+ /* an id tree used to allocate ids */
+ struct idr_context *idtree_req;
+
+ /* this is the limit of pending requests values for this connection */
+ uint32_t idtree_limit;
+
+ /* list of open tree connects */
+ struct smb2srv_request *list;
+ } requests2;
+
+ struct smb_signing_context signing;
+
+ struct stream_connection *connection;
+
+ /* this holds a partially received request */
+ struct packet_context *packet;
+
+ /* a list of partially received transaction requests */
+ struct smbsrv_trans_partial {
+ struct smbsrv_trans_partial *next, *prev;
+ struct smbsrv_request *req;
+ uint8_t command;
+ union {
+ struct smb_trans2 *trans;
+ struct smb_nttrans *nttrans;
+ } u;
+ } *trans_partial;
+
+ /* configuration parameters */
+ struct {
+ bool nt_status_support;
+ } config;
+
+ /* some statictics for the management tools */
+ struct {
+ /* the time when the client connects */
+ struct timeval connect_time;
+ /* the time when the last request comes in */
+ struct timeval last_request_time;
+ } statistics;
+
+ struct share_context *share_context;
+
+ struct loadparm_context *lp_ctx;
+
+ bool smb2_signing_required;
+
+ uint64_t highest_smb2_seqnum;
+};
+
+struct model_ops;
+struct loadparm_context;
+
+NTSTATUS smbsrv_add_socket(TALLOC_CTX *mem_ctx,
+ struct tevent_context *event_context,
+ struct loadparm_context *lp_ctx,
+ const struct model_ops *model_ops,
+ const char *address,
+ void *process_context);
+
+struct loadparm_context;
+
+#include "smb_server/smb_server_proto.h"
+#include "smb_server/smb/smb_proto.h"
+
+/* useful way of catching wct errors with file and line number */
+#define SMBSRV_CHECK_WCT(req, wcount) do { \
+ if ((req)->in.wct != (wcount)) { \
+ DEBUG(1,("Unexpected WCT %u at %s(%d) - expected %d\n", \
+ (req)->in.wct, __FILE__, __LINE__, wcount)); \
+ smbsrv_send_error(req, NT_STATUS_DOS(ERRSRV, ERRerror)); \
+ return; \
+ } \
+} while (0)
+
+/* useful wrapper for talloc with NO_MEMORY reply */
+#define SMBSRV_TALLOC_IO_PTR(ptr, type) do { \
+ ptr = talloc(req, type); \
+ if (!ptr) { \
+ smbsrv_send_error(req, NT_STATUS_NO_MEMORY); \
+ return; \
+ } \
+ req->io_ptr = ptr; \
+} while (0)
+
+#define SMBSRV_SETUP_NTVFS_REQUEST(send_fn, state) do { \
+ req->ntvfs = ntvfs_request_create(req->tcon->ntvfs, req, \
+ req->session->session_info,\
+ SVAL(req->in.hdr,HDR_PID), \
+ req->request_time, \
+ req, send_fn, state); \
+ if (!req->ntvfs) { \
+ smbsrv_send_error(req, NT_STATUS_NO_MEMORY); \
+ return; \
+ } \
+ (void)talloc_steal(req->tcon->ntvfs, req); \
+ req->ntvfs->frontend_data.private_data = req; \
+} while (0)
+
+#define SMBSRV_CHECK_FILE_HANDLE(handle) do { \
+ if (!handle) { \
+ smbsrv_send_error(req, NT_STATUS_INVALID_HANDLE); \
+ return; \
+ } \
+} while (0)
+
+#define SMBSRV_CHECK_FILE_HANDLE_ERROR(handle, _status) do { \
+ if (!handle) { \
+ smbsrv_send_error(req, _status); \
+ return; \
+ } \
+} while (0)
+
+#define SMBSRV_CHECK_FILE_HANDLE_NTSTATUS(handle) do { \
+ if (!handle) { \
+ return NT_STATUS_INVALID_HANDLE; \
+ } \
+} while (0)
+
+#define SMBSRV_CHECK(cmd) do {\
+ NTSTATUS _status; \
+ _status = cmd; \
+ if (!NT_STATUS_IS_OK(_status)) { \
+ smbsrv_send_error(req, _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 SMBSRV_CALL_NTVFS_BACKEND(cmd) do { \
+ req->ntvfs->async_states->status = cmd; \
+ if (req->ntvfs->async_states->state & NTVFS_ASYNC_STATE_ASYNC) { \
+ DLIST_ADD_END(req->smb_conn->requests, req); \
+ } else { \
+ req->ntvfs->async_states->send_fn(req->ntvfs); \
+ } \
+} while (0)
+
+/* check req->ntvfs->async_states->status and if not OK then send an error reply */
+#define SMBSRV_CHECK_ASYNC_STATUS_ERR_SIMPLE do { \
+ req = talloc_get_type(ntvfs->async_states->private_data, struct smbsrv_request); \
+ if (ntvfs->async_states->state & NTVFS_ASYNC_STATE_CLOSE || NT_STATUS_EQUAL(ntvfs->async_states->status, NT_STATUS_NET_WRITE_FAULT)) { \
+ smbsrv_terminate_connection(req->smb_conn, get_friendly_nt_error_msg (ntvfs->async_states->status)); \
+ talloc_free(req); \
+ return; \
+ } \
+ if (NT_STATUS_IS_ERR(ntvfs->async_states->status)) { \
+ smbsrv_send_error(req, ntvfs->async_states->status); \
+ return; \
+ } \
+} while (0)
+#define SMBSRV_CHECK_ASYNC_STATUS_ERR(ptr, type) do { \
+ SMBSRV_CHECK_ASYNC_STATUS_ERR_SIMPLE; \
+ ptr = talloc_get_type(req->io_ptr, type); \
+} while (0)
+#define SMBSRV_CHECK_ASYNC_STATUS_SIMPLE do { \
+ req = talloc_get_type(ntvfs->async_states->private_data, struct smbsrv_request); \
+ if (ntvfs->async_states->state & NTVFS_ASYNC_STATE_CLOSE || NT_STATUS_EQUAL(ntvfs->async_states->status, NT_STATUS_NET_WRITE_FAULT)) { \
+ smbsrv_terminate_connection(req->smb_conn, get_friendly_nt_error_msg (ntvfs->async_states->status)); \
+ talloc_free(req); \
+ return; \
+ } \
+ if (!NT_STATUS_IS_OK(ntvfs->async_states->status)) { \
+ smbsrv_send_error(req, ntvfs->async_states->status); \
+ return; \
+ } \
+} while (0)
+#define SMBSRV_CHECK_ASYNC_STATUS(ptr, type) do { \
+ SMBSRV_CHECK_ASYNC_STATUS_SIMPLE; \
+ ptr = talloc_get_type(req->io_ptr, type); \
+} while (0)
+
+/* zero out some reserved fields in a reply */
+#define SMBSRV_VWV_RESERVED(start, count) memset(req->out.vwv + VWV(start), 0, (count)*2)
+
+#include "smb_server/service_smb_proto.h"