Unix SMB/CIFS implementation.
process incoming packets - main loop
Copyright (C) Andrew Tridgell 1992-1998
- Copyright (C) Volker Lendecke 2005
+ Copyright (C) Volker Lendecke 2005-2007
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
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.
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "includes.h"
-uint16 global_smbpid;
extern struct auth_context *negprot_global_auth_context;
extern int smb_echo_count;
-static char *InBuffer = NULL;
-static char *OutBuffer = NULL;
-static char *current_inbuf = NULL;
+const int total_buffer_size = (BUFFER_SIZE + LARGE_WRITEX_HDR_SIZE + SAFETY_MARGIN);
/*
* Size of data we can send to client. Set
void init_smb_request(struct smb_request *req, const uint8 *inbuf)
{
+ size_t req_size = smb_len(inbuf) + 4;
+ /* Ensure we have at least smb_size bytes. */
+ if (req_size < smb_size) {
+ DEBUG(0,("init_smb_request: invalid request size %u\n",
+ (unsigned int)req_size ));
+ exit_server_cleanly("Invalid SMB request");
+ }
req->flags2 = SVAL(inbuf, smb_flg2);
req->smbpid = SVAL(inbuf, smb_pid);
req->mid = SVAL(inbuf, smb_mid);
req->vuid = SVAL(inbuf, smb_uid);
+ req->tid = SVAL(inbuf, smb_tid);
+ req->wct = CVAL(inbuf, smb_wct);
+ /* Ensure we have at least wct words and 2 bytes of bcc. */
+ if (smb_size + req->wct*2 > req_size) {
+ DEBUG(0,("init_smb_request: invalid wct number %u (size %u)\n",
+ (unsigned int)req->wct,
+ (unsigned int)req_size));
+ exit_server_cleanly("Invalid SMB request");
+ }
+ /* Ensure bcc is correct. */
+ if (((uint8 *)smb_buf(inbuf)) + smb_buflen(inbuf) > inbuf + req_size) {
+ DEBUG(0,("init_smb_request: invalid bcc number %u "
+ "(wct = %u, size %u)\n",
+ (unsigned int)smb_buflen(inbuf),
+ (unsigned int)req->wct,
+ (unsigned int)req_size));
+ exit_server_cleanly("Invalid SMB request");
+ }
+ req->inbuf = inbuf;
+ req->outbuf = NULL;
}
/****************************************************************************
static struct pending_message_list *deferred_open_queue;
/****************************************************************************
- Function to push a message onto the tail of a linked list of smb messages
- ready for processing.
+ Function to push a message onto the tail of a linked list of smb messages ready
+ for processing.
****************************************************************************/
-static BOOL push_queued_message(char *buf, int msg_len,
+static BOOL push_queued_message(struct smb_request *req,
struct timeval request_time,
struct timeval end_time,
char *private_data, size_t private_len)
{
+ int msg_len = smb_len(req->inbuf) + 4;
struct pending_message_list *msg;
msg = TALLOC_ZERO_P(NULL, struct pending_message_list);
return False;
}
- msg->buf = data_blob_talloc(msg, buf, msg_len);
+ msg->buf = data_blob_talloc(msg, req->inbuf, msg_len);
if(msg->buf.data == NULL) {
DEBUG(0,("push_message: malloc fail (2)\n"));
TALLOC_FREE(msg);
for (pml = deferred_open_queue; pml; pml = pml->next) {
if (mid == SVAL(pml->buf.data,smb_mid)) {
- DEBUG(10,("remove_deferred_open_smb_message: "
+ DEBUG(10,("remove_sharing_violation_open_smb_message: "
"deleting mid %u len %u\n",
(unsigned int)mid,
(unsigned int)pml->buf.length ));
for (pml = deferred_open_queue; pml; pml = pml->next) {
uint16 msg_mid = SVAL(pml->buf.data,smb_mid);
- DEBUG(10, ("schedule_deferred_open_smb_message: [%d] "
- "msg_mid = %u\n", i++, (unsigned int)msg_mid ));
+ DEBUG(10,("schedule_deferred_open_smb_message: [%d] msg_mid = %u\n", i++,
+ (unsigned int)msg_mid ));
if (mid == msg_mid) {
- DEBUG(10, ("schedule_deferred_open_smb_message: "
- "scheduling mid %u\n", mid));
+ DEBUG(10,("schedule_deferred_open_smb_message: scheduling mid %u\n",
+ mid ));
pml->end_time.tv_sec = 0;
pml->end_time.tv_usec = 0;
DLIST_PROMOTE(deferred_open_queue, pml);
}
}
- DEBUG(10, ("schedule_deferred_open_smb_message: failed to find "
- "message mid %u\n", mid ));
+ DEBUG(10,("schedule_deferred_open_smb_message: failed to find message mid %u\n",
+ mid ));
}
/****************************************************************************
messages ready for processing.
****************************************************************************/
-BOOL push_deferred_smb_message(uint16 mid,
+BOOL push_deferred_smb_message(struct smb_request *req,
struct timeval request_time,
struct timeval timeout,
char *private_data, size_t priv_len)
DEBUG(10,("push_deferred_open_smb_message: pushing message len %u mid %u "
"timeout time [%u.%06u]\n",
- (unsigned int) smb_len(current_inbuf)+4, (unsigned int)mid,
+ (unsigned int) smb_len(req->inbuf)+4, (unsigned int)req->mid,
(unsigned int)end_time.tv_sec,
(unsigned int)end_time.tv_usec));
- return push_queued_message(current_inbuf, smb_len(current_inbuf)+4,
- request_time, end_time,
+ return push_queued_message(req, request_time, end_time,
private_data, priv_len);
}
The timeout is in milliseconds
****************************************************************************/
-static BOOL receive_message_or_smb(char *buffer, int buffer_len, int timeout)
+static BOOL receive_message_or_smb(TALLOC_CTX *mem_ctx, char **buffer,
+ size_t *buffer_len, int timeout)
{
fd_set r_fds, w_fds;
int selrtn;
struct timeval to;
int maxfd = 0;
+ ssize_t len;
smb_read_error = 0;
}
if (pop_message) {
- memcpy(buffer, msg->buf.data, MIN(buffer_len, msg->buf.length));
-
+
+ *buffer = (char *)talloc_memdup(mem_ctx, msg->buf.data,
+ msg->buf.length);
+ if (*buffer == NULL) {
+ DEBUG(0, ("talloc failed\n"));
+ smb_read_error = READ_ERROR;
+ return False;
+ }
+ *buffer_len = msg->buf.length;
+
/* We leave this message on the queue so the open code can
know this is a retry. */
DEBUG(5,("receive_message_or_smb: returning deferred open smb message.\n"));
goto again;
}
- return receive_smb(smbd_server_fd(), buffer, 0);
+ len = receive_smb_talloc(mem_ctx, smbd_server_fd(), buffer, 0);
+
+ if (len == -1) {
+ return False;
+ }
+
+ *buffer_len = (size_t)len;
+
+ return True;
}
/*
*/
static const struct smb_message_struct {
const char *name;
- int (*fn)(connection_struct *conn, char *, char *, int, int);
+ void (*fn_new)(connection_struct *conn, struct smb_request *req);
int flags;
} smb_messages[256] = {
/* 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 },
+/* 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},
/* 0x0c */ { "SMBlock",reply_lock,AS_USER},
/* 0x0d */ { "SMBunlock",reply_unlock,AS_USER},
/* 0x0e */ { "SMBctemp",reply_ctemp,AS_USER },
-/* 0x0f */ { "SMBmknew",reply_mknew,AS_USER},
+/* 0x0f */ { "SMBmknew",reply_mknew,AS_USER},
/* 0x10 */ { "SMBcheckpath",reply_checkpath,AS_USER},
/* 0x11 */ { "SMBexit",reply_exit,DO_CHDIR},
/* 0x12 */ { "SMBlseek",reply_lseek,AS_USER},
/* 0x19 */ { NULL, NULL, 0 },
/* 0x1a */ { "SMBreadbraw",reply_readbraw,AS_USER},
/* 0x1b */ { "SMBreadBmpx",reply_readbmpx,AS_USER},
-/* 0x1c */ { "SMBreadBs",NULL,0 },
+/* 0x1c */ { "SMBreadBs",reply_readbs,AS_USER },
/* 0x1d */ { "SMBwritebraw",reply_writebraw,AS_USER},
/* 0x1e */ { "SMBwriteBmpx",reply_writebmpx,AS_USER},
/* 0x1f */ { "SMBwriteBs",reply_writebs,AS_USER},
-/* 0x20 */ { "SMBwritec",NULL,0},
+/* 0x20 */ { "SMBwritec", NULL,0},
/* 0x21 */ { NULL, NULL, 0 },
/* 0x22 */ { "SMBsetattrE",reply_setattrE,AS_USER | NEED_WRITE },
/* 0x23 */ { "SMBgetattrE",reply_getattrE,AS_USER },
/* 0x25 */ { "SMBtrans",reply_trans,AS_USER | CAN_IPC },
/* 0x26 */ { "SMBtranss",reply_transs,AS_USER | CAN_IPC},
/* 0x27 */ { "SMBioctl",reply_ioctl,0},
-/* 0x28 */ { "SMBioctls",NULL,AS_USER},
+/* 0x28 */ { "SMBioctls", NULL,AS_USER},
/* 0x29 */ { "SMBcopy",reply_copy,AS_USER | NEED_WRITE },
-/* 0x2a */ { "SMBmove",NULL,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 },
/* 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},
+/* 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 },
/* 0x71 */ { "SMBtdis",reply_tdis,DO_CHDIR},
/* 0x72 */ { "SMBnegprot",reply_negprot,0},
/* 0x73 */ { "SMBsesssetupX",reply_sesssetup_and_X,0},
-/* 0x74 */ { "SMBulogoffX", reply_ulogoffX, 0}, /* ulogoff doesn't give a valid TID */
+/* 0x74 */ { "SMBulogoffX",reply_ulogoffX, 0}, /* ulogoff doesn't give a valid TID */
/* 0x75 */ { "SMBtconX",reply_tcon_and_X,0},
/* 0x76 */ { NULL, NULL, 0 },
/* 0x77 */ { NULL, NULL, 0 },
/* 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 },
+/* 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, AS_USER | NEED_WRITE },
+/* 0xa4 */ { "SMBntcancel",reply_ntcancel, 0 },
+/* 0xa5 */ { "SMBntrename",reply_ntrename, AS_USER | NEED_WRITE },
/* 0xa6 */ { NULL, NULL, 0 },
/* 0xa7 */ { NULL, NULL, 0 },
/* 0xa8 */ { 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},
+/* 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},
};
+/*******************************************************************
+ allocate and initialize a reply packet
+********************************************************************/
+
+void reply_outbuf(struct smb_request *req, uint8 num_words, uint32 num_bytes)
+{
+ /*
+ * Protect against integer wrap
+ */
+ if ((num_bytes > 0xffffff)
+ || ((num_bytes + smb_size + num_words*2) > 0xffffff)) {
+ char *msg;
+ asprintf(&msg, "num_bytes too large: %u",
+ (unsigned)num_bytes);
+ smb_panic(msg);
+ }
+
+ if (!(req->outbuf = TALLOC_ARRAY(
+ req, uint8,
+ smb_size + num_words*2 + num_bytes))) {
+ smb_panic("could not allocate output buffer\n");
+ }
+
+ construct_reply_common((char *)req->inbuf, (char *)req->outbuf);
+ set_message((char *)req->outbuf, num_words, num_bytes, False);
+ /*
+ * Zero out the word area, the caller has to take care of the bcc area
+ * himself
+ */
+ if (num_words != 0) {
+ memset(req->outbuf + smb_vwv0, 0, num_words*2);
+ }
+
+ return;
+}
+
+
/*******************************************************************
Dump a packet to a file.
********************************************************************/
-static void smb_dump(const char *name, int type, char *data, ssize_t len)
+static void smb_dump(const char *name, int type, const char *data, ssize_t len)
{
int fd, i;
pstring fname;
}
}
-
/****************************************************************************
- Do a switch on the message type, and return the response size
+ Prepare everything for calling the actual request function, and potentially
+ call the request function via the "new" interface.
+
+ Return False if the "legacy" function needs to be called, everything is
+ prepared.
+
+ Return True if we're done.
+
+ I know this API sucks, but it is the one with the least code change I could
+ find.
****************************************************************************/
-static int switch_message(int type,char *inbuf,char *outbuf,int size,int bufsize)
+static void switch_message(uint8 type, struct smb_request *req, int size)
{
- static pid_t pid= (pid_t)-1;
- int outsize = 0;
-
- type &= 0xff;
+ int flags;
+ uint16 session_tag;
+ connection_struct *conn;
- if (pid == (pid_t)-1)
- pid = sys_getpid();
+ static uint16 last_session_tag = UID_FIELD_INVALID;
errno = 0;
last_message = type;
- /* Make sure this is an SMB packet. smb_size contains NetBIOS header so subtract 4 from it. */
- if ((strncmp(smb_base(inbuf),"\377SMB",4) != 0) || (size < (smb_size - 4))) {
- DEBUG(2,("Non-SMB packet of length %d. Terminating server\n",smb_len(inbuf)));
+ /* Make sure this is an SMB packet. smb_size contains NetBIOS header
+ * so subtract 4 from it. */
+ if ((strncmp(smb_base(req->inbuf),"\377SMB",4) != 0)
+ || (size < (smb_size - 4))) {
+ DEBUG(2,("Non-SMB packet of length %d. Terminating server\n",
+ smb_len(req->inbuf)));
exit_server_cleanly("Non-SMB packet");
- return(-1);
}
- /* yuck! this is an interim measure before we get rid of our
- current inbuf/outbuf system */
- global_smbpid = SVAL(inbuf,smb_pid);
-
- if (smb_messages[type].fn == NULL) {
+ if (smb_messages[type].fn_new == NULL) {
DEBUG(0,("Unknown message type %d!\n",type));
- smb_dump("Unknown", 1, inbuf, size);
- outsize = reply_unknown(inbuf,outbuf);
- } else {
- int flags = smb_messages[type].flags;
- static uint16 last_session_tag = UID_FIELD_INVALID;
- /* In share mode security we must ignore the vuid. */
- uint16 session_tag = (lp_security() == SEC_SHARE) ? UID_FIELD_INVALID : SVAL(inbuf,smb_uid);
- connection_struct *conn = conn_find(SVAL(inbuf,smb_tid));
+ smb_dump("Unknown", 1, (char *)req->inbuf, size);
+ reply_unknown_new(req, type);
+ return;
+ }
- DEBUG(3,("switch message %s (pid %d) conn 0x%lx\n",smb_fn_name(type),(int)pid,(unsigned long)conn));
+ flags = smb_messages[type].flags;
- smb_dump(smb_fn_name(type), 1, inbuf, size);
+ /* In share mode security we must ignore the vuid. */
+ session_tag = (lp_security() == SEC_SHARE)
+ ? UID_FIELD_INVALID : req->vuid;
+ conn = conn_find(req->tid);
- /* Ensure this value is replaced in the incoming packet. */
- SSVAL(inbuf,smb_uid,session_tag);
+ DEBUG(3,("switch message %s (pid %d) conn 0x%lx\n", smb_fn_name(type),
+ (int)sys_getpid(), (unsigned long)conn));
- /*
- * Ensure the correct username is in current_user_info.
- * This is a really ugly bugfix for problems with
- * multiple session_setup_and_X's being done and
- * allowing %U and %G substitutions to work correctly.
- * There is a reason this code is done here, don't
- * move it unless you know what you're doing... :-).
- * JRA.
- */
+ smb_dump(smb_fn_name(type), 1, (char *)req->inbuf, size);
- if (session_tag != last_session_tag) {
- user_struct *vuser = NULL;
+ /* Ensure this value is replaced in the incoming packet. */
+ SSVAL(req->inbuf,smb_uid,session_tag);
- last_session_tag = session_tag;
- if(session_tag != UID_FIELD_INVALID) {
- vuser = get_valid_user_struct(session_tag);
- if (vuser) {
- set_current_user_info(&vuser->user);
- }
- }
- }
+ /*
+ * Ensure the correct username is in current_user_info. This is a
+ * really ugly bugfix for problems with multiple session_setup_and_X's
+ * being done and allowing %U and %G substitutions to work correctly.
+ * There is a reason this code is done here, don't move it unless you
+ * know what you're doing... :-).
+ * JRA.
+ */
- /* Does this call need to be run as the connected user? */
- if (flags & AS_USER) {
-
- /* Does this call need a valid tree connection? */
- if (!conn) {
- /* Amazingly, the error code depends on the command (from Samba4). */
- if (type == SMBntcreateX) {
- return ERROR_NT(NT_STATUS_INVALID_HANDLE);
- } else {
- return ERROR_DOS(ERRSRV, ERRinvnid);
- }
- }
+ if (session_tag != last_session_tag) {
+ user_struct *vuser = NULL;
- if (!change_to_user(conn,session_tag)) {
- return(ERROR_NT(NT_STATUS_DOS(ERRSRV,ERRbaduid)));
+ last_session_tag = session_tag;
+ if(session_tag != UID_FIELD_INVALID) {
+ vuser = get_valid_user_struct(session_tag);
+ if (vuser) {
+ set_current_user_info(&vuser->user);
}
+ }
+ }
- /* All NEED_WRITE and CAN_IPC flags must also have AS_USER. */
-
- /* Does it need write permission? */
- if ((flags & NEED_WRITE) && !CAN_WRITE(conn)) {
- return ERROR_NT(NT_STATUS_MEDIA_WRITE_PROTECTED);
+ /* Does this call need to be run as the connected user? */
+ if (flags & AS_USER) {
+
+ /* Does this call need a valid tree connection? */
+ if (!conn) {
+ /*
+ * Amazingly, the error code depends on the command
+ * (from Samba4).
+ */
+ if (type == SMBntcreateX) {
+ reply_nterror(req, NT_STATUS_INVALID_HANDLE);
+ } else {
+ reply_doserror(req, ERRSRV, ERRinvnid);
}
+ return;
+ }
- /* IPC services are limited */
- if (IS_IPC(conn) && !(flags & CAN_IPC)) {
- return(ERROR_DOS(ERRSRV,ERRaccess));
- }
- } else {
- /* This call needs to be run as root */
- change_to_root_user();
+ if (!change_to_user(conn,session_tag)) {
+ reply_nterror(req, NT_STATUS_DOS(ERRSRV, ERRbaduid));
+ return;
}
- /* load service specific parameters */
- if (conn) {
- if (!set_current_service(conn,SVAL(inbuf,smb_flg),(flags & (AS_USER|DO_CHDIR)?True:False))) {
- return(ERROR_DOS(ERRSRV,ERRaccess));
- }
- conn->num_smb_operations++;
+ /* All NEED_WRITE and CAN_IPC flags must also have AS_USER. */
+
+ /* Does it need write permission? */
+ if ((flags & NEED_WRITE) && !CAN_WRITE(conn)) {
+ reply_nterror(req, NT_STATUS_MEDIA_WRITE_PROTECTED);
+ return;
}
- /* does this protocol need to be run as guest? */
- if ((flags & AS_GUEST) && (!change_to_guest() ||
- !check_access(smbd_server_fd(), lp_hostsallow(-1), lp_hostsdeny(-1)))) {
- return(ERROR_DOS(ERRSRV,ERRaccess));
+ /* IPC services are limited */
+ if (IS_IPC(conn) && !(flags & CAN_IPC)) {
+ reply_doserror(req, ERRSRV,ERRaccess);
+ return;
}
+ } else {
+ /* This call needs to be run as root */
+ change_to_root_user();
+ }
- current_inbuf = inbuf; /* In case we need to defer this message in open... */
- outsize = smb_messages[type].fn(conn, inbuf,outbuf,size,bufsize);
+ /* load service specific parameters */
+ if (conn) {
+ if (!set_current_service(conn,SVAL(req->inbuf,smb_flg),
+ (flags & (AS_USER|DO_CHDIR)
+ ?True:False))) {
+ reply_doserror(req, ERRSRV, ERRaccess);
+ return;
+ }
+ conn->num_smb_operations++;
}
- smb_dump(smb_fn_name(type), 0, outbuf, outsize);
+ /* does this protocol need to be run as guest? */
+ if ((flags & AS_GUEST)
+ && (!change_to_guest() ||
+ !check_access(smbd_server_fd(), lp_hostsallow(-1),
+ lp_hostsdeny(-1)))) {
+ reply_doserror(req, ERRSRV, ERRaccess);
+ return;
+ }
- return(outsize);
+ smb_messages[type].fn_new(conn, req);
}
/****************************************************************************
Construct a reply to the incoming packet.
****************************************************************************/
-static int construct_reply(char *inbuf,char *outbuf,int size,int bufsize)
+static void construct_reply(char *inbuf, int size)
{
- int type = CVAL(inbuf,smb_com);
- int outsize = 0;
- int msg_type = CVAL(inbuf,0);
+ uint8 type = CVAL(inbuf,smb_com);
+ struct smb_request *req;
chain_size = 0;
file_chain_reset();
reset_chain_p();
- if (msg_type != 0)
- return(reply_special(inbuf,outbuf));
+ if (!(req = talloc(talloc_tos(), struct smb_request))) {
+ smb_panic("could not allocate smb_request");
+ }
+ init_smb_request(req, (uint8 *)inbuf);
- construct_reply_common(inbuf, outbuf);
+ switch_message(type, req, size);
- outsize = switch_message(type,inbuf,outbuf,size,bufsize);
+ if (req->outbuf == NULL) {
+ return;
+ }
- outsize += chain_size;
+ if (CVAL(req->outbuf,0) == 0) {
+ show_msg((char *)req->outbuf);
+ }
- if(outsize > 4) {
- smb_setlen(inbuf,outbuf,outsize - 4);
+ if (!send_smb(smbd_server_fd(), (char *)req->outbuf)) {
+ exit_server_cleanly("construct_reply: send_smb failed.");
}
- return(outsize);
+
+ TALLOC_FREE(req);
+
+ return;
}
/****************************************************************************
Process an smb from the client
****************************************************************************/
-static void process_smb(char *inbuf, char *outbuf)
+static void process_smb(char *inbuf, size_t nread)
{
static int trans_num;
int msg_type = CVAL(inbuf,0);
- int32 len = smb_len(inbuf);
- int nread = len + 4;
DO_PROFILE_INC(smb_count);
}
}
- DEBUG( 6, ( "got message type 0x%x of len 0x%x\n", msg_type, len ) );
- DEBUG( 3, ( "Transaction %d of length %d\n", trans_num, nread ) );
+ DEBUG( 6, ( "got message type 0x%x of len 0x%x\n", msg_type,
+ smb_len(inbuf) ) );
+ DEBUG( 3, ( "Transaction %d of length %d\n", trans_num, (int)nread ) );
- if (msg_type == 0)
- show_msg(inbuf);
- else if(msg_type == SMBkeepalive)
- return; /* Keepalive packet. */
+ if (msg_type != 0) {
+ /*
+ * NetBIOS session request, keepalive, etc.
+ */
+ reply_special(inbuf);
+ return;
+ }
- nread = construct_reply(inbuf,outbuf,nread,max_send);
+ show_msg(inbuf);
+
+ construct_reply(inbuf,nread);
- if(nread > 0) {
- if (CVAL(outbuf,0) == 0)
- show_msg(outbuf);
-
- if (nread != smb_len(outbuf) + 4) {
- DEBUG(0,("ERROR: Invalid message response size! %d %d\n",
- nread, smb_len(outbuf)));
- } else if (!send_smb(smbd_server_fd(),outbuf)) {
- exit_server_cleanly("process_smb: send_smb failed.");
- }
- }
trans_num++;
}
void construct_reply_common(const char *inbuf, char *outbuf)
{
- set_message(inbuf,outbuf,0,0,False);
+ set_message(outbuf,0,0,False);
SCVAL(outbuf,smb_com,CVAL(inbuf,smb_com));
SIVAL(outbuf,smb_rcls,0);
Construct a chained reply and add it to the already made reply
****************************************************************************/
-int chain_reply(char *inbuf,char *outbuf,int size,int bufsize)
+void chain_reply(struct smb_request *req)
{
static char *orig_inbuf;
- static char *orig_outbuf;
+
+ /*
+ * Dirty little const_discard: We mess with req->inbuf, which is
+ * declared as const. If maybe at some point this routine gets
+ * rewritten, this const_discard could go away.
+ */
+ char *inbuf = CONST_DISCARD(char *, req->inbuf);
+ int size = smb_len(req->inbuf)+4;
+
int smb_com1, smb_com2 = CVAL(inbuf,smb_vwv0);
unsigned smb_off2 = SVAL(inbuf,smb_vwv1);
- char *inbuf2, *outbuf2;
+ char *inbuf2;
int outsize2;
int new_size;
char inbuf_saved[smb_wct];
- char outbuf_saved[smb_wct];
- int outsize = smb_len(outbuf) + 4;
+ char *outbuf = (char *)req->outbuf;
+ size_t outsize = smb_len(outbuf) + 4;
+ size_t outsize_padded;
+ size_t ofs, to_move;
+
+ struct smb_request *req2;
+ size_t caller_outputlen;
+ char *caller_output;
/* Maybe its not chained, or it's an error packet. */
if (smb_com2 == 0xFF || SVAL(outbuf,smb_rcls) != 0) {
SCVAL(outbuf,smb_vwv0,0xFF);
- return outsize;
+ return;
}
if (chain_size == 0) {
/* this is the first part of the chain */
orig_inbuf = inbuf;
- orig_outbuf = outbuf;
+ }
+
+ /*
+ * We need to save the output the caller added to the chain so that we
+ * can splice it into the final output buffer later.
+ */
+
+ caller_outputlen = outsize - smb_wct;
+
+ caller_output = (char *)memdup(outbuf + smb_wct, caller_outputlen);
+
+ if (caller_output == NULL) {
+ /* TODO: NT_STATUS_NO_MEMORY */
+ smb_panic("could not dup outbuf");
}
/*
* 4 byte aligned. JRA.
*/
- outsize = (outsize + 3) & ~3;
+ outsize_padded = (outsize + 3) & ~3;
- /* we need to tell the client where the next part of the reply will be */
- SSVAL(outbuf,smb_vwv1,smb_offset(outbuf+outsize,outbuf));
- SCVAL(outbuf,smb_vwv0,smb_com2);
-
- /* remember how much the caller added to the chain, only counting stuff
- after the parameter words */
- chain_size += outsize - smb_wct;
+ /*
+ * remember how much the caller added to the chain, only counting
+ * stuff after the parameter words
+ */
+ chain_size += outsize_padded - smb_wct;
- /* work out pointers into the original packets. The
- headers on these need to be filled in */
+ /*
+ * work out pointers into the original packets. The
+ * headers on these need to be filled in
+ */
inbuf2 = orig_inbuf + smb_off2 + 4 - smb_wct;
- outbuf2 = orig_outbuf + SVAL(outbuf,smb_vwv1) + 4 - smb_wct;
/* remember the original command type */
smb_com1 = CVAL(orig_inbuf,smb_com);
/* save the data which will be overwritten by the new headers */
memcpy(inbuf_saved,inbuf2,smb_wct);
- memcpy(outbuf_saved,outbuf2,smb_wct);
/* give the new packet the same header as the last part of the SMB */
memmove(inbuf2,inbuf,smb_wct);
"(orig size = %d, offset = %d)\n",
size, (int)(inbuf2 - inbuf) ));
exit_server_cleanly("Bad chained packet");
- return(-1);
+ return;
}
/* And set it in the header. */
- smb_setlen(inbuf, inbuf2, new_size);
-
- /* create the out buffer */
- construct_reply_common(inbuf2, outbuf2);
+ smb_setlen(inbuf2, new_size - 4);
DEBUG(3,("Chained message\n"));
show_msg(inbuf2);
+ if (!(req2 = talloc(talloc_tos(), struct smb_request))) {
+ smb_panic("could not allocate smb_request");
+ }
+ init_smb_request(req2, (uint8 *)inbuf2);
+
/* process the request */
- outsize2 = switch_message(smb_com2,inbuf2,outbuf2,new_size,
- bufsize-chain_size);
+ switch_message(smb_com2, req2, new_size);
- /* copy the new reply and request headers over the old ones, but
- preserve the smb_com field */
- memmove(orig_outbuf,outbuf2,smb_wct);
- SCVAL(orig_outbuf,smb_com,smb_com1);
+ /*
+ * We don't accept deferred operations in chained requests.
+ */
+ SMB_ASSERT(req2->outbuf != NULL);
+ outsize2 = smb_len(req2->outbuf)+4;
- /* restore the saved data, being careful not to overwrite any
- data from the reply header */
- memcpy(inbuf2,inbuf_saved,smb_wct);
+ /*
+ * Move away the new command output so that caller_output fits in,
+ * copy in the caller_output saved above.
+ */
- {
- int ofs = smb_wct - PTR_DIFF(outbuf2,orig_outbuf);
- if (ofs < 0) {
- ofs = 0;
- }
- memmove(outbuf2+ofs,outbuf_saved+ofs,smb_wct-ofs);
+ SMB_ASSERT(outsize_padded >= smb_wct);
+
+ /*
+ * "ofs" is the space we need for caller_output. Equal to
+ * caller_outputlen plus the padding.
+ */
+
+ ofs = outsize_padded - smb_wct;
+
+ /*
+ * "to_move" is the amount of bytes the secondary routine gave us
+ */
+
+ to_move = outsize2 - smb_wct;
+
+ if (to_move + ofs + smb_wct + chain_size > max_send) {
+ smb_panic("replies too large -- would have to cut");
+ }
+
+ /*
+ * In the "new" API "outbuf" is allocated via reply_outbuf, just for
+ * the first request in the chain. So we have to re-allocate it. In
+ * the "old" API the only outbuf ever used is the global OutBuffer
+ * which is always large enough.
+ */
+
+ outbuf = TALLOC_REALLOC_ARRAY(NULL, outbuf, char,
+ to_move + ofs + smb_wct);
+ if (outbuf == NULL) {
+ smb_panic("could not realloc outbuf");
}
- return outsize2;
+ req->outbuf = (uint8 *)outbuf;
+
+ memmove(outbuf + smb_wct + ofs, req2->outbuf + smb_wct, to_move);
+ memcpy(outbuf + smb_wct, caller_output, caller_outputlen);
+
+ /*
+ * copy the new reply header over the old one but preserve the smb_com
+ * field
+ */
+ memmove(outbuf, req2->outbuf, smb_wct);
+ SCVAL(outbuf, smb_com, smb_com1);
+
+ /*
+ * We've just copied in the whole "wct" area from the secondary
+ * function. Fix up the chaining: com2 and the offset need to be
+ * readjusted.
+ */
+
+ SCVAL(outbuf, smb_vwv0, smb_com2);
+ SSVAL(outbuf, smb_vwv1, chain_size + smb_wct - 4);
+
+ if (outsize_padded > outsize) {
+
+ /*
+ * Due to padding we have some uninitialized bytes after the
+ * caller's output
+ */
+
+ memset(outbuf + outsize, 0, outsize_padded - outsize);
+ }
+
+ smb_setlen(outbuf, outsize2 + chain_size - 4);
+
+ /*
+ * restore the saved data, being careful not to overwrite any data
+ * from the reply header
+ */
+ memcpy(inbuf2,inbuf_saved,smb_wct);
+
+ SAFE_FREE(caller_output);
+ TALLOC_FREE(req2);
+
+ return;
}
/****************************************************************************
return True;
}
-/****************************************************************************
- Accessor functions for InBuffer, OutBuffer.
-****************************************************************************/
-
-char *get_InBuffer(void)
-{
- return InBuffer;
-}
-
-char *get_OutBuffer(void)
-{
- return OutBuffer;
-}
-
-const int total_buffer_size = (BUFFER_SIZE + LARGE_WRITEX_HDR_SIZE + SAFETY_MARGIN);
-
-/****************************************************************************
- Allocate a new InBuffer. Returns the new and old ones.
-****************************************************************************/
-
-static char *NewInBuffer(char **old_inbuf)
-{
- char *new_inbuf = (char *)SMB_MALLOC(total_buffer_size);
- if (!new_inbuf) {
- return NULL;
- }
- if (old_inbuf) {
- *old_inbuf = InBuffer;
- }
- InBuffer = new_inbuf;
-#if defined(DEVELOPER)
- clobber_region(SAFE_STRING_FUNCTION_NAME, SAFE_STRING_LINE, InBuffer, total_buffer_size);
-#endif
- return InBuffer;
-}
-
-/****************************************************************************
- Allocate a new OutBuffer. Returns the new and old ones.
-****************************************************************************/
-
-static char *NewOutBuffer(char **old_outbuf)
-{
- char *new_outbuf = (char *)SMB_MALLOC(total_buffer_size);
- if (!new_outbuf) {
- return NULL;
- }
- if (old_outbuf) {
- *old_outbuf = OutBuffer;
- }
- OutBuffer = new_outbuf;
-#if defined(DEVELOPER)
- clobber_region(SAFE_STRING_FUNCTION_NAME, SAFE_STRING_LINE, OutBuffer, total_buffer_size);
-#endif
- return OutBuffer;
-}
-
/****************************************************************************
Process commands from the client
****************************************************************************/
time_t last_timeout_processing_time = time(NULL);
unsigned int num_smbs = 0;
- /* Allocate the primary Inbut/Output buffers. */
-
- if ((NewInBuffer(NULL) == NULL) || (NewOutBuffer(NULL) == NULL))
- return;
-
max_recv = MIN(lp_maxxmit(),BUFFER_SIZE);
while (True) {
int select_timeout = setup_select_timeout();
int num_echos;
+ char *inbuf;
+ size_t inbuf_len;
+ TALLOC_CTX *frame = talloc_stackframe();
errno = 0;
- /* free up temporary memory */
- lp_TALLOC_FREE();
- main_loop_TALLOC_FREE();
-
/* Did someone ask for immediate checks on things like blocking locks ? */
if (select_timeout == 0) {
if(!timeout_processing(&select_timeout,
run_events(smbd_event_context(), 0, NULL, NULL);
-#if defined(DEVELOPER)
- clobber_region(SAFE_STRING_FUNCTION_NAME, SAFE_STRING_LINE, InBuffer, total_buffer_size);
-#endif
-
- while (!receive_message_or_smb(InBuffer,BUFFER_SIZE+LARGE_WRITEX_HDR_SIZE,select_timeout)) {
+ while (!receive_message_or_smb(NULL, &inbuf, &inbuf_len,
+ select_timeout)) {
if(!timeout_processing(&select_timeout,
&last_timeout_processing_time))
return;
num_smbs = 0; /* Reset smb counter. */
}
+
/*
* Ensure we do timeout processing if the SMB we just got was
* only an echo request. This allows us to set the select
*/
num_echos = smb_echo_count;
- clobber_region(SAFE_STRING_FUNCTION_NAME, SAFE_STRING_LINE, OutBuffer, total_buffer_size);
+ process_smb(inbuf, inbuf_len);
- process_smb(InBuffer, OutBuffer);
+ TALLOC_FREE(inbuf);
if (smb_echo_count != num_echos) {
if(!timeout_processing( &select_timeout, &last_timeout_processing_time))
change_to_root_user();
check_log_size();
}
+ TALLOC_FREE(frame);
}
}