*/
#include "includes.h"
+#include "smbd/globals.h"
-extern int smb_echo_count;
-
-/*
- * Size of data we can send to client. Set
- * by the client for all protocols above CORE.
- * Set by us for CORE protocol.
- */
-int max_send = BUFFER_SIZE;
-/*
- * Size of the data we can receive. Set by us.
- * Can be modified by the max xmit parameter.
- */
-int max_recv = BUFFER_SIZE;
-
-SIG_ATOMIC_T reload_after_sighup = 0;
-SIG_ATOMIC_T got_sig_term = 0;
extern bool global_machine_password_needs_changing;
-extern int max_send;
+
+static void construct_reply_common(struct smb_request *req, const char *inbuf,
+ char *outbuf);
/* Accessor function for smb_read_error for smbd functions. */
Send an smb to a fd.
****************************************************************************/
-bool srv_send_smb(int fd, char *buffer, bool do_encrypt)
+bool srv_send_smb(int fd, char *buffer,
+ bool do_signing, uint32_t seqnum,
+ bool do_encrypt,
+ struct smb_perfcount_data *pcd)
{
- size_t len;
+ size_t len = 0;
size_t nwritten=0;
ssize_t ret;
char *buf_out = buffer;
- /* Sign the outgoing packet if required. */
- srv_calculate_sign_mac(buf_out);
+ if (do_signing) {
+ /* Sign the outgoing packet if required. */
+ srv_calculate_sign_mac(smbd_server_conn, buf_out, seqnum);
+ }
if (do_encrypt) {
NTSTATUS status = srv_encrypt_buffer(buffer, &buf_out);
DEBUG(0, ("send_smb: SMB encryption failed "
"on outgoing packet! Error %s\n",
nt_errstr(status) ));
- return false;
+ goto out;
}
}
len = smb_len(buf_out) + 4;
- while (nwritten < len) {
- ret = write_data(fd,buf_out+nwritten,len - nwritten);
- if (ret <= 0) {
- DEBUG(0,("Error writing %d bytes to client. %d. (%s)\n",
- (int)len,(int)ret, strerror(errno) ));
- srv_free_enc_buffer(buf_out);
- return false;
- }
- nwritten += ret;
+ ret = write_data(fd,buf_out+nwritten,len - nwritten);
+ if (ret <= 0) {
+ DEBUG(0,("Error writing %d bytes to client. %d. (%s)\n",
+ (int)len,(int)ret, strerror(errno) ));
+ srv_free_enc_buffer(buf_out);
+ goto out;
}
+ SMB_PERFCOUNT_SET_MSGLEN_OUT(pcd, len);
srv_free_enc_buffer(buf_out);
+out:
+ SMB_PERFCOUNT_END(pcd);
return true;
}
if (is_encrypted_packet(inbuf)) {
return true;
}
- return (strncmp(smb_base(inbuf),"\377SMB",4) == 0);
+ /*
+ * This used to be (strncmp(smb_base(inbuf),"\377SMB",4) == 0)
+ * but it just looks weird to call strncmp for this one.
+ */
+ return (IVAL(smb_base(inbuf), 0) == 0x424D53FF);
}
/* Socket functions for smbd packet processing. */
return NT_STATUS_OK;
}
- return read_socket_with_timeout(fd, buffer, len, len, timeout, NULL);
+ return read_fd_with_timeout(fd, buffer, len, len, timeout, NULL);
}
/****************************************************************************
memcpy(writeX_header, lenbuf, 4);
- status = read_socket_with_timeout(
+ status = read_fd_with_timeout(
fd, writeX_header + 4,
STANDARD_WRITE_AND_X_HEADER_SIZE,
STANDARD_WRITE_AND_X_HEADER_SIZE,
return status;
}
- if (CVAL(lenbuf,0) == 0 &&
- min_recv_size &&
- smb_len_large(lenbuf) > min_recv_size && /* Could be a UNIX large writeX. */
- !srv_is_signing_active()) {
+ if (CVAL(lenbuf,0) == 0 && min_recv_size &&
+ (smb_len_large(lenbuf) > /* Could be a UNIX large writeX. */
+ (min_recv_size + STANDARD_WRITE_AND_X_HEADER_SIZE)) &&
+ !srv_is_signing_active(smbd_server_conn)) {
return receive_smb_raw_talloc_partial_read(
mem_ctx, lenbuf, fd, buffer, timeout, p_unread, plen);
static NTSTATUS receive_smb_talloc(TALLOC_CTX *mem_ctx, int fd,
char **buffer, unsigned int timeout,
size_t *p_unread, bool *p_encrypted,
- size_t *p_len)
+ size_t *p_len,
+ uint32_t *seqnum)
{
size_t len = 0;
NTSTATUS status;
}
/* Check the incoming SMB signature. */
- if (!srv_check_sign_mac(*buffer, true)) {
+ if (!srv_check_sign_mac(smbd_server_conn, *buffer, seqnum)) {
DEBUG(0, ("receive_smb: SMB Signature verification failed on "
"incoming packet!\n"));
return NT_STATUS_INVALID_NETWORK_RESPONSE;
size_t unread_bytes,
bool encrypted)
{
+ struct smbd_server_connection *sconn = smbd_server_conn;
size_t req_size = smb_len(inbuf) + 4;
/* Ensure we have at least smb_size bytes. */
if (req_size < smb_size) {
(unsigned int)req_size ));
exit_server_cleanly("Invalid SMB request");
}
+ req->cmd = CVAL(inbuf, smb_com);
req->flags2 = SVAL(inbuf, smb_flg2);
req->smbpid = SVAL(inbuf, smb_pid);
req->mid = SVAL(inbuf, smb_mid);
+ req->seqnum = 0;
req->vuid = SVAL(inbuf, smb_uid);
req->tid = SVAL(inbuf, smb_tid);
req->wct = CVAL(inbuf, smb_wct);
+ req->vwv = (uint16_t *)(inbuf+smb_vwv);
+ req->buflen = smb_buflen(inbuf);
+ req->buf = (const uint8_t *)smb_buf(inbuf);
req->unread_bytes = unread_bytes;
req->encrypted = encrypted;
- req->conn = conn_find(req->tid);
+ req->conn = conn_find(sconn,req->tid);
+ req->sconn = smbd_server_conn;
req->chain_fsp = NULL;
+ req->chain_outbuf = NULL;
+ req->done = false;
+ smb_init_perfcount_data(&req->pcd);
/* Ensure we have at least wct words and 2 bytes of bcc. */
if (smb_size + req->wct*2 > req_size) {
exit_server_cleanly("Invalid SMB request");
}
/* Ensure bcc is correct. */
- if (((uint8 *)smb_buf(inbuf)) + smb_buflen(inbuf) > inbuf + req_size) {
+ if (((uint8 *)smb_buf(inbuf)) + req->buflen > 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->buflen,
(unsigned int)req->wct,
(unsigned int)req_size));
exit_server_cleanly("Invalid SMB request");
}
- req->inbuf = inbuf;
+
req->outbuf = NULL;
}
-/****************************************************************************
- structure to hold a linked list of queued messages.
- for processing.
-****************************************************************************/
+static void process_smb(struct smbd_server_connection *conn,
+ uint8_t *inbuf, size_t nread, size_t unread_bytes,
+ uint32_t seqnum, bool encrypted,
+ struct smb_perfcount_data *deferred_pcd);
+
+static void smbd_deferred_open_timer(struct event_context *ev,
+ struct timed_event *te,
+ struct timeval _tval,
+ void *private_data)
+{
+ struct pending_message_list *msg = talloc_get_type(private_data,
+ struct pending_message_list);
+ TALLOC_CTX *mem_ctx = talloc_tos();
+ uint16_t mid = SVAL(msg->buf.data,smb_mid);
+ uint8_t *inbuf;
+
+ inbuf = (uint8_t *)talloc_memdup(mem_ctx, msg->buf.data,
+ msg->buf.length);
+ if (inbuf == NULL) {
+ exit_server("smbd_deferred_open_timer: talloc failed\n");
+ return;
+ }
+
+ /* We leave this message on the queue so the open code can
+ know this is a retry. */
+ DEBUG(5,("smbd_deferred_open_timer: trigger mid %u.\n",
+ (unsigned int)mid ));
+
+ /* Mark the message as processed so this is not
+ * re-processed in error. */
+ msg->processed = true;
+
+ process_smb(smbd_server_conn, inbuf,
+ msg->buf.length, 0,
+ msg->seqnum, msg->encrypted, &msg->pcd);
-static struct pending_message_list *deferred_open_queue;
+ /* If it's still there and was processed, remove it. */
+ msg = get_open_deferred_message(mid);
+ if (msg && msg->processed) {
+ remove_deferred_open_smb_message(mid);
+ }
+}
/****************************************************************************
Function to push a message onto the tail of a linked list of smb messages ready
}
msg->request_time = request_time;
- msg->end_time = end_time;
+ msg->seqnum = req->seqnum;
msg->encrypted = req->encrypted;
+ msg->processed = false;
+ SMB_PERFCOUNT_DEFER_OP(&req->pcd, &msg->pcd);
if (private_data) {
msg->private_data = data_blob_talloc(msg, private_data,
}
}
+ msg->te = event_add_timed(smbd_event_context(),
+ msg,
+ end_time,
+ smbd_deferred_open_timer,
+ msg);
+ if (!msg->te) {
+ DEBUG(0,("push_message: event_add_timed failed\n"));
+ TALLOC_FREE(msg);
+ return false;
+ }
+
DLIST_ADD_END(deferred_open_queue, msg, struct pending_message_list *);
DEBUG(10,("push_message: pushed message length %u on "
for (pml = deferred_open_queue; pml; pml = pml->next) {
if (mid == SVAL(pml->buf.data,smb_mid)) {
- DEBUG(10,("remove_sharing_violation_open_smb_message: "
+ DEBUG(10,("remove_deferred_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 ));
+
if (mid == msg_mid) {
+ struct timed_event *te;
+
+ if (pml->processed) {
+ /* A processed message should not be
+ * rescheduled. */
+ DEBUG(0,("schedule_deferred_open_smb_message: LOGIC ERROR "
+ "message mid %u was already processed\n",
+ msg_mid ));
+ continue;
+ }
+
DEBUG(10,("schedule_deferred_open_smb_message: scheduling mid %u\n",
mid ));
- pml->end_time.tv_sec = 0;
- pml->end_time.tv_usec = 0;
+
+ te = event_add_timed(smbd_event_context(),
+ pml,
+ timeval_zero(),
+ smbd_deferred_open_timer,
+ pml);
+ if (!te) {
+ DEBUG(10,("schedule_deferred_open_smb_message: "
+ "event_add_timed() failed, skipping mid %u\n",
+ mid ));
+ }
+
+ TALLOC_FREE(pml->te);
+ pml->te = te;
DLIST_PROMOTE(deferred_open_queue, pml);
return;
}
}
/****************************************************************************
- Return true if this mid is on the deferred queue.
+ Return true if this mid is on the deferred queue and was not yet processed.
****************************************************************************/
bool open_was_deferred(uint16 mid)
struct pending_message_list *pml;
for (pml = deferred_open_queue; pml; pml = pml->next) {
- if (SVAL(pml->buf.data,smb_mid) == mid) {
+ if (SVAL(pml->buf.data,smb_mid) == mid && !pml->processed) {
return True;
}
}
void *private_data;
};
-static void idle_event_handler(struct event_context *ctx,
- struct timed_event *te,
- const struct timeval *now,
- void *private_data)
+static void smbd_idle_event_handler(struct event_context *ctx,
+ struct timed_event *te,
+ struct timeval now,
+ void *private_data)
{
struct idle_event *event =
talloc_get_type_abort(private_data, struct idle_event);
TALLOC_FREE(event->te);
- if (!event->handler(now, event->private_data)) {
+ DEBUG(10,("smbd_idle_event_handler: %s %p called\n",
+ event->name, event->te));
+
+ if (!event->handler(&now, event->private_data)) {
+ DEBUG(10,("smbd_idle_event_handler: %s %p stopped\n",
+ event->name, event->te));
/* Don't repeat, delete ourselves */
TALLOC_FREE(event);
return;
}
+ DEBUG(10,("smbd_idle_event_handler: %s %p rescheduled\n",
+ event->name, event->te));
+
event->te = event_add_timed(ctx, event,
- timeval_sum(now, &event->interval),
- event->name,
- idle_event_handler, event);
+ timeval_sum(&now, &event->interval),
+ smbd_idle_event_handler, event);
/* We can't do much but fail here. */
SMB_ASSERT(event->te != NULL);
result->te = event_add_timed(event_ctx, result,
timeval_sum(&now, &interval),
- result->name,
- idle_event_handler, result);
+ smbd_idle_event_handler, result);
if (result->te == NULL) {
DEBUG(0, ("event_add_timed failed\n"));
TALLOC_FREE(result);
return NULL;
}
+ DEBUG(10,("event_add_idle: %s %p\n", result->name, result->te));
return result;
}
-/****************************************************************************
- Do all async processing in here. This includes kernel oplock messages, change
- notify events etc.
-****************************************************************************/
-
-static void async_processing(fd_set *pfds)
+static void smbd_sig_term_handler(struct tevent_context *ev,
+ struct tevent_signal *se,
+ int signum,
+ int count,
+ void *siginfo,
+ void *private_data)
{
- DEBUG(10,("async_processing: Doing async processing.\n"));
-
- process_aio_queue();
-
- process_kernel_oplocks(smbd_messaging_context(), pfds);
-
- /* Do the aio check again after receive_local_message as it does a
- select and may have eaten our signal. */
- /* Is this till true? -- vl */
- process_aio_queue();
+ exit_server_cleanly("termination signal");
+}
- if (got_sig_term) {
- exit_server_cleanly("termination signal");
- }
+void smbd_setup_sig_term_handler(void)
+{
+ struct tevent_signal *se;
- /* check for sighup processing */
- if (reload_after_sighup) {
- change_to_root_user();
- DEBUG(1,("Reloading services after SIGHUP\n"));
- reload_services(False);
- reload_after_sighup = 0;
+ se = tevent_add_signal(smbd_event_context(),
+ smbd_event_context(),
+ SIGTERM, 0,
+ smbd_sig_term_handler,
+ NULL);
+ if (!se) {
+ exit_server("failed to setup SIGTERM handler");
}
}
-/****************************************************************************
- Add a fd to the set we will be select(2)ing on.
-****************************************************************************/
-
-static int select_on_fd(int fd, int maxfd, fd_set *fds)
+static void smbd_sig_hup_handler(struct tevent_context *ev,
+ struct tevent_signal *se,
+ int signum,
+ int count,
+ void *siginfo,
+ void *private_data)
{
- if (fd != -1) {
- FD_SET(fd, fds);
- maxfd = MAX(maxfd, fd);
- }
-
- return maxfd;
+ change_to_root_user();
+ DEBUG(1,("Reloading services after SIGHUP\n"));
+ reload_services(False);
}
-/****************************************************************************
- Do a select on an two fd's - with timeout.
-
- If a local udp message has been pushed onto the
- queue (this can only happen during oplock break
- processing) call async_processing()
-
- If a pending smb message has been pushed onto the
- queue (this can only happen during oplock break
- processing) return this next.
-
- If the first smbfd is ready then read an smb from it.
- if the second (loopback UDP) fd is ready then read a message
- from it and setup the buffer header to identify the length
- and from address.
- Returns False on timeout or error.
- Else returns True.
+void smbd_setup_sig_hup_handler(void)
+{
+ struct tevent_signal *se;
-The timeout is in milliseconds
-****************************************************************************/
+ se = tevent_add_signal(smbd_event_context(),
+ smbd_event_context(),
+ SIGHUP, 0,
+ smbd_sig_hup_handler,
+ NULL);
+ if (!se) {
+ exit_server("failed to setup SIGHUP handler");
+ }
+}
-static NTSTATUS receive_message_or_smb(TALLOC_CTX *mem_ctx, char **buffer,
- size_t *buffer_len,
- size_t *p_unread, bool *p_encrypted)
+static NTSTATUS smbd_server_connection_loop_once(struct smbd_server_connection *conn)
{
fd_set r_fds, w_fds;
int selrtn;
struct timeval to;
int maxfd = 0;
- size_t len = 0;
- NTSTATUS status;
-
- *p_unread = 0;
-
- again:
to.tv_sec = SMBD_SELECT_TIMEOUT;
to.tv_usec = 0;
- /*
- * Note that this call must be before processing any SMB
- * messages as we need to synchronously process any messages
- * we may have sent to ourselves from the previous SMB.
- */
- message_dispatch(smbd_messaging_context());
-
- /*
- * Check to see if we already have a message on the deferred open queue
- * and it's time to schedule.
- */
- if(deferred_open_queue != NULL) {
- bool pop_message = False;
- struct pending_message_list *msg = deferred_open_queue;
-
- if (timeval_is_zero(&msg->end_time)) {
- pop_message = True;
- } else {
- struct timeval tv;
- SMB_BIG_INT tdif;
-
- GetTimeOfDay(&tv);
- tdif = usec_time_diff(&msg->end_time, &tv);
- if (tdif <= 0) {
- /* Timed out. Schedule...*/
- pop_message = True;
- DEBUG(10,("receive_message_or_smb: queued message timed out.\n"));
- } else {
- /* Make a more accurate select timeout. */
- to.tv_sec = tdif / 1000000;
- to.tv_usec = tdif % 1000000;
- DEBUG(10,("receive_message_or_smb: select with timeout of [%u.%06u]\n",
- (unsigned int)to.tv_sec, (unsigned int)to.tv_usec ));
- }
- }
-
- if (pop_message) {
-
- *buffer = (char *)talloc_memdup(mem_ctx, msg->buf.data,
- msg->buf.length);
- if (*buffer == NULL) {
- DEBUG(0, ("talloc failed\n"));
- return NT_STATUS_NO_MEMORY;
- }
- *buffer_len = msg->buf.length;
- *p_encrypted = msg->encrypted;
-
- /* 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"));
- return NT_STATUS_OK;
- }
- }
-
/*
* Setup the select fd sets.
*/
FD_ZERO(&r_fds);
FD_ZERO(&w_fds);
- /*
- * Ensure we process oplock break messages by preference.
- * We have to do this before the select, after the select
- * and if the select returns EINTR. This is due to the fact
- * that the selects called from async_processing can eat an EINTR
- * caused by a signal (we can't take the break message there).
- * This is hideously complex - *MUST* be simplified for 3.0 ! JRA.
- */
-
- if (oplock_message_waiting(&r_fds)) {
- DEBUG(10,("receive_message_or_smb: oplock_message is waiting.\n"));
- async_processing(&r_fds);
- /*
- * After async processing we must go and do the select again, as
- * the state of the flag in fds for the server file descriptor is
- * indeterminate - we may have done I/O on it in the oplock processing. JRA.
- */
- goto again;
- }
-
/*
* Are there any timed events waiting ? If so, ensure we don't
* select for longer than it would take to wait for them.
&r_fds, &w_fds, &to, &maxfd);
}
- if (timeval_is_zero(&to)) {
- /* Process a timed event now... */
- if (run_events(smbd_event_context(), 0, NULL, NULL)) {
- goto again;
- }
+ /* Process a signal and timed events now... */
+ if (run_events(smbd_event_context(), 0, NULL, NULL)) {
+ return NT_STATUS_RETRY;
}
-
+
{
int sav;
START_PROFILE(smbd_idle);
- maxfd = select_on_fd(smbd_server_fd(), maxfd, &r_fds);
- maxfd = select_on_fd(oplock_notify_fd(), maxfd, &r_fds);
-
selrtn = sys_select(maxfd+1,&r_fds,&w_fds,NULL,&to);
sav = errno;
}
if (run_events(smbd_event_context(), selrtn, &r_fds, &w_fds)) {
- goto again;
- }
-
- /* if we get EINTR then maybe we have received an oplock
- signal - treat this as select returning 1. This is ugly, but
- is the best we can do until the oplock code knows more about
- signals */
- if (selrtn == -1 && errno == EINTR) {
- async_processing(&r_fds);
- /*
- * After async processing we must go and do the select again, as
- * the state of the flag in fds for the server file descriptor is
- * indeterminate - we may have done I/O on it in the oplock processing. JRA.
- */
- goto again;
+ return NT_STATUS_RETRY;
}
/* Check if error */
/* Did we timeout ? */
if (selrtn == 0) {
- goto again;
- }
-
- /*
- * Ensure we process oplock break messages by preference.
- * This is IMPORTANT ! Otherwise we can starve other processes
- * sending us an oplock break message. JRA.
- */
-
- if (oplock_message_waiting(&r_fds)) {
- async_processing(&r_fds);
- /*
- * After async processing we must go and do the select again, as
- * the state of the flag in fds for the server file descriptor is
- * indeterminate - we may have done I/O on it in the oplock processing. JRA.
- */
- goto again;
- }
-
- /*
- * We've just woken up from a protentially long select sleep.
- * Ensure we process local messages as we need to synchronously
- * process any messages from other smbd's to avoid file rename race
- * conditions. This call is cheap if there are no messages waiting.
- * JRA.
- */
- message_dispatch(smbd_messaging_context());
-
- status = receive_smb_talloc(mem_ctx, smbd_server_fd(), buffer, 0,
- p_unread, p_encrypted, &len);
-
- if (!NT_STATUS_IS_OK(status)) {
- return status;
+ return NT_STATUS_RETRY;
}
- *buffer_len = len;
-
- return NT_STATUS_OK;
+ /* should not be reached */
+ return NT_STATUS_INTERNAL_ERROR;
}
/*
return NT_STATUS_OK;
}
-/****************************************************************************
- We're terminating and have closed all our files/connections etc.
- If there are any pending local messages we need to respond to them
- before termination so that other smbds don't think we just died whilst
- holding oplocks.
-****************************************************************************/
-
-void respond_to_all_remaining_local_messages(void)
-{
- /*
- * Assert we have no exclusive open oplocks.
- */
-
- if(get_number_of_exclusive_open_oplocks()) {
- DEBUG(0,("respond_to_all_remaining_local_messages: PANIC : we have %d exclusive oplocks.\n",
- get_number_of_exclusive_open_oplocks() ));
- return;
- }
-
- process_kernel_oplocks(smbd_messaging_context(), NULL);
-
- return;
-}
-
-
/*
These flags determine some of the permissions required to do an operation
*/
static const struct smb_message_struct {
const char *name;
- void (*fn_new)(struct smb_request *req);
+ void (*fn)(struct smb_request *req);
int flags;
} smb_messages[256] = {
/* 0x30 */ { NULL, NULL, 0 },
/* 0x31 */ { NULL, NULL, 0 },
/* 0x32 */ { "SMBtrans2",reply_trans2, AS_USER | CAN_IPC },
-/* 0x33 */ { "SMBtranss2",reply_transs2, AS_USER},
+/* 0x33 */ { "SMBtranss2",reply_transs2, AS_USER | CAN_IPC },
/* 0x34 */ { "SMBfindclose",reply_findclose,AS_USER},
/* 0x35 */ { "SMBfindnclose",reply_findnclose,AS_USER},
/* 0x36 */ { NULL, NULL, 0 },
allocate and initialize a reply packet
********************************************************************/
-bool create_outbuf(TALLOC_CTX *mem_ctx, const char *inbuf, char **outbuf,
- uint8_t num_words, uint32_t num_bytes)
+static bool create_outbuf(TALLOC_CTX *mem_ctx, struct smb_request *req,
+ const char *inbuf, char **outbuf, uint8_t num_words,
+ uint32_t num_bytes)
{
/*
* Protect against integer wrap
return false;
}
- construct_reply_common(inbuf, *outbuf);
+ construct_reply_common(req, inbuf, *outbuf);
srv_set_message(*outbuf, num_words, num_bytes, false);
/*
* Zero out the word area, the caller has to take care of the bcc area
void reply_outbuf(struct smb_request *req, uint8 num_words, uint32 num_bytes)
{
char *outbuf;
- if (!create_outbuf(req, (char *)req->inbuf, &outbuf, num_words,
+ if (!create_outbuf(req, req, (char *)req->inbuf, &outbuf, num_words,
num_bytes)) {
smb_panic("could not allocate output buffer\n");
}
int flags;
uint16 session_tag;
connection_struct *conn = NULL;
-
- static uint16 last_session_tag = UID_FIELD_INVALID;
+ struct smbd_server_connection *sconn = smbd_server_conn;
errno = 0;
exit_server_cleanly("Non-SMB packet");
}
- if (smb_messages[type].fn_new == NULL) {
+ if (smb_messages[type].fn == NULL) {
DEBUG(0,("Unknown message type %d!\n",type));
smb_dump("Unknown", 1, (char *)req->inbuf, size);
reply_unknown_new(req, type);
* JRA.
*/
- if (session_tag != last_session_tag) {
+ if (session_tag != sconn->smb1.sessions.last_session_tag) {
user_struct *vuser = NULL;
- last_session_tag = session_tag;
+ sconn->smb1.sessions.last_session_tag = session_tag;
if(session_tag != UID_FIELD_INVALID) {
- vuser = get_valid_user_struct(session_tag);
+ vuser = get_valid_user_struct(sconn, session_tag);
if (vuser) {
set_current_user_info(
vuser->server_info->sanitized_username,
vuser->server_info->unix_name,
- pdb_get_fullname(vuser->server_info
- ->sam_account),
pdb_get_domain(vuser->server_info
->sam_account));
}
}
if (!change_to_user(conn,session_tag)) {
+ DEBUG(0, ("Error: Could not change to user. Removing "
+ "deferred open, mid=%d.\n", req->mid));
reply_nterror(req, NT_STATUS_DOS(ERRSRV, ERRbaduid));
return conn;
}
/* encrypted required from now on. */
conn->encrypt_level = Required;
} else if (ENCRYPTION_REQUIRED(conn)) {
- uint8 com = CVAL(req->inbuf,smb_com);
- if (com != SMBtrans2 && com != SMBtranss2) {
+ if (req->cmd != SMBtrans2 && req->cmd != SMBtranss2) {
exit_server_cleanly("encryption required "
"on connection");
return conn;
return conn;
}
- smb_messages[type].fn_new(req);
+ smb_messages[type].fn(req);
return req->conn;
}
Construct a reply to the incoming packet.
****************************************************************************/
-static void construct_reply(char *inbuf, int size, size_t unread_bytes, bool encrypted)
+static void construct_reply(char *inbuf, int size, size_t unread_bytes,
+ uint32_t seqnum, bool encrypted,
+ struct smb_perfcount_data *deferred_pcd)
{
- uint8 type = CVAL(inbuf,smb_com);
connection_struct *conn;
struct smb_request *req;
- chain_size = 0;
-
if (!(req = talloc(talloc_tos(), struct smb_request))) {
smb_panic("could not allocate smb_request");
}
+
init_smb_request(req, (uint8 *)inbuf, unread_bytes, encrypted);
+ req->inbuf = (uint8_t *)talloc_move(req, &inbuf);
+ req->seqnum = seqnum;
- conn = switch_message(type, req, size);
+ /* we popped this message off the queue - keep original perf data */
+ if (deferred_pcd)
+ req->pcd = *deferred_pcd;
+ else {
+ SMB_PERFCOUNT_START(&req->pcd);
+ SMB_PERFCOUNT_SET_OP(&req->pcd, req->cmd);
+ SMB_PERFCOUNT_SET_MSGLEN_IN(&req->pcd, size);
+ }
+
+ conn = switch_message(req->cmd, req, size);
if (req->unread_bytes) {
/* writeX failed. drain socket. */
req->unread_bytes = 0;
}
+ if (req->done) {
+ TALLOC_FREE(req);
+ return;
+ }
+
if (req->outbuf == NULL) {
return;
}
if (!srv_send_smb(smbd_server_fd(),
(char *)req->outbuf,
- IS_CONN_ENCRYPTED(conn)||req->encrypted)) {
+ true, req->seqnum+1,
+ IS_CONN_ENCRYPTED(conn)||req->encrypted,
+ &req->pcd)) {
exit_server_cleanly("construct_reply: srv_send_smb failed.");
}
/****************************************************************************
Process an smb from the client
****************************************************************************/
-
-static void process_smb(char *inbuf, size_t nread, size_t unread_bytes, bool encrypted)
+static void process_smb(struct smbd_server_connection *conn,
+ uint8_t *inbuf, size_t nread, size_t unread_bytes,
+ uint32_t seqnum, bool encrypted,
+ struct smb_perfcount_data *deferred_pcd)
{
- static int trans_num;
int msg_type = CVAL(inbuf,0);
DO_PROFILE_INC(smb_count);
- if (trans_num == 0) {
- char addr[INET6_ADDRSTRLEN];
-
- /* on the first packet, check the global hosts allow/ hosts
- deny parameters before doing any parsing of the packet
- passed to us by the client. This prevents attacks on our
- parsing code from hosts not in the hosts allow list */
-
- if (!check_access(smbd_server_fd(), lp_hostsallow(-1),
- lp_hostsdeny(-1))) {
- /* send a negative session response "not listening on calling name" */
- static unsigned char buf[5] = {0x83, 0, 0, 1, 0x81};
- DEBUG( 1, ( "Connection denied from %s\n",
- client_addr(get_client_fd(),addr,sizeof(addr)) ) );
- (void)srv_send_smb(smbd_server_fd(),(char *)buf,false);
- exit_server_cleanly("connection denied");
- }
- }
-
DEBUG( 6, ( "got message type 0x%x of len 0x%x\n", msg_type,
smb_len(inbuf) ) );
DEBUG( 3, ( "Transaction %d of length %d (%u toread)\n", trans_num,
/*
* NetBIOS session request, keepalive, etc.
*/
- reply_special(inbuf);
- return;
+ reply_special((char *)inbuf);
+ goto done;
}
- show_msg(inbuf);
+ if (smbd_server_conn->allow_smb2) {
+ if (smbd_is_smb2_header(inbuf, nread)) {
+ smbd_smb2_first_negprot(smbd_server_conn, inbuf, nread);
+ return;
+ }
+ smbd_server_conn->allow_smb2 = false;
+ }
- construct_reply(inbuf,nread,unread_bytes,encrypted);
+ show_msg((char *)inbuf);
+ construct_reply((char *)inbuf,nread,unread_bytes,seqnum,encrypted,deferred_pcd);
trans_num++;
+
+done:
+ conn->smb1.num_requests++;
+
+ /* The timeout_processing function isn't run nearly
+ often enough to implement 'max log size' without
+ overrunning the size of the file by many megabytes.
+ This is especially true if we are running at debug
+ level 10. Checking every 50 SMBs is a nice
+ tradeoff of performance vs log file size overrun. */
+
+ if ((conn->smb1.num_requests % 50) == 0 &&
+ need_to_check_log_size()) {
+ change_to_root_user();
+ check_log_size();
+ }
}
/****************************************************************************
Helper functions for contruct_reply.
****************************************************************************/
-static uint32 common_flags2 = FLAGS2_LONG_PATH_COMPONENTS|FLAGS2_32_BIT_ERROR_CODES;
-
void add_to_common_flags2(uint32 v)
{
common_flags2 |= v;
common_flags2 &= ~v;
}
-void construct_reply_common(const char *inbuf, char *outbuf)
+static void construct_reply_common(struct smb_request *req, const char *inbuf,
+ char *outbuf)
{
srv_set_message(outbuf,0,0,false);
- SCVAL(outbuf,smb_com,CVAL(inbuf,smb_com));
+ SCVAL(outbuf, smb_com, req->cmd);
SIVAL(outbuf,smb_rcls,0);
SCVAL(outbuf,smb_flg, FLAG_REPLY | (CVAL(inbuf,smb_flg) & FLAG_CASELESS_PATHNAMES));
SSVAL(outbuf,smb_flg2,
SSVAL(outbuf,smb_mid,SVAL(inbuf,smb_mid));
}
+void construct_reply_common_req(struct smb_request *req, char *outbuf)
+{
+ construct_reply_common(req, (char *)req->inbuf, outbuf);
+}
+
+/*
+ * How many bytes have we already accumulated up to the current wct field
+ * offset?
+ */
+
+size_t req_wct_ofs(struct smb_request *req)
+{
+ size_t buf_size;
+
+ if (req->chain_outbuf == NULL) {
+ return smb_wct - 4;
+ }
+ buf_size = talloc_get_size(req->chain_outbuf);
+ if ((buf_size % 4) != 0) {
+ buf_size += (4 - (buf_size % 4));
+ }
+ return buf_size - 4;
+}
+
+/*
+ * Hack around reply_nterror & friends not being aware of chained requests,
+ * generating illegal (i.e. wct==0) chain replies.
+ */
+
+static void fixup_chain_error_packet(struct smb_request *req)
+{
+ uint8_t *outbuf = req->outbuf;
+ req->outbuf = NULL;
+ reply_outbuf(req, 2, 0);
+ memcpy(req->outbuf, outbuf, smb_wct);
+ TALLOC_FREE(outbuf);
+ SCVAL(req->outbuf, smb_vwv0, 0xff);
+}
+
/****************************************************************************
Construct a chained reply and add it to the already made reply
****************************************************************************/
void chain_reply(struct smb_request *req)
{
- static char *orig_inbuf;
+ size_t smblen = smb_len(req->inbuf);
+ size_t already_used, length_needed;
+ uint8_t chain_cmd;
+ uint32_t chain_offset; /* uint32_t to avoid overflow */
- /*
- * 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;
- int outsize2;
- int new_size;
- char inbuf_saved[smb_wct];
- char *outbuf = (char *)req->outbuf;
- size_t outsize = smb_len(outbuf) + 4;
- size_t outsize_padded;
- size_t padding;
- 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;
- }
+ uint8_t wct;
+ uint16_t *vwv;
+ uint16_t buflen;
+ uint8_t *buf;
- if (chain_size == 0) {
- /* this is the first part of the chain */
- orig_inbuf = inbuf;
+ if (IVAL(req->outbuf, smb_rcls) != 0) {
+ fixup_chain_error_packet(req);
}
/*
- * We need to save the output the caller added to the chain so that we
- * can splice it into the final output buffer later.
+ * Any of the AndX requests and replies have at least a wct of
+ * 2. vwv[0] is the next command, vwv[1] is the offset from the
+ * beginning of the SMB header to the next wct field.
+ *
+ * None of the AndX requests put anything valuable in vwv[0] and [1],
+ * so we can overwrite it here to form the chain.
*/
- 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");
+ if ((req->wct < 2) || (CVAL(req->outbuf, smb_wct) < 2)) {
+ goto error;
}
/*
- * The original Win95 redirector dies on a reply to
- * a lockingX and read chain unless the chain reply is
- * 4 byte aligned. JRA.
+ * Here we assume that this is the end of the chain. For that we need
+ * to set "next command" to 0xff and the offset to 0. If we later find
+ * more commands in the chain, this will be overwritten again.
*/
- outsize_padded = (outsize + 3) & ~3;
- padding = outsize_padded - outsize;
+ SCVAL(req->outbuf, smb_vwv0, 0xff);
+ SCVAL(req->outbuf, smb_vwv0+1, 0);
+ SSVAL(req->outbuf, smb_vwv1, 0);
- /*
- * remember how much the caller added to the chain, only counting
- * stuff after the parameter words
- */
- chain_size += (outsize_padded - smb_wct);
+ if (req->chain_outbuf == NULL) {
+ /*
+ * In req->chain_outbuf we collect all the replies. Start the
+ * chain by copying in the first reply.
+ *
+ * We do the realloc because later on we depend on
+ * talloc_get_size to determine the length of
+ * chain_outbuf. The reply_xxx routines might have
+ * over-allocated (reply_pipe_read_and_X used to be such an
+ * example).
+ */
+ req->chain_outbuf = TALLOC_REALLOC_ARRAY(
+ req, req->outbuf, uint8_t, smb_len(req->outbuf) + 4);
+ if (req->chain_outbuf == NULL) {
+ goto error;
+ }
+ req->outbuf = NULL;
+ } else {
+ /*
+ * Update smb headers where subsequent chained commands
+ * may have updated them.
+ */
+ SCVAL(req->chain_outbuf, smb_tid, CVAL(req->outbuf, smb_tid));
+ SCVAL(req->chain_outbuf, smb_uid, CVAL(req->outbuf, smb_uid));
+
+ if (!smb_splice_chain(&req->chain_outbuf,
+ CVAL(req->outbuf, smb_com),
+ CVAL(req->outbuf, smb_wct),
+ (uint16_t *)(req->outbuf + smb_vwv),
+ 0, smb_buflen(req->outbuf),
+ (uint8_t *)smb_buf(req->outbuf))) {
+ goto error;
+ }
+ TALLOC_FREE(req->outbuf);
+ }
/*
- * work out pointers into the original packets. The
- * headers on these need to be filled in
+ * We use the old request's vwv field to grab the next chained command
+ * and offset into the chained fields.
*/
- inbuf2 = orig_inbuf + smb_off2 + 4 - smb_wct;
- /* remember the original command type */
- smb_com1 = CVAL(orig_inbuf,smb_com);
+ chain_cmd = CVAL(req->vwv+0, 0);
+ chain_offset = SVAL(req->vwv+1, 0);
- /* save the data which will be overwritten by the new headers */
- memcpy(inbuf_saved,inbuf2,smb_wct);
-
- /* give the new packet the same header as the last part of the SMB */
- memmove(inbuf2,inbuf,smb_wct);
-
- /* create the in buffer */
- SCVAL(inbuf2,smb_com,smb_com2);
-
- /* work out the new size for the in buffer. */
- new_size = size - (inbuf2 - inbuf);
- if (new_size < 0) {
- DEBUG(0,("chain_reply: chain packet size incorrect "
- "(orig size = %d, offset = %d)\n",
- size, (int)(inbuf2 - inbuf) ));
- exit_server_cleanly("Bad chained packet");
+ if (chain_cmd == 0xff) {
+ /*
+ * End of chain, no more requests from the client. So ship the
+ * replies.
+ */
+ smb_setlen((char *)(req->chain_outbuf),
+ talloc_get_size(req->chain_outbuf) - 4);
+
+ if (!srv_send_smb(smbd_server_fd(), (char *)req->chain_outbuf,
+ true, req->seqnum+1,
+ IS_CONN_ENCRYPTED(req->conn)
+ ||req->encrypted,
+ &req->pcd)) {
+ exit_server_cleanly("chain_reply: srv_send_smb "
+ "failed.");
+ }
+ TALLOC_FREE(req->chain_outbuf);
+ req->done = true;
return;
}
- /* And set it in the header. */
- 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,0, req->encrypted);
- req2->chain_fsp = req->chain_fsp;
-
- /* process the request */
- switch_message(smb_com2, req2, new_size);
+ /* add a new perfcounter for this element of chain */
+ SMB_PERFCOUNT_ADD(&req->pcd);
+ SMB_PERFCOUNT_SET_OP(&req->pcd, chain_cmd);
+ SMB_PERFCOUNT_SET_MSGLEN_IN(&req->pcd, smblen);
/*
- * We don't accept deferred operations in chained requests.
+ * Check if the client tries to fool us. The request so far uses the
+ * space to the end of the byte buffer in the request just
+ * processed. The chain_offset can't point into that area. If that was
+ * the case, we could end up with an endless processing of the chain,
+ * we would always handle the same request.
*/
- SMB_ASSERT(req2->outbuf != NULL);
- outsize2 = smb_len(req2->outbuf)+4;
+
+ already_used = PTR_DIFF(req->buf+req->buflen, smb_base(req->inbuf));
+ if (chain_offset < already_used) {
+ goto error;
+ }
/*
- * Move away the new command output so that caller_output fits in,
- * copy in the caller_output saved above.
+ * Next check: Make sure the chain offset does not point beyond the
+ * overall smb request length.
*/
- SMB_ASSERT(outsize_padded >= smb_wct);
+ length_needed = chain_offset+1; /* wct */
+ if (length_needed > smblen) {
+ goto error;
+ }
/*
- * "ofs" is the space we need for caller_output. Equal to
- * caller_outputlen plus the padding.
+ * Now comes the pointer magic. Goal here is to set up req->vwv and
+ * req->buf correctly again to be able to call the subsequent
+ * switch_message(). The chain offset (the former vwv[1]) points at
+ * the new wct field.
*/
- ofs = outsize_padded - smb_wct;
+ wct = CVAL(smb_base(req->inbuf), chain_offset);
/*
- * "to_move" is the amount of bytes the secondary routine gave us
+ * Next consistency check: Make the new vwv array fits in the overall
+ * smb request.
*/
- to_move = outsize2 - smb_wct;
-
- if (to_move + ofs + smb_wct + chain_size > max_send) {
- smb_panic("replies too large -- would have to cut");
+ length_needed += (wct+1)*sizeof(uint16_t); /* vwv+buflen */
+ if (length_needed > smblen) {
+ goto error;
}
+ vwv = (uint16_t *)(smb_base(req->inbuf) + chain_offset + 1);
/*
- * 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.
+ * Now grab the new byte buffer....
*/
- outbuf = TALLOC_REALLOC_ARRAY(NULL, outbuf, char,
- to_move + ofs + smb_wct);
- if (outbuf == NULL) {
- smb_panic("could not realloc outbuf");
- }
-
- req->outbuf = (uint8 *)outbuf;
-
- memmove(outbuf + smb_wct + ofs, req2->outbuf + smb_wct, to_move);
- memcpy(outbuf + smb_wct, caller_output, caller_outputlen);
+ buflen = SVAL(vwv+wct, 0);
/*
- * copy the new reply header over the old one but preserve the smb_com
- * field
+ * .. and check that it fits.
*/
- 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.
- */
+ length_needed += buflen;
+ if (length_needed > smblen) {
+ goto error;
+ }
+ buf = (uint8_t *)(vwv+wct+1);
- SCVAL(outbuf, smb_vwv0, smb_com2);
- SSVAL(outbuf, smb_vwv1, chain_size + smb_wct - 4);
+ req->cmd = chain_cmd;
+ req->wct = wct;
+ req->vwv = vwv;
+ req->buflen = buflen;
+ req->buf = buf;
- if (padding != 0) {
+ switch_message(chain_cmd, req, smblen);
+ if (req->outbuf == NULL) {
/*
- * Due to padding we have some uninitialized bytes after the
- * caller's output
+ * This happens if the chained command has suspended itself or
+ * if it has called srv_send_smb() itself.
*/
-
- memset(outbuf + outsize, 0, padding);
+ return;
}
- smb_setlen(outbuf, outsize2 + caller_outputlen + padding - 4);
+ /*
+ * We end up here if the chained command was not itself chained or
+ * suspended, but for example a close() command. We now need to splice
+ * the chained commands' outbuf into the already built up chain_outbuf
+ * and ship the result.
+ */
+ goto done;
+ error:
/*
- * restore the saved data, being careful not to overwrite any data
- * from the reply header
+ * We end up here if there's any error in the chain syntax. Report a
+ * DOS error, just like Windows does.
*/
- memcpy(inbuf2,inbuf_saved,smb_wct);
+ reply_nterror(req, NT_STATUS_DOS(ERRSRV, ERRerror));
+ fixup_chain_error_packet(req);
- SAFE_FREE(caller_output);
- TALLOC_FREE(req2);
+ done:
+ /*
+ * This scary statement intends to set the
+ * FLAGS2_32_BIT_ERROR_CODES flg2 field in req->chain_outbuf
+ * to the value req->outbuf carries
+ */
+ SSVAL(req->chain_outbuf, smb_flg2,
+ (SVAL(req->chain_outbuf, smb_flg2) & ~FLAGS2_32_BIT_ERROR_CODES)
+ | (SVAL(req->outbuf, smb_flg2) & FLAGS2_32_BIT_ERROR_CODES));
/*
- * Reset the chain_size for our caller's offset calculations
+ * Transfer the error codes from the subrequest to the main one
*/
+ SSVAL(req->chain_outbuf, smb_rcls, SVAL(req->outbuf, smb_rcls));
+ SSVAL(req->chain_outbuf, smb_err, SVAL(req->outbuf, smb_err));
- chain_size -= (outsize_padded - smb_wct);
+ if (!smb_splice_chain(&req->chain_outbuf,
+ CVAL(req->outbuf, smb_com),
+ CVAL(req->outbuf, smb_wct),
+ (uint16_t *)(req->outbuf + smb_vwv),
+ 0, smb_buflen(req->outbuf),
+ (uint8_t *)smb_buf(req->outbuf))) {
+ exit_server_cleanly("chain_reply: smb_splice_chain failed\n");
+ }
+ TALLOC_FREE(req->outbuf);
- return;
+ smb_setlen((char *)(req->chain_outbuf),
+ talloc_get_size(req->chain_outbuf) - 4);
+
+ show_msg((char *)(req->chain_outbuf));
+
+ if (!srv_send_smb(smbd_server_fd(), (char *)req->chain_outbuf,
+ true, req->seqnum+1,
+ IS_CONN_ENCRYPTED(req->conn)||req->encrypted,
+ &req->pcd)) {
+ exit_server_cleanly("construct_reply: srv_send_smb failed.");
+ }
+ TALLOC_FREE(req->chain_outbuf);
+ req->done = true;
}
/****************************************************************************
void check_reload(time_t t)
{
- static pid_t mypid = 0;
- static time_t last_smb_conf_reload_time = 0;
- static time_t last_printer_reload_time = 0;
time_t printcap_cache_time = (time_t)lp_printcap_cache_time();
if(last_smb_conf_reload_time == 0) {
mypid = getpid();
}
- if (reload_after_sighup || (t >= last_smb_conf_reload_time+SMBD_RELOAD_CHECK)) {
+ if (t >= last_smb_conf_reload_time+SMBD_RELOAD_CHECK) {
reload_services(True);
- reload_after_sighup = False;
last_smb_conf_reload_time = t;
}
}
}
+static void smbd_server_connection_write_handler(struct smbd_server_connection *conn)
+{
+ /* TODO: make write nonblocking */
+}
+
+static void smbd_server_connection_read_handler(struct smbd_server_connection *conn)
+{
+ uint8_t *inbuf = NULL;
+ size_t inbuf_len = 0;
+ size_t unread_bytes = 0;
+ bool encrypted = false;
+ TALLOC_CTX *mem_ctx = talloc_tos();
+ NTSTATUS status;
+ uint32_t seqnum;
+
+ /* TODO: make this completely nonblocking */
+
+ status = receive_smb_talloc(mem_ctx, smbd_server_fd(),
+ (char **)(void *)&inbuf,
+ 0, /* timeout */
+ &unread_bytes,
+ &encrypted,
+ &inbuf_len, &seqnum);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_RETRY)) {
+ goto process;
+ }
+ if (NT_STATUS_IS_ERR(status)) {
+ exit_server_cleanly("failed to receive smb request");
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ return;
+ }
+
+process:
+ process_smb(conn, inbuf, inbuf_len, unread_bytes,
+ seqnum, encrypted, NULL);
+}
+
+static void smbd_server_connection_handler(struct event_context *ev,
+ struct fd_event *fde,
+ uint16_t flags,
+ void *private_data)
+{
+ struct smbd_server_connection *conn = talloc_get_type(private_data,
+ struct smbd_server_connection);
+
+ if (flags & EVENT_FD_WRITE) {
+ smbd_server_connection_write_handler(conn);
+ } else if (flags & EVENT_FD_READ) {
+ smbd_server_connection_read_handler(conn);
+ }
+}
+
+
+/****************************************************************************
+received when we should release a specific IP
+****************************************************************************/
+static void release_ip(const char *ip, void *priv)
+{
+ char addr[INET6_ADDRSTRLEN];
+ char *p = addr;
+
+ client_socket_addr(get_client_fd(),addr,sizeof(addr));
+
+ if (strncmp("::ffff:", addr, 7) == 0) {
+ p = addr + 7;
+ }
+
+ if ((strcmp(p, ip) == 0) || ((p != addr) && strcmp(addr, ip) == 0)) {
+ /* we can't afford to do a clean exit - that involves
+ database writes, which would potentially mean we
+ are still running after the failover has finished -
+ we have to get rid of this process ID straight
+ away */
+ DEBUG(0,("Got release IP message for our IP %s - exiting immediately\n",
+ ip));
+ /* note we must exit with non-zero status so the unclean handler gets
+ called in the parent, so that the brl database is tickled */
+ _exit(1);
+ }
+}
+
+static void msg_release_ip(struct messaging_context *msg_ctx, void *private_data,
+ uint32_t msg_type, struct server_id server_id, DATA_BLOB *data)
+{
+ release_ip((char *)data->data, NULL);
+}
+
+#ifdef CLUSTER_SUPPORT
+static int client_get_tcp_info(struct sockaddr_storage *server,
+ struct sockaddr_storage *client)
+{
+ socklen_t length;
+ if (server_fd == -1) {
+ return -1;
+ }
+ length = sizeof(*server);
+ if (getsockname(server_fd, (struct sockaddr *)server, &length) != 0) {
+ return -1;
+ }
+ length = sizeof(*client);
+ if (getpeername(server_fd, (struct sockaddr *)client, &length) != 0) {
+ return -1;
+ }
+ return 0;
+}
+#endif
+
+/*
+ * Send keepalive packets to our client
+ */
+static bool keepalive_fn(const struct timeval *now, void *private_data)
+{
+ if (!send_keepalive(smbd_server_fd())) {
+ DEBUG( 2, ( "Keepalive failed - exiting.\n" ) );
+ return False;
+ }
+ return True;
+}
+
+/*
+ * Do the recurring check if we're idle
+ */
+static bool deadtime_fn(const struct timeval *now, void *private_data)
+{
+ struct smbd_server_connection *sconn = smbd_server_conn;
+ if ((conn_num_open(sconn) == 0)
+ || (conn_idle_all(sconn, now->tv_sec))) {
+ DEBUG( 2, ( "Closing idle connection\n" ) );
+ messaging_send(smbd_messaging_context(), procid_self(),
+ MSG_SHUTDOWN, &data_blob_null);
+ return False;
+ }
+
+ return True;
+}
+
+/*
+ * Do the recurring log file and smb.conf reload checks.
+ */
+
+static bool housekeeping_fn(const struct timeval *now, void *private_data)
+{
+ change_to_root_user();
+
+ /* update printer queue caches if necessary */
+ update_monitored_printq_cache();
+
+ /* check if we need to reload services */
+ check_reload(time(NULL));
+
+ /* Change machine password if neccessary. */
+ attempt_machine_password_change();
+
+ /*
+ * Force a log file check.
+ */
+ force_check_log_size();
+ check_log_size();
+ return true;
+}
+
/****************************************************************************
Process commands from the client
****************************************************************************/
void smbd_process(void)
{
- unsigned int num_smbs = 0;
- size_t unread_bytes = 0;
+ TALLOC_CTX *frame = talloc_stackframe();
+ char remaddr[INET6_ADDRSTRLEN];
- max_recv = MIN(lp_maxxmit(),BUFFER_SIZE);
+ if (lp_maxprotocol() == PROTOCOL_SMB2 &&
+ lp_security() != SEC_SHARE) {
+ smbd_server_conn->allow_smb2 = true;
+ }
- while (True) {
- NTSTATUS status;
- char *inbuf = NULL;
- size_t inbuf_len = 0;
- bool encrypted = false;
- TALLOC_CTX *frame = talloc_stackframe_pool(8192);
+ /* Ensure child is set to blocking mode */
+ set_blocking(smbd_server_fd(),True);
- errno = 0;
+ set_socket_options(smbd_server_fd(),"SO_KEEPALIVE");
+ set_socket_options(smbd_server_fd(), lp_socket_options());
- run_events(smbd_event_context(), 0, NULL, NULL);
+ /* this is needed so that we get decent entries
+ in smbstatus for port 445 connects */
+ set_remote_machine_name(get_peer_addr(smbd_server_fd(),
+ remaddr,
+ sizeof(remaddr)),
+ false);
+ reload_services(true);
- status = receive_message_or_smb(
- talloc_tos(), &inbuf, &inbuf_len,
- &unread_bytes, &encrypted);
+ /*
+ * Before the first packet, check the global hosts allow/ hosts deny
+ * parameters before doing any parsing of packets passed to us by the
+ * client. This prevents attacks on our parsing code from hosts not in
+ * the hosts allow list.
+ */
- if (!NT_STATUS_IS_OK(status)) {
- DEBUG(3, ("receive_message_or_smb failed: %s, "
- "exiting\n", nt_errstr(status)));
- return;
+ if (!check_access(smbd_server_fd(), lp_hostsallow(-1),
+ lp_hostsdeny(-1))) {
+ char addr[INET6_ADDRSTRLEN];
+
+ /*
+ * send a negative session response "not listening on calling
+ * name"
+ */
+ unsigned char buf[5] = {0x83, 0, 0, 1, 0x81};
+ DEBUG( 1, ("Connection denied from %s\n",
+ client_addr(get_client_fd(),addr,sizeof(addr)) ) );
+ (void)srv_send_smb(smbd_server_fd(),(char *)buf, false,
+ 0, false, NULL);
+ exit_server_cleanly("connection denied");
+ }
+
+ static_init_rpc;
+
+ init_modules();
+
+ smb_perfcount_init();
+
+ if (!init_account_policy()) {
+ exit_server("Could not open account policy tdb.\n");
+ }
+
+ if (*lp_rootdir()) {
+ if (chroot(lp_rootdir()) != 0) {
+ DEBUG(0,("Failed to change root to %s\n", lp_rootdir()));
+ exit_server("Failed to chroot()");
+ }
+ if (chdir("/") == -1) {
+ DEBUG(0,("Failed to chdir to / on chroot to %s\n", lp_rootdir()));
+ exit_server("Failed to chroot()");
+ }
+ DEBUG(0,("Changed root to %s\n", lp_rootdir()));
+ }
+
+ if (!srv_init_signing(smbd_server_conn)) {
+ exit_server("Failed to init smb_signing");
+ }
+
+ /* Setup oplocks */
+ if (!init_oplocks(smbd_messaging_context()))
+ exit_server("Failed to init oplocks");
+
+ /* Setup aio signal handler. */
+ initialize_async_io_handler();
+
+ /* register our message handlers */
+ messaging_register(smbd_messaging_context(), NULL,
+ MSG_SMB_FORCE_TDIS, msg_force_tdis);
+ messaging_register(smbd_messaging_context(), NULL,
+ MSG_SMB_RELEASE_IP, msg_release_ip);
+ messaging_register(smbd_messaging_context(), NULL,
+ MSG_SMB_CLOSE_FILE, msg_close_file);
+
+ /*
+ * Use the default MSG_DEBUG handler to avoid rebroadcasting
+ * MSGs to all child processes
+ */
+ messaging_deregister(smbd_messaging_context(),
+ MSG_DEBUG, NULL);
+ messaging_register(smbd_messaging_context(), NULL,
+ MSG_DEBUG, debug_message);
+
+ if ((lp_keepalive() != 0)
+ && !(event_add_idle(smbd_event_context(), NULL,
+ timeval_set(lp_keepalive(), 0),
+ "keepalive", keepalive_fn,
+ NULL))) {
+ DEBUG(0, ("Could not add keepalive event\n"));
+ exit(1);
+ }
+
+ if (!(event_add_idle(smbd_event_context(), NULL,
+ timeval_set(IDLE_CLOSED_TIMEOUT, 0),
+ "deadtime", deadtime_fn, NULL))) {
+ DEBUG(0, ("Could not add deadtime event\n"));
+ exit(1);
+ }
+
+ if (!(event_add_idle(smbd_event_context(), NULL,
+ timeval_set(SMBD_SELECT_TIMEOUT, 0),
+ "housekeeping", housekeeping_fn, NULL))) {
+ DEBUG(0, ("Could not add housekeeping event\n"));
+ exit(1);
+ }
+
+#ifdef CLUSTER_SUPPORT
+
+ if (lp_clustering()) {
+ /*
+ * We need to tell ctdb about our client's TCP
+ * connection, so that for failover ctdbd can send
+ * tickle acks, triggering a reconnection by the
+ * client.
+ */
+
+ struct sockaddr_storage srv, clnt;
+
+ if (client_get_tcp_info(&srv, &clnt) == 0) {
+
+ NTSTATUS status;
+
+ status = ctdbd_register_ips(
+ messaging_ctdbd_connection(),
+ &srv, &clnt, release_ip, NULL);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("ctdbd_register_ips failed: %s\n",
+ nt_errstr(status)));
+ }
+ } else
+ {
+ DEBUG(0,("Unable to get tcp info for "
+ "CTDB_CONTROL_TCP_CLIENT: %s\n",
+ strerror(errno)));
}
+ }
- process_smb(inbuf, inbuf_len, unread_bytes, encrypted);
+#endif
- TALLOC_FREE(inbuf);
+ smbd_server_conn->nbt.got_session = false;
- num_smbs++;
+ smbd_server_conn->smb1.negprot.max_recv = MIN(lp_maxxmit(),BUFFER_SIZE);
- /* The timeout_processing function isn't run nearly
- often enough to implement 'max log size' without
- overrunning the size of the file by many megabytes.
- This is especially true if we are running at debug
- level 10. Checking every 50 SMBs is a nice
- tradeoff of performance vs log file size overrun. */
+ smbd_server_conn->smb1.sessions.done_sesssetup = false;
+ smbd_server_conn->smb1.sessions.max_send = BUFFER_SIZE;
+ smbd_server_conn->smb1.sessions.last_session_tag = UID_FIELD_INVALID;
+ /* users from session setup */
+ smbd_server_conn->smb1.sessions.session_userlist = NULL;
+ /* workgroup from session setup. */
+ smbd_server_conn->smb1.sessions.session_workgroup = NULL;
+ /* this holds info on user ids that are already validated for this VC */
+ smbd_server_conn->smb1.sessions.validated_users = NULL;
+ smbd_server_conn->smb1.sessions.next_vuid = VUID_OFFSET;
+ smbd_server_conn->smb1.sessions.num_validated_vuids = 0;
+#ifdef HAVE_NETGROUP
+ smbd_server_conn->smb1.sessions.my_yp_domain = NULL;
+#endif
- if ((num_smbs % 50) == 0 && need_to_check_log_size()) {
- change_to_root_user();
- check_log_size();
+ conn_init(smbd_server_conn);
+ if (!init_dptrs(smbd_server_conn)) {
+ exit_server("init_dptrs() failed");
+ }
+
+ smbd_server_conn->smb1.fde = event_add_fd(smbd_event_context(),
+ smbd_server_conn,
+ smbd_server_fd(),
+ EVENT_FD_READ,
+ smbd_server_connection_handler,
+ smbd_server_conn);
+ if (!smbd_server_conn->smb1.fde) {
+ exit_server("failed to create smbd_server_connection fde");
+ }
+
+ TALLOC_FREE(frame);
+
+ while (True) {
+ NTSTATUS status;
+
+ frame = talloc_stackframe_pool(8192);
+
+ errno = 0;
+
+ status = smbd_server_connection_loop_once(smbd_server_conn);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_RETRY) &&
+ !NT_STATUS_IS_OK(status)) {
+ DEBUG(3, ("smbd_server_connection_loop_once failed: %s,"
+ " exiting\n", nt_errstr(status)));
+ break;
}
+
TALLOC_FREE(frame);
}
+
+ exit_server_cleanly(NULL);
+}
+
+bool req_is_in_chain(struct smb_request *req)
+{
+ if (req->vwv != (uint16_t *)(req->inbuf+smb_vwv)) {
+ /*
+ * We're right now handling a subsequent request, so we must
+ * be in a chain
+ */
+ return true;
+ }
+
+ if (!is_andx_req(req->cmd)) {
+ return false;
+ }
+
+ if (req->wct < 2) {
+ /*
+ * Okay, an illegal request, but definitely not chained :-)
+ */
+ return false;
+ }
+
+ return (CVAL(req->vwv+0, 0) != 0xFF);
}