diff options
author | Stefan Metzmacher <metze@samba.org> | 2004-02-03 05:47:36 +0000 |
---|---|---|
committer | Stefan Metzmacher <metze@samba.org> | 2004-02-03 05:47:36 +0000 |
commit | 26ecaa32266e0c8699f280dd190811c310f2939c (patch) | |
tree | f33117f0ace3da6141e932f97cfcc51ef53233ab /source4/smb_server | |
parent | b2f097ad38e3216946dcc45f749198d337fc117e (diff) | |
download | samba-26ecaa32266e0c8699f280dd190811c310f2939c.tar.gz samba-26ecaa32266e0c8699f280dd190811c310f2939c.tar.xz samba-26ecaa32266e0c8699f280dd190811c310f2939c.zip |
- move all SMB server stuff to smb_server/*
and create the SMB server subsystem
- remove unused XML and MYSQL configure tests
metze
(This used to be commit 956d212c83d8ebd8e31ec109f17dc2105ca72c30)
Diffstat (limited to 'source4/smb_server')
-rw-r--r-- | source4/smb_server/config.m4 | 16 | ||||
-rw-r--r-- | source4/smb_server/conn.c | 158 | ||||
-rw-r--r-- | source4/smb_server/connection.c | 223 | ||||
-rw-r--r-- | source4/smb_server/negprot.c | 526 | ||||
-rw-r--r-- | source4/smb_server/nttrans.c | 273 | ||||
-rw-r--r-- | source4/smb_server/password.c | 492 | ||||
-rw-r--r-- | source4/smb_server/reply.c | 2380 | ||||
-rw-r--r-- | source4/smb_server/request.c | 602 | ||||
-rw-r--r-- | source4/smb_server/search.c | 229 | ||||
-rw-r--r-- | source4/smb_server/service.c | 339 | ||||
-rw-r--r-- | source4/smb_server/session.c | 42 | ||||
-rw-r--r-- | source4/smb_server/sesssetup.c | 149 | ||||
-rw-r--r-- | source4/smb_server/smb_server.c | 769 | ||||
-rw-r--r-- | source4/smb_server/trans2.c | 1411 |
14 files changed, 7609 insertions, 0 deletions
diff --git a/source4/smb_server/config.m4 b/source4/smb_server/config.m4 new file mode 100644 index 0000000000..4ce688850c --- /dev/null +++ b/source4/smb_server/config.m4 @@ -0,0 +1,16 @@ +dnl # SMB server subsystem + +SMB_SUBSYSTEM(SMB,smb_server/smb_server.o, + [smb_server/conn.o ]\ + [smb_server/connection.o]\ + [smb_server/negprot.o]\ + [smb_server/nttrans.o]\ + [smb_server/password.o]\ + [smb_server/reply.o]\ + [smb_server/request.o]\ + [smb_server/search.o]\ + [smb_server/service.o]\ + [smb_server/session.o]\ + [smb_server/sesssetup.o]\ + [smb_server/trans2.o], + smb_server/smb_server_public_proto.h) diff --git a/source4/smb_server/conn.c b/source4/smb_server/conn.c new file mode 100644 index 0000000000..0498b3cb5d --- /dev/null +++ b/source4/smb_server/conn.c @@ -0,0 +1,158 @@ +/* + Unix SMB/CIFS implementation. + Manage connections_struct structures + Copyright (C) Andrew Tridgell 1998 + Copyright (C) Alexander Bokovoy 2002 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" + +/* set these to define the limits of the server. NOTE These are on a + per-client basis. Thus any one machine can't connect to more than + MAX_CONNECTIONS services, but any number of machines may connect at + one time. */ +#define MAX_CONNECTIONS 128 + +/**************************************************************************** +init the conn structures +****************************************************************************/ +void conn_init(struct server_context *smb) +{ + smb->tree.bmap = bitmap_allocate(MAX_CONNECTIONS); +} + +/**************************************************************************** +check if a snum is in use +****************************************************************************/ +BOOL conn_snum_used(struct server_context *smb, int snum) +{ + struct tcon_context *conn; + for (conn=smb->tree.connections;conn;conn=conn->next) { + if (conn->service == snum) { + return(True); + } + } + return(False); +} + + +/**************************************************************************** +find a conn given a cnum +****************************************************************************/ +struct tcon_context *conn_find(struct server_context *smb, unsigned cnum) +{ + int count=0; + struct tcon_context *conn; + + for (conn=smb->tree.connections;conn;conn=conn->next,count++) { + if (conn->cnum == cnum) { + if (count > 10) { + DLIST_PROMOTE(smb->tree.connections, conn); + } + return conn; + } + } + + return NULL; +} + + +/**************************************************************************** + find first available connection slot, starting from a random position. +The randomisation stops problems with the server dieing and clients +thinking the server is still available. +****************************************************************************/ +struct tcon_context *conn_new(struct server_context *smb) +{ + TALLOC_CTX *mem_ctx; + struct tcon_context *conn; + int i; + + i = bitmap_find(smb->tree.bmap, 1); + + if (i == -1) { + DEBUG(1,("ERROR! Out of connection structures\n")); + return NULL; + } + + mem_ctx = talloc_init("tcon_context[%d]", i); + + conn = (struct tcon_context *)talloc(mem_ctx, sizeof(*conn)); + if (!conn) return NULL; + + ZERO_STRUCTP(conn); + + conn->mem_ctx = mem_ctx; + conn->cnum = i; + conn->smb = smb; + + bitmap_set(smb->tree.bmap, i); + + smb->tree.num_open++; + + DLIST_ADD(smb->tree.connections, conn); + + return conn; +} + +/**************************************************************************** +close all conn structures +****************************************************************************/ +void conn_close_all(struct server_context *smb) +{ + struct tcon_context *conn, *next; + for (conn=smb->tree.connections;conn;conn=next) { + next=conn->next; + close_cnum(conn); + } +} + + +#if REWRITE_REMOVED +/**************************************************************************** +clear a vuid out of the validity cache, and as the 'owner' of a connection. +****************************************************************************/ +void conn_clear_vuid_cache(struct server_context *smb, uint16 vuid) +{ + struct tcon_context *conn; + unsigned int i; + + for (conn=smb->tree.connections;conn;conn=conn->next) { + for (i=0;i<conn->vuid_cache.entries && i< VUID_CACHE_SIZE;i++) { + if (conn->vuid_cache.list[i] == vuid) { + conn->vuid_cache.list[i] = UID_FIELD_INVALID; + } + } + } +} +#endif + +/**************************************************************************** + Free a conn structure. +****************************************************************************/ + +void conn_free(struct server_context *smb, struct tcon_context *conn) +{ + DLIST_REMOVE(smb->tree.connections, conn); + + bitmap_clear(smb->tree.bmap, conn->cnum); + smb->tree.num_open--; + + ZERO_STRUCTP(conn); + SAFE_FREE(conn); +} + diff --git a/source4/smb_server/connection.c b/source4/smb_server/connection.c new file mode 100644 index 0000000000..35f5138956 --- /dev/null +++ b/source4/smb_server/connection.c @@ -0,0 +1,223 @@ +/* + Unix SMB/CIFS implementation. + connection claim routines + Copyright (C) Andrew Tridgell 1998 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" + +static TDB_CONTEXT *tdb; + + +static void make_conn_key(struct tcon_context *conn, const char *name, TDB_DATA *pkbuf, struct connections_key *pkey) +{ + ZERO_STRUCTP(pkey); + pkey->pid = getpid(); + pkey->cnum = conn?conn->cnum:-1; + fstrcpy(pkey->name, name); + + pkbuf->dptr = (char *)pkey; + pkbuf->dsize = sizeof(*pkey); +} + +/**************************************************************************** + Delete a connection record. +****************************************************************************/ + +BOOL yield_connection(struct tcon_context *conn, const char *name) +{ + struct connections_key key; + TDB_DATA kbuf; + + if (!tdb) + return False; + + DEBUG(3,("Yielding connection to %s\n",name)); + + make_conn_key(conn, name, &kbuf, &key); + + if (tdb_delete(tdb, kbuf) != 0) { + int dbg_lvl = (!conn && (tdb_error(tdb) == TDB_ERR_NOEXIST)) ? 3 : 0; + DEBUG(dbg_lvl,("yield_connection: tdb_delete for name %s failed with error %s.\n", + name, tdb_errorstr(tdb) )); + return (False); + } + + return(True); +} + +struct count_stat { + pid_t mypid; + int curr_connections; + char *name; + BOOL Clear; +}; + +/**************************************************************************** + Count the entries belonging to a service in the connection db. +****************************************************************************/ + +static int count_fn( TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *udp) +{ + struct connections_data crec; + struct count_stat *cs = (struct count_stat *)udp; + + if (dbuf.dsize != sizeof(crec)) + return 0; + + memcpy(&crec, dbuf.dptr, sizeof(crec)); + + if (crec.cnum == -1) + return 0; + + /* If the pid was not found delete the entry from connections.tdb */ + + if (cs->Clear && !process_exists(crec.pid) && (errno == ESRCH)) { + DEBUG(2,("pid %u doesn't exist - deleting connections %d [%s]\n", + (unsigned int)crec.pid, crec.cnum, crec.name)); + if (tdb_delete(the_tdb, kbuf) != 0) + DEBUG(0,("count_fn: tdb_delete failed with error %s\n", tdb_errorstr(tdb) )); + return 0; + } + + if (strequal(crec.name, cs->name)) + cs->curr_connections++; + + return 0; +} + +/**************************************************************************** + Claim an entry in the connections database. +****************************************************************************/ + +BOOL claim_connection(struct tcon_context *conn, const char *name,int max_connections,BOOL Clear, uint32 msg_flags) +{ + struct connections_key key; + struct connections_data crec; + TDB_DATA kbuf, dbuf; + + if (!tdb) + tdb = tdb_open_log(lock_path(conn->mem_ctx, "connections.tdb"), 0, TDB_CLEAR_IF_FIRST|TDB_DEFAULT, + O_RDWR | O_CREAT, 0644); + + if (!tdb) + return False; + + /* + * Enforce the max connections parameter. + */ + + if (max_connections > 0) { + struct count_stat cs; + + cs.mypid = getpid(); + cs.curr_connections = 0; + cs.name = lp_servicename(SNUM(conn)); + cs.Clear = Clear; + + /* + * This has a race condition, but locking the chain before hand is worse + * as it leads to deadlock. + */ + + if (tdb_traverse(tdb, count_fn, &cs) == -1) { + DEBUG(0,("claim_connection: traverse of connections.tdb failed with error %s.\n", + tdb_errorstr(tdb) )); + return False; + } + + if (cs.curr_connections >= max_connections) { + DEBUG(1,("claim_connection: Max connections (%d) exceeded for %s\n", + max_connections, name )); + return False; + } + } + + DEBUG(5,("claiming %s %d\n",name,max_connections)); + + make_conn_key(conn, name, &kbuf, &key); + + /* fill in the crec */ + ZERO_STRUCT(crec); + crec.magic = 0x280267; + crec.pid = getpid(); + crec.cnum = conn?conn->cnum:-1; + if (conn) { + crec.uid = -1; + crec.gid = -1; + StrnCpy(crec.name, + lp_servicename(SNUM(conn)),sizeof(crec.name)-1); + } + crec.start = time(NULL); + crec.bcast_msg_flags = msg_flags; + + StrnCpy(crec.machine,sub_get_remote_machine(),sizeof(crec.machine)-1); + StrnCpy(crec.addr,conn?conn->smb->socket.client_addr:"NONE",sizeof(crec.addr)-1); + + dbuf.dptr = (char *)&crec; + dbuf.dsize = sizeof(crec); + + if (tdb_store(tdb, kbuf, dbuf, TDB_REPLACE) != 0) { + DEBUG(0,("claim_connection: tdb_store failed with error %s.\n", + tdb_errorstr(tdb) )); + return False; + } + + return True; +} + +BOOL register_message_flags(BOOL doreg, uint32 msg_flags) +{ + struct connections_key key; + struct connections_data *pcrec; + TDB_DATA kbuf, dbuf; + + if (!tdb) + return False; + + DEBUG(10,("register_message_flags: %s flags 0x%x\n", + doreg ? "adding" : "removing", + (unsigned int)msg_flags )); + + make_conn_key(NULL, "", &kbuf, &key); + + dbuf = tdb_fetch(tdb, kbuf); + if (!dbuf.dptr) { + DEBUG(0,("register_message_flags: tdb_fetch failed\n")); + return False; + } + + pcrec = (struct connections_data *)dbuf.dptr; + pcrec->bcast_msg_flags = msg_flags; + if (doreg) + pcrec->bcast_msg_flags |= msg_flags; + else + pcrec->bcast_msg_flags &= ~msg_flags; + + if (tdb_store(tdb, kbuf, dbuf, TDB_REPLACE) != 0) { + DEBUG(0,("register_message_flags: tdb_store failed with error %s.\n", + tdb_errorstr(tdb) )); + SAFE_FREE(dbuf.dptr); + return False; + } + + DEBUG(10,("register_message_flags: new flags 0x%x\n", + (unsigned int)pcrec->bcast_msg_flags )); + + SAFE_FREE(dbuf.dptr); + return True; +} diff --git a/source4/smb_server/negprot.c b/source4/smb_server/negprot.c new file mode 100644 index 0000000000..caf3ce33b9 --- /dev/null +++ b/source4/smb_server/negprot.c @@ -0,0 +1,526 @@ +/* + Unix SMB/CIFS implementation. + negprot reply code + Copyright (C) Andrew Tridgell 1992-1998 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" + +/* initialise the auth_context for this server and return the cryptkey */ +static void get_challenge(struct server_context *smb, char buff[8]) +{ + NTSTATUS nt_status; + const uint8 *cryptkey; + + /* muliple negprots are not premitted */ + if (smb->negotiate.auth_context) { + DEBUG(3,("get challenge: is this a secondary negprot? auth_context is non-NULL!\n")); + smb_panic("secondary negprot"); + } + + DEBUG(10, ("get challenge: creating negprot_global_auth_context\n")); + + nt_status = make_auth_context_subsystem(&smb->negotiate.auth_context); + + if (!NT_STATUS_IS_OK(nt_status)) { + DEBUG(0, ("make_auth_context_subsystem returned %s", nt_errstr(nt_status))); + smb_panic("cannot make_negprot_global_auth_context!\n"); + } + + DEBUG(10, ("get challenge: getting challenge\n")); + cryptkey = smb->negotiate.auth_context->get_ntlm_challenge(smb->negotiate.auth_context); + memcpy(buff, cryptkey, 8); +} + +/**************************************************************************** + Reply for the core protocol. +****************************************************************************/ +static void reply_corep(struct request_context *req, uint16 choice) +{ + req_setup_reply(req, 1, 0); + + SSVAL(req->out.vwv, VWV(0), choice); + + req->smb->negotiate.protocol = PROTOCOL_CORE; + + req_send_reply(req); +} + +/**************************************************************************** + Reply for the coreplus protocol. +this is quite incomplete - we only fill in a small part of the reply, but as nobody uses +this any more it probably doesn't matter +****************************************************************************/ +static void reply_coreplus(struct request_context *req, uint16 choice) +{ + uint16 raw = (lp_readraw()?1:0) | (lp_writeraw()?2:0); + + req_setup_reply(req, 13, 0); + + /* Reply, SMBlockread, SMBwritelock supported. */ + SCVAL(req->out.hdr,HDR_FLG, + CVAL(req->out.hdr, HDR_FLG) | FLAG_SUPPORT_LOCKREAD); + + SSVAL(req->out.vwv, VWV(0), choice); + SSVAL(req->out.vwv, VWV(1), 0x1); /* user level security, don't encrypt */ + + /* tell redirector we support + readbraw and writebraw (possibly) */ + SSVAL(req->out.vwv, VWV(5), raw); + + req->smb->negotiate.protocol = PROTOCOL_COREPLUS; + + req_send_reply(req); +} + +/**************************************************************************** + Reply for the lanman 1.0 protocol. +****************************************************************************/ +static void reply_lanman1(struct request_context *req, uint16 choice) +{ + int raw = (lp_readraw()?1:0) | (lp_writeraw()?2:0); + int secword=0; + time_t t = req->request_time.tv_sec; + + req->smb->negotiate.encrypted_passwords = lp_encrypted_passwords(); + + if (lp_security() != SEC_SHARE) + secword |= NEGOTIATE_SECURITY_USER_LEVEL; + + if (req->smb->negotiate.encrypted_passwords) + secword |= NEGOTIATE_SECURITY_CHALLENGE_RESPONSE; + + req->smb->negotiate.protocol = PROTOCOL_LANMAN1; + + req_setup_reply(req, 13, req->smb->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->negotiate.max_recv); + SSVAL(req->out.vwv, VWV(3), lp_maxmux()); + SSVAL(req->out.vwv, VWV(4), 1); + SSVAL(req->out.vwv, VWV(5), raw); + SIVAL(req->out.vwv, VWV(6), req->smb->pid); + put_dos_date(req->out.vwv, VWV(8), t); + SSVAL(req->out.vwv, VWV(10), TimeDiff(t)/60); + + /* Create a token value and add it to the outgoing packet. */ + if (req->smb->negotiate.encrypted_passwords) { + SSVAL(req->out.vwv, VWV(11), 8); + get_challenge(req->smb, req->out.data); + } + + req_send_reply(req); +} + +/**************************************************************************** + Reply for the lanman 2.0 protocol. +****************************************************************************/ +static void reply_lanman2(struct request_context *req, uint16 choice) +{ + int raw = (lp_readraw()?1:0) | (lp_writeraw()?2:0); + int secword=0; + time_t t = req->request_time.tv_sec; + + req->smb->negotiate.encrypted_passwords = lp_encrypted_passwords(); + + if (lp_security() != SEC_SHARE) + secword |= NEGOTIATE_SECURITY_USER_LEVEL; + + if (req->smb->negotiate.encrypted_passwords) + secword |= NEGOTIATE_SECURITY_CHALLENGE_RESPONSE; + + req->smb->negotiate.protocol = PROTOCOL_LANMAN2; + + req_setup_reply(req, 13, 0); + + SSVAL(req->out.vwv, VWV(0), choice); + SSVAL(req->out.vwv, VWV(1), secword); + SSVAL(req->out.vwv, VWV(2), req->smb->negotiate.max_recv); + SSVAL(req->out.vwv, VWV(3), lp_maxmux()); + SSVAL(req->out.vwv, VWV(4), 1); + SSVAL(req->out.vwv, VWV(5), raw); + SIVAL(req->out.vwv, VWV(6), req->smb->pid); + put_dos_date(req->out.vwv, VWV(8), t); + SSVAL(req->out.vwv, VWV(10), TimeDiff(t)/60); + + /* Create a token value and add it to the outgoing packet. */ + if (req->smb->negotiate.encrypted_passwords) { + SSVAL(req->out.vwv, VWV(11), 8); + req_grow_data(req, 8); + get_challenge(req->smb, req->out.data); + } + + req_push_str(req, NULL, lp_workgroup(), -1, STR_TERMINATE); + + req_send_reply(req); +} + + +#if 0 +/**************************************************************************** + Generate the spnego negprot reply blob. Return the number of bytes used. +****************************************************************************/ +static DATA_BLOB negprot_spnego(struct server_context *smb) +{ + DATA_BLOB blob; + uint8 guid[16]; + const char *OIDs_krb5[] = {OID_KERBEROS5, + OID_KERBEROS5_OLD, + OID_NTLMSSP, + NULL}; + const char *OIDs_plain[] = {OID_NTLMSSP, NULL}; + char *principal; + + smb->negotiate.spnego_negotiated = True; + + memset(guid, 0, 16); + safe_strcpy((char *)guid, lp_netbios_name(), 16); + strlower((char *)guid); + +#if 0 + /* strangely enough, NT does not send the single OID NTLMSSP when + not a ADS member, it sends no OIDs at all + + we can't do this until we teach our sesssion setup parser to know + about raw NTLMSSP (clients send no ASN.1 wrapping if we do this) + */ + if (lp_security() != SEC_ADS) { + memcpy(p, guid, 16); + return 16; + } +#endif + if (lp_security() != SEC_ADS) { + blob = spnego_gen_negTokenInit(guid, OIDs_plain, "NONE"); + } else { + asprintf(&principal, "%s$@%s", guid, lp_realm()); + blob = spnego_gen_negTokenInit(guid, OIDs_krb5, principal); + free(principal); + } + + return blob; +} +#endif + +/**************************************************************************** + Reply for the nt protocol. +****************************************************************************/ +static void reply_nt1(struct request_context *req, uint16 choice) +{ + /* dual names + lock_and_read + nt SMBs + remote API calls */ + int capabilities; + int secword=0; + time_t t = req->request_time.tv_sec; + BOOL negotiate_spnego = False; + + capabilities = + CAP_NT_FIND | CAP_LOCK_AND_READ | + CAP_LEVEL_II_OPLOCKS | CAP_NT_SMBS | CAP_RPC_REMOTE_APIS; + + req->smb->negotiate.encrypted_passwords = lp_encrypted_passwords(); + + /* do spnego in user level security if the client + supports it and we can do encrypted passwords */ + + if (req->smb->negotiate.encrypted_passwords && + (lp_security() != SEC_SHARE) && + lp_use_spnego() && + (req->flags2 & FLAGS2_EXTENDED_SECURITY)) { +/* REWRITE negotiate_spnego = True; + capabilities |= CAP_EXTENDED_SECURITY; +*/ + } + + if (lp_unix_extensions()) { + capabilities |= CAP_UNIX; + } + + if (lp_large_readwrite() && (SMB_OFF_T_BITS == 64)) { + capabilities |= CAP_LARGE_READX | CAP_LARGE_WRITEX | CAP_W2K_SMBS; + } + + if (SMB_OFF_T_BITS >= 64) { + capabilities |= CAP_LARGE_FILES; + } + + if (lp_readraw() && lp_writeraw()) { + capabilities |= CAP_RAW_MODE; + } + + /* allow for disabling unicode */ + if (lp_unicode()) { + capabilities |= CAP_UNICODE; + } + + if (lp_nt_status_support()) { + capabilities |= CAP_STATUS32; + } + + if (lp_host_msdfs()) { + capabilities |= CAP_DFS; + } + + if (lp_security() != SEC_SHARE) { + secword |= NEGOTIATE_SECURITY_USER_LEVEL; + } + + if (req->smb->negotiate.encrypted_passwords) { + secword |= NEGOTIATE_SECURITY_CHALLENGE_RESPONSE; + } + + req->smb->negotiate.protocol = PROTOCOL_NT1; + + req_setup_reply(req, 17, 0); + + SSVAL(req->out.vwv, VWV(0), choice); + SCVAL(req->out.vwv, VWV(1), secword); + + /* notice the strange +1 on vwv here? That's because + this is the one and only SMB packet that is malformed in + the specification - all the command words after the secword + are offset by 1 byte */ + SSVAL(req->out.vwv+1, VWV(1), lp_maxmux()); + SSVAL(req->out.vwv+1, VWV(2), 1); /* num vcs */ + SIVAL(req->out.vwv+1, VWV(3), req->smb->negotiate.max_recv); + SIVAL(req->out.vwv+1, VWV(5), 0x10000); /* raw size. full 64k */ + SIVAL(req->out.vwv+1, VWV(7), req->smb->pid); /* session key */ + SIVAL(req->out.vwv+1, VWV(9), capabilities); + put_long_date(req->out.vwv + VWV(11) + 1, t); + SSVALS(req->out.vwv+1,VWV(15), TimeDiff(t)/60); + + if (!negotiate_spnego) { + /* Create a token value and add it to the outgoing packet. */ + if (req->smb->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, req->out.ptr); + req->out.ptr += 8; + SCVAL(req->out.vwv+1, VWV(16), 8); + } + req_push_str(req, NULL, lp_workgroup(), -1, STR_UNICODE|STR_TERMINATE|STR_NOALIGN); + DEBUG(3,("not using SPNEGO\n")); + } else { +#if 0 + DATA_BLOB blob = negprot_spnego(req->smb); + + req_grow_data(req, blob.length); + memcpy(req->out.ptr, blob.data, blob.length); + DEBUG(3,("using SPNEGO\n")); +#else + exit_server(req->smb, "no SPNEGO please"); +#endif + } + + req_send_reply(req); +} + +/* these are the protocol lists used for auto architecture detection: + +WinNT 3.51: +protocol [PC NETWORK PROGRAM 1.0] +protocol [XENIX CORE] +protocol [MICROSOFT NETWORKS 1.03] +protocol [LANMAN1.0] +protocol [Windows for Workgroups 3.1a] +protocol [LM1.2X002] +protocol [LANMAN2.1] +protocol [NT LM 0.12] + +Win95: +protocol [PC NETWORK PROGRAM 1.0] +protocol [XENIX CORE] +protocol [MICROSOFT NETWORKS 1.03] +protocol [LANMAN1.0] +protocol [Windows for Workgroups 3.1a] +protocol [LM1.2X002] +protocol [LANMAN2.1] +protocol [NT LM 0.12] + +Win2K: +protocol [PC NETWORK PROGRAM 1.0] +protocol [LANMAN1.0] +protocol [Windows for Workgroups 3.1a] +protocol [LM1.2X002] +protocol [LANMAN2.1] +protocol [NT LM 0.12] + +OS/2: +protocol [PC NETWORK PROGRAM 1.0] +protocol [XENIX CORE] +protocol [LANMAN1.0] +protocol [LM1.2X002] +protocol [LANMAN2.1] +*/ + +/* + * Modified to recognize the architecture of the remote machine better. + * + * This appears to be the matrix of which protocol is used by which + * MS product. + Protocol WfWg Win95 WinNT Win2K OS/2 + PC NETWORK PROGRAM 1.0 1 1 1 1 1 + XENIX CORE 2 2 + MICROSOFT NETWORKS 3.0 2 2 + DOS LM1.2X002 3 3 + MICROSOFT NETWORKS 1.03 3 + DOS LANMAN2.1 4 4 + LANMAN1.0 4 2 3 + Windows for Workgroups 3.1a 5 5 5 3 + LM1.2X002 6 4 4 + LANMAN2.1 7 5 5 + NT LM 0.12 6 8 6 + * + * tim@fsg.com 09/29/95 + * Win2K added by matty 17/7/99 + */ + +#define ARCH_WFWG 0x3 /* This is a fudge because WfWg is like Win95 */ +#define ARCH_WIN95 0x2 +#define ARCH_WINNT 0x4 +#define ARCH_WIN2K 0xC /* Win2K is like NT */ +#define ARCH_OS2 0x14 /* Again OS/2 is like NT */ +#define ARCH_SAMBA 0x20 + +#define ARCH_ALL 0x3F + +/* List of supported protocols, most desired first */ +static const struct { + const char *proto_name; + const char *short_name; + void (*proto_reply_fn)(struct request_context *req, uint16 choice); + int protocol_level; +} supported_protocols[] = { + {"NT LANMAN 1.0", "NT1", reply_nt1, PROTOCOL_NT1}, + {"NT LM 0.12", "NT1", reply_nt1, PROTOCOL_NT1}, + {"LM1.2X002", "LANMAN2", reply_lanman2, PROTOCOL_LANMAN2}, + {"Samba", "LANMAN2", reply_lanman2, PROTOCOL_LANMAN2}, + {"DOS LM1.2X002", "LANMAN2", reply_lanman2, PROTOCOL_LANMAN2}, + {"LANMAN1.0", "LANMAN1", reply_lanman1, PROTOCOL_LANMAN1}, + {"MICROSOFT NETWORKS 3.0", "LANMAN1", reply_lanman1, PROTOCOL_LANMAN1}, + {"MICROSOFT NETWORKS 1.03", "COREPLUS", reply_coreplus, PROTOCOL_COREPLUS}, + {"PC NETWORK PROGRAM 1.0", "CORE", reply_corep, PROTOCOL_CORE}, + {NULL,NULL,NULL,0}, +}; + +/**************************************************************************** + Reply to a negprot. +****************************************************************************/ + +void reply_negprot(struct request_context *req) +{ + int Index=0; + int choice = -1; + int protocol; + char *p; + int arch = ARCH_ALL; + + if (req->smb->negotiate.done_negprot) { + exit_server(req->smb, "multiple negprot's are not permitted"); + } + req->smb->negotiate.done_negprot = True; + + p = req->in.data + 1; + + while (p < req->in.data + req->in.data_size) { + Index++; + DEBUG(3,("Requested protocol [%s]\n",p)); + if (strcmp(p,"Windows for Workgroups 3.1a") == 0) + arch &= ( ARCH_WFWG | ARCH_WIN95 | ARCH_WINNT | ARCH_WIN2K ); + else if (strcmp(p,"DOS LM1.2X002") == 0) + arch &= ( ARCH_WFWG | ARCH_WIN95 ); + else if (strcmp(p,"DOS LANMAN2.1") == 0) + arch &= ( ARCH_WFWG | ARCH_WIN95 ); + else if (strcmp(p,"NT LM 0.12") == 0) + arch &= ( ARCH_WIN95 | ARCH_WINNT | ARCH_WIN2K ); + else if (strcmp(p,"LANMAN2.1") == 0) + arch &= ( ARCH_WINNT | ARCH_WIN2K | ARCH_OS2 ); + else if (strcmp(p,"LM1.2X002") == 0) + arch &= ( ARCH_WINNT | ARCH_WIN2K | ARCH_OS2 ); + else if (strcmp(p,"MICROSOFT NETWORKS 1.03") == 0) + arch &= ARCH_WINNT; + else if (strcmp(p,"XENIX CORE") == 0) + arch &= ( ARCH_WINNT | ARCH_OS2 ); + else if (strcmp(p,"Samba") == 0) { + arch = ARCH_SAMBA; + break; + } + + p += strlen(p) + 2; + } + + switch (arch) { + case ARCH_SAMBA: + set_remote_arch(req->smb, RA_SAMBA); + break; + case ARCH_WFWG: + set_remote_arch(req->smb, RA_WFWG); + break; + case ARCH_WIN95: + set_remote_arch(req->smb, RA_WIN95); + break; + case ARCH_WINNT: + if (req->flags2==FLAGS2_WIN2K_SIGNATURE) + set_remote_arch(req->smb, RA_WIN2K); + else + set_remote_arch(req->smb, RA_WINNT); + break; + case ARCH_WIN2K: + set_remote_arch(req->smb, RA_WIN2K); + break; + case ARCH_OS2: + set_remote_arch(req->smb, RA_OS2); + break; + default: + set_remote_arch(req->smb, RA_UNKNOWN); + break; + } + + /* possibly reload - change of architecture */ + reload_services(req->smb, True); + + /* Check for protocols, most desirable first */ + for (protocol = 0; supported_protocols[protocol].proto_name; protocol++) { + p = req->in.data+1; + Index = 0; + if ((supported_protocols[protocol].protocol_level <= lp_maxprotocol()) && + (supported_protocols[protocol].protocol_level >= lp_minprotocol())) + while (p < (req->in.data + req->in.data_size)) { + if (strequal(p,supported_protocols[protocol].proto_name)) + choice = Index; + Index++; + p += strlen(p) + 2; + } + if(choice != -1) + break; + } + + if(choice != -1) { + sub_set_remote_proto(supported_protocols[protocol].short_name); + reload_services(req->smb, True); + supported_protocols[protocol].proto_reply_fn(req, choice); + DEBUG(3,("Selected protocol %s\n",supported_protocols[protocol].proto_name)); + } else { + DEBUG(0,("No protocol supported !\n")); + } + + DEBUG(5,("negprot index=%d\n", choice)); +} diff --git a/source4/smb_server/nttrans.c b/source4/smb_server/nttrans.c new file mode 100644 index 0000000000..13afdeeea2 --- /dev/null +++ b/source4/smb_server/nttrans.c @@ -0,0 +1,273 @@ +/* + Unix SMB/CIFS implementation. + NT transaction handling + Copyright (C) Andrew Tridgell 2003 + Copyright (C) James J Myers 2003 <myersjj@samba.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +/* + This file handles the parsing of transact2 requests +*/ + +#include "includes.h" + + +#define CHECK_MIN_BLOB_SIZE(blob, size) do { \ + if ((blob)->length < (size)) { \ + return NT_STATUS_INFO_LENGTH_MISMATCH; \ + }} while (0) + + +/* setup a nttrans reply, given the data and params sizes */ +static void nttrans_setup_reply(struct request_context *req, + struct smb_nttrans *trans, + uint16 param_size, uint16 data_size, + uint16 setup_count) +{ + trans->out.setup_count = setup_count; + if (setup_count != 0) { + trans->out.setup = talloc_zero(req->mem_ctx, sizeof(uint16) * setup_count); + } + trans->out.params = data_blob_talloc(req->mem_ctx, NULL, param_size); + trans->out.data = data_blob_talloc(req->mem_ctx, NULL, data_size); +} + + +/* parse NTTRANS_CREATE request + */ +static NTSTATUS nttrans_create(struct request_context *req, + struct smb_nttrans *trans) +{ + return NT_STATUS_FOOBAR; +} + +/* parse NTTRANS_RENAME request + */ +static NTSTATUS nttrans_rename(struct request_context *req, + struct smb_nttrans *trans) +{ + return NT_STATUS_FOOBAR; +} +/* parse NTTRANS_IOCTL request + */ +static NTSTATUS nttrans_ioctl(struct request_context *req, + struct smb_nttrans *trans) +{ + union smb_ioctl nt; + uint32 function; + uint16 fnum; + uint8 filter; + BOOL fsctl; + DATA_BLOB *blob; + + /* should have at least 4 setup words */ + if (trans->in.setup_count != 4) { + return NT_STATUS_INVALID_PARAMETER; + } + + function = IVAL(trans->in.setup, 0); + fnum = SVAL(trans->in.setup, 4); + fsctl = CVAL(trans->in.setup, 6); + filter = CVAL(trans->in.setup, 7); + + blob = &trans->in.data; + + nt.ntioctl.level = RAW_IOCTL_NTIOCTL; + nt.ntioctl.in.fnum = fnum; + nt.ntioctl.in.function = function; + nt.ntioctl.in.fsctl = fsctl; + nt.ntioctl.in.filter = filter; + + nttrans_setup_reply(req, trans, 0, 0, 1); + trans->out.setup[0] = 0; + + return req->conn->ntvfs_ops->ioctl(req, &nt); +} + +/* + backend for nttrans requests +*/ +static NTSTATUS nttrans_backend(struct request_context *req, + struct smb_nttrans *trans) +{ + DEBUG(9,("nttrans_backend: setup_count=%d function=%d\n", + trans->in.setup_count, trans->in.function)); + /* must have at least one setup word */ + if (trans->in.setup_count < 1) { + return NT_STATUS_FOOBAR; + } + + /* the nttrans command is in function */ + switch (trans->in.function) { + case NT_TRANSACT_CREATE: + return nttrans_create(req, trans); + case NT_TRANSACT_IOCTL: + return nttrans_ioctl(req, trans); + case NT_TRANSACT_RENAME: + return nttrans_rename(req, trans); + } + + /* an unknown nttrans command */ + return NT_STATUS_FOOBAR; +} + + +/**************************************************************************** + Reply to an SMBnttrans request +****************************************************************************/ +void reply_nttrans(struct request_context *req) +{ + struct smb_nttrans trans; + int i; + uint16 param_ofs, data_ofs; + uint16 param_count, data_count; + uint16 params_left, data_left; + uint16 param_total, data_total; + char *params, *data; + NTSTATUS status; + + /* parse request */ + if (req->in.wct < 19) { + req_reply_error(req, NT_STATUS_FOOBAR); + return; + } + + trans.in.max_setup = CVAL(req->in.vwv, 0); + param_total = IVAL(req->in.vwv, 3); + data_total = IVAL(req->in.vwv, 7); + trans.in.max_param = IVAL(req->in.vwv, 11); + trans.in.max_data = IVAL(req->in.vwv, 15); + param_count = IVAL(req->in.vwv, 19); + param_ofs = IVAL(req->in.vwv, 23); + data_count = IVAL(req->in.vwv, 27); + data_ofs = IVAL(req->in.vwv, 31); + trans.in.setup_count = CVAL(req->in.vwv, 35); + trans.in.function = SVAL(req->in.vwv, 36); + + if (req->in.wct != 19 + trans.in.setup_count) { + req_reply_dos_error(req, ERRSRV, ERRerror); + return; + } + + /* parse out the setup words */ + trans.in.setup = talloc(req->mem_ctx, trans.in.setup_count * sizeof(uint16)); + if (!trans.in.setup) { + req_reply_error(req, NT_STATUS_NO_MEMORY); + return; + } + for (i=0;i<trans.in.setup_count;i++) { + trans.in.setup[i] = SVAL(req->in.vwv, VWV(19+i)); + } + + if (!req_pull_blob(req, req->in.hdr + param_ofs, param_count, &trans.in.params) || + !req_pull_blob(req, req->in.hdr + data_ofs, data_count, &trans.in.data)) { + req_reply_error(req, NT_STATUS_FOOBAR); + return; + } + + /* is it a partial request? if so, then send a 'send more' message */ + if (param_total > param_count || + data_total > data_count) { + DEBUG(0,("REWRITE: not handling partial nttrans requests!\n")); + return; + } + + /* its a full request, give it to the backend */ + status = nttrans_backend(req, &trans); + + if (!NT_STATUS_IS_OK(status)) { + req_reply_error(req, status); + return; + } + + params_left = trans.out.params.length; + data_left = trans.out.data.length; + params = trans.out.params.data; + data = trans.out.data.data; + + req->control_flags |= REQ_CONTROL_PROTECTED; + + /* we need to divide up the reply into chunks that fit into + the negotiated buffer size */ + do { + uint16 this_data, this_param, max_bytes; + uint_t align1 = 1, align2 = (params_left ? 2 : 0); + + req_setup_reply(req, 18 + trans.out.setup_count, 0); + + 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; + } + + req_grow_data(req, this_param + this_data + (align1 + align2)); + + SIVAL(req->out.vwv, 3, trans.out.params.length); + SIVAL(req->out.vwv, 7, trans.out.data.length); + + SIVAL(req->out.vwv, 11, this_param); + SIVAL(req->out.vwv, 15, align1 + PTR_DIFF(req->out.data, req->out.hdr)); + SIVAL(req->out.vwv, 19, PTR_DIFF(params, trans.out.params.data)); + + SIVAL(req->out.vwv, 23, this_data); + SIVAL(req->out.vwv, 27, align1 + align2 + + PTR_DIFF(req->out.data + this_param, req->out.hdr)); + SIVAL(req->out.vwv, 31, PTR_DIFF(data, trans.out.data.data)); + + SCVAL(req->out.vwv, 35, trans.out.setup_count); + for (i=0;i<trans.out.setup_count;i++) { + SSVAL(req->out.vwv, VWV(18+i)+1, trans.out.setup[i]); + } + + memset(req->out.data, 0, align1); + if (this_param != 0) { + memcpy(req->out.data + align1, params, this_param); + } + memset(req->out.data+this_param+align1, 0, align2); + if (this_data != 0) { + memcpy(req->out.data+this_param+align1+align2, data, this_data); + } + + params_left -= this_param; + data_left -= this_data; + params += this_param; + data += this_data; + + /* if this is the last chunk then the request can be destroyed */ + if (params_left == 0 && data_left == 0) { + req->control_flags &= ~REQ_CONTROL_PROTECTED; + } + + req_send_reply(req); + } while (params_left != 0 || data_left != 0); +} + + +/**************************************************************************** + Reply to an SMBnttranss request +****************************************************************************/ +void reply_nttranss(struct request_context *req) +{ + req_reply_error(req, NT_STATUS_FOOBAR); +} diff --git a/source4/smb_server/password.c b/source4/smb_server/password.c new file mode 100644 index 0000000000..53530abf7a --- /dev/null +++ b/source4/smb_server/password.c @@ -0,0 +1,492 @@ +/* + Unix SMB/CIFS implementation. + Password and authentication handling + Copyright (C) Andrew Tridgell 1992-1998 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" + + +/**************************************************************************** +check if a uid has been validated, and return an pointer to the user_struct +if it has. NULL if not. vuid is biased by an offset. This allows us to +tell random client vuid's (normally zero) from valid vuids. +****************************************************************************/ +struct user_struct *get_valid_user_struct(struct server_context *smb, uint16 vuid) +{ + user_struct *usp; + int count=0; + + if (vuid == UID_FIELD_INVALID) + return NULL; + + for (usp=smb->users.validated_users;usp;usp=usp->next,count++) { + if (vuid == usp->vuid) { + if (count > 10) { + DLIST_PROMOTE(smb->users.validated_users, usp); + } + return usp; + } + } + + return NULL; +} + +/**************************************************************************** +invalidate a uid +****************************************************************************/ +void invalidate_vuid(struct server_context *smb, uint16 vuid) +{ + user_struct *vuser = get_valid_user_struct(smb, vuid); + + if (vuser == NULL) + return; + + SAFE_FREE(vuser->homedir); + SAFE_FREE(vuser->unix_homedir); + SAFE_FREE(vuser->logon_script); + + session_yield(vuser); + + free_server_info(&vuser->server_info); + + DLIST_REMOVE(smb->users.validated_users, vuser); + + /* clear the vuid from the 'cache' on each connection, and + from the vuid 'owner' of connections */ + /* REWRITE: conn_clear_vuid_cache(smb, vuid); */ + + SAFE_FREE(vuser->groups); + delete_nt_token(&vuser->nt_user_token); + SAFE_FREE(vuser); + smb->users.num_validated_vuids--; +} + +/**************************************************************************** +invalidate all vuid entries for this process +****************************************************************************/ +void invalidate_all_vuids(struct server_context *smb) +{ + user_struct *usp, *next=NULL; + + for (usp=smb->users.validated_users;usp;usp=next) { + next = usp->next; + + invalidate_vuid(smb, usp->vuid); + } +} + +/** + * register that a valid login has been performed, establish 'session'. + * @param server_info The token returned from the authentication process. + * (now 'owned' by register_vuid) + * + * @return Newly allocated vuid, biased by an offset. (This allows us to + * tell random client vuid's (normally zero) from valid vuids.) + * + */ + +int register_vuid(struct server_context *smb, + struct auth_serversupplied_info *server_info, + const char *smb_name) +{ + user_struct *vuser = NULL; + + /* Ensure no vuid gets registered in share level security. */ + if(lp_security() == SEC_SHARE) + return UID_FIELD_INVALID; + + /* Limit allowed vuids to 16bits - VUID_OFFSET. */ + if (smb->users.num_validated_vuids >= 0xFFFF-VUID_OFFSET) + return UID_FIELD_INVALID; + + if((vuser = (user_struct *)malloc( sizeof(user_struct) )) == NULL) { + DEBUG(0,("Failed to malloc users struct!\n")); + return UID_FIELD_INVALID; + } + + ZERO_STRUCTP(vuser); + + /* Allocate a free vuid. Yes this is a linear search... :-) */ + while (get_valid_user_struct(smb, smb->users.next_vuid) != NULL ) { + smb->users.next_vuid++; + /* Check for vuid wrap. */ + if (smb->users.next_vuid == UID_FIELD_INVALID) + smb->users.next_vuid = VUID_OFFSET; + } + + DEBUG(10,("register_vuid: allocated vuid = %u\n", + (unsigned int)smb->users.next_vuid)); + + vuser->vuid = smb->users.next_vuid; + + /* the next functions should be done by a SID mapping system (SMS) as + * the new real sam db won't have reference to unix uids or gids + */ + if (!IS_SAM_UNIX_USER(server_info->sam_account)) { + DEBUG(0,("Attempted session setup with invalid user. No uid/gid in SAM_ACCOUNT\n")); + free(vuser); + free_server_info(&server_info); + return UID_FIELD_INVALID; + } + + vuser->uid = pdb_get_uid(server_info->sam_account); + vuser->gid = pdb_get_gid(server_info->sam_account); + + vuser->n_groups = server_info->n_groups; + if (vuser->n_groups) { + if (!(vuser->groups = memdup(server_info->groups, sizeof(gid_t) * vuser->n_groups))) { + DEBUG(0,("register_vuid: failed to memdup vuser->groups\n")); + free(vuser); + free_server_info(&server_info); + return UID_FIELD_INVALID; + } + } + + vuser->guest = server_info->guest; + fstrcpy(vuser->user.unix_name, pdb_get_username(server_info->sam_account)); + + /* This is a potentially untrusted username */ + alpha_strcpy(vuser->user.smb_name, smb_name, ". _-$", sizeof(vuser->user.smb_name)); + + fstrcpy(vuser->user.domain, pdb_get_domain(server_info->sam_account)); + fstrcpy(vuser->user.full_name, pdb_get_fullname(server_info->sam_account)); + + { + /* Keep the homedir handy */ + const char *homedir = pdb_get_homedir(server_info->sam_account); + const char *unix_homedir = pdb_get_unix_homedir(server_info->sam_account); + const char *logon_script = pdb_get_logon_script(server_info->sam_account); + if (homedir) { + vuser->homedir = smb_xstrdup(homedir); + } + + if (unix_homedir) { + vuser->unix_homedir = smb_xstrdup(unix_homedir); + } + + if (logon_script) { + vuser->logon_script = smb_xstrdup(logon_script); + } + } + + memcpy(vuser->session_key, server_info->session_key, sizeof(vuser->session_key)); + + DEBUG(10,("register_vuid: (%u,%u) %s %s %s guest=%d\n", + (unsigned int)vuser->uid, + (unsigned int)vuser->gid, + vuser->user.unix_name, vuser->user.smb_name, vuser->user.domain, vuser->guest )); + + DEBUG(3, ("User name: %s\tReal name: %s\n",vuser->user.unix_name,vuser->user.full_name)); + + if (server_info->ptok) { + vuser->nt_user_token = dup_nt_token(server_info->ptok); + } else { + DEBUG(1, ("server_info does not contain a user_token - cannot continue\n")); + free_server_info(&server_info); + SAFE_FREE(vuser->homedir); + SAFE_FREE(vuser->unix_homedir); + SAFE_FREE(vuser->logon_script); + + SAFE_FREE(vuser); + return UID_FIELD_INVALID; + } + + /* use this to keep tabs on all our info from the authentication */ + vuser->server_info = server_info; + + DEBUG(3,("UNIX uid %d is UNIX user %s, and will be vuid %u\n",(int)vuser->uid,vuser->user.unix_name, vuser->vuid)); + + smb->users.next_vuid++; + smb->users.num_validated_vuids++; + + DLIST_ADD(smb->users.validated_users, vuser); + + if (!session_claim(smb, vuser)) { + DEBUG(1,("Failed to claim session for vuid=%d\n", vuser->vuid)); + invalidate_vuid(smb, vuser->vuid); + return -1; + } + + /* Register a home dir service for this user */ + if ((!vuser->guest) && vuser->unix_homedir && *(vuser->unix_homedir)) { + DEBUG(3, ("Adding/updating homes service for user '%s' using home direcotry: '%s'\n", + vuser->user.unix_name, vuser->unix_homedir)); + vuser->homes_snum = add_home_service(vuser->user.unix_name, vuser->user.unix_name, vuser->unix_homedir); + } else { + vuser->homes_snum = -1; + } + + return vuser->vuid; +} + + +/**************************************************************************** +add a name to the session users list +****************************************************************************/ +void add_session_user(struct server_context *smb, const char *user) +{ + char *suser; + struct passwd *passwd; + + if (!(passwd = Get_Pwnam(user))) return; + + suser = strdup(passwd->pw_name); + if (!suser) { + return; + } + + if (suser && *suser && !in_list(suser,smb->users.session_users,False)) { + char *p; + if (!smb->users.session_users) { + asprintf(&p, "%s", suser); + } else { + asprintf(&p, "%s %s", smb->users.session_users, suser); + } + SAFE_FREE(smb->users.session_users); + smb->users.session_users = p; + } + + free(suser); +} + + +/**************************************************************************** +check if a username is valid +****************************************************************************/ +BOOL user_ok(const char *user,int snum, gid_t *groups, size_t n_groups) +{ + char **valid, **invalid; + BOOL ret; + + valid = invalid = NULL; + ret = True; + + if (lp_invalid_users(snum)) { + str_list_copy(&invalid, lp_invalid_users(snum)); + if (invalid && str_list_substitute(invalid, "%S", lp_servicename(snum))) { + ret = !user_in_list(user, (const char **)invalid, groups, n_groups); + } + } + if (invalid) + str_list_free (&invalid); + + if (ret && lp_valid_users(snum)) { + str_list_copy(&valid, lp_valid_users(snum)); + if (valid && str_list_substitute(valid, "%S", lp_servicename(snum))) { + ret = user_in_list(user, (const char **)valid, groups, n_groups); + } + } + if (valid) + str_list_free (&valid); + + if (ret && lp_onlyuser(snum)) { + char **user_list = str_list_make (lp_username(snum), NULL); + if (user_list && str_list_substitute(user_list, "%S", lp_servicename(snum))) { + ret = user_in_list(user, (const char **)user_list, groups, n_groups); + } + if (user_list) str_list_free (&user_list); + } + + return(ret); +} + +/**************************************************************************** +validate a group username entry. Return the username or NULL +****************************************************************************/ +static const char *validate_group(struct server_context *smb, const char *group, DATA_BLOB password,int snum) +{ +#ifdef HAVE_NETGROUP + { + char *host, *user, *domain; + setnetgrent(group); + while (getnetgrent(&host, &user, &domain)) { + if (user) { + if (user_ok(user, snum, NULL, 0) && + password_ok(smb, user,password)) { + endnetgrent(); + return(user); + } + } + } + endnetgrent(); + } +#endif + +#ifdef HAVE_GETGRENT + { + struct group *gptr; + setgrent(); + while ((gptr = (struct group *)getgrent())) { + if (strequal(gptr->gr_name,group)) + break; + } + + /* + * As user_ok can recurse doing a getgrent(), we must + * copy the member list into a pstring on the stack before + * use. Bug pointed out by leon@eatworms.swmed.edu. + */ + + if (gptr) { + pstring member_list; + char *member; + size_t copied_len = 0; + int i; + + *member_list = '\0'; + member = member_list; + + for(i = 0; gptr->gr_mem && gptr->gr_mem[i]; i++) { + size_t member_len = strlen(gptr->gr_mem[i]) + 1; + if( copied_len + member_len < sizeof(pstring)) { + + DEBUG(10,("validate_group: = gr_mem = %s\n", gptr->gr_mem[i])); + + safe_strcpy(member, gptr->gr_mem[i], sizeof(pstring) - copied_len - 1); + copied_len += member_len; + member += copied_len; + } else { + *member = '\0'; + } + } + + endgrent(); + + member = member_list; + while (*member) { + const char *name = member; + if (user_ok(name,snum, NULL, 0) && + password_ok(smb,name,password)) { + endgrent(); + return(&name[0]); + } + + DEBUG(10,("validate_group = member = %s\n", member)); + + member += strlen(member) + 1; + } + } else { + endgrent(); + return NULL; + } + } +#endif + return(NULL); +} + +/**************************************************************************** + Check for authority to login to a service with a given username/password. + Note this is *NOT* used when logging on using sessionsetup_and_X. +****************************************************************************/ + +BOOL authorise_login(struct server_context *smb, + int snum, const char *user, DATA_BLOB password, + BOOL *guest) +{ + BOOL ok = False; + +#if DEBUG_PASSWORD + DEBUG(100,("authorise_login: checking authorisation on user=%s pass=%s\n", + user,password.data)); +#endif + + *guest = False; + + /* there are several possibilities: + 1) login as the given user with given password + 2) login as a previously registered username with the given password + 3) login as a session list username with the given password + 4) login as a previously validated user/password pair + 5) login as the "user =" user with given password + 6) login as the "user =" user with no password (guest connection) + 7) login as guest user with no password + + if the service is guest_only then steps 1 to 5 are skipped + */ + + /* now check the list of session users */ + if (!ok) { + char *auser; + char *user_list = strdup(smb->users.session_users); + if (!user_list) + return(False); + + for (auser=strtok(user_list,LIST_SEP); !ok && auser; + auser = strtok(NULL,LIST_SEP)) { + const char *user2 = auser; + + if (!user_ok(user2,snum, NULL, 0)) + continue; + + if (password_ok(smb, user2,password)) { + ok = True; + DEBUG(3,("authorise_login: ACCEPTED: session list username (%s) \ +and given password ok\n", user2)); + } + } + + SAFE_FREE(user_list); + } + + /* check the user= fields and the given password */ + if (!ok && lp_username(snum)) { + const char *auser; + pstring user_list; + StrnCpy(user_list,lp_username(snum),sizeof(pstring)); + + pstring_sub(user_list,"%S",lp_servicename(snum)); + + for (auser=strtok(user_list,LIST_SEP); auser && !ok; + auser = strtok(NULL,LIST_SEP)) { + if (*auser == '@') { + auser = validate_group(smb, auser+1,password,snum); + if (auser) { + ok = True; + DEBUG(3,("authorise_login: ACCEPTED: group username \ +and given password ok (%s)\n", auser)); + } + } else { + const char *user2 = auser; + if (user_ok(user2,snum, NULL, 0) && password_ok(smb, user2,password)) { + ok = True; + DEBUG(3,("authorise_login: ACCEPTED: user list username \ +and given password ok (%s)\n", user2)); + } + } + } + } + + /* check for a normal guest connection */ + if (!ok && GUEST_OK(snum)) { + const char *guestname = lp_guestaccount(); + if (Get_Pwnam(guestname)) { + ok = True; + DEBUG(3,("authorise_login: ACCEPTED: guest account and guest ok (%s)\n", guestname)); + } else { + DEBUG(0,("authorise_login: Invalid guest account %s??\n",guestname)); + } + *guest = True; + } + + if (ok && !user_ok(user, snum, NULL, 0)) { + DEBUG(0,("authorise_login: rejected invalid user %s\n",user)); + ok = False; + } + + return(ok); +} diff --git a/source4/smb_server/reply.c b/source4/smb_server/reply.c new file mode 100644 index 0000000000..7d6450b395 --- /dev/null +++ b/source4/smb_server/reply.c @@ -0,0 +1,2380 @@ +/* + Unix SMB/CIFS implementation. + Main SMB reply routines + Copyright (C) Andrew Tridgell 1992-2003 + Copyright (C) James J Myers 2003 <myersjj@samba.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +/* + This file handles most of the reply_ calls that the server + makes to handle specific protocols +*/ + +#include "includes.h" + +/* useful way of catching wct errors with file and line number */ +#define REQ_CHECK_WCT(req, wcount) do { \ + if ((req)->in.wct != (wcount)) { \ + DEBUG(1,("Unexpected WCT %d at %s(%d) - expected %d\n", \ + (req)->in.wct, __FILE__, __LINE__, wcount)); \ + req_reply_dos_error(req, ERRSRV, ERRerror); \ + return; \ + }} while (0) + +/* check req->async.status and if not OK then send an error reply */ +#define CHECK_ASYNC_STATUS do { \ + if (!NT_STATUS_IS_OK(req->async.status)) { \ + req_reply_error(req, req->async.status); \ + return; \ + }} while (0) + +/* useful wrapper for talloc with NO_MEMORY reply */ +#define REQ_TALLOC(ptr, size) do { \ + ptr = talloc(req->mem_ctx, size); \ + if (!ptr) { \ + req_reply_error(req, NT_STATUS_NO_MEMORY); \ + return; \ + }} while (0) + +/* + check if the backend wants to handle the request asynchronously. + if it wants it handled synchronously then call the send function + immediately +*/ +#define REQ_ASYNC_TAIL do { \ + if (!(req->control_flags & REQ_CONTROL_ASYNC)) { \ + req->async.send_fn(req); \ + }} while (0) + +/* zero out some reserved fields in a reply */ +#define REQ_VWV_RESERVED(start, count) memset(req->out.vwv + VWV(start), 0, (count)*2) + +/* + put a NTTIME into a packet +*/ +void push_nttime(void *base, uint16 offset, NTTIME *t) +{ + SIVAL(base, offset, t->low); + SIVAL(base, offset+4, t->high); +} + +/* + pull a NTTIME from a packet +*/ +NTTIME pull_nttime(void *base, uint16 offset) +{ + NTTIME ret; + ret.low = IVAL(base, offset); + ret.high = IVAL(base, offset+4); + return ret; +} + + +/**************************************************************************** + Reply to a simple request (async send) +****************************************************************************/ +static void reply_simple_send(struct request_context *req) +{ + CHECK_ASYNC_STATUS; + + req_setup_reply(req, 0, 0); + req_send_reply(req); +} + + +/**************************************************************************** + Reply to a tcon. +****************************************************************************/ +void reply_tcon(struct request_context *req) +{ + union smb_tcon con; + NTSTATUS status; + char *p; + + /* parse request */ + REQ_CHECK_WCT(req, 0); + + con.tcon.level = RAW_TCON_TCON; + + p = req->in.data; + p += req_pull_ascii4(req, &con.tcon.in.service, p, STR_TERMINATE); + p += req_pull_ascii4(req, &con.tcon.in.password, p, STR_TERMINATE); + p += req_pull_ascii4(req, &con.tcon.in.dev, p, STR_TERMINATE); + + if (!con.tcon.in.service || !con.tcon.in.password || !con.tcon.in.dev) { + req_reply_error(req, NT_STATUS_INVALID_PARAMETER); + return; + } + + /* call backend */ + status = tcon_backend(req, &con); + + if (!NT_STATUS_IS_OK(status)) { + req_reply_error(req, status); + return; + } + + /* construct reply */ + req_setup_reply(req, 2, 0); + + SSVAL(req->out.vwv, VWV(0), con.tcon.out.max_xmit); + SSVAL(req->out.vwv, VWV(1), con.tcon.out.cnum); + SSVAL(req->out.hdr, HDR_TID, req->conn->cnum); + + req_send_reply(req); +} + + +/**************************************************************************** + Reply to a tcon and X. +****************************************************************************/ +void reply_tcon_and_X(struct request_context *req) +{ + NTSTATUS status; + union smb_tcon con; + char *p; + uint16 passlen; + + con.tconx.level = RAW_TCON_TCONX; + + /* parse request */ + REQ_CHECK_WCT(req, 4); + + con.tconx.in.flags = SVAL(req->in.vwv, VWV(2)); + passlen = SVAL(req->in.vwv, VWV(3)); + + p = req->in.data; + + if (!req_pull_blob(req, p, passlen, &con.tconx.in.password)) { + req_reply_error(req, NT_STATUS_ILL_FORMED_PASSWORD); + return; + } + p += passlen; + + p += req_pull_string(req, &con.tconx.in.path, p, -1, STR_TERMINATE); + p += req_pull_string(req, &con.tconx.in.device, p, -1, STR_ASCII); + + if (!con.tconx.in.path || !con.tconx.in.device) { + req_reply_error(req, NT_STATUS_BAD_DEVICE_TYPE); + return; + } + + /* call backend */ + status = tcon_backend(req, &con); + + if (!NT_STATUS_IS_OK(status)) { + req_reply_error(req, status); + return; + } + + /* construct reply - two varients */ + if (req->smb->negotiate.protocol < PROTOCOL_NT1) { + req_setup_reply(req, 2, 0); + + SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE); + SSVAL(req->out.vwv, VWV(1), 0); + + req_push_str(req, NULL, con.tconx.out.dev_type, -1, STR_TERMINATE|STR_ASCII); + } else { + req_setup_reply(req, 3, 0); + + SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE); + SSVAL(req->out.vwv, VWV(1), 0); + SSVAL(req->out.vwv, VWV(2), con.tconx.out.options); + + req_push_str(req, NULL, con.tconx.out.dev_type, -1, STR_TERMINATE|STR_ASCII); + req_push_str(req, NULL, con.tconx.out.fs_type, -1, STR_TERMINATE); + } + + /* set the incoming and outgoing tid to the just created one */ + SSVAL(req->in.hdr, HDR_TID, con.tconx.out.cnum); + SSVAL(req->out.hdr,HDR_TID, con.tconx.out.cnum); + + chain_reply(req); +} + + +/**************************************************************************** + Reply to an unknown request +****************************************************************************/ +void reply_unknown(struct request_context *req) +{ + int type; + + type = CVAL(req->in.hdr, HDR_COM); + + DEBUG(0,("unknown command type %d (0x%X)\n", type, type)); + + req_reply_dos_error(req, ERRSRV, ERRunknownsmb); +} + + +/**************************************************************************** + Reply to an ioctl (async reply) +****************************************************************************/ +static void reply_ioctl_send(struct request_context *req) +{ + union smb_ioctl *io = req->async.private; + + CHECK_ASYNC_STATUS; + + /* the +1 is for nicer alignment */ + req_setup_reply(req, 8, io->ioctl.out.blob.length+1); + SSVAL(req->out.vwv, VWV(1), io->ioctl.out.blob.length); + SSVAL(req->out.vwv, VWV(5), io->ioctl.out.blob.length); + SSVAL(req->out.vwv, VWV(6), PTR_DIFF(req->out.data, req->out.hdr) + 1); + + memcpy(req->out.data+1, io->ioctl.out.blob.data, io->ioctl.out.blob.length); + + req_send_reply(req); +} + +/**************************************************************************** + Reply to an ioctl. +****************************************************************************/ +void reply_ioctl(struct request_context *req) +{ + union smb_ioctl *io; + + /* parse request */ + REQ_CHECK_WCT(req, 3); + REQ_TALLOC(io, sizeof(*io)); + + io->ioctl.level = RAW_IOCTL_IOCTL; + io->ioctl.in.fnum = req_fnum(req, req->in.vwv, VWV(0)); + io->ioctl.in.request = IVAL(req->in.vwv, VWV(1)); + + req->async.send_fn = reply_ioctl_send; + req->async.private = io; + + /* call backend */ + req->async.status = req->conn->ntvfs_ops->ioctl(req, io); + + REQ_ASYNC_TAIL; +} + + +/**************************************************************************** + Reply to a chkpth. +****************************************************************************/ +void reply_chkpth(struct request_context *req) +{ + struct smb_chkpath *io; + + REQ_TALLOC(io, sizeof(*io)); + + req_pull_ascii4(req, &io->in.path, req->in.data, STR_TERMINATE); + + req->async.send_fn = reply_simple_send; + + req->async.status = req->conn->ntvfs_ops->chkpath(req, io); + + REQ_ASYNC_TAIL; +} + +/**************************************************************************** + Reply to a getatr (async reply) +****************************************************************************/ +static void reply_getatr_send(struct request_context *req) +{ + union smb_fileinfo *st = req->async.private; + + CHECK_ASYNC_STATUS; + + /* construct reply */ + req_setup_reply(req, 10, 0); + + SSVAL(req->out.vwv, VWV(0), st->getattr.out.attrib); + put_dos_date3(req->out.vwv, VWV(1), st->getattr.out.write_time); + SIVAL(req->out.vwv, VWV(3), st->getattr.out.size); + + REQ_VWV_RESERVED(5, 5); + + req_send_reply(req); +} + + +/**************************************************************************** + Reply to a getatr. +****************************************************************************/ +void reply_getatr(struct request_context *req) +{ + union smb_fileinfo *st; + + REQ_TALLOC(st, sizeof(*st)); + + st->getattr.level = RAW_FILEINFO_GETATTR; + + /* parse request */ + req_pull_ascii4(req, &st->getattr.in.fname, req->in.data, STR_TERMINATE); + if (!st->getattr.in.fname) { + req_reply_error(req, NT_STATUS_OBJECT_NAME_NOT_FOUND); + return; + } + + req->async.send_fn = reply_getatr_send; + req->async.private = st; + + /* call backend */ + req->async.status = req->conn->ntvfs_ops->qpathinfo(req, st); + + REQ_ASYNC_TAIL; +} + + +/**************************************************************************** + Reply to a setatr. +****************************************************************************/ +void reply_setatr(struct request_context *req) +{ + union smb_setfileinfo *st; + + /* parse request */ + REQ_CHECK_WCT(req, 8); + REQ_TALLOC(st, sizeof(*st)); + + st->setattr.level = RAW_SFILEINFO_SETATTR; + st->setattr.in.attrib = SVAL(req->in.vwv, VWV(0)); + st->setattr.in.write_time = make_unix_date3(req->in.vwv + VWV(1)); + + req_pull_ascii4(req, &st->setattr.file.fname, req->in.data, STR_TERMINATE); + + if (!st->setattr.file.fname) { + req_reply_error(req, NT_STATUS_OBJECT_NAME_NOT_FOUND); + return; + } + + req->async.send_fn = reply_simple_send; + + /* call backend */ + req->async.status = req->conn->ntvfs_ops->setpathinfo(req, st); + + REQ_ASYNC_TAIL; +} + + +/**************************************************************************** + Reply to a dskattr (async reply) +****************************************************************************/ +static void reply_dskattr_send(struct request_context *req) +{ + union smb_fsinfo *fs = req->async.private; + + CHECK_ASYNC_STATUS; + + /* construct reply */ + req_setup_reply(req, 5, 0); + + SSVAL(req->out.vwv, VWV(0), fs->dskattr.out.units_total); + SSVAL(req->out.vwv, VWV(1), fs->dskattr.out.blocks_per_unit); + SSVAL(req->out.vwv, VWV(2), fs->dskattr.out.block_size); + SSVAL(req->out.vwv, VWV(3), fs->dskattr.out.units_free); + + REQ_VWV_RESERVED(4, 1); + + req_send_reply(req); +} + + +/**************************************************************************** + Reply to a dskattr. +****************************************************************************/ +void reply_dskattr(struct request_context *req) +{ + union smb_fsinfo *fs; + + REQ_TALLOC(fs, sizeof(*fs)); + + fs->dskattr.level = RAW_QFS_DSKATTR; + + req->async.send_fn = reply_dskattr_send; + req->async.private = fs; + + /* call backend */ + req->async.status = req->conn->ntvfs_ops->fsinfo(req, fs); + + REQ_ASYNC_TAIL; +} + + + +/**************************************************************************** + Reply to an open (async reply) +****************************************************************************/ +static void reply_open_send(struct request_context *req) +{ + union smb_open *oi = req->async.private; + + CHECK_ASYNC_STATUS; + + /* construct reply */ + req_setup_reply(req, 7, 0); + + SSVAL(req->out.vwv, VWV(0), oi->open.out.fnum); + SSVAL(req->out.vwv, VWV(1), oi->open.out.attrib); + put_dos_date3(req->out.vwv, VWV(2), oi->open.out.write_time); + SIVAL(req->out.vwv, VWV(4), oi->open.out.size); + SSVAL(req->out.vwv, VWV(6), oi->open.out.rmode); + + req_send_reply(req); +} + +/**************************************************************************** + Reply to an open. +****************************************************************************/ +void reply_open(struct request_context *req) +{ + union smb_open *oi; + + /* parse request */ + REQ_CHECK_WCT(req, 2); + REQ_TALLOC(oi, sizeof(*oi)); + + oi->open.level = RAW_OPEN_OPEN; + oi->open.in.flags = SVAL(req->in.vwv, VWV(0)); + oi->open.in.search_attrs = SVAL(req->in.vwv, VWV(1)); + + req_pull_ascii4(req, &oi->open.in.fname, req->in.data, STR_TERMINATE); + + if (!oi->open.in.fname) { + req_reply_error(req, NT_STATUS_OBJECT_NAME_NOT_FOUND); + return; + } + + req->async.send_fn = reply_open_send; + req->async.private = oi; + + /* call backend */ + req->async.status = req->conn->ntvfs_ops->open(req, oi); + + REQ_ASYNC_TAIL; +} + + +/**************************************************************************** + Reply to an open and X (async reply) +****************************************************************************/ +static void reply_open_and_X_send(struct request_context *req) +{ + union smb_open *oi = req->async.private; + + CHECK_ASYNC_STATUS; + + /* build the reply */ + if (oi->openx.in.flags & OPENX_FLAGS_EXTENDED_RETURN) { + req_setup_reply(req, 19, 0); + } else { + req_setup_reply(req, 15, 0); + } + + SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE); + SSVAL(req->out.vwv, VWV(1), 0); + SSVAL(req->out.vwv, VWV(2), oi->openx.out.fnum); + SSVAL(req->out.vwv, VWV(3), oi->openx.out.attrib); + put_dos_date3(req->out.vwv, VWV(4), oi->openx.out.write_time); + SIVAL(req->out.vwv, VWV(6), oi->openx.out.size); + SSVAL(req->out.vwv, VWV(8), oi->openx.out.access); + SSVAL(req->out.vwv, VWV(9), oi->openx.out.ftype); + SSVAL(req->out.vwv, VWV(10),oi->openx.out.devstate); + SSVAL(req->out.vwv, VWV(11),oi->openx.out.action); + SIVAL(req->out.vwv, VWV(12),oi->openx.out.unique_fid); + SSVAL(req->out.vwv, VWV(14),0); /* reserved */ + if (oi->openx.in.flags & OPENX_FLAGS_EXTENDED_RETURN) { + SIVAL(req->out.vwv, VWV(15),oi->openx.out.access_mask); + REQ_VWV_RESERVED(17, 2); + } + + chain_reply(req); +} + + +/**************************************************************************** + Reply to an open and X. +****************************************************************************/ +void reply_open_and_X(struct request_context *req) +{ + union smb_open *oi; + + /* parse the request */ + REQ_CHECK_WCT(req, 15); + REQ_TALLOC(oi, sizeof(*oi)); + + oi->openx.level = RAW_OPEN_OPENX; + oi->openx.in.flags = SVAL(req->in.vwv, VWV(2)); + oi->openx.in.open_mode = SVAL(req->in.vwv, VWV(3)); + oi->openx.in.search_attrs = SVAL(req->in.vwv, VWV(4)); + oi->openx.in.file_attrs = SVAL(req->in.vwv, VWV(5)); + oi->openx.in.write_time = make_unix_date3(req->in.vwv + VWV(6)); + oi->openx.in.open_func = SVAL(req->in.vwv, VWV(8)); + oi->openx.in.size = IVAL(req->in.vwv, VWV(9)); + oi->openx.in.timeout = IVAL(req->in.vwv, VWV(11)); + + req_pull_ascii4(req, &oi->openx.in.fname, req->in.data, STR_TERMINATE); + + if (!oi->openx.in.fname) { + req_reply_error(req, NT_STATUS_OBJECT_NAME_NOT_FOUND); + return; + } + + req->async.send_fn = reply_open_and_X_send; + req->async.private = oi; + + /* call the backend */ + req->async.status = req->conn->ntvfs_ops->open(req, oi); + + REQ_ASYNC_TAIL; +} + + +/**************************************************************************** + Reply to a mknew or a create. +****************************************************************************/ +static void reply_mknew_send(struct request_context *req) +{ + union smb_open *oi = req->async.private; + + CHECK_ASYNC_STATUS; + + /* build the reply */ + req_setup_reply(req, 1, 0); + + SSVAL(req->out.vwv, VWV(0), oi->mknew.out.fnum); + + req_send_reply(req); +} + + +/**************************************************************************** + Reply to a mknew or a create. +****************************************************************************/ +void reply_mknew(struct request_context *req) +{ + union smb_open *oi; + + /* parse the request */ + REQ_CHECK_WCT(req, 3); + REQ_TALLOC(oi, sizeof(*oi)); + + oi->mknew.level = RAW_OPEN_MKNEW; + oi->mknew.in.attrib = SVAL(req->in.vwv, VWV(0)); + oi->mknew.in.write_time = make_unix_date3(req->in.vwv + VWV(1)); + + req_pull_ascii4(req, &oi->mknew.in.fname, req->in.data, STR_TERMINATE); + + if (!oi->mknew.in.fname) { + req_reply_error(req, NT_STATUS_OBJECT_NAME_NOT_FOUND); + return; + } + + req->async.send_fn = reply_mknew_send; + req->async.private = oi; + + /* call the backend */ + req->async.status = req->conn->ntvfs_ops->open(req, oi); + + REQ_ASYNC_TAIL; +} + +/**************************************************************************** + Reply to a create temporary file (async reply) +****************************************************************************/ +static void reply_ctemp_send(struct request_context *req) +{ + union smb_open *oi = req->async.private; + + CHECK_ASYNC_STATUS; + + /* build the reply */ + req_setup_reply(req, 1, 0); + + SSVAL(req->out.vwv, VWV(0), oi->ctemp.out.fnum); + + /* the returned filename is relative to the directory */ + req_push_str(req, NULL, oi->ctemp.out.name, -1, STR_TERMINATE); + + req_send_reply(req); +} + +/**************************************************************************** + Reply to a create temporary file. +****************************************************************************/ +void reply_ctemp(struct request_context *req) +{ + union smb_open *oi; + + /* parse the request */ + REQ_CHECK_WCT(req, 3); + REQ_TALLOC(oi, sizeof(*oi)); + + oi->ctemp.level = RAW_OPEN_CTEMP; + oi->ctemp.in.attrib = SVAL(req->in.vwv, VWV(0)); + oi->ctemp.in.write_time = make_unix_date3(req->in.vwv + VWV(1)); + + /* the filename is actually a directory name, the server provides a filename + in that directory */ + req_pull_ascii4(req, &oi->ctemp.in.directory, req->in.data, STR_TERMINATE); + + if (!oi->ctemp.in.directory) { + req_reply_error(req, NT_STATUS_OBJECT_NAME_NOT_FOUND); + return; + } + + req->async.send_fn = reply_ctemp_send; + req->async.private = oi; + + /* call the backend */ + req->async.status = req->conn->ntvfs_ops->open(req, oi); + + REQ_ASYNC_TAIL; +} + + +/**************************************************************************** + Reply to a unlink +****************************************************************************/ +void reply_unlink(struct request_context *req) +{ + struct smb_unlink *unl; + + /* parse the request */ + REQ_CHECK_WCT(req, 1); + REQ_TALLOC(unl, sizeof(*unl)); + + unl->in.attrib = SVAL(req->in.vwv, VWV(0)); + + req_pull_ascii4(req, &unl->in.pattern, req->in.data, STR_TERMINATE); + + req->async.send_fn = reply_simple_send; + + /* call backend */ + req->async.status = req->conn->ntvfs_ops->unlink(req, unl); + + REQ_ASYNC_TAIL; +} + + +/**************************************************************************** + Reply to a readbraw (core+ protocol). + this is a strange packet because it doesn't use a standard SMB header in the reply, + only the 4 byte NBT header + This command must be replied to synchronously +****************************************************************************/ +void reply_readbraw(struct request_context *req) +{ + NTSTATUS status; + union smb_read io; + + io.readbraw.level = RAW_READ_READBRAW; + + /* there are two varients, one with 10 and one with 8 command words */ + if (req->in.wct != 10) { + REQ_CHECK_WCT(req, 8); + } + + io.readbraw.in.fnum = req_fnum(req, req->in.vwv, VWV(0)); + io.readbraw.in.offset = IVAL(req->in.vwv, VWV(1)); + io.readbraw.in.mincnt = SVAL(req->in.vwv, VWV(3)); + io.readbraw.in.maxcnt = SVAL(req->in.vwv, VWV(4)); + io.readbraw.in.timeout = IVAL(req->in.vwv, VWV(5)); + + /* the 64 bit varient */ + if (req->in.wct == 10) { + uint32 offset_high = IVAL(req->in.vwv, VWV(8)); +#ifdef LARGE_SMB_OFF_T + io.readbraw.in.offset |= (((SMB_OFF_T)offset_high) << 32); +#else + if (offset_high != 0) { + goto failed; + } +#endif + } + + /* 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(req->mem_ctx, req->out.size); + if (req->out.buffer == NULL) { + goto failed; + } + SIVAL(req->out.buffer, 0, 0); /* init NBT header */ + + /* tell the backend where to put the data */ + io.readbraw.out.data = req->out.buffer + NBT_HDR_SIZE; + + /* call the backend */ + status = req->conn->ntvfs_ops->read(req, &io); + + if (!NT_STATUS_IS_OK(status)) { + goto failed; + } + + req->out.size = io.readbraw.out.nread + NBT_HDR_SIZE; + + req_send_reply(req); + return; + +failed: + /* any failure in readbraw is equivalent to reading zero bytes */ + req->out.size = 4; + req->out.buffer = talloc(req->mem_ctx, req->out.size); + SIVAL(req->out.buffer, 0, 0); /* init NBT header */ + req_send_reply(req); +} + + +/**************************************************************************** + Reply to a lockread (async reply) +****************************************************************************/ +static void reply_lockread_send(struct request_context *req) +{ + union smb_read *io = req->async.private; + + CHECK_ASYNC_STATUS; + + /* trim packet */ + io->lockread.out.nread = MIN(io->lockread.out.nread, + req_max_data(req) - 3); + req_grow_data(req, 3 + io->lockread.out.nread); + + /* construct reply */ + SSVAL(req->out.vwv, VWV(0), io->lockread.out.nread); + REQ_VWV_RESERVED(1, 4); + + SCVAL(req->out.data, 0, SMB_DATA_BLOCK); + SSVAL(req->out.data, 1, io->lockread.out.nread); + + req_send_reply(req); +} + + +/**************************************************************************** + Reply to a lockread (core+ protocol). + note that the lock is a write lock, not a read lock! +****************************************************************************/ +void reply_lockread(struct request_context *req) +{ + union smb_read *io; + + /* parse request */ + REQ_CHECK_WCT(req, 5); + REQ_TALLOC(io, sizeof(*io)); + + io->lockread.level = RAW_READ_LOCKREAD; + io->lockread.in.fnum = req_fnum(req, req->in.vwv, VWV(0)); + io->lockread.in.count = SVAL(req->in.vwv, VWV(1)); + io->lockread.in.offset = IVAL(req->in.vwv, VWV(2)); + io->lockread.in.remaining = SVAL(req->in.vwv, VWV(4)); + + /* setup the reply packet assuming the maximum possible read */ + req_setup_reply(req, 5, 3 + io->lockread.in.count); + + /* tell the backend where to put the data */ + io->lockread.out.data = req->out.data + 3; + + req->async.send_fn = reply_lockread_send; + req->async.private = io; + + /* call backend */ + req->async.status = req->conn->ntvfs_ops->read(req, io); + + REQ_ASYNC_TAIL; +} + + + +/**************************************************************************** + Reply to a read (async reply) +****************************************************************************/ +static void reply_read_send(struct request_context *req) +{ + union smb_read *io = req->async.private; + + CHECK_ASYNC_STATUS; + + /* trim packet */ + io->read.out.nread = MIN(io->read.out.nread, + req_max_data(req) - 3); + req_grow_data(req, 3 + io->read.out.nread); + + /* construct reply */ + SSVAL(req->out.vwv, VWV(0), io->read.out.nread); + REQ_VWV_RESERVED(1, 4); + + SCVAL(req->out.data, 0, SMB_DATA_BLOCK); + SSVAL(req->out.data, 1, io->read.out.nread); + + req_send_reply(req); +} + +/**************************************************************************** + Reply to a read. +****************************************************************************/ +void reply_read(struct request_context *req) +{ + union smb_read *io; + + /* parse request */ + REQ_CHECK_WCT(req, 5); + REQ_TALLOC(io, sizeof(*io)); + + io->read.level = RAW_READ_READ; + io->read.in.fnum = req_fnum(req, req->in.vwv, VWV(0)); + io->read.in.count = SVAL(req->in.vwv, VWV(1)); + io->read.in.offset = IVAL(req->in.vwv, VWV(2)); + io->read.in.remaining = SVAL(req->in.vwv, VWV(4)); + + /* setup the reply packet assuming the maximum possible read */ + req_setup_reply(req, 5, 3 + io->read.in.count); + + /* tell the backend where to put the data */ + io->read.out.data = req->out.data + 3; + + req->async.send_fn = reply_read_send; + req->async.private = io; + + /* call backend */ + req->async.status = req->conn->ntvfs_ops->read(req, io); + + REQ_ASYNC_TAIL; +} + + + +/**************************************************************************** + Reply to a read and X (async reply) +****************************************************************************/ +static void reply_read_and_X_send(struct request_context *req) +{ + union smb_read *io = req->async.private; + + CHECK_ASYNC_STATUS; + + /* trim the packet to the right size */ + io->readx.out.nread = MIN(io->readx.out.nread, + req_max_data(req) - 1); + req_grow_data(req, 1 + io->readx.out.nread); + + /* construct reply */ + SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE); + SSVAL(req->out.vwv, VWV(1), 0); + SSVAL(req->out.vwv, VWV(2), io->readx.out.remaining); + SSVAL(req->out.vwv, VWV(3), io->readx.out.compaction_mode); + REQ_VWV_RESERVED(4, 1); + SSVAL(req->out.vwv, VWV(5), io->readx.out.nread); + SSVAL(req->out.vwv, VWV(6), PTR_DIFF(io->readx.out.data, req->out.hdr)); + SCVAL(req->out.data, 0, 0); /* padding */ + REQ_VWV_RESERVED(7, 5); + + chain_reply(req); +} + +/**************************************************************************** + Reply to a read and X. +****************************************************************************/ +void reply_read_and_X(struct request_context *req) +{ + union smb_read *io; + + /* parse request */ + if (req->in.wct != 12) { + REQ_CHECK_WCT(req, 10); + } + + REQ_TALLOC(io, sizeof(*io)); + + io->readx.level = RAW_READ_READX; + io->readx.in.fnum = req_fnum(req, req->in.vwv, VWV(2)); + io->readx.in.offset = IVAL(req->in.vwv, VWV(3)); + io->readx.in.maxcnt = SVAL(req->in.vwv, VWV(5)); + io->readx.in.mincnt = SVAL(req->in.vwv, VWV(6)); + io->readx.in.remaining = SVAL(req->in.vwv, VWV(9)); + + /* the 64 bit varient */ + if (req->in.wct == 12) { + uint32 offset_high = IVAL(req->in.vwv, VWV(10)); +#ifdef LARGE_SMB_OFF_T + io->readx.in.offset |= (((SMB_OFF_T)offset_high) << 32); +#else + if (offset_high != 0) { + req_reply_error(req, NT_STATUS_FOOBAR); + return; + } +#endif + } + + /* setup the reply packet assuming the maximum possible read */ + req_setup_reply(req, 12, 1 + io->readx.in.maxcnt); + + /* tell the backend where to put the data. Notice the pad byte. */ + io->readx.out.data = req->out.data + 1; + + req->async.send_fn = reply_read_and_X_send; + req->async.private = io; + + /* call backend */ + req->async.status = req->conn->ntvfs_ops->read(req, io); + + REQ_ASYNC_TAIL; +} + + +/**************************************************************************** + Reply to a writebraw (core+ or LANMAN1.0 protocol). +****************************************************************************/ +void reply_writebraw(struct request_context *req) +{ + /* this one is damn complex - put it off for now */ + req_reply_error(req, NT_STATUS_FOOBAR); +} + + +/**************************************************************************** + Reply to a writeunlock (async reply) +****************************************************************************/ +static void reply_writeunlock_send(struct request_context *req) +{ + union smb_write *io = req->async.private; + + CHECK_ASYNC_STATUS; + + /* construct reply */ + req_setup_reply(req, 1, 0); + + SSVAL(req->out.vwv, VWV(0), io->writeunlock.out.nwritten); + + req_send_reply(req); +} + +/**************************************************************************** + Reply to a writeunlock (core+). +****************************************************************************/ +void reply_writeunlock(struct request_context *req) +{ + union smb_write *io; + + REQ_CHECK_WCT(req, 5); + REQ_TALLOC(io, sizeof(*io)); + + io->writeunlock.level = RAW_WRITE_WRITEUNLOCK; + io->writeunlock.in.fnum = req_fnum(req, req->in.vwv, VWV(0)); + io->writeunlock.in.count = SVAL(req->in.vwv, VWV(1)); + io->writeunlock.in.offset = IVAL(req->in.vwv, VWV(2)); + io->writeunlock.in.remaining = SVAL(req->in.vwv, VWV(4)); + io->writeunlock.in.data = req->in.data + 3; + + /* make sure they gave us the data they promised */ + if (io->writeunlock.in.count+3 > req->in.data_size) { + req_reply_error(req, NT_STATUS_FOOBAR); + return; + } + + /* make sure the data block is big enough */ + if (SVAL(req->in.data, 1) < io->writeunlock.in.count) { + req_reply_error(req, NT_STATUS_FOOBAR); + return; + } + + req->async.send_fn = reply_writeunlock_send; + req->async.private = io; + + /* call backend */ + req->async.status = req->conn->ntvfs_ops->write(req, io); + + REQ_ASYNC_TAIL; +} + + + +/**************************************************************************** + Reply to a write (async reply) +****************************************************************************/ +static void reply_write_send(struct request_context *req) +{ + union smb_write *io = req->async.private; + + CHECK_ASYNC_STATUS; + + /* construct reply */ + req_setup_reply(req, 1, 0); + + SSVAL(req->out.vwv, VWV(0), io->write.out.nwritten); + + req_send_reply(req); +} + +/**************************************************************************** + Reply to a write +****************************************************************************/ +void reply_write(struct request_context *req) +{ + union smb_write *io; + + REQ_CHECK_WCT(req, 5); + REQ_TALLOC(io, sizeof(*io)); + + io->write.level = RAW_WRITE_WRITE; + io->write.in.fnum = req_fnum(req, req->in.vwv, VWV(0)); + io->write.in.count = SVAL(req->in.vwv, VWV(1)); + io->write.in.offset = IVAL(req->in.vwv, VWV(2)); + io->write.in.remaining = SVAL(req->in.vwv, VWV(4)); + io->write.in.data = req->in.data + 3; + + /* make sure they gave us the data they promised */ + if (req_data_oob(req, io->write.in.data, io->write.in.count)) { + req_reply_error(req, NT_STATUS_FOOBAR); + return; + } + + /* make sure the data block is big enough */ + if (SVAL(req->in.data, 1) < io->write.in.count) { + req_reply_error(req, NT_STATUS_FOOBAR); + return; + } + + req->async.send_fn = reply_write_send; + req->async.private = io; + + /* call backend */ + req->async.status = req->conn->ntvfs_ops->write(req, io); + + REQ_ASYNC_TAIL; +} + + +/**************************************************************************** + Reply to a write and X (async reply) +****************************************************************************/ +static void reply_write_and_X_send(struct request_context *req) +{ + union smb_write *io = req->async.private; + + CHECK_ASYNC_STATUS; + + /* construct reply */ + req_setup_reply(req, 6, 0); + + SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE); + SSVAL(req->out.vwv, VWV(1), 0); + SSVAL(req->out.vwv, VWV(2), io->writex.out.nwritten & 0xFFFF); + SSVAL(req->out.vwv, VWV(3), io->writex.out.remaining); + SSVAL(req->out.vwv, VWV(4), io->writex.out.nwritten >> 16); + REQ_VWV_RESERVED(5, 1); + + chain_reply(req); +} + +/**************************************************************************** + Reply to a write and X. +****************************************************************************/ +void reply_write_and_X(struct request_context *req) +{ + union smb_write *io; + + if (req->in.wct != 14) { + REQ_CHECK_WCT(req, 12); + } + + REQ_TALLOC(io, sizeof(*io)); + + io->writex.level = RAW_WRITE_WRITEX; + io->writex.in.fnum = req_fnum(req, req->in.vwv, VWV(2)); + io->writex.in.offset = IVAL(req->in.vwv, VWV(3)); + io->writex.in.wmode = SVAL(req->in.vwv, VWV(7)); + io->writex.in.remaining = SVAL(req->in.vwv, VWV(8)); + io->writex.in.count = SVAL(req->in.vwv, VWV(10)); + io->writex.in.data = req->in.hdr + SVAL(req->in.vwv, VWV(11)); + + if (req->in.wct == 14) { + uint32 offset_high = IVAL(req->in.vwv, VWV(12)); + uint16 count_high = SVAL(req->in.vwv, VWV(9)); +#ifdef LARGE_SMB_OFF_T + io->writex.in.offset |= (((SMB_OFF_T)offset_high) << 32); +#else + if (offset_high != 0) { + req_reply_error(req, NT_STATUS_FOOBAR); + return; + } +#endif + io->writex.in.count |= ((uint32)count_high) << 16; + } + + /* make sure the data is in bounds */ + if (req_data_oob(req, io->writex.in.data, io->writex.in.count)) { + req_reply_error(req, NT_STATUS_FOOBAR); + return; + } + + req->async.send_fn = reply_write_and_X_send; + req->async.private = io; + + /* call backend */ + req->async.status = req->conn->ntvfs_ops->write(req, io); + + REQ_ASYNC_TAIL; +} + + +/**************************************************************************** + Reply to a lseek (async reply) +****************************************************************************/ +static void reply_lseek_send(struct request_context *req) +{ + struct smb_seek *io = req->async.private; + + CHECK_ASYNC_STATUS; + + /* construct reply */ + req_setup_reply(req, 2, 0); + + SIVALS(req->out.vwv, VWV(0), io->out.offset); + + req_send_reply(req); +} + +/**************************************************************************** + Reply to a lseek. +****************************************************************************/ +void reply_lseek(struct request_context *req) +{ + struct smb_seek *io; + + REQ_CHECK_WCT(req, 4); + REQ_TALLOC(io, sizeof(*io)); + + io->in.fnum = req_fnum(req, req->in.vwv, VWV(0)); + io->in.mode = SVAL(req->in.vwv, VWV(1)); + io->in.offset = IVALS(req->in.vwv, VWV(2)); + + req->async.send_fn = reply_lseek_send; + req->async.private = io; + + /* call backend */ + req->async.status = req->conn->ntvfs_ops->seek(req, io); + + REQ_ASYNC_TAIL; +} + +/**************************************************************************** + Reply to a flush. +****************************************************************************/ +void reply_flush(struct request_context *req) +{ + struct smb_flush *io; + + /* parse request */ + REQ_CHECK_WCT(req, 1); + REQ_TALLOC(io, sizeof(*io)); + + io->in.fnum = req_fnum(req, req->in.vwv, VWV(0)); + + req->async.send_fn = reply_simple_send; + + /* call backend */ + req->async.status = req->conn->ntvfs_ops->flush(req, io); + + REQ_ASYNC_TAIL; +} + + +/**************************************************************************** + Reply to a exit. +****************************************************************************/ +void reply_exit(struct request_context *req) +{ + REQ_CHECK_WCT(req, 0); + + req->async.send_fn = reply_simple_send; + + if (!req->conn) { + req_reply_error(req, NT_STATUS_INVALID_HANDLE); + return; + } + + /* call backend */ + req->async.status = req->conn->ntvfs_ops->exit(req); + + REQ_ASYNC_TAIL; +} + + +/**************************************************************************** + Reply to a close + + Note that this has to deal with closing a directory opened by NT SMB's. +****************************************************************************/ +void reply_close(struct request_context *req) +{ + union smb_close *io; + + /* parse request */ + REQ_CHECK_WCT(req, 3); + REQ_TALLOC(io, sizeof(*io)); + + io->close.level = RAW_CLOSE_CLOSE; + io->close.in.fnum = req_fnum(req, req->in.vwv, VWV(0)); + io->close.in.write_time = make_unix_date3(req->in.vwv + VWV(1)); + + req->async.send_fn = reply_simple_send; + + /* call backend */ + req->async.status = req->conn->ntvfs_ops->close(req, io); + + REQ_ASYNC_TAIL; +} + + + +/**************************************************************************** + Reply to a writeclose (async reply) +****************************************************************************/ +static void reply_writeclose_send(struct request_context *req) +{ + union smb_write *io = req->async.private; + + CHECK_ASYNC_STATUS; + + /* construct reply */ + req_setup_reply(req, 1, 0); + + SSVAL(req->out.vwv, VWV(0), io->write.out.nwritten); + + req_send_reply(req); +} + +/**************************************************************************** + Reply to a writeclose (Core+ protocol). +****************************************************************************/ +void reply_writeclose(struct request_context *req) +{ + union smb_write *io; + + /* this one is pretty weird - the wct can be 6 or 12 */ + if (req->in.wct != 12) { + REQ_CHECK_WCT(req, 6); + } + + REQ_TALLOC(io, sizeof(*io)); + + io->writeclose.level = RAW_WRITE_WRITECLOSE; + io->writeclose.in.fnum = req_fnum(req, req->in.vwv, VWV(0)); + io->writeclose.in.count = SVAL(req->in.vwv, VWV(1)); + io->writeclose.in.offset = IVAL(req->in.vwv, VWV(2)); + io->writeclose.in.mtime = make_unix_date3(req->in.vwv + VWV(4)); + io->writeclose.in.data = req->in.data + 1; + + /* make sure they gave us the data they promised */ + if (req_data_oob(req, io->writeclose.in.data, io->writeclose.in.count)) { + req_reply_error(req, NT_STATUS_FOOBAR); + return; + } + + req->async.send_fn = reply_writeclose_send; + req->async.private = io; + + /* call backend */ + req->async.status = req->conn->ntvfs_ops->write(req, io); + + REQ_ASYNC_TAIL; +} + +/**************************************************************************** + Reply to a lock. +****************************************************************************/ +void reply_lock(struct request_context *req) +{ + union smb_lock *lck; + + /* parse request */ + REQ_CHECK_WCT(req, 5); + REQ_TALLOC(lck, sizeof(*lck)); + + lck->lock.level = RAW_LOCK_LOCK; + lck->lock.in.fnum = req_fnum(req, req->in.vwv, VWV(0)); + lck->lock.in.count = IVAL(req->in.vwv, VWV(1)); + lck->lock.in.offset = IVAL(req->in.vwv, VWV(3)); + + req->async.send_fn = reply_simple_send; + + /* call backend */ + req->async.status = req->conn->ntvfs_ops->lock(req, lck); + + REQ_ASYNC_TAIL; +} + + +/**************************************************************************** + Reply to a unlock. +****************************************************************************/ +void reply_unlock(struct request_context *req) +{ + union smb_lock *lck; + + /* parse request */ + REQ_CHECK_WCT(req, 5); + REQ_TALLOC(lck, sizeof(*lck)); + + lck->unlock.level = RAW_LOCK_UNLOCK; + lck->unlock.in.fnum = req_fnum(req, req->in.vwv, VWV(0)); + lck->unlock.in.count = IVAL(req->in.vwv, VWV(1)); + lck->unlock.in.offset = IVAL(req->in.vwv, VWV(3)); + + req->async.send_fn = reply_simple_send; + + /* call backend */ + req->async.status = req->conn->ntvfs_ops->lock(req, lck); + + REQ_ASYNC_TAIL; +} + + +/**************************************************************************** + Reply to a tdis. +****************************************************************************/ +void reply_tdis(struct request_context *req) +{ + REQ_CHECK_WCT(req, 0); + + close_cnum(req->conn); + + /* construct reply */ + req_setup_reply(req, 0, 0); + + req_send_reply(req); +} + + +/**************************************************************************** + Reply to a echo. This is one of the few calls that is handled directly (the + backends don't see it at all) +****************************************************************************/ +void reply_echo(struct request_context *req) +{ + uint16 count; + int i; + + REQ_CHECK_WCT(req, 0); + + count = SVAL(req->in.vwv, VWV(0)); + + req_setup_reply(req, 1, req->in.data_size); + + memcpy(req->out.data, req->in.data, req->in.data_size); + + /* we need to make sure the request isn't destroyed till the + * last packet */ + req->control_flags |= REQ_CONTROL_PROTECTED; + + for (i=1; i <= count;i++) { + if (i == count) { + req->control_flags &= ~REQ_CONTROL_PROTECTED; + } + + SSVAL(req->out.vwv, VWV(0), i); + req_send_reply(req); + } +} + + + +/**************************************************************************** + Reply to a printopen (async reply) +****************************************************************************/ +static void reply_printopen_send(struct request_context *req) +{ + union smb_open *oi = req->async.private; + + CHECK_ASYNC_STATUS; + + /* construct reply */ + req_setup_reply(req, 1, 0); + + SSVAL(req->out.vwv, VWV(0), oi->open.out.fnum); + + req_send_reply(req); +} + +/**************************************************************************** + Reply to a printopen. +****************************************************************************/ +void reply_printopen(struct request_context *req) +{ + union smb_open *oi; + + /* parse request */ + REQ_CHECK_WCT(req, 2); + REQ_TALLOC(oi, sizeof(*oi)); + + oi->splopen.level = RAW_OPEN_SPLOPEN; + oi->splopen.in.setup_length = SVAL(req->in.vwv, VWV(0)); + oi->splopen.in.mode = SVAL(req->in.vwv, VWV(1)); + + req_pull_ascii4(req, &oi->splopen.in.ident, req->in.data, STR_TERMINATE); + + req->async.send_fn = reply_printopen_send; + req->async.private = oi; + + /* call backend */ + req->async.status = req->conn->ntvfs_ops->open(req, oi); + + REQ_ASYNC_TAIL; +} + +/**************************************************************************** + Reply to a printclose. +****************************************************************************/ +void reply_printclose(struct request_context *req) +{ + union smb_close *io; + + /* parse request */ + REQ_CHECK_WCT(req, 3); + REQ_TALLOC(io, sizeof(*io)); + + io->splclose.level = RAW_CLOSE_SPLCLOSE; + io->splclose.in.fnum = req_fnum(req, req->in.vwv, VWV(0)); + + req->async.send_fn = reply_simple_send; + + /* call backend */ + req->async.status = req->conn->ntvfs_ops->close(req, io); + + REQ_ASYNC_TAIL; +} + +/**************************************************************************** + Reply to a printqueue. +****************************************************************************/ +void reply_printqueue_send(struct request_context *req) +{ + union smb_lpq *lpq = req->async.private; + int i, maxcount; + const uint_t el_size = 28; + + CHECK_ASYNC_STATUS; + + /* construct reply */ + req_setup_reply(req, 2, 0); + + /* truncate the returned list to fit in the negotiated buffer size */ + maxcount = (req_max_data(req) - 3) / el_size; + if (maxcount < lpq->retq.out.count) { + lpq->retq.out.count = maxcount; + } + + /* setup enough space in the reply */ + req_grow_data(req, 3 + el_size*lpq->retq.out.count); + + /* and fill it in */ + SSVAL(req->out.vwv, VWV(0), lpq->retq.out.count); + SSVAL(req->out.vwv, VWV(1), lpq->retq.out.restart_idx); + + SCVAL(req->out.data, 0, SMB_DATA_BLOCK); + SSVAL(req->out.data, 1, el_size*lpq->retq.out.count); + + req->out.ptr = req->out.data + 3; + + for (i=0;i<lpq->retq.out.count;i++) { + put_dos_date2(req->out.ptr, 0 , lpq->retq.out.queue[i].time); + SCVAL(req->out.ptr, 4, lpq->retq.out.queue[i].status); + SSVAL(req->out.ptr, 5, lpq->retq.out.queue[i].job); + SIVAL(req->out.ptr, 7, lpq->retq.out.queue[i].size); + SCVAL(req->out.ptr, 11, 0); /* reserved */ + req_push_str(req, req->out.ptr+12, lpq->retq.out.queue[i].user, 16, STR_ASCII); + req->out.ptr += el_size; + } + + req_send_reply(req); +} + +/**************************************************************************** + Reply to a printqueue. +****************************************************************************/ +void reply_printqueue(struct request_context *req) +{ + union smb_lpq *lpq; + + /* parse request */ + REQ_CHECK_WCT(req, 2); + REQ_TALLOC(lpq, sizeof(*lpq)); + + lpq->retq.level = RAW_LPQ_RETQ; + lpq->retq.in.maxcount = SVAL(req->in.vwv, VWV(0)); + lpq->retq.in.startidx = SVAL(req->in.vwv, VWV(1)); + + req->async.send_fn = reply_printqueue_send; + req->async.private = lpq; + + /* call backend */ + req->async.status = req->conn->ntvfs_ops->lpq(req, lpq); + + REQ_ASYNC_TAIL; +} + + +/**************************************************************************** + Reply to a printwrite. +****************************************************************************/ +void reply_printwrite(struct request_context *req) +{ + union smb_write *io; + + /* parse request */ + REQ_CHECK_WCT(req, 1); + REQ_TALLOC(io, sizeof(*io)); + + io->splwrite.level = RAW_WRITE_SPLWRITE; + + if (req->in.data_size < 3) { + req_reply_error(req, NT_STATUS_FOOBAR); + return; + } + + io->splwrite.in.fnum = req_fnum(req, req->in.vwv, VWV(0)); + io->splwrite.in.count = SVAL(req->in.data, 1); + io->splwrite.in.data = req->in.data + 3; + + /* make sure they gave us the data they promised */ + if (req_data_oob(req, io->splwrite.in.data, io->splwrite.in.count)) { + req_reply_error(req, NT_STATUS_FOOBAR); + return; + } + + req->async.send_fn = reply_simple_send; + + /* call backend */ + req->async.status = req->conn->ntvfs_ops->write(req, io); + + REQ_ASYNC_TAIL; +} + + +/**************************************************************************** + Reply to a mkdir. +****************************************************************************/ +void reply_mkdir(struct request_context *req) +{ + union smb_mkdir *io; + + /* parse the request */ + REQ_CHECK_WCT(req, 0); + REQ_TALLOC(io, sizeof(*io)); + + io->generic.level = RAW_MKDIR_MKDIR; + req_pull_ascii4(req, &io->mkdir.in.path, req->in.data, STR_TERMINATE); + + req->async.send_fn = reply_simple_send; + + /* call backend */ + req->async.status = req->conn->ntvfs_ops->mkdir(req, io); + + REQ_ASYNC_TAIL; +} + + +/**************************************************************************** + Reply to a rmdir. +****************************************************************************/ +void reply_rmdir(struct request_context *req) +{ + struct smb_rmdir *io; + + /* parse the request */ + REQ_CHECK_WCT(req, 0); + REQ_TALLOC(io, sizeof(*io)); + + req_pull_ascii4(req, &io->in.path, req->in.data, STR_TERMINATE); + + req->async.send_fn = reply_simple_send; + + /* call backend */ + req->async.status = req->conn->ntvfs_ops->rmdir(req, io); + + REQ_ASYNC_TAIL; +} + + +/**************************************************************************** + Reply to a mv. +****************************************************************************/ +void reply_mv(struct request_context *req) +{ + union smb_rename *io; + char *p; + + /* parse the request */ + REQ_CHECK_WCT(req, 1); + REQ_TALLOC(io, sizeof(*io)); + + io->generic.level = RAW_RENAME_RENAME; + io->rename.in.attrib = SVAL(req->in.vwv, VWV(0)); + + p = req->in.data; + p += req_pull_ascii4(req, &io->rename.in.pattern1, p, STR_TERMINATE); + p += req_pull_ascii4(req, &io->rename.in.pattern2, p, STR_TERMINATE); + + if (!io->rename.in.pattern1 || !io->rename.in.pattern2) { + req_reply_error(req, NT_STATUS_FOOBAR); + return; + } + + req->async.send_fn = reply_simple_send; + + /* call backend */ + req->async.status = req->conn->ntvfs_ops->rename(req, io); + + REQ_ASYNC_TAIL; +} + + +/**************************************************************************** + Reply to an NT rename. +****************************************************************************/ +void reply_ntrename(struct request_context *req) +{ + union smb_rename *io; + char *p; + + /* parse the request */ + REQ_CHECK_WCT(req, 4); + REQ_TALLOC(io, sizeof(*io)); + + io->generic.level = RAW_RENAME_NTRENAME; + io->ntrename.in.attrib = SVAL(req->in.vwv, VWV(0)); + io->ntrename.in.flags = SVAL(req->in.vwv, VWV(1)); + io->ntrename.in.cluster_size = IVAL(req->in.vwv, VWV(2)); + + p = req->in.data; + p += req_pull_ascii4(req, &io->ntrename.in.old_name, p, STR_TERMINATE); + p += req_pull_ascii4(req, &io->ntrename.in.new_name, p, STR_TERMINATE); + + if (!io->ntrename.in.old_name || !io->ntrename.in.new_name) { + req_reply_error(req, NT_STATUS_FOOBAR); + return; + } + + req->async.send_fn = reply_simple_send; + + /* call backend */ + req->async.status = req->conn->ntvfs_ops->rename(req, io); + + REQ_ASYNC_TAIL; +} + +/**************************************************************************** + Reply to a file copy (async reply) +****************************************************************************/ +static void reply_copy_send(struct request_context *req) +{ + struct smb_copy *cp = req->async.private; + + CHECK_ASYNC_STATUS; + + /* build the reply */ + req_setup_reply(req, 1, 0); + + SSVAL(req->out.vwv, VWV(0), cp->out.count); + + req_send_reply(req); +} + +/**************************************************************************** + Reply to a file copy. +****************************************************************************/ +void reply_copy(struct request_context *req) +{ + struct smb_copy *cp; + char *p; + + /* parse request */ + REQ_CHECK_WCT(req, 3); + REQ_TALLOC(cp, sizeof(*cp)); + + cp->in.tid2 = SVAL(req->in.vwv, VWV(0)); + cp->in.ofun = SVAL(req->in.vwv, VWV(1)); + cp->in.flags = SVAL(req->in.vwv, VWV(2)); + + p = req->in.data; + p += req_pull_ascii4(req, &cp->in.path1, p, STR_TERMINATE); + p += req_pull_ascii4(req, &cp->in.path2, p, STR_TERMINATE); + + if (!cp->in.path1 || !cp->in.path2) { + req_reply_error(req, NT_STATUS_FOOBAR); + return; + } + + req->async.send_fn = reply_copy_send; + req->async.private = cp; + + /* call backend */ + req->async.status = req->conn->ntvfs_ops->copy(req, cp); + + REQ_ASYNC_TAIL; +} + +/**************************************************************************** + Reply to a lockingX request (async send) +****************************************************************************/ +static void reply_lockingX_send(struct request_context *req) +{ + union smb_lock *lck = req->async.private; + + CHECK_ASYNC_STATUS; + + /* if it was an oplock break ack then we only send a reply if + there was an error */ + if (lck->lockx.in.ulock_cnt + lck->lockx.in.lock_cnt == 0) { + req_destroy(req); + return; + } + + /* construct reply */ + req_setup_reply(req, 2, 0); + + SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE); + SSVAL(req->out.vwv, VWV(1), 0); + + chain_reply(req); +} + + +/**************************************************************************** + Reply to a lockingX request. +****************************************************************************/ +void reply_lockingX(struct request_context *req) +{ + union smb_lock *lck; + uint_t total_locks, i; + uint_t lck_size; + char *p; + + /* parse request */ + REQ_CHECK_WCT(req, 8); + REQ_TALLOC(lck, sizeof(*lck)); + + lck->lockx.level = RAW_LOCK_LOCKX; + lck->lockx.in.fnum = req_fnum(req, req->in.vwv, VWV(2)); + lck->lockx.in.mode = SVAL(req->in.vwv, VWV(3)); + lck->lockx.in.timeout = IVAL(req->in.vwv, VWV(4)); + lck->lockx.in.ulock_cnt = SVAL(req->in.vwv, VWV(6)); + lck->lockx.in.lock_cnt = SVAL(req->in.vwv, VWV(7)); + + total_locks = lck->lockx.in.ulock_cnt + lck->lockx.in.lock_cnt; + + /* there are two varients, one with 64 bit offsets and counts */ + if (lck->lockx.in.mode & LOCKING_ANDX_LARGE_FILES) { + lck_size = 20; + } else { + lck_size = 10; + } + + /* make sure we got the promised data */ + if (req_data_oob(req, req->in.data, total_locks * lck_size)) { + req_reply_error(req, NT_STATUS_FOOBAR); + return; + } + + /* allocate the locks array */ + if (total_locks) { + REQ_TALLOC(lck->lockx.in.locks, total_locks * sizeof(lck->lockx.in.locks[0])); + } + + p = req->in.data; + + /* construct the locks array */ + for (i=0;i<total_locks;i++) { + uint32 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) { +#ifdef LARGE_SMB_OFF_T + lck->lockx.in.locks[i].count |= ((SMB_OFF_T)count_high) << 32; + lck->lockx.in.locks[i].offset |= ((SMB_OFF_T)ofs_high) << 32; +#else + req_reply_error(req, NT_STATUS_FOOBAR); + return; +#endif + } + p += lck_size; + } + + req->async.send_fn = reply_lockingX_send; + req->async.private = lck; + + /* call backend */ + req->async.status = req->conn->ntvfs_ops->lock(req, lck); + + REQ_ASYNC_TAIL; +} + +/**************************************************************************** + Reply to a SMBreadbmpx (read block multiplex) request. +****************************************************************************/ +void reply_readbmpx(struct request_context *req) +{ + /* tell the client to not use a multiplexed read - its too broken to use */ + req_reply_dos_error(req, ERRSRV, ERRuseSTD); +} + + +/**************************************************************************** + Reply to a SMBsetattrE. +****************************************************************************/ +void reply_setattrE(struct request_context *req) +{ + union smb_setfileinfo *info; + + /* parse request */ + REQ_CHECK_WCT(req, 7); + REQ_TALLOC(info, sizeof(*info)); + + info->setattre.level = RAW_SFILEINFO_SETATTRE; + info->setattre.file.fnum = req_fnum(req, req->in.vwv, VWV(0)); + info->setattre.in.create_time = make_unix_date2(req->in.vwv + VWV(1)); + info->setattre.in.access_time = make_unix_date2(req->in.vwv + VWV(3)); + info->setattre.in.write_time = make_unix_date2(req->in.vwv + VWV(5)); + + req->async.send_fn = reply_simple_send; + + /* call backend */ + req->async.status = req->conn->ntvfs_ops->setfileinfo(req, info); + + REQ_ASYNC_TAIL; +} + + +/**************************************************************************** + Reply to a SMBwritebmpx (write block multiplex primary) request. +****************************************************************************/ +void reply_writebmpx(struct request_context *req) +{ + /* we will need to implement this one for OS/2, but right now I can't be bothered */ + req_reply_error(req, NT_STATUS_FOOBAR); +} + + +/**************************************************************************** + Reply to a SMBwritebs (write block multiplex secondary) request. +****************************************************************************/ +void reply_writebs(struct request_context *req) +{ + /* see reply_writebmpx */ + req_reply_error(req, NT_STATUS_FOOBAR); +} + + + +/**************************************************************************** + Reply to a SMBgetattrE (async reply) +****************************************************************************/ +static void reply_getattrE_send(struct request_context *req) +{ + union smb_fileinfo *info = req->async.private; + + CHECK_ASYNC_STATUS; + + /* setup reply */ + req_setup_reply(req, 11, 0); + + put_dos_date2(req->out.vwv, VWV(0), info->getattre.out.create_time); + put_dos_date2(req->out.vwv, VWV(2), info->getattre.out.access_time); + put_dos_date2(req->out.vwv, VWV(4), info->getattre.out.write_time); + SIVAL(req->out.vwv, VWV(6), info->getattre.out.size); + SIVAL(req->out.vwv, VWV(8), info->getattre.out.alloc_size); + SSVAL(req->out.vwv, VWV(10), info->getattre.out.attrib); + + req_send_reply(req); +} + +/**************************************************************************** + Reply to a SMBgetattrE. +****************************************************************************/ +void reply_getattrE(struct request_context *req) +{ + union smb_fileinfo *info; + + /* parse request */ + REQ_CHECK_WCT(req, 1); + REQ_TALLOC(info, sizeof(*info)); + + info->getattr.level = RAW_FILEINFO_GETATTRE; + info->getattr.in.fnum = req_fnum(req, req->in.vwv, VWV(0)); + + req->async.send_fn = reply_getattrE_send; + req->async.private = info; + + /* call backend */ + req->async.status = req->conn->ntvfs_ops->qfileinfo(req, info); + + REQ_ASYNC_TAIL; +} + + +/**************************************************************************** +reply to an old style session setup command +****************************************************************************/ +static void reply_sesssetup_old(struct request_context *req) +{ + NTSTATUS status; + union smb_sesssetup sess; + char *p; + uint16 passlen; + + sess.old.level = RAW_SESSSETUP_OLD; + + /* parse request */ + sess.old.in.bufsize = SVAL(req->in.vwv, VWV(2)); + sess.old.in.mpx_max = SVAL(req->in.vwv, VWV(3)); + sess.old.in.vc_num = SVAL(req->in.vwv, VWV(4)); + sess.old.in.sesskey = IVAL(req->in.vwv, VWV(5)); + passlen = SVAL(req->in.vwv, VWV(7)); + + /* check the request isn't malformed */ + if (req_data_oob(req, req->in.data, passlen)) { + req_reply_error(req, NT_STATUS_FOOBAR); + return; + } + + p = req->in.data; + if (!req_pull_blob(req, p, passlen, &sess.old.in.password)) { + req_reply_error(req, NT_STATUS_FOOBAR); + return; + } + p += passlen; + + p += req_pull_string(req, &sess.old.in.user, p, -1, STR_TERMINATE); + p += req_pull_string(req, &sess.old.in.domain, p, -1, STR_TERMINATE); + p += req_pull_string(req, &sess.old.in.os, p, -1, STR_TERMINATE); + p += req_pull_string(req, &sess.old.in.lanman, p, -1, STR_TERMINATE); + + /* call the generic handler */ + status = sesssetup_backend(req, &sess); + + if (!NT_STATUS_IS_OK(status)) { + req_reply_error(req, status); + return; + } + + /* construct reply */ + req_setup_reply(req, 3, 0); + + SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE); + SSVAL(req->out.vwv, VWV(1), 0); + SSVAL(req->out.vwv, VWV(2), sess.old.out.action); + + SSVAL(req->out.hdr, HDR_UID, sess.old.out.vuid); + + chain_reply(req); +} + + +/**************************************************************************** +reply to an NT1 style session setup command +****************************************************************************/ +static void reply_sesssetup_nt1(struct request_context *req) +{ + NTSTATUS status; + union smb_sesssetup sess; + char *p; + uint16 passlen1, passlen2; + + sess.nt1.level = RAW_SESSSETUP_NT1; + + /* parse request */ + sess.nt1.in.bufsize = SVAL(req->in.vwv, VWV(2)); + sess.nt1.in.mpx_max = SVAL(req->in.vwv, VWV(3)); + sess.nt1.in.vc_num = SVAL(req->in.vwv, VWV(4)); + sess.nt1.in.sesskey = IVAL(req->in.vwv, VWV(5)); + passlen1 = SVAL(req->in.vwv, VWV(7)); + passlen2 = SVAL(req->in.vwv, VWV(8)); + sess.nt1.in.capabilities = IVAL(req->in.vwv, VWV(11)); + + /* check the request isn't malformed */ + if (req_data_oob(req, req->in.data, passlen1) || + req_data_oob(req, req->in.data + passlen1, passlen2)) { + req_reply_error(req, NT_STATUS_FOOBAR); + return; + } + + p = req->in.data; + if (!req_pull_blob(req, p, passlen1, &sess.nt1.in.password1)) { + req_reply_error(req, NT_STATUS_FOOBAR); + return; + } + p += passlen1; + if (!req_pull_blob(req, p, passlen2, &sess.nt1.in.password2)) { + req_reply_error(req, NT_STATUS_FOOBAR); + return; + } + p += passlen2; + + p += req_pull_string(req, &sess.nt1.in.user, p, -1, STR_TERMINATE); + p += req_pull_string(req, &sess.nt1.in.domain, p, -1, STR_TERMINATE); + p += req_pull_string(req, &sess.nt1.in.os, p, -1, STR_TERMINATE); + p += req_pull_string(req, &sess.nt1.in.lanman, p, -1, STR_TERMINATE); + + /* call the generic handler */ + status = sesssetup_backend(req, &sess); + + if (!NT_STATUS_IS_OK(status)) { + req_reply_error(req, status); + return; + } + + /* construct reply */ + req_setup_reply(req, 3, 0); + + SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE); + SSVAL(req->out.vwv, VWV(1), 0); + SSVAL(req->out.vwv, VWV(2), sess.nt1.out.action); + + SSVAL(req->out.hdr, HDR_UID, sess.nt1.out.vuid); + + req_push_str(req, NULL, sess.nt1.out.os, -1, STR_TERMINATE); + req_push_str(req, NULL, sess.nt1.out.lanman, -1, STR_TERMINATE); + req_push_str(req, NULL, sess.nt1.out.domain, -1, STR_TERMINATE); + + chain_reply(req); +} + + +/**************************************************************************** +reply to an SPNEGO style session setup command +****************************************************************************/ +static void reply_sesssetup_spnego(struct request_context *req) +{ + NTSTATUS status; + union smb_sesssetup sess; + char *p; + uint16 blob_len; + + sess.spnego.level = RAW_SESSSETUP_SPNEGO; + + /* parse request */ + sess.spnego.in.bufsize = SVAL(req->in.vwv, VWV(2)); + sess.spnego.in.mpx_max = SVAL(req->in.vwv, VWV(3)); + sess.spnego.in.vc_num = SVAL(req->in.vwv, VWV(4)); + sess.spnego.in.sesskey = IVAL(req->in.vwv, VWV(5)); + blob_len = SVAL(req->in.vwv, VWV(7)); + sess.spnego.in.capabilities = IVAL(req->in.vwv, VWV(10)); + + p = req->in.data; + if (!req_pull_blob(req, p, blob_len, &sess.spnego.in.secblob)) { + req_reply_error(req, NT_STATUS_FOOBAR); + return; + } + p += blob_len; + + p += req_pull_string(req, &sess.spnego.in.os, p, -1, STR_TERMINATE); + p += req_pull_string(req, &sess.spnego.in.lanman, p, -1, STR_TERMINATE); + p += req_pull_string(req, &sess.spnego.in.domain, p, -1, STR_TERMINATE); + + /* call the generic handler */ + status = sesssetup_backend(req, &sess); + + if (!NT_STATUS_IS_OK(status)) { + req_reply_error(req, status); + return; + } + + /* construct reply */ + req_setup_reply(req, 4, sess.spnego.out.secblob.length); + + SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE); + SSVAL(req->out.vwv, VWV(1), 0); + SSVAL(req->out.vwv, VWV(2), sess.spnego.out.action); + SSVAL(req->out.vwv, VWV(3), sess.spnego.out.secblob.length); + + SSVAL(req->out.hdr, HDR_UID, sess.spnego.out.vuid); + + memcpy(req->out.data, sess.spnego.out.secblob.data, sess.spnego.out.secblob.length); + req_push_str(req, NULL, sess.spnego.out.os, -1, STR_TERMINATE); + req_push_str(req, NULL, sess.spnego.out.lanman, -1, STR_TERMINATE); + + chain_reply(req); +} + + +/**************************************************************************** +reply to a session setup command +****************************************************************************/ +void reply_sesssetup(struct request_context *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 varient */ + req_reply_error(req, NT_STATUS_FOOBAR); +} + + +/**************************************************************************** + Reply to a SMBulogoffX. +****************************************************************************/ +void reply_ulogoffX(struct request_context *req) +{ + uint16 vuid; + + vuid = SVAL(req->in.hdr, HDR_UID); + + /* in user level security we are supposed to close any files + open by this user */ + if ((vuid != 0) && (lp_security() != SEC_SHARE)) { + DEBUG(0,("REWRITE: not closing user files\n")); + } + + invalidate_vuid(req->smb, vuid); + + req_setup_reply(req, 2, 0); + + SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE); + SSVAL(req->out.vwv, VWV(1), 0); + + chain_reply(req); +} + + +/**************************************************************************** + Reply to an SMBfindclose request +****************************************************************************/ +void reply_findclose(struct request_context *req) +{ + NTSTATUS status; + union smb_search_close io; + + io.findclose.level = RAW_FINDCLOSE_CLOSE; + + /* parse request */ + REQ_CHECK_WCT(req, 1); + + io.findclose.in.handle = SVAL(req->in.vwv, VWV(0)); + + /* call backend */ + status = req->conn->ntvfs_ops->search_close(req, &io); + + if (!NT_STATUS_IS_OK(status)) { + req_reply_error(req, status); + return; + } + + /* construct reply */ + req_setup_reply(req, 0, 0); + + req_send_reply(req); +} + +/**************************************************************************** + Reply to an SMBfindnclose request +****************************************************************************/ +void reply_findnclose(struct request_context *req) +{ + req_reply_error(req, NT_STATUS_FOOBAR); +} + + +/**************************************************************************** + Reply to an SMBntcreateX request (async send) +****************************************************************************/ +static void reply_ntcreate_and_X_send(struct request_context *req) +{ + union smb_open *io = req->async.private; + + CHECK_ASYNC_STATUS; + + /* construct reply */ + req_setup_reply(req, 34, 0); + + SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE); + SSVAL(req->out.vwv, VWV(1), 0); + SCVAL(req->out.vwv, VWV(2), io->ntcreatex.out.oplock_level); + + /* the rest of the parameters are not aligned! */ + SSVAL(req->out.vwv, 5, io->ntcreatex.out.fnum); + SIVAL(req->out.vwv, 7, io->ntcreatex.out.create_action); + push_nttime(req->out.vwv, 11, &io->ntcreatex.out.create_time); + push_nttime(req->out.vwv, 19, &io->ntcreatex.out.access_time); + push_nttime(req->out.vwv, 27, &io->ntcreatex.out.write_time); + push_nttime(req->out.vwv, 35, &io->ntcreatex.out.change_time); + SIVAL(req->out.vwv, 43, io->ntcreatex.out.attrib); + SBVAL(req->out.vwv, 47, io->ntcreatex.out.alloc_size); + SBVAL(req->out.vwv, 55, io->ntcreatex.out.size); + SSVAL(req->out.vwv, 63, io->ntcreatex.out.file_type); + SSVAL(req->out.vwv, 65, io->ntcreatex.out.ipc_state); + SCVAL(req->out.vwv, 67, io->ntcreatex.out.is_directory); + + chain_reply(req); +} + +/**************************************************************************** + Reply to an SMBntcreateX request +****************************************************************************/ +void reply_ntcreate_and_X(struct request_context *req) +{ + union smb_open *io; + uint16 fname_len; + + /* parse the request */ + REQ_CHECK_WCT(req, 24); + REQ_TALLOC(io, sizeof(*io)); + + io->ntcreatex.level = RAW_OPEN_NTCREATEX; + + /* notice that the word parameters are not word aligned, so we don't use VWV() */ + fname_len = SVAL(req->in.vwv, 5); + io->ntcreatex.in.flags = IVAL(req->in.vwv, 7); + io->ntcreatex.in.root_fid = IVAL(req->in.vwv, 11); + io->ntcreatex.in.access_mask = IVAL(req->in.vwv, 15); + io->ntcreatex.in.alloc_size = BVAL(req->in.vwv, 19); + io->ntcreatex.in.file_attr = IVAL(req->in.vwv, 27); + io->ntcreatex.in.share_access = IVAL(req->in.vwv, 31); + io->ntcreatex.in.open_disposition = IVAL(req->in.vwv, 35); + io->ntcreatex.in.create_options = IVAL(req->in.vwv, 39); + io->ntcreatex.in.impersonation = IVAL(req->in.vwv, 43); + io->ntcreatex.in.security_flags = CVAL(req->in.vwv, 47); + + /* we need a neater way to handle this alignment */ + if ((req->flags2 & FLAGS2_UNICODE_STRINGS) && + ucs2_align(req->in.buffer, req->in.data, STR_TERMINATE|STR_UNICODE)) { + fname_len++; + } + + req_pull_string(req, &io->ntcreatex.in.fname, req->in.data, fname_len, STR_TERMINATE); + if (!io->ntcreatex.in.fname) { + req_reply_error(req, NT_STATUS_FOOBAR); + return; + } + + req->async.send_fn = reply_ntcreate_and_X_send; + req->async.private = io; + + /* call the backend */ + req->async.status = req->conn->ntvfs_ops->open(req, io); + + REQ_ASYNC_TAIL; +} + + +/**************************************************************************** + Reply to an SMBntcancel request +****************************************************************************/ +void reply_ntcancel(struct request_context *req) +{ + req_reply_error(req, NT_STATUS_FOOBAR); +} + +/**************************************************************************** + Reply to an SMBsends request +****************************************************************************/ +void reply_sends(struct request_context *req) +{ + req_reply_error(req, NT_STATUS_FOOBAR); +} + +/**************************************************************************** + Reply to an SMBsendstrt request +****************************************************************************/ +void reply_sendstrt(struct request_context *req) +{ + req_reply_error(req, NT_STATUS_FOOBAR); +} + +/**************************************************************************** + Reply to an SMBsendend request +****************************************************************************/ +void reply_sendend(struct request_context *req) +{ + req_reply_error(req, NT_STATUS_FOOBAR); +} + +/**************************************************************************** + Reply to an SMBsendtxt request +****************************************************************************/ +void reply_sendtxt(struct request_context *req) +{ + req_reply_error(req, NT_STATUS_FOOBAR); +} + + + +/**************************************************************************** + Reply to a special message - a SMB packet with non zero NBT message type +****************************************************************************/ +void reply_special(struct request_context *req) +{ + uint8 msg_type; + char buf[4]; + + msg_type = CVAL(req->in.buffer,0); + + SIVAL(buf, 0, 0); + + switch (msg_type) { + case 0x81: /* session request */ + if (req->smb->negotiate.done_nbt_session) { + exit_server(req->smb, "multiple session request not permitted"); + } + + SCVAL(buf,0,0x82); + SCVAL(buf,3,0); + + DEBUG(0,("REWRITE: not parsing netbios names in NBT session request!\n")); + + req->smb->negotiate.done_nbt_session = True; + + req->out.buffer = buf; + req->out.size = 4; + req_send_reply(req); + return; + + case 0x89: /* session keepalive request + (some old clients produce this?) */ + SCVAL(buf, 0, SMBkeepalive); + SCVAL(buf, 3, 0); + req->out.buffer = buf; + req->out.size = 4; + req_send_reply(req); + return; + + case SMBkeepalive: + /* session keepalive - swallow it */ + req_destroy(req); + return; + } + + DEBUG(0,("Unexpected NBT session packet (%d)\n", msg_type)); + req_destroy(req); +} diff --git a/source4/smb_server/request.c b/source4/smb_server/request.c new file mode 100644 index 0000000000..065e63a8d2 --- /dev/null +++ b/source4/smb_server/request.c @@ -0,0 +1,602 @@ +/* + Unix SMB/CIFS implementation. + + Copyright (C) Andrew Tridgell 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/* + this file implements functions for manipulating the 'struct request_context' structure in smbd +*/ + +#include "includes.h" + +/* we over allocate the data buffer to prevent too many realloc calls */ +#define REQ_OVER_ALLOCATION 256 + +/* destroy a request structure */ +void req_destroy(struct request_context *req) +{ + /* the request might be marked protected. This is done by the + * SMBecho code for example */ + if (req->control_flags & REQ_CONTROL_PROTECTED) { + return; + } + + /* ahh, its so nice to destroy a complex structure in such a + * simple way! */ + talloc_destroy(req->mem_ctx); +} + +/**************************************************************************** +construct a basic request packet, mostly used to construct async packets +such as change notify and oplock break requests +****************************************************************************/ +struct request_context *init_smb_request(struct server_context *smb) +{ + struct request_context *req; + TALLOC_CTX *mem_ctx; + + /* each request gets its own talloc context. The request + structure itself is also allocated inside this context, so + we need to allocate it before we construct the request + */ + mem_ctx = talloc_init("request_context[%d]", smb->socket.pkt_count); + if (!mem_ctx) { + return NULL; + } + + smb->socket.pkt_count++; + + req = talloc(mem_ctx, sizeof(*req)); + if (!req) { + return NULL; + } + + ZERO_STRUCTP(req); + + /* setup the request context */ + req->smb = smb; + req->mem_ctx = mem_ctx; + + 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 request_context *req, unsigned wct, unsigned buflen) +{ + uint32 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->mem_ctx, req->out.buffer, req->out.allocated); + if (!req->out.buffer) { + exit_server(req->smb, "allocation failed"); + } + + req->out.hdr = req->out.buffer + NBT_HDR_SIZE; + req->out.vwv = req->out.buffer + chain_base_size + 1; + req->out.wct = wct; + req->out.data = req->out.vwv + VWV(wct) + 2; + req->out.data_size = buflen; + req->out.ptr = req->out.data; + + SCVAL(req->out.buffer, chain_base_size, wct); + SSVAL(req->out.vwv, VWV(wct), buflen); +} + + +/* + setup a reply in req->out with the given word count and initial data buffer size. + the caller will then fill in the command words and data before calling req_send_reply() to + send the reply on its way +*/ +void req_setup_reply(struct request_context *req, unsigned wct, unsigned buflen) +{ + if (req->chain_count != 0) { + req_setup_chain_reply(req, wct, buflen); + return; + } + + req->out.size = NBT_HDR_SIZE + MIN_SMB_SIZE + wct*2 + buflen; + + /* over allocate by a small amount */ + req->out.allocated = req->out.size + REQ_OVER_ALLOCATION; + + req->out.buffer = talloc(req->mem_ctx, req->out.allocated); + if (!req->out.buffer) { + exit_server(req->smb, "allocation failed"); + } + + 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, + (req->flags2 & FLAGS2_UNICODE_STRINGS) | + FLAGS2_LONG_PATH_COMPONENTS | FLAGS2_32_BIT_ERROR_CODES | FLAGS2_EXTENDED_SECURITY); + + SSVAL(req->out.hdr,HDR_PIDHIGH,0); + memset(req->out.hdr + HDR_SS_FIELD, 0, 10); + + if (req->in.hdr) { + /* copy the cmd, tid, pid, uid and mid from the request */ + SCVAL(req->out.hdr,HDR_COM,CVAL(req->in.hdr,HDR_COM)); + SSVAL(req->out.hdr,HDR_TID,SVAL(req->in.hdr,HDR_TID)); + SSVAL(req->out.hdr,HDR_PID,SVAL(req->in.hdr,HDR_PID)); + SSVAL(req->out.hdr,HDR_UID,SVAL(req->in.hdr,HDR_UID)); + SSVAL(req->out.hdr,HDR_MID,SVAL(req->in.hdr,HDR_MID)); + } else { + SSVAL(req->out.hdr,HDR_TID,0); + SSVAL(req->out.hdr,HDR_PID,0); + SSVAL(req->out.hdr,HDR_UID,0); + SSVAL(req->out.hdr,HDR_MID,0); + } +} + +/* + 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 request_context *req) +{ + int ret; + ret = req->smb->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 request_context *req, unsigned new_size) +{ + int delta; + char *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->mem_ctx, req->out.buffer, 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 request_context *req, unsigned new_size) +{ + int delta; + + if (!(req->control_flags & REQ_CONTROL_LARGE) && new_size > req_max_data(req)) { + smb_panic("reply buffer too large!"); + } + + req_grow_allocation(req, new_size); + + delta = new_size - req->out.data_size; + + req->out.size += delta; + req->out.data_size += delta; + + /* set the BCC to the new data size */ + SSVAL(req->out.vwv, VWV(req->out.wct), new_size); +} + +/* + send a reply and destroy the request buffer + + note that this only looks at req->out.buffer and req->out.size, allowing manually + constructed packets to be sent +*/ +void req_send_reply(struct request_context *req) +{ + if (req->out.size > NBT_HDR_SIZE) { + _smb_setlen(req->out.buffer, req->out.size - NBT_HDR_SIZE); + } + + if (write_data(req->smb->socket.fd, req->out.buffer, req->out.size) != req->out.size) { + smb_panic("failed to send reply\n"); + } + + req_destroy(req); +} + + + +/* + construct and send an error packet with a forced DOS error code + this is needed to match win2000 behaviour for some parts of the protocol +*/ +void req_reply_dos_error(struct request_context *req, uint8 eclass, uint16 ecode) +{ + /* if the basic packet hasn't been setup yet then do it now */ + if (req->out.buffer == NULL) { + req_setup_reply(req, 0, 0); + } + + SCVAL(req->out.hdr, HDR_RCLS, eclass); + SSVAL(req->out.hdr, HDR_ERR, ecode); + + SSVAL(req->out.hdr, HDR_FLG2, SVAL(req->out.hdr, HDR_FLG2) & ~FLAGS2_32_BIT_ERROR_CODES); + + req_send_reply(req); +} + +/* + construct and send an error packet, then destroy the request + auto-converts to DOS error format when appropriate +*/ +void req_reply_error(struct request_context *req, NTSTATUS status) +{ + req_setup_reply(req, 0, 0); + + /* error returns never have any data */ + req_grow_data(req, 0); + + if (!lp_nt_status_support() || !(req->smb->negotiate.client_caps & CAP_STATUS32)) { + /* convert to DOS error codes */ + uint8 eclass; + uint32 ecode; + ntstatus_to_dos(status, &eclass, &ecode); + req_reply_dos_error(req, eclass, ecode); + return; + } + + 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); + + req_send_reply(req); +} + + +/* + push a string into the data portion of the request packet, growing it if necessary + this gets quite tricky - please be very careful to cover all cases when modifying this + + if dest is NULL, then put the string at the end of the data portion of the packet + + if dest_len is -1 then no limit applies +*/ +size_t req_push_str(struct request_context *req, char *dest, const char *str, int dest_len, unsigned flags) +{ + size_t len; + unsigned grow_size; + char *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(req->out.hdr, 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 request_context *req, + const uint8 *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 request_context *req, + const uint8 *bytes, uint16 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_context *req, const char **dest, const char *src, int byte_len, unsigned flags) +{ + int src_len, src_len2, alignment=0; + ssize_t ret; + + if (!(flags & STR_NOALIGN) && ucs2_align(req->in.buffer, src, flags)) { + src++; + alignment=1; + if (byte_len != -1) { + byte_len--; + } + } + + if (flags & STR_NO_RANGE_CHECK) { + src_len = byte_len; + } else { + src_len = req->in.data_size - PTR_DIFF(src, req->in.data); + if (src_len < 0) { + *dest = NULL; + return 0; + } + + if (byte_len != -1 && src_len > byte_len) { + src_len = byte_len; + } + } + + src_len2 = strnlen_w((const smb_ucs2_t *)src, src_len/2) * 2; + + if (src_len2 <= src_len - 2) { + /* include the termination if we didn't reach the end of the packet */ + src_len2 += 2; + } + + ret = convert_string_talloc(req->mem_ctx, CH_UCS2, CH_UNIX, src, src_len2, (const void **)dest); + + if (ret == -1) { + *dest = NULL; + return 0; + } + + 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_context *req, const char **dest, const char *src, int byte_len, unsigned flags) +{ + int src_len, src_len2; + ssize_t ret; + + if (flags & STR_NO_RANGE_CHECK) { + src_len = byte_len; + } else { + src_len = req->in.data_size - PTR_DIFF(src, req->in.data); + if (src_len < 0) { + *dest = NULL; + return 0; + } + if (byte_len != -1 && src_len > byte_len) { + src_len = byte_len; + } + } + + src_len2 = strnlen(src, src_len); + if (src_len2 <= src_len - 1) { + /* include the termination if we didn't reach the end of the packet */ + src_len2++; + } + + ret = convert_string_talloc(req->mem_ctx, CH_DOS, CH_UNIX, src, src_len2, (const void **)dest); + + if (ret == -1) { + *dest = NULL; + return 0; + } + + 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_context *req, const char **dest, const char *src, int byte_len, unsigned flags) +{ + if (!(flags & STR_ASCII) && + (((flags & STR_UNICODE) || (req->flags2 & FLAGS2_UNICODE_STRINGS)))) { + return req_pull_ucs2(req, dest, src, byte_len, flags); + } + + return req_pull_ascii(req, dest, src, byte_len, flags); +} + + +/* + pull a ASCII4 string buffer from a request packet, returning a talloced string + + an ASCII4 buffer is a null terminated string that has a prefix + of the character 0x4. It tends to be used in older parts of the protocol. + + on failure *dest is set to the zero length string. This seems to + match win2000 behaviour +*/ +size_t req_pull_ascii4(struct request_context *req, const char **dest, const char *src, unsigned flags) +{ + ssize_t ret; + + if (PTR_DIFF(src, req->in.data) + 1 > req->in.data_size) { + /* win2000 treats this as the NULL string! */ + (*dest) = talloc_strdup(req->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(req, dest, src, -1, flags); + if (ret == -1) { + (*dest) = talloc_strdup(req->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_context *req, const char *src, int len, DATA_BLOB *blob) +{ + if (len != 0 && req_data_oob(req, src, len)) { + return False; + } + + (*blob) = data_blob_talloc(req->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_context *req, const char *ptr, uint32 count) +{ + if (count == 0) { + return False; + } + + /* be careful with wraparound! */ + if (ptr < req->in.data || + ptr >= req->in.data + req->in.data_size || + count > req->in.data_size || + ptr + count > req->in.data + req->in.data_size) { + return True; + } + return False; +} + + +/* + pull an open file handle from a packet, taking account of the chained_fnum +*/ +uint16 req_fnum(struct request_context *req, const char *base, unsigned offset) +{ + if (req->chained_fnum != -1) { + return req->chained_fnum; + } + return SVAL(base, offset); +} diff --git a/source4/smb_server/search.c b/source4/smb_server/search.c new file mode 100644 index 0000000000..9d01a0e98f --- /dev/null +++ b/source4/smb_server/search.c @@ -0,0 +1,229 @@ +/* + Unix SMB/CIFS implementation. + SMBsearch handling + Copyright (C) Andrew Tridgell 2003 + Copyright (C) James J Myers 2003 <myersjj@samba.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +/* + This file handles the parsing of transact2 requests +*/ + +#include "includes.h" + +/* check req->async.status and if not OK then send an error reply */ +#define CHECK_ASYNC_STATUS do { \ + if (!NT_STATUS_IS_OK(req->async.status)) { \ + req_reply_error(req, req->async.status); \ + return; \ + }} while (0) + +/* + check if the backend wants to handle the request asynchronously. + if it wants it handled synchronously then call the send function + immediately +*/ +#define REQ_ASYNC_TAIL do { \ + if (!(req->control_flags & REQ_CONTROL_ASYNC)) { \ + req->async.send_fn(req); \ + }} while (0) + +/* useful wrapper for talloc with NO_MEMORY reply */ +#define REQ_TALLOC(ptr, size) do { \ + ptr = talloc(req->mem_ctx, size); \ + if (!ptr) { \ + req_reply_error(req, NT_STATUS_NO_MEMORY); \ + return; \ + }} while (0) + +#define CHECK_MIN_BLOB_SIZE(blob, size) do { \ + if ((blob)->length < (size)) { \ + return NT_STATUS_INFO_LENGTH_MISMATCH; \ + }} while (0) + +/* a structure to encapsulate the state information about + * an in-progress search first/next operation */ +struct search_state { + struct request_context *req; + union smb_search_data *file; + uint16 last_entry_offset; +}; + +/* + fill a single entry in a search find reply +*/ +static void find_fill_info(struct request_context *req, + union smb_search_data *file) +{ + char *p = req->out.data + req->out.data_size; + uint32 dos_date; + char search_name[13]; + + DEBUG(9,("find_fill_info: input file data: attr=0x%x size=%u time=0x%x name=%13s\n", + file->search.attrib, file->search.size, + (uint32)file->search.write_time, file->search.name)); + + p += req_append_bytes(req, file->search.search_id.data, 21); + p += req_append_bytes(req, (char*)&file->search.attrib, 1); + put_dos_date((char*)&dos_date, 0, file->search.write_time); + p += req_append_bytes(req, (char*)&dos_date, 4); + p += req_append_bytes(req, (char*)&file->search.size, 4); + memset(&search_name[0], ' ', 13); + memcpy(&search_name[0], file->search.name, + MAX(13, strlen(file->search.name))); + p += req_append_bytes(req, &search_name[0], 13); +} + +/* callback function for search first/next */ +static BOOL find_callback(void *private, union smb_search_data *file) +{ + struct search_state *state = (struct search_state *)private; + + find_fill_info(state->req, file); + + return True; +} + +/**************************************************************************** + Reply to a search. +****************************************************************************/ +void reply_search(struct request_context *req) +{ + union smb_search_first *sf; + union smb_search_next *sn; + DATA_BLOB resume_key; + uint16 resume_key_length; + struct search_state state; + char *p; + + REQ_TALLOC(sf, sizeof(*sf)); + + /* parse request */ + if (req->in.wct != 2) { + req_reply_error(req, NT_STATUS_INVALID_PARAMETER); + return; + } + + p = req->in.data; + p += req_pull_ascii4(req, &sf->search_first.in.pattern, + p, STR_TERMINATE); + if (!sf->search_first.in.pattern) { + req_reply_error(req, NT_STATUS_OBJECT_NAME_NOT_FOUND); + return; + } + /* pull in type 5 byte and length */ + if (!req_pull_blob(req, p, 3, &resume_key)) + req_reply_error(req, NT_STATUS_INVALID_PARAMETER); + resume_key_length = SVAL(resume_key.data, 1); + p += 3; + DEBUG(19,("reply_search: pattern=%s, key_length=%d\n", + sf->search_first.in.pattern, resume_key_length)); + + /* setup state for callback */ + state.req = req; + state.file = NULL; + state.last_entry_offset = 0; + + /* construct reply */ + req_setup_reply(req, 1, 0); + req_append_var_block(req, NULL, 0); + + if (resume_key_length > 0) { + /* do a search next operation */ + REQ_TALLOC(sn, sizeof(*sn)); + sn->search_next.level = RAW_SEARCH_SEARCH; + req->async.private = sn; + if (!req_pull_blob(req, req->in.data, resume_key_length, + &(sn->search_next.in.search_id))) + req_reply_error(req, NT_STATUS_INVALID_PARAMETER); + sn->search_next.in.search_attrib = SVAL(req->in.vwv, VWV(1)); + sn->search_next.in.max_count = SVAL(req->in.vwv, VWV(0)); + + /* call backend */ + req->async.status = req->conn->ntvfs_ops->search_next(req, + sn, &state, find_callback); + SSVAL(req->out.vwv, VWV(0), sn->search_next.out.count); + } else { + /* do a search first operation */ + req->async.private = sf; + sf->search_first.level = RAW_SEARCH_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)); + + /* call backend */ + req->async.status = req->conn->ntvfs_ops->search_first(req, + sf, &state, find_callback); + SSVAL(req->out.vwv, VWV(0), sf->search_first.out.count); + } + + req_send_reply(req); +} + + +/**************************************************************************** + Reply to a fclose (async reply) +****************************************************************************/ +static void reply_fclose_send(struct request_context *req) +{ + CHECK_ASYNC_STATUS; + + /* construct reply */ + req_setup_reply(req, 1, 0); + + req_send_reply(req); +} + + +/**************************************************************************** + Reply to fclose (stop directory search). +****************************************************************************/ +void reply_fclose(struct request_context *req) +{ + union smb_search_next *sn; + DATA_BLOB resume_key; + uint16 resume_key_length; + + REQ_TALLOC(sn, sizeof(*sn)); + + /* parse request */ + if (req->in.wct != 2) { + req_reply_error(req, NT_STATUS_INVALID_PARAMETER); + return; + } + + sn->search_next.level = RAW_SEARCH_FCLOSE; + + /* pull in type 5 byte and length */ + if (!req_pull_blob(req, req->in.data, 3, &resume_key)) + req_reply_error(req, NT_STATUS_INVALID_PARAMETER); + resume_key_length = SVAL(resume_key.data, 1); + if (resume_key_length > 0) { + /* do a search close operation */ + if (!req_pull_blob(req, req->in.data, resume_key_length, + &(sn->search_next.in.search_id))) + req_reply_error(req, NT_STATUS_INVALID_PARAMETER); + } else + req_reply_error(req, NT_STATUS_INVALID_PARAMETER); + + req->async.send_fn = reply_fclose_send; + req->async.private = sn; + + /* call backend */ + req->async.status = req->conn->ntvfs_ops->search_next(req, sn, + NULL, NULL); + + REQ_ASYNC_TAIL; +} diff --git a/source4/smb_server/service.c b/source4/smb_server/service.c new file mode 100644 index 0000000000..d219e6cee0 --- /dev/null +++ b/source4/smb_server/service.c @@ -0,0 +1,339 @@ +/* + Unix SMB/CIFS implementation. + service (connection) handling + Copyright (C) Andrew Tridgell 1992-2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" + + +/**************************************************************************** + Add a home service. Returns the new service number or -1 if fail. +****************************************************************************/ +int add_home_service(const char *service, const char *username, const char *homedir) +{ + int iHomeService; + + if (!service || !homedir) + return -1; + + if ((iHomeService = lp_servicenumber(HOMES_NAME)) < 0) + return -1; + + /* + * If this is a winbindd provided username, remove + * the domain component before adding the service. + * Log a warning if the "path=" parameter does not + * include any macros. + */ + + { + const char *p = strchr(service,*lp_winbind_separator()); + + /* We only want the 'user' part of the string */ + if (p) { + service = p + 1; + } + } + + if (!lp_add_home(service, iHomeService, username, homedir)) { + return -1; + } + + return lp_servicenumber(service); + +} + + +/** + * Find a service entry. service is always in dos codepage. + * + * @param service is modified (to canonical form??) + **/ +static int find_service(const char *service) +{ + int iService; + + iService = lp_servicenumber(service); + + /* now handle the special case of a home directory */ + if (iService == -1) { + char *phome_dir = get_user_home_dir(service); + + if(!phome_dir) { + /* + * Try mapping the servicename, it may + * be a Windows to unix mapped user name. + */ +/* REWRITE: + if (map_username(service)) + phome_dir = get_user_home_dir(service); +*/ + } + + DEBUG(3,("checking for home directory %s gave %s\n",service, + phome_dir?phome_dir:"(NULL)")); + + iService = add_home_service(service,service /* 'username' */, phome_dir); + } + + /* If we still don't have a service, attempt to add it as a printer. */ + if (iService == -1) { + int iPrinterService; + + if ((iPrinterService = lp_servicenumber(PRINTERS_NAME)) >= 0) { + char *pszTemp; + + DEBUG(3,("checking whether %s is a valid printer name...\n", service)); + pszTemp = lp_printcapname(); + if ((pszTemp != NULL) && pcap_printername_ok(service, pszTemp)) { + DEBUG(3,("%s is a valid printer name\n", service)); + DEBUG(3,("adding %s as a printer service\n", service)); + lp_add_printer(service, iPrinterService); + iService = lp_servicenumber(service); + if (iService < 0) + DEBUG(0,("failed to add %s as a printer service!\n", service)); + } else { + DEBUG(3,("%s is not a valid printer name\n", service)); + } + } + } + + /* Check for default vfs service? Unsure whether to implement this */ + if (iService == -1) { + } + + /* just possibly it's a default service? */ + if (iService == -1) { + char *pdefservice = lp_defaultservice(); + if (pdefservice && *pdefservice && + !strequal(pdefservice,service) && + !strstr(service,"..")) { + /* + * We need to do a local copy here as lp_defaultservice() + * returns one of the rotating lp_string buffers that + * could get overwritten by the recursive find_service() call + * below. Fix from Josef Hinteregger <joehtg@joehtg.co.at>. + */ + pstring defservice; + pstrcpy(defservice, pdefservice); + iService = find_service(defservice); + if (iService >= 0) { + /* REWRITE: all_string_sub(service, "_","/",0); */ + iService = lp_add_service(service, iService); + } + } + } + + if (iService >= 0 && !VALID_SNUM(iService)) { + DEBUG(0,("Invalid snum %d for %s\n",iService, service)); + iService = -1; + } + + if (iService == -1) { + DEBUG(3,("find_service() failed to find service %s\n", service)); + } + + return iService; +} + + +/**************************************************************************** + Make a connection, given the snum to connect to, and the vuser of the + connecting user if appropriate. +****************************************************************************/ +static NTSTATUS make_connection_snum(struct request_context *req, + int snum, enum ntvfs_type type, + DATA_BLOB password, + const char *dev) +{ + struct tcon_context *conn; + NTSTATUS status; + + conn = conn_new(req->smb); + if (!conn) { + DEBUG(0,("Couldn't find free connection.\n")); + return NT_STATUS_INSUFFICIENT_RESOURCES; + } + req->conn = conn; + + conn->service = snum; + conn->type = type; + + /* + * New code to check if there's a share security descripter + * added from NT server manager. This is done after the + * smb.conf checks are done as we need a uid and token. JRA. + * + */ + + if (!share_access_check(req, conn, snum, SA_RIGHT_FILE_WRITE_DATA)) { + if (!share_access_check(req, conn, snum, SA_RIGHT_FILE_READ_DATA)) { + /* No access, read or write. */ + DEBUG(0,( "make_connection: connection to %s denied due to security descriptor.\n", + lp_servicename(snum))); + conn_free(req->smb, conn); + return NT_STATUS_ACCESS_DENIED; + } else { + conn->read_only = True; + } + } + + /* check number of connections */ + if (!claim_connection(conn, + lp_servicename(SNUM(conn)), + lp_max_connections(SNUM(conn)), + False,0)) { + DEBUG(1,("too many connections - rejected\n")); + conn_free(req->smb, conn); + return NT_STATUS_INSUFFICIENT_RESOURCES; + } + + /* init ntvfs function pointers */ + status = ntvfs_init_connection(req); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("ntvfs_init_connection failed for service %s\n", lp_servicename(SNUM(conn)))); + conn_free(req->smb, conn); + return status; + } + + /* Invoke NTVFS connection hook */ + if (conn->ntvfs_ops->connect) { + status = conn->ntvfs_ops->connect(req, lp_servicename(snum)); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0,("make_connection: NTVFS make connection failed!\n")); + conn_free(req->smb, conn); + return status; + } + } + + return NT_STATUS_OK; +} + +/**************************************************************************** + Make a connection to a service. + * + * @param service +****************************************************************************/ +static NTSTATUS make_connection(struct request_context *req, + const char *service, DATA_BLOB password, + const char *dev, uint16 vuid) +{ + int snum; + enum ntvfs_type type; + const char *type_str; + + /* the service might be of the form \\SERVER\SHARE. Should we put + the server name we get from this somewhere? */ + if (strncmp(service, "\\\\", 2) == 0) { + char *p = strchr(service+2, '\\'); + if (p) { + service = p + 1; + } + } + + snum = find_service(service); + + if (snum == -1) { + DEBUG(0,("%s couldn't find service %s\n", + sub_get_remote_machine(), service)); + return NT_STATUS_BAD_NETWORK_NAME; + } + + /* work out what sort of connection this is */ + if (strcmp(lp_fstype(snum), "IPC") == 0) { + type = NTVFS_IPC; + type_str = "IPC"; + } else if (lp_print_ok(snum)) { + type = NTVFS_PRINT; + type_str = "LPT:"; + } else { + type = NTVFS_DISK; + type_str = "A:"; + } + + if (strcmp(dev, "?????") != 0 && strcasecmp(type_str, dev) != 0) { + /* the client gave us the wrong device type */ + return NT_STATUS_BAD_DEVICE_TYPE; + } + + return make_connection_snum(req, snum, type, password, dev); +} + +/**************************************************************************** +close a cnum +****************************************************************************/ +void close_cnum(struct tcon_context *conn) +{ + DEBUG(3, ("%s (%s) closed connection to service %s\n", + sub_get_remote_machine(),conn->smb->socket.client_addr, + lp_servicename(SNUM(conn)))); + + yield_connection(conn, lp_servicename(SNUM(conn))); + + /* tell the ntvfs backend that we are disconnecting */ + conn->ntvfs_ops->disconnect(conn); + + conn_free(conn->smb, conn); +} + + + +/* + backend for tree connect call +*/ +NTSTATUS tcon_backend(struct request_context *req, union smb_tcon *con) +{ + NTSTATUS status; + + /* can only do bare tcon in share level security */ + if (req->user_ctx == NULL && lp_security() != SEC_SHARE) { + return NT_STATUS_ACCESS_DENIED; + } + + if (con->generic.level == RAW_TCON_TCON) { + DATA_BLOB password; + password = data_blob(con->tcon.in.password, strlen(con->tcon.in.password) + 1); + + status = make_connection(req, con->tcon.in.service, password, con->tcon.in.dev, req->user_ctx->vuid); + + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + con->tcon.out.max_xmit = req->smb->negotiate.max_recv; + con->tcon.out.cnum = req->conn->cnum; + + return status; + } + + status = make_connection(req, con->tconx.in.path, con->tconx.in.password, + con->tconx.in.device, req->user_ctx->vuid); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + con->tconx.out.cnum = req->conn->cnum; + con->tconx.out.dev_type = talloc_strdup(req->mem_ctx, req->conn->dev_type); + con->tconx.out.fs_type = talloc_strdup(req->mem_ctx, req->conn->fs_type); + con->tconx.out.options = SMB_SUPPORT_SEARCH_BITS | (lp_csc_policy(req->conn->service) << 2); + if (lp_msdfs_root(req->conn->service) && lp_host_msdfs()) { + con->tconx.out.options |= SMB_SHARE_IN_DFS; + } + + return status; +} diff --git a/source4/smb_server/session.c b/source4/smb_server/session.c new file mode 100644 index 0000000000..7f85fca2ac --- /dev/null +++ b/source4/smb_server/session.c @@ -0,0 +1,42 @@ +/* + Unix SMB/CIFS implementation. + session handling for utmp and PAM + Copyright (C) tridge@samba.org 2001 + Copyright (C) abartlet@pcug.org.au 2001 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/* a "session" is claimed when we do a SessionSetupX operation + and is yielded when the corresponding vuid is destroyed. + + sessions are used to populate utmp and PAM session structures +*/ + +#include "includes.h" + +/* called when a session is created */ +BOOL session_claim(struct server_context *smb, user_struct *vuser) +{ + DEBUG(0,("rewrite: Not doing session claim\n")); + return True; +} + +/* called when a session is destroyed */ +void session_yield(user_struct *vuser) +{ + DEBUG(0,("rewrite: Not doing session yield\n")); +} + diff --git a/source4/smb_server/sesssetup.c b/source4/smb_server/sesssetup.c new file mode 100644 index 0000000000..14e300c191 --- /dev/null +++ b/source4/smb_server/sesssetup.c @@ -0,0 +1,149 @@ +/* + Unix SMB/CIFS implementation. + handle SMBsessionsetup + Copyright (C) Andrew Tridgell 1998-2001 + Copyright (C) Andrew Bartlett 2001 + Copyright (C) Jim McDonough 2002 + Copyright (C) Luke Howard 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" + +/* + setup the OS, Lanman and domain portions of a session setup reply +*/ +static void sesssetup_common_strings(struct request_context *req, + char **os, char **lanman, char **domain) +{ + (*os) = talloc_asprintf(req->mem_ctx, "Unix"); + (*lanman) = talloc_asprintf(req->mem_ctx, "Samba %s", SAMBA_VERSION_STRING); + (*domain) = talloc_asprintf(req->mem_ctx, "%s", lp_workgroup()); +} + + +/* + handler for old style session setup +*/ +static NTSTATUS sesssetup_old(struct request_context *req, union smb_sesssetup *sess) +{ + NTSTATUS status; + auth_usersupplied_info *user_info = NULL; + auth_serversupplied_info *server_info = NULL; + DATA_BLOB null_blob; + + if (!req->smb->negotiate.done_sesssetup) { + req->smb->negotiate.max_send = sess->old.in.bufsize; + } + + null_blob.length = 0; + + status = make_user_info_for_reply_enc(&user_info, + sess->old.in.user, sess->old.in.domain, + sess->old.in.password, + null_blob); + if (!NT_STATUS_IS_OK(status)) { + return NT_STATUS_ACCESS_DENIED; + } + + status = req->smb->negotiate.auth_context->check_ntlm_password(req->smb->negotiate.auth_context, + user_info, + &server_info); + if (!NT_STATUS_IS_OK(status)) { + return NT_STATUS_ACCESS_DENIED; + } + + sess->old.out.action = 0; + sess->old.out.vuid = register_vuid(req->smb, server_info, sess->old.in.user); + sesssetup_common_strings(req, + &sess->old.out.os, + &sess->old.out.lanman, + &sess->old.out.domain); + + return NT_STATUS_OK; +} + + +/* + handler for NT1 style session setup +*/ +static NTSTATUS sesssetup_nt1(struct request_context *req, union smb_sesssetup *sess) +{ + NTSTATUS status; + auth_usersupplied_info *user_info = NULL; + auth_serversupplied_info *server_info = NULL; + + if (!req->smb->negotiate.done_sesssetup) { + req->smb->negotiate.max_send = sess->nt1.in.bufsize; + req->smb->negotiate.client_caps = sess->nt1.in.capabilities; + } + + status = make_user_info_for_reply_enc(&user_info, + sess->nt1.in.user, sess->nt1.in.domain, + sess->nt1.in.password1, + sess->nt1.in.password2); + if (!NT_STATUS_IS_OK(status)) { + return NT_STATUS_ACCESS_DENIED; + } + + status = req->smb->negotiate.auth_context->check_ntlm_password(req->smb->negotiate.auth_context, + user_info, + &server_info); + if (!NT_STATUS_IS_OK(status)) { + return NT_STATUS_ACCESS_DENIED; + } + + sess->nt1.out.action = 0; + sess->nt1.out.vuid = register_vuid(req->smb, server_info, sess->old.in.user); + sesssetup_common_strings(req, + &sess->nt1.out.os, + &sess->nt1.out.lanman, + &sess->nt1.out.domain); + + return NT_STATUS_OK; +} + + +/* + handler for SPNEGO style session setup +*/ +static NTSTATUS sesssetup_spnego(struct request_context *req, union smb_sesssetup *sess) +{ + /* defer this one for now */ + return NT_STATUS_INVALID_LEVEL; +} + +/* + backend for sessionsetup call - this takes all 3 varients of the call +*/ +NTSTATUS sesssetup_backend(struct request_context *req, + union smb_sesssetup *sess) +{ + switch (sess->generic.level) { + case RAW_SESSSETUP_OLD: + return sesssetup_old(req, sess); + case RAW_SESSSETUP_NT1: + return sesssetup_nt1(req, sess); + case RAW_SESSSETUP_SPNEGO: + return sesssetup_spnego(req, sess); + } + + req->smb->negotiate.done_sesssetup = True; + + return NT_STATUS_INVALID_LEVEL; +} + + diff --git a/source4/smb_server/smb_server.c b/source4/smb_server/smb_server.c new file mode 100644 index 0000000000..b8e1e032e2 --- /dev/null +++ b/source4/smb_server/smb_server.c @@ -0,0 +1,769 @@ +/* + Unix SMB/CIFS implementation. + process incoming packets - main loop + Copyright (C) Andrew Tridgell 1992-2003 + Copyright (C) James J Myers 2003 <myersjj@samba.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" + + +/* + send an oplock break request to a client +*/ +BOOL req_send_oplock_break(struct tcon_context *conn, uint16 fnum, uint8 level) +{ + struct request_context *req; + + req = init_smb_request(conn->smb); + + req_setup_reply(req, 8, 0); + + SCVAL(req->out.hdr,HDR_COM,SMBlockingX); + SSVAL(req->out.hdr,HDR_TID,conn->cnum); + SSVAL(req->out.hdr,HDR_PID,0xFFFF); + SSVAL(req->out.hdr,HDR_UID,0); + SSVAL(req->out.hdr,HDR_MID,0xFFFF); + SCVAL(req->out.hdr,HDR_FLG,0); + SSVAL(req->out.hdr,HDR_FLG2,0); + + SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE); + SSVAL(req->out.vwv, VWV(1), 0); + SSVAL(req->out.vwv, VWV(2), fnum); + SSVAL(req->out.vwv, VWV(3), level); + SIVAL(req->out.vwv, VWV(4), 0); + SSVAL(req->out.vwv, VWV(6), 0); + SSVAL(req->out.vwv, VWV(7), 0); + + req_send_reply(req); + return True; +} + +/**************************************************************************** +receive a SMB request from the wire, forming a request_context from the result +****************************************************************************/ +static struct request_context *receive_smb_request(struct server_context *smb) +{ + ssize_t len, len2; + char header[4]; + struct request_context *req; + + len = read_data(smb->socket.fd, header, 4); + if (len != 4) { + return NULL; + } + + len = smb_len(header); + + req = init_smb_request(smb); + + GetTimeOfDay(&req->request_time); + req->chained_fnum = -1; + + /* allocate the incoming buffer at the right size */ + req->in.buffer = talloc(req->mem_ctx, len + NBT_HDR_SIZE); + + /* fill in the already received header */ + memcpy(req->in.buffer, header, 4); + + len2 = read_data(smb->socket.fd, req->in.buffer + NBT_HDR_SIZE, len); + if (len2 != len) { + return NULL; + } + + /* fill in the rest of the req->in structure */ + req->in.size = len + NBT_HDR_SIZE; + req->in.allocated = req->in.size; + req->in.hdr = req->in.buffer + NBT_HDR_SIZE; + req->in.vwv = req->in.hdr + HDR_VWV; + req->in.wct = CVAL(req->in.hdr, HDR_WCT); + if (req->in.vwv + VWV(req->in.wct) <= req->in.buffer + req->in.size) { + req->in.data = req->in.vwv + VWV(req->in.wct) + 2; + req->in.data_size = SVAL(req->in.vwv, VWV(req->in.wct)); + + /* the bcc length is only 16 bits, but some packets + (such as SMBwriteX) can be much larger than 64k. We + detect this by looking for a large non-chained NBT + packet (at least 64k bigger than what is + specified). If it is detected then the NBT size is + used instead of the bcc size */ + if (req->in.data_size + 0x10000 <= + req->in.size - PTR_DIFF(req->in.data, req->in.buffer) && + (req->in.wct < 1 || SVAL(req->in.vwv, VWV(0)) == SMB_CHAIN_NONE)) { + /* its an oversized packet! fun for all the family */ + req->in.data_size = req->in.size - PTR_DIFF(req->in.data,req->in.buffer); + } + } + + return req; +} + +/* + setup the user_ctx element of a request +*/ +static void setup_user_context(struct request_context *req) +{ + struct user_context *ctx; + + ctx = talloc(req->mem_ctx, sizeof(*ctx)); + ctx->vuid = SVAL(req->in.hdr, HDR_UID); + ctx->vuser = get_valid_user_struct(req->smb, ctx->vuid); + + req->user_ctx = ctx; +} + + +/* +These flags determine some of the permissions required to do an operation + +Note that I don't set NEED_WRITE on some write operations because they +are used by some brain-dead clients when printing, and I don't want to +force write permissions on print services. +*/ +#define AS_USER (1<<0) +#define NEED_WRITE (1<<1) +#define TIME_INIT (1<<2) +#define CAN_IPC (1<<3) +#define AS_GUEST (1<<5) +#define USE_MUTEX (1<<7) + +/* + 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 request_context *); + int flags; +} + smb_messages[256] = { +/* 0x00 */ { "SMBmkdir",reply_mkdir,AS_USER | NEED_WRITE}, +/* 0x01 */ { "SMBrmdir",reply_rmdir,AS_USER | NEED_WRITE}, +/* 0x02 */ { "SMBopen",reply_open,AS_USER }, +/* 0x03 */ { "SMBcreate",reply_mknew,AS_USER}, +/* 0x04 */ { "SMBclose",reply_close,AS_USER | CAN_IPC }, +/* 0x05 */ { "SMBflush",reply_flush,AS_USER}, +/* 0x06 */ { "SMBunlink",reply_unlink,AS_USER | NEED_WRITE }, +/* 0x07 */ { "SMBmv",reply_mv,AS_USER | NEED_WRITE }, +/* 0x08 */ { "SMBgetatr",reply_getatr,AS_USER}, +/* 0x09 */ { "SMBsetatr",reply_setatr,AS_USER | NEED_WRITE}, +/* 0x0a */ { "SMBread",reply_read,AS_USER}, +/* 0x0b */ { "SMBwrite",reply_write,AS_USER | CAN_IPC }, +/* 0x0c */ { "SMBlock",reply_lock,AS_USER}, +/* 0x0d */ { "SMBunlock",reply_unlock,AS_USER}, +/* 0x0e */ { "SMBctemp",reply_ctemp,AS_USER }, +/* 0x0f */ { "SMBmknew",reply_mknew,AS_USER}, +/* 0x10 */ { "SMBchkpth",reply_chkpth,AS_USER}, +/* 0x11 */ { "SMBexit",reply_exit,0}, +/* 0x12 */ { "SMBlseek",reply_lseek,AS_USER}, +/* 0x13 */ { "SMBlockread",reply_lockread,AS_USER}, +/* 0x14 */ { "SMBwriteunlock",reply_writeunlock,AS_USER}, +/* 0x15 */ { NULL, NULL, 0 }, +/* 0x16 */ { NULL, NULL, 0 }, +/* 0x17 */ { NULL, NULL, 0 }, +/* 0x18 */ { NULL, NULL, 0 }, +/* 0x19 */ { NULL, NULL, 0 }, +/* 0x1a */ { "SMBreadbraw",reply_readbraw,AS_USER}, +/* 0x1b */ { "SMBreadBmpx",reply_readbmpx,AS_USER}, +/* 0x1c */ { "SMBreadBs",NULL,0 }, +/* 0x1d */ { "SMBwritebraw",reply_writebraw,AS_USER}, +/* 0x1e */ { "SMBwriteBmpx",reply_writebmpx,AS_USER}, +/* 0x1f */ { "SMBwriteBs",reply_writebs,AS_USER}, +/* 0x20 */ { "SMBwritec",NULL,0}, +/* 0x21 */ { NULL, NULL, 0 }, +/* 0x22 */ { "SMBsetattrE",reply_setattrE,AS_USER | NEED_WRITE }, +/* 0x23 */ { "SMBgetattrE",reply_getattrE,AS_USER }, +/* 0x24 */ { "SMBlockingX",reply_lockingX,AS_USER }, +/* 0x25 */ { "SMBtrans",reply_trans,AS_USER | CAN_IPC }, +/* 0x26 */ { "SMBtranss",NULL,AS_USER | CAN_IPC}, +/* 0x27 */ { "SMBioctl",reply_ioctl,0}, +/* 0x28 */ { "SMBioctls",NULL,AS_USER}, +/* 0x29 */ { "SMBcopy",reply_copy,AS_USER | NEED_WRITE }, +/* 0x2a */ { "SMBmove",NULL,AS_USER | NEED_WRITE }, +/* 0x2b */ { "SMBecho",reply_echo,0}, +/* 0x2c */ { "SMBwriteclose",reply_writeclose,AS_USER}, +/* 0x2d */ { "SMBopenX",reply_open_and_X,AS_USER | CAN_IPC }, +/* 0x2e */ { "SMBreadX",reply_read_and_X,AS_USER | CAN_IPC }, +/* 0x2f */ { "SMBwriteX",reply_write_and_X,AS_USER | CAN_IPC }, +/* 0x30 */ { NULL, NULL, 0 }, +/* 0x31 */ { NULL, NULL, 0 }, +/* 0x32 */ { "SMBtrans2", reply_trans2, AS_USER | CAN_IPC }, +/* 0x33 */ { "SMBtranss2", reply_transs2, AS_USER}, +/* 0x34 */ { "SMBfindclose", reply_findclose,AS_USER}, +/* 0x35 */ { "SMBfindnclose", reply_findnclose, AS_USER}, +/* 0x36 */ { NULL, NULL, 0 }, +/* 0x37 */ { NULL, NULL, 0 }, +/* 0x38 */ { NULL, NULL, 0 }, +/* 0x39 */ { NULL, NULL, 0 }, +/* 0x3a */ { NULL, NULL, 0 }, +/* 0x3b */ { NULL, NULL, 0 }, +/* 0x3c */ { NULL, NULL, 0 }, +/* 0x3d */ { NULL, NULL, 0 }, +/* 0x3e */ { NULL, NULL, 0 }, +/* 0x3f */ { NULL, NULL, 0 }, +/* 0x40 */ { NULL, NULL, 0 }, +/* 0x41 */ { NULL, NULL, 0 }, +/* 0x42 */ { NULL, NULL, 0 }, +/* 0x43 */ { NULL, NULL, 0 }, +/* 0x44 */ { NULL, NULL, 0 }, +/* 0x45 */ { NULL, NULL, 0 }, +/* 0x46 */ { NULL, NULL, 0 }, +/* 0x47 */ { NULL, NULL, 0 }, +/* 0x48 */ { NULL, NULL, 0 }, +/* 0x49 */ { NULL, NULL, 0 }, +/* 0x4a */ { NULL, NULL, 0 }, +/* 0x4b */ { NULL, NULL, 0 }, +/* 0x4c */ { NULL, NULL, 0 }, +/* 0x4d */ { NULL, NULL, 0 }, +/* 0x4e */ { NULL, NULL, 0 }, +/* 0x4f */ { NULL, NULL, 0 }, +/* 0x50 */ { NULL, NULL, 0 }, +/* 0x51 */ { NULL, NULL, 0 }, +/* 0x52 */ { NULL, NULL, 0 }, +/* 0x53 */ { NULL, NULL, 0 }, +/* 0x54 */ { NULL, NULL, 0 }, +/* 0x55 */ { NULL, NULL, 0 }, +/* 0x56 */ { NULL, NULL, 0 }, +/* 0x57 */ { NULL, NULL, 0 }, +/* 0x58 */ { NULL, NULL, 0 }, +/* 0x59 */ { NULL, NULL, 0 }, +/* 0x5a */ { NULL, NULL, 0 }, +/* 0x5b */ { NULL, NULL, 0 }, +/* 0x5c */ { NULL, NULL, 0 }, +/* 0x5d */ { NULL, NULL, 0 }, +/* 0x5e */ { NULL, NULL, 0 }, +/* 0x5f */ { NULL, NULL, 0 }, +/* 0x60 */ { NULL, NULL, 0 }, +/* 0x61 */ { NULL, NULL, 0 }, +/* 0x62 */ { NULL, NULL, 0 }, +/* 0x63 */ { NULL, NULL, 0 }, +/* 0x64 */ { NULL, NULL, 0 }, +/* 0x65 */ { NULL, NULL, 0 }, +/* 0x66 */ { NULL, NULL, 0 }, +/* 0x67 */ { NULL, NULL, 0 }, +/* 0x68 */ { NULL, NULL, 0 }, +/* 0x69 */ { NULL, NULL, 0 }, +/* 0x6a */ { NULL, NULL, 0 }, +/* 0x6b */ { NULL, NULL, 0 }, +/* 0x6c */ { NULL, NULL, 0 }, +/* 0x6d */ { NULL, NULL, 0 }, +/* 0x6e */ { NULL, NULL, 0 }, +/* 0x6f */ { NULL, NULL, 0 }, +/* 0x70 */ { "SMBtcon",reply_tcon,USE_MUTEX}, +/* 0x71 */ { "SMBtdis",reply_tdis,0}, +/* 0x72 */ { "SMBnegprot",reply_negprot,USE_MUTEX}, +/* 0x73 */ { "SMBsesssetupX",reply_sesssetup,USE_MUTEX}, +/* 0x74 */ { "SMBulogoffX", reply_ulogoffX, 0}, /* ulogoff doesn't give a valid TID */ +/* 0x75 */ { "SMBtconX",reply_tcon_and_X,USE_MUTEX}, +/* 0x76 */ { NULL, NULL, 0 }, +/* 0x77 */ { NULL, NULL, 0 }, +/* 0x78 */ { NULL, NULL, 0 }, +/* 0x79 */ { NULL, NULL, 0 }, +/* 0x7a */ { NULL, NULL, 0 }, +/* 0x7b */ { NULL, NULL, 0 }, +/* 0x7c */ { NULL, NULL, 0 }, +/* 0x7d */ { NULL, NULL, 0 }, +/* 0x7e */ { NULL, NULL, 0 }, +/* 0x7f */ { NULL, NULL, 0 }, +/* 0x80 */ { "SMBdskattr",reply_dskattr,AS_USER}, +/* 0x81 */ { "SMBsearch",reply_search,AS_USER}, +/* 0x82 */ { "SMBffirst",reply_search,AS_USER}, +/* 0x83 */ { "SMBfunique",reply_search,AS_USER}, +/* 0x84 */ { "SMBfclose",reply_fclose,AS_USER}, +/* 0x85 */ { NULL, NULL, 0 }, +/* 0x86 */ { NULL, NULL, 0 }, +/* 0x87 */ { NULL, NULL, 0 }, +/* 0x88 */ { NULL, NULL, 0 }, +/* 0x89 */ { NULL, NULL, 0 }, +/* 0x8a */ { NULL, NULL, 0 }, +/* 0x8b */ { NULL, NULL, 0 }, +/* 0x8c */ { NULL, NULL, 0 }, +/* 0x8d */ { NULL, NULL, 0 }, +/* 0x8e */ { NULL, NULL, 0 }, +/* 0x8f */ { NULL, NULL, 0 }, +/* 0x90 */ { NULL, NULL, 0 }, +/* 0x91 */ { NULL, NULL, 0 }, +/* 0x92 */ { NULL, NULL, 0 }, +/* 0x93 */ { NULL, NULL, 0 }, +/* 0x94 */ { NULL, NULL, 0 }, +/* 0x95 */ { NULL, NULL, 0 }, +/* 0x96 */ { NULL, NULL, 0 }, +/* 0x97 */ { NULL, NULL, 0 }, +/* 0x98 */ { NULL, NULL, 0 }, +/* 0x99 */ { NULL, NULL, 0 }, +/* 0x9a */ { NULL, NULL, 0 }, +/* 0x9b */ { NULL, NULL, 0 }, +/* 0x9c */ { NULL, NULL, 0 }, +/* 0x9d */ { NULL, NULL, 0 }, +/* 0x9e */ { NULL, NULL, 0 }, +/* 0x9f */ { NULL, NULL, 0 }, +/* 0xa0 */ { "SMBnttrans", reply_nttrans, AS_USER | CAN_IPC }, +/* 0xa1 */ { "SMBnttranss", reply_nttranss, AS_USER | CAN_IPC }, +/* 0xa2 */ { "SMBntcreateX", reply_ntcreate_and_X, AS_USER | CAN_IPC }, +/* 0xa3 */ { NULL, NULL, 0 }, +/* 0xa4 */ { "SMBntcancel", reply_ntcancel, 0 }, +/* 0xa5 */ { "SMBntrename", reply_ntrename, 0 }, +/* 0xa6 */ { NULL, NULL, 0 }, +/* 0xa7 */ { NULL, NULL, 0 }, +/* 0xa8 */ { NULL, NULL, 0 }, +/* 0xa9 */ { NULL, NULL, 0 }, +/* 0xaa */ { NULL, NULL, 0 }, +/* 0xab */ { NULL, NULL, 0 }, +/* 0xac */ { NULL, NULL, 0 }, +/* 0xad */ { NULL, NULL, 0 }, +/* 0xae */ { NULL, NULL, 0 }, +/* 0xaf */ { NULL, NULL, 0 }, +/* 0xb0 */ { NULL, NULL, 0 }, +/* 0xb1 */ { NULL, NULL, 0 }, +/* 0xb2 */ { NULL, NULL, 0 }, +/* 0xb3 */ { NULL, NULL, 0 }, +/* 0xb4 */ { NULL, NULL, 0 }, +/* 0xb5 */ { NULL, NULL, 0 }, +/* 0xb6 */ { NULL, NULL, 0 }, +/* 0xb7 */ { NULL, NULL, 0 }, +/* 0xb8 */ { NULL, NULL, 0 }, +/* 0xb9 */ { NULL, NULL, 0 }, +/* 0xba */ { NULL, NULL, 0 }, +/* 0xbb */ { NULL, NULL, 0 }, +/* 0xbc */ { NULL, NULL, 0 }, +/* 0xbd */ { NULL, NULL, 0 }, +/* 0xbe */ { NULL, NULL, 0 }, +/* 0xbf */ { NULL, NULL, 0 }, +/* 0xc0 */ { "SMBsplopen",reply_printopen,AS_USER }, +/* 0xc1 */ { "SMBsplwr",reply_printwrite,AS_USER}, +/* 0xc2 */ { "SMBsplclose",reply_printclose,AS_USER}, +/* 0xc3 */ { "SMBsplretq",reply_printqueue,AS_USER}, +/* 0xc4 */ { NULL, NULL, 0 }, +/* 0xc5 */ { NULL, NULL, 0 }, +/* 0xc6 */ { NULL, NULL, 0 }, +/* 0xc7 */ { NULL, NULL, 0 }, +/* 0xc8 */ { NULL, NULL, 0 }, +/* 0xc9 */ { NULL, NULL, 0 }, +/* 0xca */ { NULL, NULL, 0 }, +/* 0xcb */ { NULL, NULL, 0 }, +/* 0xcc */ { NULL, NULL, 0 }, +/* 0xcd */ { NULL, NULL, 0 }, +/* 0xce */ { NULL, NULL, 0 }, +/* 0xcf */ { NULL, NULL, 0 }, +/* 0xd0 */ { "SMBsends",reply_sends,AS_GUEST}, +/* 0xd1 */ { "SMBsendb",NULL,AS_GUEST}, +/* 0xd2 */ { "SMBfwdname",NULL,AS_GUEST}, +/* 0xd3 */ { "SMBcancelf",NULL,AS_GUEST}, +/* 0xd4 */ { "SMBgetmac",NULL,AS_GUEST}, +/* 0xd5 */ { "SMBsendstrt",reply_sendstrt,AS_GUEST}, +/* 0xd6 */ { "SMBsendend",reply_sendend,AS_GUEST}, +/* 0xd7 */ { "SMBsendtxt",reply_sendtxt,AS_GUEST}, +/* 0xd8 */ { NULL, NULL, 0 }, +/* 0xd9 */ { NULL, NULL, 0 }, +/* 0xda */ { NULL, NULL, 0 }, +/* 0xdb */ { NULL, NULL, 0 }, +/* 0xdc */ { NULL, NULL, 0 }, +/* 0xdd */ { NULL, NULL, 0 }, +/* 0xde */ { NULL, NULL, 0 }, +/* 0xdf */ { NULL, NULL, 0 }, +/* 0xe0 */ { NULL, NULL, 0 }, +/* 0xe1 */ { NULL, NULL, 0 }, +/* 0xe2 */ { NULL, NULL, 0 }, +/* 0xe3 */ { NULL, NULL, 0 }, +/* 0xe4 */ { NULL, NULL, 0 }, +/* 0xe5 */ { NULL, NULL, 0 }, +/* 0xe6 */ { NULL, NULL, 0 }, +/* 0xe7 */ { NULL, NULL, 0 }, +/* 0xe8 */ { NULL, NULL, 0 }, +/* 0xe9 */ { NULL, NULL, 0 }, +/* 0xea */ { NULL, NULL, 0 }, +/* 0xeb */ { NULL, NULL, 0 }, +/* 0xec */ { NULL, NULL, 0 }, +/* 0xed */ { NULL, NULL, 0 }, +/* 0xee */ { NULL, NULL, 0 }, +/* 0xef */ { NULL, NULL, 0 }, +/* 0xf0 */ { NULL, NULL, 0 }, +/* 0xf1 */ { NULL, NULL, 0 }, +/* 0xf2 */ { NULL, NULL, 0 }, +/* 0xf3 */ { NULL, NULL, 0 }, +/* 0xf4 */ { NULL, NULL, 0 }, +/* 0xf5 */ { NULL, NULL, 0 }, +/* 0xf6 */ { NULL, NULL, 0 }, +/* 0xf7 */ { NULL, NULL, 0 }, +/* 0xf8 */ { NULL, NULL, 0 }, +/* 0xf9 */ { NULL, NULL, 0 }, +/* 0xfa */ { NULL, NULL, 0 }, +/* 0xfb */ { NULL, NULL, 0 }, +/* 0xfc */ { NULL, NULL, 0 }, +/* 0xfd */ { NULL, NULL, 0 }, +/* 0xfe */ { NULL, NULL, 0 }, +/* 0xff */ { NULL, NULL, 0 } +}; + +/**************************************************************************** +return a string containing the function name of a SMB command +****************************************************************************/ +static const char *smb_fn_name(uint8 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 request_context *req) +{ + int flags; + uint16 session_tag; + struct server_context *smb = req->smb; + + type &= 0xff; + + errno = 0; + + if (smb_messages[type].fn == NULL) { + DEBUG(0,("Unknown message type %d!\n",type)); + reply_unknown(req); + return; + } + + flags = smb_messages[type].flags; + + /* In share mode security we must ignore the vuid. */ + session_tag = (lp_security() == SEC_SHARE) ? + UID_FIELD_INVALID : + SVAL(req->in.hdr,HDR_UID); + + req->conn = conn_find(req->smb, SVAL(req->in.hdr,HDR_TID)); + + /* setup the user context for this request */ + setup_user_context(req); + + /* Ensure this value is replaced in the incoming packet. */ + SSVAL(req->in.hdr,HDR_UID,session_tag); + + if (req->user_ctx) { + req->user_ctx->vuid = session_tag; + } + DEBUG(3,("switch message %s (task_id %d)\n",smb_fn_name(type), smb->model_ops->get_id(req))); + + /* does this protocol need to be run as root? */ + if (!(flags & AS_USER)) { + change_to_root_user(); + } + + /* does this protocol need a valid tree connection? */ + if ((flags & AS_USER) && !req->conn) { + req_reply_error(req, NT_STATUS_NETWORK_NAME_DELETED); + return; + } + + /* does this protocol need to be run as the connected user? */ +#if HACK_REWRITE + if ((flags & AS_USER) && !change_to_user(req->conn,session_tag)) { + if (!(flags & AS_GUEST)) { + req_reply_error(req, NT_STATUS_ACCESS_DENIED); + return; + } + + /* we'll run it as guest */ + flags &= ~AS_USER; + } +#endif + + /* this code is to work around a bug is MS client 3 without + introducing a security hole - it needs to be able to do + print queue checks as guest if it isn't logged in properly */ + if (flags & AS_USER) { + flags &= ~AS_GUEST; + } + + /* does it need write permission? */ + if ((flags & NEED_WRITE) && !CAN_WRITE(req->conn)) { + req_reply_error(req, NT_STATUS_ACCESS_DENIED); + return; + } + + /* ipc services are limited */ + if (req->conn && req->conn->type == NTVFS_IPC && (flags & AS_USER) && !(flags & CAN_IPC)) { + req_reply_error(req, NT_STATUS_ACCESS_DENIED); + return; + } + + /* load service specific parameters */ + if (req->conn && !set_current_service(req->conn,(flags & AS_USER)?True:False)) { + req_reply_error(req, NT_STATUS_ACCESS_DENIED); + return; + } + + /* does this protocol need to be run as guest? */ +#if HACK_REWRITE + if ((flags & AS_GUEST) && + !change_to_guest()) { + req_reply_error(req, NT_STATUS_ACCESS_DENIED); + return; + } +#endif + /* THREAD TESTING: use mutex to serialize calls to critical functions with global state */ + if (flags & USE_MUTEX) { + MUTEX_LOCK_BY_ID(MUTEX_SMBD); + } + smb_messages[type].fn(req); + if (flags & USE_MUTEX) { + MUTEX_UNLOCK_BY_ID(MUTEX_SMBD); + } +} + + +/**************************************************************************** + Construct a reply to the incoming packet. +****************************************************************************/ +static void construct_reply(struct request_context *req) +{ + uint8 type = CVAL(req->in.hdr,HDR_COM); + + /* see if its a special NBT packet */ + if (CVAL(req->in.buffer,0) != 0) { + reply_special(req); + return; + } + + + /* Make sure this is an SMB packet */ + if (memcmp(req->in.hdr,"\377SMB",4) != 0) { + DEBUG(2,("Non-SMB packet of length %d. Terminating connection\n", + req->in.size)); + exit_server(req->smb, "Non-SMB packet"); + return; + } + + 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)); + exit_server(req->smb, "Invalid SMB packet"); + return; + } + + if (NBT_HDR_SIZE + MIN_SMB_SIZE + 2*req->in.wct + req->in.data_size > req->in.size) { + DEBUG(2,("Invalid SMB buffer length count %d\n", req->in.data_size)); + exit_server(req->smb, "Invalid SMB packet"); + return; + } + + + req->smbpid = SVAL(req->in.hdr,HDR_PID); + req->flags = CVAL(req->in.hdr, HDR_FLG); + req->flags2 = SVAL(req->in.hdr, HDR_FLG2); + + switch_message(type, req); +} + + +/* + we call this when first first part of a possibly chained request has been completed + and we need to call the 2nd part, if any +*/ +void chain_reply(struct request_context *req) +{ + uint16 chain_cmd, chain_offset; + char *vwv, *data; + uint16 wct; + uint16 data_size; + + if (req->in.wct < 2 || req->out.wct < 2) { + req_reply_dos_error(req, ERRSRV, ERRerror); + return; + } + + chain_cmd = CVAL(req->in.vwv, VWV(0)); + chain_offset = SVAL(req->in.vwv, VWV(1)); + + if (chain_cmd == SMB_CHAIN_NONE) { + /* end of chain */ + SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE); + SSVAL(req->out.vwv, VWV(1), 0); + req_send_reply(req); + return; + } + + if (chain_offset + req->in.hdr >= req->in.buffer + req->in.size) { + goto error; + } + + wct = CVAL(req->in.hdr, chain_offset); + vwv = req->in.hdr + chain_offset + 1; + + if (vwv + VWV(wct) + 2 > req->in.buffer + req->in.size) { + goto error; + } + + data_size = SVAL(vwv, VWV(wct)); + data = vwv + VWV(wct) + 2; + + if (data + data_size > req->in.buffer + req->in.size) { + goto error; + } + + /* all seems legit */ + req->in.vwv = vwv; + req->in.wct = wct; + req->in.data = data; + req->in.data_size = data_size; + req->in.ptr = data; + + req->chain_count++; + + SSVAL(req->out.vwv, VWV(0), chain_cmd); + SSVAL(req->out.vwv, VWV(1), req->out.size - NBT_HDR_SIZE); + + /* the current request in the chain might have used an async reply, + but that doesn't mean the next element needs to */ + ZERO_STRUCT(req->async); + req->control_flags &= ~REQ_CONTROL_ASYNC; + + switch_message(chain_cmd, req); + return; + +error: + SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE); + SSVAL(req->out.vwv, VWV(1), 0); + req_reply_dos_error(req, ERRSRV, ERRerror); +} + + +/* + close the socket and shutdown a server_context +*/ +void server_terminate(struct server_context *smb) +{ + close(smb->socket.fd); + event_remove_fd_all(smb->events, smb->socket.fd); + + conn_close_all(smb); + + talloc_destroy(smb->mem_ctx); +} + + +/* + called when a SMB socket becomes readable +*/ +void smbd_read_handler(struct event_context *ev, struct fd_event *fde, + time_t t, uint16 flags) +{ + struct request_context *req; + struct server_context *smb = fde->private; + + req = receive_smb_request(smb); + if (!req) { + smb->model_ops->terminate_connection(smb, "receive error"); + return; + } + + construct_reply(req); + + /* free up temporary memory */ + lp_talloc_free(); +} + + +/* + process a message from an SMB socket while still processing a + previous message this is used by backends who need to ensure that + new messages from clients are still processed while they are + performing long operations +*/ +void smbd_process_async(struct server_context *smb) +{ + struct request_context *req; + + req = receive_smb_request(smb); + if (!req) { + smb->model_ops->terminate_connection(smb, "receive error"); + return; + } + + construct_reply(req); +} + + +/* + initialise a server_context from a open socket and register a event handler + for reading from that socket +*/ +void init_smbsession(struct event_context *ev, struct model_ops *model_ops, int fd, + void (*read_handler)(struct event_context *, struct fd_event *, time_t, uint16)) +{ + struct server_context *smb; + TALLOC_CTX *mem_ctx; + struct fd_event fde; + char *socket_addr; + + set_socket_options(fd,"SO_KEEPALIVE"); + set_socket_options(fd, lp_socket_options()); + + mem_ctx = talloc_init("server_context"); + + smb = (struct server_context *)talloc(mem_ctx, sizeof(*smb)); + if (!smb) return; + + ZERO_STRUCTP(smb); + + smb->mem_ctx = mem_ctx; + smb->socket.fd = fd; + smb->pid = getpid(); + + sub_set_context(&smb->substitute); + + /* set an initial client name based on its IP address. This will be replaced with + the netbios name later if it gives us one */ + socket_addr = get_socket_addr(smb->mem_ctx, fd); + sub_set_remote_machine(socket_addr); + smb->socket.client_addr = socket_addr; + + /* now initialise a few default values associated with this smb socket */ + smb->negotiate.max_send = 0xFFFF; + + /* this is the size that w2k uses, and it appears to be important for + good performance */ + smb->negotiate.max_recv = 4356; + + smb->users.next_vuid = VUID_OFFSET; + + smb->events = ev; + smb->model_ops = model_ops; + + conn_init(smb); + + /* setup a event handler for this socket. We are initially + only interested in reading from the socket */ + fde.fd = fd; + fde.handler = read_handler; + fde.private = smb; + fde.flags = EVENT_FD_READ; + + event_add_fd(ev, &fde); + + /* setup the DCERPC server subsystem */ + dcesrv_init_context(&smb->dcesrv); +} diff --git a/source4/smb_server/trans2.c b/source4/smb_server/trans2.c new file mode 100644 index 0000000000..6a15aad137 --- /dev/null +++ b/source4/smb_server/trans2.c @@ -0,0 +1,1411 @@ +/* + Unix SMB/CIFS implementation. + transaction2 handling + Copyright (C) Andrew Tridgell 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +/* + This file handles the parsing of transact2 requests +*/ + +#include "includes.h" + + +#define CHECK_MIN_BLOB_SIZE(blob, size) do { \ + if ((blob)->length < (size)) { \ + return NT_STATUS_INFO_LENGTH_MISMATCH; \ + }} while (0) + +/* grow the data allocation size of a trans2 reply - this guarantees + that requests to grow the data size later will not change the + pointer */ +static void trans2_grow_data_allocation(struct request_context *req, + struct smb_trans2 *trans, + uint16 new_size) +{ + if (new_size <= trans->out.data.length) { + return; + } + trans->out.data.data = talloc_realloc(req->mem_ctx, trans->out.data.data, new_size); +} + + +/* grow the data size of a trans2 reply */ +static void trans2_grow_data(struct request_context *req, + struct smb_trans2 *trans, + uint16 new_size) +{ + trans2_grow_data_allocation(req, trans, new_size); + trans->out.data.length = new_size; +} + +/* grow the data, zero filling any new bytes */ +static void trans2_grow_data_fill(struct request_context *req, + struct smb_trans2 *trans, + uint16 new_size) +{ + uint16 old_size = trans->out.data.length; + trans2_grow_data(req, trans, new_size); + if (new_size > old_size) { + memset(trans->out.data.data + old_size, 0, new_size - old_size); + } +} + + +/* setup a trans2 reply, given the data and params sizes */ +static void trans2_setup_reply(struct request_context *req, + struct smb_trans2 *trans, + uint16 param_size, uint16 data_size, + uint16 setup_count) +{ + trans->out.setup_count = setup_count; + if (setup_count != 0) { + trans->out.setup = talloc_zero(req->mem_ctx, sizeof(uint16) * setup_count); + } + trans->out.params = data_blob_talloc(req->mem_ctx, NULL, param_size); + trans->out.data = data_blob_talloc(req->mem_ctx, NULL, data_size); +} + + +/* + pull a string from a blob in a trans2 request +*/ +static size_t trans2_pull_blob_string(struct request_context *req, + const DATA_BLOB *blob, + uint16 offset, + const char **str, + int flags) +{ + /* we use STR_NO_RANGE_CHECK because the params are allocated + separately in a DATA_BLOB, so we need to do our own range + checking */ + if (offset >= blob->length) { + *str = NULL; + return 0; + } + + return req_pull_string(req, str, + blob->data + offset, + blob->length - offset, + STR_NO_RANGE_CHECK | flags); +} + +/* + push a string into the data section of a trans2 request + return the number of bytes consumed in the output +*/ +static size_t trans2_push_data_string(struct request_context *req, + struct smb_trans2 *trans, + uint16 len_offset, + uint16 offset, + const WIRE_STRING *str, + int dest_len, + int flags) +{ + int alignment = 0, ret = 0, pkt_len; + + /* we use STR_NO_RANGE_CHECK because the params are allocated + separately in a DATA_BLOB, so we need to do our own range + checking */ + if (!str->s || offset >= trans->out.data.length) { + if (flags & STR_LEN8BIT) { + SCVAL(trans->out.data.data, len_offset, 0); + } else { + SIVAL(trans->out.data.data, len_offset, 0); + } + return 0; + } + + flags |= STR_NO_RANGE_CHECK; + + if (dest_len == -1 || (dest_len > trans->out.data.length - offset)) { + dest_len = trans->out.data.length - offset; + } + + if (!(flags & (STR_ASCII|STR_UNICODE))) { + flags |= (req->flags2 & FLAGS2_UNICODE_STRINGS) ? STR_UNICODE : STR_ASCII; + } + + if ((offset&1) && (flags & STR_UNICODE) && !(flags & STR_NOALIGN)) { + alignment = 1; + if (dest_len > 0) { + SCVAL(trans->out.data.data + offset, 0, 0); + ret = push_string(NULL, trans->out.data.data + offset + 1, str->s, dest_len-1, flags); + } + } else { + ret = push_string(NULL, trans->out.data.data + offset, str->s, dest_len, flags); + } + + /* sometimes the string needs to be terminated, but the length + on the wire must not include the termination! */ + pkt_len = ret; + + if ((flags & STR_LEN_NOTERM) && (flags & STR_TERMINATE)) { + if ((flags & STR_UNICODE) && ret >= 2) { + pkt_len = ret-2; + } + if ((flags & STR_ASCII) && ret >= 1) { + pkt_len = ret-1; + } + } + + if (flags & STR_LEN8BIT) { + SCVAL(trans->out.data.data, len_offset, pkt_len); + } else { + SIVAL(trans->out.data.data, len_offset, pkt_len); + } + + return ret + alignment; +} + +/* + append a string to the data section of a trans2 reply + len_offset points to the place in the packet where the length field + should go +*/ +static void trans2_append_data_string(struct request_context *req, + struct smb_trans2 *trans, + const WIRE_STRING *str, + uint_t len_offset, + int flags) +{ + size_t ret; + uint16 offset; + const int max_bytes_per_char = 3; + + offset = trans->out.data.length; + trans2_grow_data(req, trans, offset + (2+strlen(str->s))*max_bytes_per_char); + ret = trans2_push_data_string(req, trans, len_offset, offset, str, -1, flags); + trans2_grow_data(req, trans, offset + ret); +} + + +/* + trans2 qfsinfo implementation +*/ +static NTSTATUS trans2_qfsinfo(struct request_context *req, struct smb_trans2 *trans) +{ + union smb_fsinfo fsinfo; + NTSTATUS status; + uint16 level; + uint_t i; + DATA_BLOB guid_blob; + + /* make sure we got enough parameters */ + if (trans->in.params.length != 2) { + return NT_STATUS_FOOBAR; + } + + level = SVAL(trans->in.params.data, 0); + + switch (level) { + case SMB_QFS_ALLOCATION: + fsinfo.allocation.level = RAW_QFS_ALLOCATION; + + status = req->conn->ntvfs_ops->fsinfo(req, &fsinfo); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + trans2_setup_reply(req, trans, 0, 18, 0); + + SIVAL(trans->out.data.data, 0, fsinfo.allocation.out.fs_id); + SIVAL(trans->out.data.data, 4, fsinfo.allocation.out.sectors_per_unit); + SIVAL(trans->out.data.data, 8, fsinfo.allocation.out.total_alloc_units); + SIVAL(trans->out.data.data, 12, fsinfo.allocation.out.avail_alloc_units); + SSVAL(trans->out.data.data, 16, fsinfo.allocation.out.bytes_per_sector); + + return NT_STATUS_OK; + + case SMB_QFS_VOLUME: + fsinfo.volume.level = RAW_QFS_VOLUME; + + status = req->conn->ntvfs_ops->fsinfo(req, &fsinfo); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + trans2_setup_reply(req, trans, 0, 5, 0); + + SIVAL(trans->out.data.data, 0, fsinfo.volume.out.serial_number); + /* w2k3 implements this incorrectly for unicode - it + * leaves the last byte off the string */ + trans2_append_data_string(req, trans, + &fsinfo.volume.out.volume_name, + 4, STR_LEN8BIT|STR_NOALIGN); + + return NT_STATUS_OK; + + case SMB_QFS_VOLUME_INFO: + case SMB_QFS_VOLUME_INFORMATION: + fsinfo.volume_info.level = RAW_QFS_VOLUME_INFO; + + status = req->conn->ntvfs_ops->fsinfo(req, &fsinfo); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + trans2_setup_reply(req, trans, 0, 18, 0); + + push_nttime(trans->out.data.data, 0, &fsinfo.volume_info.out.create_time); + SIVAL(trans->out.data.data, 8, fsinfo.volume_info.out.serial_number); + trans2_append_data_string(req, trans, + &fsinfo.volume_info.out.volume_name, + 12, STR_UNICODE); + + return NT_STATUS_OK; + + case SMB_QFS_SIZE_INFO: + case SMB_QFS_SIZE_INFORMATION: + fsinfo.size_info.level = RAW_QFS_SIZE_INFO; + + status = req->conn->ntvfs_ops->fsinfo(req, &fsinfo); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + trans2_setup_reply(req, trans, 0, 24, 0); + + SBVAL(trans->out.data.data, 0, fsinfo.size_info.out.total_alloc_units); + SBVAL(trans->out.data.data, 8, fsinfo.size_info.out.avail_alloc_units); + SIVAL(trans->out.data.data, 16, fsinfo.size_info.out.sectors_per_unit); + SIVAL(trans->out.data.data, 20, fsinfo.size_info.out.bytes_per_sector); + + return NT_STATUS_OK; + + case SMB_QFS_DEVICE_INFO: + case SMB_QFS_DEVICE_INFORMATION: + fsinfo.device_info.level = RAW_QFS_DEVICE_INFO; + + status = req->conn->ntvfs_ops->fsinfo(req, &fsinfo); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + trans2_setup_reply(req, trans, 0, 8, 0); + SIVAL(trans->out.data.data, 0, fsinfo.device_info.out.device_type); + SIVAL(trans->out.data.data, 4, fsinfo.device_info.out.characteristics); + return NT_STATUS_OK; + + + case SMB_QFS_ATTRIBUTE_INFO: + case SMB_QFS_ATTRIBUTE_INFORMATION: + fsinfo.attribute_info.level = RAW_QFS_ATTRIBUTE_INFO; + + status = req->conn->ntvfs_ops->fsinfo(req, &fsinfo); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + trans2_setup_reply(req, trans, 0, 12, 0); + + SIVAL(trans->out.data.data, 0, fsinfo.attribute_info.out.fs_attr); + SIVAL(trans->out.data.data, 4, fsinfo.attribute_info.out.max_file_component_length); + /* this must not be null terminated or win98 gets + confused! also note that w2k3 returns this as + unicode even when ascii is negotiated */ + trans2_append_data_string(req, trans, + &fsinfo.attribute_info.out.fs_type, + 8, STR_UNICODE); + return NT_STATUS_OK; + + + case SMB_QFS_QUOTA_INFORMATION: + fsinfo.quota_information.level = RAW_QFS_QUOTA_INFORMATION; + + status = req->conn->ntvfs_ops->fsinfo(req, &fsinfo); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + trans2_setup_reply(req, trans, 0, 48, 0); + + SBVAL(trans->out.data.data, 0, fsinfo.quota_information.out.unknown[0]); + SBVAL(trans->out.data.data, 8, fsinfo.quota_information.out.unknown[1]); + SBVAL(trans->out.data.data, 16, fsinfo.quota_information.out.unknown[2]); + SBVAL(trans->out.data.data, 24, fsinfo.quota_information.out.quota_soft); + SBVAL(trans->out.data.data, 32, fsinfo.quota_information.out.quota_hard); + SBVAL(trans->out.data.data, 40, fsinfo.quota_information.out.quota_flags); + + return NT_STATUS_OK; + + + case SMB_QFS_FULL_SIZE_INFORMATION: + fsinfo.full_size_information.level = RAW_QFS_FULL_SIZE_INFORMATION; + + status = req->conn->ntvfs_ops->fsinfo(req, &fsinfo); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + trans2_setup_reply(req, trans, 0, 32, 0); + + SBVAL(trans->out.data.data, 0, fsinfo.full_size_information.out.total_alloc_units); + SBVAL(trans->out.data.data, 8, fsinfo.full_size_information.out.call_avail_alloc_units); + SBVAL(trans->out.data.data, 16, fsinfo.full_size_information.out.actual_avail_alloc_units); + SIVAL(trans->out.data.data, 24, fsinfo.full_size_information.out.sectors_per_unit); + SIVAL(trans->out.data.data, 28, fsinfo.full_size_information.out.bytes_per_sector); + + return NT_STATUS_OK; + + case SMB_QFS_OBJECTID_INFORMATION: + fsinfo.objectid_information.level = RAW_QFS_OBJECTID_INFORMATION; + + status = req->conn->ntvfs_ops->fsinfo(req, &fsinfo); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + trans2_setup_reply(req, trans, 0, 64, 0); + + status = ndr_push_struct_blob(&guid_blob, req->mem_ctx, + &fsinfo.objectid_information.out.guid, + (ndr_push_flags_fn_t)ndr_push_GUID); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + memcpy(trans->out.data.data, guid_blob.data, GUID_SIZE); + + for (i=0;i<6;i++) { + SBVAL(trans->out.data.data, 16 + 8*i, fsinfo.objectid_information.out.unknown[i]); + } + return NT_STATUS_OK; + } + + return NT_STATUS_INVALID_LEVEL; +} + +/* + fill in the reply from a qpathinfo or qfileinfo call +*/ +static NTSTATUS trans2_fileinfo_fill(struct request_context *req, struct smb_trans2 *trans, + union smb_fileinfo *st) +{ + uint_t i; + + switch (st->generic.level) { + case RAW_FILEINFO_GENERIC: + case RAW_FILEINFO_GETATTR: + case RAW_FILEINFO_GETATTRE: + /* handled elsewhere */ + return NT_STATUS_INVALID_LEVEL; + + case RAW_FILEINFO_BASIC_INFO: + case RAW_FILEINFO_BASIC_INFORMATION: + trans2_setup_reply(req, trans, 2, 40, 0); + + SSVAL(trans->out.params.data, 0, 0); + push_nttime(trans->out.data.data, 0, &st->basic_info.out.create_time); + push_nttime(trans->out.data.data, 8, &st->basic_info.out.access_time); + push_nttime(trans->out.data.data, 16, &st->basic_info.out.write_time); + push_nttime(trans->out.data.data, 24, &st->basic_info.out.change_time); + SIVAL(trans->out.data.data, 32, st->basic_info.out.attrib); + SIVAL(trans->out.data.data, 36, 0); /* padding */ + return NT_STATUS_OK; + + case RAW_FILEINFO_STANDARD: + trans2_setup_reply(req, trans, 2, 22, 0); + + SSVAL(trans->out.params.data, 0, 0); + put_dos_date2(trans->out.data.data, 0, st->standard.out.create_time); + put_dos_date2(trans->out.data.data, 4, st->standard.out.access_time); + put_dos_date2(trans->out.data.data, 8, st->standard.out.write_time); + SIVAL(trans->out.data.data, 12, st->standard.out.size); + SIVAL(trans->out.data.data, 16, st->standard.out.alloc_size); + SSVAL(trans->out.data.data, 20, st->standard.out.attrib); + return NT_STATUS_OK; + + case RAW_FILEINFO_EA_SIZE: + trans2_setup_reply(req, trans, 2, 26, 0); + + SSVAL(trans->out.params.data, 0, 0); + put_dos_date2(trans->out.data.data, 0, st->ea_size.out.create_time); + put_dos_date2(trans->out.data.data, 4, st->ea_size.out.access_time); + put_dos_date2(trans->out.data.data, 8, st->ea_size.out.write_time); + SIVAL(trans->out.data.data, 12, st->ea_size.out.size); + SIVAL(trans->out.data.data, 16, st->ea_size.out.alloc_size); + SSVAL(trans->out.data.data, 20, st->ea_size.out.attrib); + SIVAL(trans->out.data.data, 22, st->ea_size.out.ea_size); + return NT_STATUS_OK; + + case RAW_FILEINFO_NETWORK_OPEN_INFORMATION: + trans2_setup_reply(req, trans, 2, 56, 0); + + SSVAL(trans->out.params.data, 0, 0); + push_nttime(trans->out.data.data, 0, &st->network_open_information.out.create_time); + push_nttime(trans->out.data.data, 8, &st->network_open_information.out.access_time); + push_nttime(trans->out.data.data, 16, &st->network_open_information.out.write_time); + push_nttime(trans->out.data.data, 24, &st->network_open_information.out.change_time); + SBVAL(trans->out.data.data, 32, st->network_open_information.out.alloc_size); + SBVAL(trans->out.data.data, 40, st->network_open_information.out.size); + SIVAL(trans->out.data.data, 48, st->network_open_information.out.attrib); + SIVAL(trans->out.data.data, 52, 0); /* padding */ + return NT_STATUS_OK; + + case RAW_FILEINFO_STANDARD_INFO: + case RAW_FILEINFO_STANDARD_INFORMATION: + trans2_setup_reply(req, trans, 2, 24, 0); + SSVAL(trans->out.params.data, 0, 0); + SBVAL(trans->out.data.data, 0, st->standard_info.out.alloc_size); + SBVAL(trans->out.data.data, 8, st->standard_info.out.size); + SIVAL(trans->out.data.data, 16, st->standard_info.out.nlink); + SCVAL(trans->out.data.data, 20, st->standard_info.out.delete_pending); + SCVAL(trans->out.data.data, 21, st->standard_info.out.directory); + SSVAL(trans->out.data.data, 22, 0); /* padding */ + return NT_STATUS_OK; + + case RAW_FILEINFO_ATTRIBUTE_TAG_INFORMATION: + trans2_setup_reply(req, trans, 2, 8, 0); + SSVAL(trans->out.params.data, 0, 0); + SIVAL(trans->out.data.data, 0, st->attribute_tag_information.out.attrib); + SIVAL(trans->out.data.data, 4, st->attribute_tag_information.out.reparse_tag); + return NT_STATUS_OK; + + case RAW_FILEINFO_EA_INFO: + case RAW_FILEINFO_EA_INFORMATION: + trans2_setup_reply(req, trans, 2, 4, 0); + SSVAL(trans->out.params.data, 0, 0); + SIVAL(trans->out.data.data, 0, st->ea_info.out.ea_size); + return NT_STATUS_OK; + + case RAW_FILEINFO_MODE_INFORMATION: + trans2_setup_reply(req, trans, 2, 4, 0); + SSVAL(trans->out.params.data, 0, 0); + SIVAL(trans->out.data.data, 0, st->mode_information.out.mode); + return NT_STATUS_OK; + + case RAW_FILEINFO_ALIGNMENT_INFORMATION: + trans2_setup_reply(req, trans, 2, 4, 0); + SSVAL(trans->out.params.data, 0, 0); + SIVAL(trans->out.data.data, 0, + st->alignment_information.out.alignment_requirement); + return NT_STATUS_OK; + + case RAW_FILEINFO_ALL_EAS: + if (st->all_eas.out.num_eas == 0) { + trans2_setup_reply(req, trans, 2, 4, 0); + SSVAL(trans->out.params.data, 0, 0); + SIVAL(trans->out.data.data, 0, 0); + } else { + uint32 list_size = ea_list_size(st->all_eas.out.num_eas, + st->all_eas.out.eas); + trans2_setup_reply(req, trans, 2, list_size, 0); + SSVAL(trans->out.params.data, 0, 0); + ea_put_list(trans->out.data.data, + st->all_eas.out.num_eas, st->all_eas.out.eas); + } + return NT_STATUS_OK; + + case RAW_FILEINFO_ACCESS_INFORMATION: + trans2_setup_reply(req, trans, 2, 4, 0); + SSVAL(trans->out.params.data, 0, 0); + SIVAL(trans->out.data.data, 0, st->access_information.out.access_flags); + return NT_STATUS_OK; + + case RAW_FILEINFO_POSITION_INFORMATION: + trans2_setup_reply(req, trans, 2, 8, 0); + SSVAL(trans->out.params.data, 0, 0); + SBVAL(trans->out.data.data, 0, st->position_information.out.position); + return NT_STATUS_OK; + + case RAW_FILEINFO_COMPRESSION_INFO: + case RAW_FILEINFO_COMPRESSION_INFORMATION: + trans2_setup_reply(req, trans, 2, 16, 0); + SSVAL(trans->out.params.data, 0, 0); + SBVAL(trans->out.data.data, 0, st->compression_info.out.compressed_size); + SSVAL(trans->out.data.data, 8, st->compression_info.out.format); + SCVAL(trans->out.data.data, 10, st->compression_info.out.unit_shift); + SCVAL(trans->out.data.data, 11, st->compression_info.out.chunk_shift); + SCVAL(trans->out.data.data, 12, st->compression_info.out.cluster_shift); + SSVAL(trans->out.data.data, 13, 0); /* 3 bytes padding */ + SCVAL(trans->out.data.data, 15, 0); + return NT_STATUS_OK; + + case RAW_FILEINFO_IS_NAME_VALID: + trans2_setup_reply(req, trans, 2, 0, 0); + SSVAL(trans->out.params.data, 0, 0); + return NT_STATUS_OK; + + case RAW_FILEINFO_INTERNAL_INFORMATION: + trans2_setup_reply(req, trans, 2, 8, 0); + SSVAL(trans->out.params.data, 0, 0); + SBVAL(trans->out.data.data, 0, st->internal_information.out.file_id); + return NT_STATUS_OK; + + case RAW_FILEINFO_ALL_INFO: + case RAW_FILEINFO_ALL_INFORMATION: + trans2_setup_reply(req, trans, 2, 72, 0); + + SSVAL(trans->out.params.data, 0, 0); + push_nttime(trans->out.data.data, 0, &st->all_info.out.create_time); + push_nttime(trans->out.data.data, 8, &st->all_info.out.access_time); + push_nttime(trans->out.data.data, 16, &st->all_info.out.write_time); + push_nttime(trans->out.data.data, 24, &st->all_info.out.change_time); + SIVAL(trans->out.data.data, 32, st->all_info.out.attrib); + SBVAL(trans->out.data.data, 40, st->all_info.out.alloc_size); + SBVAL(trans->out.data.data, 48, st->all_info.out.size); + SIVAL(trans->out.data.data, 56, st->all_info.out.nlink); + SCVAL(trans->out.data.data, 60, st->all_info.out.delete_pending); + SCVAL(trans->out.data.data, 61, st->all_info.out.directory); + SSVAL(trans->out.data.data, 62, 0); /* padding */ + SIVAL(trans->out.data.data, 64, st->all_info.out.ea_size); + trans2_append_data_string(req, trans, &st->all_info.out.fname, + 68, STR_UNICODE); + return NT_STATUS_OK; + + case RAW_FILEINFO_NAME_INFO: + case RAW_FILEINFO_NAME_INFORMATION: + trans2_setup_reply(req, trans, 2, 4, 0); + SSVAL(trans->out.params.data, 0, 0); + trans2_append_data_string(req, trans, &st->name_info.out.fname, 0, STR_UNICODE); + return NT_STATUS_OK; + + case RAW_FILEINFO_ALT_NAME_INFO: + case RAW_FILEINFO_ALT_NAME_INFORMATION: + trans2_setup_reply(req, trans, 2, 4, 0); + SSVAL(trans->out.params.data, 0, 0); + trans2_append_data_string(req, trans, &st->alt_name_info.out.fname, 0, STR_UNICODE); + return NT_STATUS_OK; + + case RAW_FILEINFO_STREAM_INFO: + case RAW_FILEINFO_STREAM_INFORMATION: + trans2_setup_reply(req, trans, 2, 0, 0); + + SSVAL(trans->out.params.data, 0, 0); + + for (i=0;i<st->stream_info.out.num_streams;i++) { + uint16 data_size = trans->out.data.length; + char *data; + + trans2_grow_data(req, trans, data_size + 24); + data = trans->out.data.data + data_size; + SBVAL(data, 8, st->stream_info.out.streams[i].size); + SBVAL(data, 16, st->stream_info.out.streams[i].alloc_size); + trans2_append_data_string(req, trans, + &st->stream_info.out.streams[i].stream_name, + data_size + 4, STR_UNICODE); + if (i == st->stream_info.out.num_streams - 1) { + SIVAL(trans->out.data.data, data_size, 0); + } else { + trans2_grow_data_fill(req, trans, (trans->out.data.length+7)&~7); + SIVAL(trans->out.data.data, data_size, + trans->out.data.length - data_size); + } + } + return NT_STATUS_OK; + } + + return NT_STATUS_INVALID_LEVEL; +} + +/* + trans2 qpathinfo implementation +*/ +static NTSTATUS trans2_qpathinfo(struct request_context *req, struct smb_trans2 *trans) +{ + union smb_fileinfo st; + NTSTATUS status; + uint16 level; + + /* make sure we got enough parameters */ + if (trans->in.params.length < 8) { + return NT_STATUS_FOOBAR; + } + + level = SVAL(trans->in.params.data, 0); + + trans2_pull_blob_string(req, &trans->in.params, 6, &st.generic.in.fname, 0); + if (st.generic.in.fname == NULL) { + return NT_STATUS_FOOBAR; + } + + /* work out the backend level - we make it 1-1 in the header */ + st.generic.level = (enum fileinfo_level)level; + if (st.generic.level >= RAW_FILEINFO_GENERIC) { + return NT_STATUS_INVALID_LEVEL; + } + + /* call the backend */ + status = req->conn->ntvfs_ops->qpathinfo(req, &st); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + /* fill in the reply parameters */ + status = trans2_fileinfo_fill(req, trans, &st); + + return status; +} + + +/* + trans2 qpathinfo implementation +*/ +static NTSTATUS trans2_qfileinfo(struct request_context *req, struct smb_trans2 *trans) +{ + union smb_fileinfo st; + NTSTATUS status; + uint16 level; + + /* make sure we got enough parameters */ + if (trans->in.params.length < 4) { + return NT_STATUS_FOOBAR; + } + + st.generic.in.fnum = SVAL(trans->in.params.data, 0); + level = SVAL(trans->in.params.data, 2); + + /* work out the backend level - we make it 1-1 in the header */ + st.generic.level = (enum fileinfo_level)level; + if (st.generic.level >= RAW_FILEINFO_GENERIC) { + return NT_STATUS_INVALID_LEVEL; + } + + /* call the backend */ + status = req->conn->ntvfs_ops->qfileinfo(req, &st); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + /* fill in the reply parameters */ + status = trans2_fileinfo_fill(req, trans, &st); + + return status; +} + + +/* + parse a trans2 setfileinfo/setpathinfo data blob +*/ +static NTSTATUS trans2_parse_sfileinfo(struct request_context *req, + union smb_setfileinfo *st, + const DATA_BLOB *blob) +{ + uint32 len; + + switch (st->generic.level) { + case RAW_SFILEINFO_GENERIC: + case RAW_SFILEINFO_SETATTR: + case RAW_SFILEINFO_SETATTRE: + /* handled elsewhere */ + return NT_STATUS_INVALID_LEVEL; + + case RAW_SFILEINFO_STANDARD: + CHECK_MIN_BLOB_SIZE(blob, 12); + st->standard.in.create_time = make_unix_date2(blob->data + 0); + st->standard.in.access_time = make_unix_date2(blob->data + 4); + st->standard.in.write_time = make_unix_date2(blob->data + 8); + return NT_STATUS_OK; + + case RAW_SFILEINFO_EA_SET: + CHECK_MIN_BLOB_SIZE(blob, 4); + len = IVAL(blob->data, 0); + if (len > blob->length || len < 4) { + return NT_STATUS_INFO_LENGTH_MISMATCH; + } + { + DATA_BLOB blob2; + blob2.data = blob->data+4; + blob2.length = len-4; + len = ea_pull_struct(&blob2, req->mem_ctx, &st->ea_set.in.ea); + } + if (len == 0) { + return NT_STATUS_INVALID_PARAMETER; + } + return NT_STATUS_OK; + + case SMB_SFILEINFO_BASIC_INFO: + case SMB_SFILEINFO_BASIC_INFORMATION: + CHECK_MIN_BLOB_SIZE(blob, 36); + st->basic_info.in.create_time = pull_nttime(blob->data, 0); + st->basic_info.in.access_time = pull_nttime(blob->data, 8); + st->basic_info.in.write_time = pull_nttime(blob->data, 16); + st->basic_info.in.change_time = pull_nttime(blob->data, 24); + st->basic_info.in.attrib = IVAL(blob->data, 32); + return NT_STATUS_OK; + + case SMB_SFILEINFO_DISPOSITION_INFO: + case SMB_SFILEINFO_DISPOSITION_INFORMATION: + CHECK_MIN_BLOB_SIZE(blob, 1); + st->disposition_info.in.delete_on_close = CVAL(blob->data, 0); + return NT_STATUS_OK; + + case SMB_SFILEINFO_ALLOCATION_INFO: + case SMB_SFILEINFO_ALLOCATION_INFORMATION: + CHECK_MIN_BLOB_SIZE(blob, 8); + st->allocation_info.in.alloc_size = BVAL(blob->data, 0); + return NT_STATUS_OK; + + case RAW_SFILEINFO_END_OF_FILE_INFO: + case RAW_SFILEINFO_END_OF_FILE_INFORMATION: + CHECK_MIN_BLOB_SIZE(blob, 8); + st->end_of_file_info.in.size = BVAL(blob->data, 0); + return NT_STATUS_OK; + + case RAW_SFILEINFO_RENAME_INFORMATION: { + DATA_BLOB blob2; + + CHECK_MIN_BLOB_SIZE(blob, 12); + st->rename_information.in.overwrite = CVAL(blob->data, 0); + st->rename_information.in.root_fid = IVAL(blob->data, 4); + len = IVAL(blob->data, 8); + blob2.data = blob->data+12; + blob2.length = MIN(blob->length, len); + trans2_pull_blob_string(req, &blob2, 0, + &st->rename_information.in.new_name, STR_UNICODE); + return NT_STATUS_OK; + } + + case RAW_SFILEINFO_POSITION_INFORMATION: + CHECK_MIN_BLOB_SIZE(blob, 8); + st->position_information.in.position = BVAL(blob->data, 0); + return NT_STATUS_OK; + + case RAW_SFILEINFO_MODE_INFORMATION: + CHECK_MIN_BLOB_SIZE(blob, 4); + st->mode_information.in.mode = IVAL(blob->data, 0); + return NT_STATUS_OK; + } + + return NT_STATUS_INVALID_LEVEL; +} + +/* + trans2 setfileinfo implementation +*/ +static NTSTATUS trans2_setfileinfo(struct request_context *req, struct smb_trans2 *trans) +{ + union smb_setfileinfo st; + NTSTATUS status; + uint16 level, fnum; + DATA_BLOB *blob; + + /* make sure we got enough parameters */ + if (trans->in.params.length < 4) { + return NT_STATUS_FOOBAR; + } + + fnum = SVAL(trans->in.params.data, 0); + level = SVAL(trans->in.params.data, 2); + + blob = &trans->in.data; + + st.generic.file.fnum = fnum; + st.generic.level = (enum setfileinfo_level)level; + + status = trans2_parse_sfileinfo(req, &st, blob); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + status = req->conn->ntvfs_ops->setfileinfo(req, &st); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + trans2_setup_reply(req, trans, 2, 0, 0); + SSVAL(trans->out.params.data, 0, 0); + return NT_STATUS_OK; +} + +/* + trans2 setpathinfo implementation +*/ +static NTSTATUS trans2_setpathinfo(struct request_context *req, struct smb_trans2 *trans) +{ + union smb_setfileinfo st; + NTSTATUS status; + uint16 level; + DATA_BLOB *blob; + + /* make sure we got enough parameters */ + if (trans->in.params.length < 4) { + return NT_STATUS_FOOBAR; + } + + level = SVAL(trans->in.params.data, 0); + blob = &trans->in.data; + st.generic.level = (enum setfileinfo_level)level; + + trans2_pull_blob_string(req, &trans->in.params, 4, &st.generic.file.fname, 0); + if (st.generic.file.fname == NULL) { + return NT_STATUS_FOOBAR; + } + + status = trans2_parse_sfileinfo(req, &st, blob); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + status = req->conn->ntvfs_ops->setpathinfo(req, &st); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + trans2_setup_reply(req, trans, 2, 0, 0); + SSVAL(trans->out.params.data, 0, 0); + return NT_STATUS_OK; +} + + +/* a structure to encapsulate the state information about an in-progress ffirst/fnext operation */ +struct find_state { + struct request_context *req; + struct smb_trans2 *trans; + enum search_level level; + uint16 last_entry_offset; + uint16 flags; +}; + +/* + fill a single entry in a trans2 find reply +*/ +static void find_fill_info(struct request_context *req, + struct smb_trans2 *trans, + struct find_state *state, + union smb_search_data *file) +{ + char *data; + uint_t ofs = trans->out.data.length; + + switch (state->level) { + case RAW_SEARCH_SEARCH: + case RAW_SEARCH_GENERIC: + /* handled elsewhere */ + break; + + case RAW_SEARCH_STANDARD: + if (state->flags & FLAG_TRANS2_FIND_REQUIRE_RESUME) { + trans2_grow_data(req, trans, ofs + 27); + SIVAL(trans->out.data.data, ofs, file->standard.resume_key); + ofs += 4; + } else { + trans2_grow_data(req, trans, ofs + 23); + } + data = trans->out.data.data + ofs; + put_dos_date2(data, 0, file->standard.create_time); + put_dos_date2(data, 4, file->standard.access_time); + put_dos_date2(data, 8, file->standard.write_time); + SIVAL(data, 12, file->standard.size); + SIVAL(data, 16, file->standard.alloc_size); + SSVAL(data, 20, file->standard.attrib); + trans2_append_data_string(req, trans, &file->standard.name, + ofs + 22, STR_LEN8BIT | STR_TERMINATE | STR_LEN_NOTERM); + break; + + case RAW_SEARCH_EA_SIZE: + if (state->flags & FLAG_TRANS2_FIND_REQUIRE_RESUME) { + trans2_grow_data(req, trans, ofs + 31); + SIVAL(trans->out.data.data, ofs, file->ea_size.resume_key); + ofs += 4; + } else { + trans2_grow_data(req, trans, ofs + 27); + } + data = trans->out.data.data + ofs; + put_dos_date2(data, 0, file->ea_size.create_time); + put_dos_date2(data, 4, file->ea_size.access_time); + put_dos_date2(data, 8, file->ea_size.write_time); + SIVAL(data, 12, file->ea_size.size); + SIVAL(data, 16, file->ea_size.alloc_size); + SSVAL(data, 20, file->ea_size.attrib); + SIVAL(data, 22, file->ea_size.ea_size); + trans2_append_data_string(req, trans, &file->ea_size.name, + ofs + 26, STR_LEN8BIT | STR_TERMINATE | STR_NOALIGN); + break; + + case RAW_SEARCH_DIRECTORY_INFO: + trans2_grow_data(req, trans, ofs + 64); + data = trans->out.data.data + ofs; + SIVAL(data, 4, file->directory_info.file_index); + push_nttime(data, 8, &file->directory_info.create_time); + push_nttime(data, 16, &file->directory_info.access_time); + push_nttime(data, 24, &file->directory_info.write_time); + push_nttime(data, 32, &file->directory_info.change_time); + SBVAL(data, 40, file->directory_info.size); + SBVAL(data, 48, file->directory_info.alloc_size); + SIVAL(data, 56, file->directory_info.attrib); + trans2_append_data_string(req, trans, &file->directory_info.name, + ofs + 60, STR_TERMINATE_ASCII); + data = trans->out.data.data + ofs; + SIVAL(data, 0, trans->out.data.length - ofs); + break; + + case RAW_SEARCH_FULL_DIRECTORY_INFO: + trans2_grow_data(req, trans, ofs + 68); + data = trans->out.data.data + ofs; + SIVAL(data, 4, file->full_directory_info.file_index); + push_nttime(data, 8, &file->full_directory_info.create_time); + push_nttime(data, 16, &file->full_directory_info.access_time); + push_nttime(data, 24, &file->full_directory_info.write_time); + push_nttime(data, 32, &file->full_directory_info.change_time); + SBVAL(data, 40, file->full_directory_info.size); + SBVAL(data, 48, file->full_directory_info.alloc_size); + SIVAL(data, 56, file->full_directory_info.attrib); + SIVAL(data, 64, file->full_directory_info.ea_size); + trans2_append_data_string(req, trans, &file->full_directory_info.name, + ofs + 60, STR_TERMINATE_ASCII); + data = trans->out.data.data + ofs; + SIVAL(data, 0, trans->out.data.length - ofs); + break; + + case RAW_SEARCH_NAME_INFO: + trans2_grow_data(req, trans, ofs + 12); + data = trans->out.data.data + ofs; + SIVAL(data, 4, file->name_info.file_index); + trans2_append_data_string(req, trans, &file->name_info.name, + ofs + 8, STR_TERMINATE_ASCII); + data = trans->out.data.data + ofs; + SIVAL(data, 0, trans->out.data.length - ofs); + break; + + case RAW_SEARCH_BOTH_DIRECTORY_INFO: + trans2_grow_data(req, trans, ofs + 94); + data = trans->out.data.data + ofs; + SIVAL(data, 4, file->both_directory_info.file_index); + push_nttime(data, 8, &file->both_directory_info.create_time); + push_nttime(data, 16, &file->both_directory_info.access_time); + push_nttime(data, 24, &file->both_directory_info.write_time); + push_nttime(data, 32, &file->both_directory_info.change_time); + SBVAL(data, 40, file->both_directory_info.size); + SBVAL(data, 48, file->both_directory_info.alloc_size); + SIVAL(data, 56, file->both_directory_info.attrib); + SIVAL(data, 64, file->both_directory_info.ea_size); + SCVAL(data, 69, 0); /* reserved */ + memset(data+70,0,24); + trans2_push_data_string(req, trans, + 68 + ofs, 70 + ofs, + &file->both_directory_info.short_name, + 24, STR_UNICODE | STR_LEN8BIT); + trans2_append_data_string(req, trans, &file->both_directory_info.name, + ofs + 60, STR_TERMINATE_ASCII); + data = trans->out.data.data + ofs; + SIVAL(data, 0, trans->out.data.length - ofs); + break; + + case RAW_SEARCH_ID_FULL_DIRECTORY_INFO: + trans2_grow_data(req, trans, ofs + 80); + data = trans->out.data.data + ofs; + SIVAL(data, 4, file->id_full_directory_info.file_index); + push_nttime(data, 8, &file->id_full_directory_info.create_time); + push_nttime(data, 16, &file->id_full_directory_info.access_time); + push_nttime(data, 24, &file->id_full_directory_info.write_time); + push_nttime(data, 32, &file->id_full_directory_info.change_time); + SBVAL(data, 40, file->id_full_directory_info.size); + SBVAL(data, 48, file->id_full_directory_info.alloc_size); + SIVAL(data, 56, file->id_full_directory_info.attrib); + SIVAL(data, 64, file->id_full_directory_info.ea_size); + SIVAL(data, 68, 0); /* padding */ + SBVAL(data, 72, file->id_full_directory_info.file_id); + trans2_append_data_string(req, trans, &file->id_full_directory_info.name, + ofs + 60, STR_TERMINATE_ASCII); + data = trans->out.data.data + ofs; + SIVAL(data, 0, trans->out.data.length - ofs); + break; + + case RAW_SEARCH_ID_BOTH_DIRECTORY_INFO: + trans2_grow_data(req, trans, ofs + 104); + data = trans->out.data.data + ofs; + SIVAL(data, 4, file->id_both_directory_info.file_index); + push_nttime(data, 8, &file->id_both_directory_info.create_time); + push_nttime(data, 16, &file->id_both_directory_info.access_time); + push_nttime(data, 24, &file->id_both_directory_info.write_time); + push_nttime(data, 32, &file->id_both_directory_info.change_time); + SBVAL(data, 40, file->id_both_directory_info.size); + SBVAL(data, 48, file->id_both_directory_info.alloc_size); + SIVAL(data, 56, file->id_both_directory_info.attrib); + SIVAL(data, 64, file->id_both_directory_info.ea_size); + SCVAL(data, 69, 0); /* reserved */ + memset(data+70,0,24); + trans2_push_data_string(req, trans, + 68 + ofs, 70 + ofs, + &file->id_both_directory_info.short_name, + 24, STR_UNICODE | STR_LEN8BIT); + SBVAL(data, 94, file->id_both_directory_info.file_id); + SSVAL(data, 102, 0); /* reserved? */ + trans2_append_data_string(req, trans, &file->id_both_directory_info.name, + ofs + 60, STR_TERMINATE_ASCII); + data = trans->out.data.data + ofs; + SIVAL(data, 0, trans->out.data.length - ofs); + break; + } +} + +/* callback function for trans2 findfirst/findnext */ +static BOOL find_callback(void *private, union smb_search_data *file) +{ + struct find_state *state = (struct find_state *)private; + struct smb_trans2 *trans = state->trans; + uint_t old_length; + + old_length = trans->out.data.length; + + find_fill_info(state->req, trans, state, file); + + /* see if we have gone beyond the user specified maximum */ + if (trans->out.data.length > trans->in.max_data) { + /* restore the old length and tell the backend to stop */ + trans2_grow_data(state->req, trans, old_length); + return False; + } + + state->last_entry_offset = old_length; + return True; +} + + +/* + trans2 findfirst implementation +*/ +static NTSTATUS trans2_findfirst(struct request_context *req, struct smb_trans2 *trans) +{ + union smb_search_first search; + NTSTATUS status; + uint16 level; + char *param; + struct find_state state; + + /* make sure we got all the parameters */ + if (trans->in.params.length < 14) { + return NT_STATUS_FOOBAR; + } + + search.t2ffirst.in.search_attrib = SVAL(trans->in.params.data, 0); + search.t2ffirst.in.max_count = SVAL(trans->in.params.data, 2); + search.t2ffirst.in.flags = SVAL(trans->in.params.data, 4); + level = SVAL(trans->in.params.data, 6); + search.t2ffirst.in.storage_type = IVAL(trans->in.params.data, 8); + + trans2_pull_blob_string(req, &trans->in.params, 12, &search.t2ffirst.in.pattern, 0); + if (search.t2ffirst.in.pattern == NULL) { + return NT_STATUS_FOOBAR; + } + + search.t2ffirst.level = (enum search_level)level; + if (search.t2ffirst.level >= RAW_SEARCH_GENERIC) { + return NT_STATUS_INVALID_LEVEL; + } + + /* setup the private state structure that the backend will give us in the callback */ + state.req = req; + state.trans = trans; + state.level = search.t2ffirst.level; + state.last_entry_offset = 0; + state.flags = search.t2ffirst.in.flags; + + /* setup for just a header in the reply */ + trans2_setup_reply(req, trans, 10, 0, 0); + + /* call the backend */ + status = req->conn->ntvfs_ops->search_first(req, &search, &state, find_callback); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + /* fill in the findfirst reply header */ + param = trans->out.params.data; + SSVAL(param, VWV(0), search.t2ffirst.out.handle); + SSVAL(param, VWV(1), search.t2ffirst.out.count); + SSVAL(param, VWV(2), search.t2ffirst.out.end_of_search); + SSVAL(param, VWV(3), 0); + SSVAL(param, VWV(4), state.last_entry_offset); + + return NT_STATUS_OK; +} + + +/* + trans2 findnext implementation +*/ +static NTSTATUS trans2_findnext(struct request_context *req, struct smb_trans2 *trans) +{ + union smb_search_next search; + NTSTATUS status; + uint16 level; + char *param; + struct find_state state; + + /* make sure we got all the parameters */ + if (trans->in.params.length < 12) { + return NT_STATUS_FOOBAR; + } + + search.t2fnext.in.handle = SVAL(trans->in.params.data, 0); + search.t2fnext.in.max_count = SVAL(trans->in.params.data, 2); + level = SVAL(trans->in.params.data, 4); + search.t2fnext.in.resume_key = IVAL(trans->in.params.data, 6); + search.t2fnext.in.flags = SVAL(trans->in.params.data, 10); + + trans2_pull_blob_string(req, &trans->in.params, 12, &search.t2fnext.in.last_name, 0); + if (search.t2fnext.in.last_name == NULL) { + return NT_STATUS_FOOBAR; + } + + search.t2fnext.level = (enum search_level)level; + if (search.t2fnext.level >= RAW_SEARCH_GENERIC) { + return NT_STATUS_INVALID_LEVEL; + } + + /* setup the private state structure that the backend will give us in the callback */ + state.req = req; + state.trans = trans; + state.level = search.t2fnext.level; + state.last_entry_offset = 0; + state.flags = search.t2fnext.in.flags; + + /* setup for just a header in the reply */ + trans2_setup_reply(req, trans, 8, 0, 0); + + /* call the backend */ + status = req->conn->ntvfs_ops->search_next(req, &search, &state, find_callback); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + /* fill in the findfirst reply header */ + param = trans->out.params.data; + SSVAL(param, VWV(0), search.t2fnext.out.count); + SSVAL(param, VWV(1), search.t2fnext.out.end_of_search); + SSVAL(param, VWV(2), 0); + SSVAL(param, VWV(3), state.last_entry_offset); + + return NT_STATUS_OK; +} + + +/* + backend for trans2 requests +*/ +static NTSTATUS trans2_backend(struct request_context *req, struct smb_trans2 *trans) +{ + if (req->conn->ntvfs_ops->trans2 != NULL) { + /* direct trans2 pass thru */ + return req->conn->ntvfs_ops->trans2(req, trans); + } + + /* must have at least one setup word */ + if (trans->in.setup_count < 1) { + return NT_STATUS_FOOBAR; + } + + /* the trans2 command is in setup[0] */ + switch (trans->in.setup[0]) { + case TRANSACT2_FINDFIRST: + return trans2_findfirst(req, trans); + case TRANSACT2_FINDNEXT: + return trans2_findnext(req, trans); + case TRANSACT2_QPATHINFO: + return trans2_qpathinfo(req, trans); + case TRANSACT2_QFILEINFO: + return trans2_qfileinfo(req, trans); + case TRANSACT2_SETFILEINFO: + return trans2_setfileinfo(req, trans); + case TRANSACT2_SETPATHINFO: + return trans2_setpathinfo(req, trans); + case TRANSACT2_QFSINFO: + return trans2_qfsinfo(req, trans); + } + + /* an unknown trans2 command */ + return NT_STATUS_FOOBAR; +} + + +/* + backend for trans requests +*/ +static NTSTATUS trans_backend(struct request_context *req, struct smb_trans2 *trans) +{ + if (!req->conn->ntvfs_ops->trans) { + return NT_STATUS_NOT_IMPLEMENTED; + } + return req->conn->ntvfs_ops->trans(req, trans); +} + + +/**************************************************************************** + Reply to an SMBtrans or SMBtrans2 request +****************************************************************************/ +void reply_trans_generic(struct request_context *req, uint8 command) +{ + struct smb_trans2 trans; + int i; + uint16 param_ofs, data_ofs; + uint16 param_count, data_count; + uint16 params_left, data_left; + uint16 param_total, data_total; + char *params, *data; + NTSTATUS status; + + /* parse request */ + if (req->in.wct < 14) { + req_reply_error(req, NT_STATUS_FOOBAR); + return; + } + + param_total = SVAL(req->in.vwv, VWV(0)); + data_total = SVAL(req->in.vwv, VWV(1)); + trans.in.max_param = SVAL(req->in.vwv, VWV(2)); + trans.in.max_data = SVAL(req->in.vwv, VWV(3)); + trans.in.max_setup = CVAL(req->in.vwv, VWV(4)); + trans.in.flags = SVAL(req->in.vwv, VWV(5)); + trans.in.timeout = IVAL(req->in.vwv, VWV(6)); + param_count = SVAL(req->in.vwv, VWV(9)); + param_ofs = SVAL(req->in.vwv, VWV(10)); + data_count = SVAL(req->in.vwv, VWV(11)); + data_ofs = SVAL(req->in.vwv, VWV(12)); + trans.in.setup_count = CVAL(req->in.vwv, VWV(13)); + + if (req->in.wct != 14 + trans.in.setup_count) { + req_reply_dos_error(req, ERRSRV, ERRerror); + return; + } + + /* parse out the setup words */ + trans.in.setup = talloc(req->mem_ctx, trans.in.setup_count * sizeof(uint16)); + if (!trans.in.setup) { + req_reply_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, &trans.in.trans_name, req->in.data, -1, STR_TERMINATE); + } + + if (!req_pull_blob(req, req->in.hdr + param_ofs, param_count, &trans.in.params) || + !req_pull_blob(req, req->in.hdr + data_ofs, data_count, &trans.in.data)) { + req_reply_error(req, NT_STATUS_FOOBAR); + return; + } + + /* is it a partial request? if so, then send a 'send more' message */ + if (param_total > param_count || + data_total > data_count) { + DEBUG(0,("REWRITE: not handling partial trans requests!\n")); + return; + } + + /* its a full request, give it to the backend */ + if (command == SMBtrans) { + status = trans_backend(req, &trans); + } else { + status = trans2_backend(req, &trans); + } + + if (!NT_STATUS_IS_OK(status)) { + req_reply_error(req, status); + return; + } + + params_left = trans.out.params.length; + data_left = trans.out.data.length; + params = trans.out.params.data; + data = trans.out.data.data; + + req->control_flags |= REQ_CONTROL_PROTECTED; + + /* we need to divide up the reply into chunks that fit into + the negotiated buffer size */ + do { + uint16 this_data, this_param, max_bytes; + uint_t align1 = 1, align2 = (params_left ? 2 : 0); + + req_setup_reply(req, 10 + trans.out.setup_count, 0); + + 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; + } + + req_grow_data(req, this_param + this_data + (align1 + align2)); + + SSVAL(req->out.vwv, VWV(0), trans.out.params.length); + SSVAL(req->out.vwv, VWV(1), trans.out.data.length); + SSVAL(req->out.vwv, VWV(2), 0); + + SSVAL(req->out.vwv, VWV(3), this_param); + SSVAL(req->out.vwv, VWV(4), align1 + PTR_DIFF(req->out.data, req->out.hdr)); + SSVAL(req->out.vwv, VWV(5), PTR_DIFF(params, trans.out.params.data)); + + SSVAL(req->out.vwv, VWV(6), this_data); + SSVAL(req->out.vwv, VWV(7), align1 + align2 + + PTR_DIFF(req->out.data + this_param, req->out.hdr)); + SSVAL(req->out.vwv, VWV(8), PTR_DIFF(data, trans.out.data.data)); + + SSVAL(req->out.vwv, VWV(9), trans.out.setup_count); + for (i=0;i<trans.out.setup_count;i++) { + SSVAL(req->out.vwv, VWV(10+i), trans.out.setup[i]); + } + + memset(req->out.data, 0, align1); + if (this_param != 0) { + memcpy(req->out.data + align1, params, this_param); + } + memset(req->out.data+this_param+align1, 0, align2); + if (this_data != 0) { + memcpy(req->out.data+this_param+align1+align2, data, this_data); + } + + params_left -= this_param; + data_left -= this_data; + params += this_param; + data += this_data; + + /* if this is the last chunk then the request can be destroyed */ + if (params_left == 0 && data_left == 0) { + req->control_flags &= ~REQ_CONTROL_PROTECTED; + } + + req_send_reply(req); + } while (params_left != 0 || data_left != 0); +} + + +/**************************************************************************** + Reply to an SMBtrans2 +****************************************************************************/ +void reply_trans2(struct request_context *req) +{ + reply_trans_generic(req, SMBtrans2); +} + +/**************************************************************************** + Reply to an SMBtrans +****************************************************************************/ +void reply_trans(struct request_context *req) +{ + reply_trans_generic(req, SMBtrans); +} + +/**************************************************************************** + Reply to an SMBtranss2 request +****************************************************************************/ +void reply_transs2(struct request_context *req) +{ + req_reply_error(req, NT_STATUS_FOOBAR); +} + + |