#include "includes.h"
#include "smbd/globals.h"
+#include "../librpc/gen_ndr/srv_dfs.h"
+#include "../librpc/gen_ndr/srv_dssetup.h"
+#include "../librpc/gen_ndr/srv_echo.h"
+#include "../librpc/gen_ndr/srv_eventlog.h"
+#include "../librpc/gen_ndr/srv_initshutdown.h"
+#include "../librpc/gen_ndr/srv_lsa.h"
+#include "../librpc/gen_ndr/srv_netlogon.h"
+#include "../librpc/gen_ndr/srv_ntsvcs.h"
+#include "../librpc/gen_ndr/srv_samr.h"
+#include "../librpc/gen_ndr/srv_spoolss.h"
+#include "../librpc/gen_ndr/srv_srvsvc.h"
+#include "../librpc/gen_ndr/srv_svcctl.h"
+#include "../librpc/gen_ndr/srv_winreg.h"
+#include "../librpc/gen_ndr/srv_wkssvc.h"
+#include "librpc/gen_ndr/messaging.h"
+#include "printing/nt_printing_migrate.h"
extern bool global_machine_password_needs_changing;
static void construct_reply_common(struct smb_request *req, const char *inbuf,
char *outbuf);
+static struct pending_message_list *get_deferred_open_message_smb(uint64_t mid);
+
+static bool smbd_lock_socket_internal(struct smbd_server_connection *sconn)
+{
+ bool ok;
+
+ if (sconn->smb1.echo_handler.socket_lock_fd == -1) {
+ return true;
+ }
+
+ sconn->smb1.echo_handler.ref_count++;
+
+ if (sconn->smb1.echo_handler.ref_count > 1) {
+ return true;
+ }
+
+ DEBUG(10,("pid[%d] wait for socket lock\n", (int)sys_getpid()));
+
+ ok = fcntl_lock(sconn->smb1.echo_handler.socket_lock_fd,
+ SMB_F_SETLKW, 0, 0, F_WRLCK);
+ if (!ok) {
+ return false;
+ }
+
+ DEBUG(10,("pid[%d] got for socket lock\n", (int)sys_getpid()));
+
+ return true;
+}
+
+void smbd_lock_socket(struct smbd_server_connection *sconn)
+{
+ if (!smbd_lock_socket_internal(sconn)) {
+ exit_server_cleanly("failed to lock socket");
+ }
+}
+
+static bool smbd_unlock_socket_internal(struct smbd_server_connection *sconn)
+{
+ bool ok;
+
+ if (sconn->smb1.echo_handler.socket_lock_fd == -1) {
+ return true;
+ }
+
+ sconn->smb1.echo_handler.ref_count--;
+
+ if (sconn->smb1.echo_handler.ref_count > 0) {
+ return true;
+ }
+
+ ok = fcntl_lock(sconn->smb1.echo_handler.socket_lock_fd,
+ SMB_F_SETLKW, 0, 0, F_UNLCK);
+ if (!ok) {
+ return false;
+ }
+
+ DEBUG(10,("pid[%d] unlocked socket\n", (int)sys_getpid()));
+
+ return true;
+}
+
+void smbd_unlock_socket(struct smbd_server_connection *sconn)
+{
+ if (!smbd_unlock_socket_internal(sconn)) {
+ exit_server_cleanly("failed to unlock socket");
+ }
+}
/* Accessor function for smb_read_error for smbd functions. */
ssize_t ret;
char *buf_out = buffer;
+ smbd_lock_socket(smbd_server_conn);
+
if (do_signing) {
/* Sign the outgoing packet if required. */
srv_calculate_sign_mac(smbd_server_conn, buf_out, seqnum);
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) ));
+ DEBUG(0,("pid[%d] Error writing %d bytes to client. %d. (%s)\n",
+ (int)sys_getpid(), (int)len,(int)ret, strerror(errno) ));
srv_free_enc_buffer(buf_out);
goto out;
}
srv_free_enc_buffer(buf_out);
out:
SMB_PERFCOUNT_END(pcd);
+
+ smbd_unlock_socket(smbd_server_conn);
return true;
}
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,
* valid writeX call.
*/
- if (is_valid_writeX_buffer((uint8_t *)writeX_header)) {
+ if (is_valid_writeX_buffer(smbd_server_conn,
+ (uint8_t *)writeX_header)) {
/*
* If the data offset is beyond what
* we've read, drain the extra bytes.
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)) {
+ !srv_is_signing_active(smbd_server_conn) &&
+ smbd_server_conn->smb1.echo_handler.trusted_fde == NULL) {
return receive_smb_raw_talloc_partial_read(
mem_ctx, lenbuf, fd, buffer, timeout, p_unread, plen);
char **buffer, unsigned int timeout,
size_t *p_unread, bool *p_encrypted,
size_t *p_len,
- uint32_t *seqnum)
+ uint32_t *seqnum,
+ bool trusted_channel)
{
size_t len = 0;
NTSTATUS status;
}
/* Check the incoming SMB signature. */
- if (!srv_check_sign_mac(smbd_server_conn, *buffer, seqnum)) {
+ if (!srv_check_sign_mac(smbd_server_conn, *buffer, seqnum, trusted_channel)) {
DEBUG(0, ("receive_smb: SMB Signature verification failed on "
"incoming packet!\n"));
return NT_STATUS_INVALID_NETWORK_RESPONSE;
* Initialize a struct smb_request from an inbuf
*/
-void init_smb_request(struct smb_request *req,
- const uint8 *inbuf,
- size_t unread_bytes,
- bool encrypted)
+static bool init_smb_request(struct smb_request *req,
+ struct smbd_server_connection *sconn,
+ const uint8 *inbuf,
+ size_t unread_bytes, bool encrypted,
+ uint32_t seqnum)
{
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");
+ return false;
}
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->mid = (uint64_t)SVAL(inbuf, smb_mid);
+ req->seqnum = seqnum;
req->vuid = SVAL(inbuf, smb_uid);
req->tid = SVAL(inbuf, smb_tid);
req->wct = CVAL(inbuf, smb_wct);
req->buf = (const uint8_t *)smb_buf(inbuf);
req->unread_bytes = unread_bytes;
req->encrypted = encrypted;
- req->conn = conn_find(req->tid);
+ req->sconn = sconn;
+ req->conn = conn_find(sconn,req->tid);
req->chain_fsp = NULL;
req->chain_outbuf = NULL;
+ req->done = false;
+ req->smb2req = NULL;
smb_init_perfcount_data(&req->pcd);
/* Ensure we have at least wct words and 2 bytes of bcc. */
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");
+ return false;
}
/* Ensure bcc is correct. */
if (((uint8 *)smb_buf(inbuf)) + req->buflen > inbuf + req_size) {
(unsigned int)req->buflen,
(unsigned int)req->wct,
(unsigned int)req_size));
- exit_server_cleanly("Invalid SMB request");
+ return false;
}
req->outbuf = NULL;
+ return true;
}
static void process_smb(struct smbd_server_connection *conn,
struct pending_message_list *msg = talloc_get_type(private_data,
struct pending_message_list);
TALLOC_CTX *mem_ctx = talloc_tos();
+ uint64_t mid = (uint64_t)SVAL(msg->buf.data,smb_mid);
uint8_t *inbuf;
inbuf = (uint8_t *)talloc_memdup(mem_ctx, msg->buf.data,
/* 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)SVAL(msg->buf.data,smb_mid)));
+ DEBUG(5,("smbd_deferred_open_timer: trigger mid %llu.\n",
+ (unsigned long long)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);
+
+ /* If it's still there and was processed, remove it. */
+ msg = get_deferred_open_message_smb(mid);
+ if (msg && msg->processed) {
+ remove_deferred_open_message_smb(mid);
+ }
}
/****************************************************************************
msg->request_time = request_time;
msg->seqnum = req->seqnum;
msg->encrypted = req->encrypted;
+ msg->processed = false;
SMB_PERFCOUNT_DEFER_OP(&req->pcd, &msg->pcd);
if (private_data) {
Function to delete a sharing violation open message by mid.
****************************************************************************/
-void remove_deferred_open_smb_message(uint16 mid)
+void remove_deferred_open_message_smb(uint64_t mid)
{
struct pending_message_list *pml;
+ if (smbd_server_conn->using_smb2) {
+ remove_deferred_open_message_smb2(smbd_server_conn, mid);
+ return;
+ }
+
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: "
- "deleting mid %u len %u\n",
- (unsigned int)mid,
+ if (mid == (uint64_t)SVAL(pml->buf.data,smb_mid)) {
+ DEBUG(10,("remove_deferred_open_message_smb: "
+ "deleting mid %llu len %u\n",
+ (unsigned long long)mid,
(unsigned int)pml->buf.length ));
DLIST_REMOVE(deferred_open_queue, pml);
TALLOC_FREE(pml);
schedule it for immediate processing.
****************************************************************************/
-void schedule_deferred_open_smb_message(uint16 mid)
+void schedule_deferred_open_message_smb(uint64_t mid)
{
struct pending_message_list *pml;
int i = 0;
+ if (smbd_server_conn->using_smb2) {
+ schedule_deferred_open_message_smb2(smbd_server_conn, mid);
+ return;
+ }
+
for (pml = deferred_open_queue; pml; pml = pml->next) {
- uint16 msg_mid = SVAL(pml->buf.data,smb_mid);
+ uint64_t msg_mid = (uint64_t)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_message_smb: [%d] "
+ "msg_mid = %llu\n",
+ i++,
+ (unsigned long long)msg_mid ));
if (mid == msg_mid) {
struct timed_event *te;
- DEBUG(10,("schedule_deferred_open_smb_message: scheduling mid %u\n",
- mid ));
+ if (pml->processed) {
+ /* A processed message should not be
+ * rescheduled. */
+ DEBUG(0,("schedule_deferred_open_message_smb: LOGIC ERROR "
+ "message mid %llu was already processed\n",
+ (unsigned long long)msg_mid ));
+ continue;
+ }
+
+ DEBUG(10,("schedule_deferred_open_message_smb: "
+ "scheduling mid %llu\n",
+ (unsigned long long)mid ));
te = event_add_timed(smbd_event_context(),
pml,
smbd_deferred_open_timer,
pml);
if (!te) {
- DEBUG(10,("schedule_deferred_open_smb_message: "
- "event_add_timed() failed, skipping mid %u\n",
- mid ));
+ DEBUG(10,("schedule_deferred_open_message_smb: "
+ "event_add_timed() failed, "
+ "skipping mid %llu\n",
+ (unsigned long long)msg_mid ));
}
TALLOC_FREE(pml->te);
}
}
- DEBUG(10,("schedule_deferred_open_smb_message: failed to find message mid %u\n",
- mid ));
+ DEBUG(10,("schedule_deferred_open_message_smb: failed to "
+ "find message mid %llu\n",
+ (unsigned long long)mid ));
}
/****************************************************************************
- 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)
+bool open_was_deferred(uint64_t mid)
{
struct pending_message_list *pml;
+ if (smbd_server_conn->using_smb2) {
+ return open_was_deferred_smb2(smbd_server_conn, mid);
+ }
+
for (pml = deferred_open_queue; pml; pml = pml->next) {
- if (SVAL(pml->buf.data,smb_mid) == mid) {
+ if (((uint64_t)SVAL(pml->buf.data,smb_mid)) == mid && !pml->processed) {
return True;
}
}
Return the message queued by this mid.
****************************************************************************/
-struct pending_message_list *get_open_deferred_message(uint16 mid)
+static struct pending_message_list *get_deferred_open_message_smb(uint64_t mid)
{
struct pending_message_list *pml;
for (pml = deferred_open_queue; pml; pml = pml->next) {
- if (SVAL(pml->buf.data,smb_mid) == mid) {
+ if (((uint64_t)SVAL(pml->buf.data,smb_mid)) == mid) {
return pml;
}
}
return NULL;
}
+/****************************************************************************
+ Get the state data queued by this mid.
+****************************************************************************/
+
+bool get_deferred_open_message_state(struct smb_request *smbreq,
+ struct timeval *p_request_time,
+ void **pp_state)
+{
+ struct pending_message_list *pml;
+
+ if (smbd_server_conn->using_smb2) {
+ return get_deferred_open_message_state_smb2(smbreq->smb2req,
+ p_request_time,
+ pp_state);
+ }
+
+ pml = get_deferred_open_message_smb(smbreq->mid);
+ if (!pml) {
+ return false;
+ }
+ if (p_request_time) {
+ *p_request_time = pml->request_time;
+ }
+ if (pp_state) {
+ *pp_state = (void *)pml->private_data.data;
+ }
+ return true;
+}
+
/****************************************************************************
Function to push a deferred open smb message onto a linked list of local smb
messages ready for processing.
****************************************************************************/
-bool push_deferred_smb_message(struct smb_request *req,
+bool push_deferred_open_message_smb(struct smb_request *req,
struct timeval request_time,
struct timeval timeout,
+ struct file_id id,
char *private_data, size_t priv_len)
{
struct timeval end_time;
+ if (req->smb2req) {
+ return push_deferred_open_message_smb2(req->smb2req,
+ request_time,
+ timeout,
+ id,
+ private_data,
+ priv_len);
+ }
+
if (req->unread_bytes) {
- DEBUG(0,("push_deferred_smb_message: logic error ! "
+ DEBUG(0,("push_deferred_open_message_smb: logic error ! "
"unread_bytes = %u\n",
(unsigned int)req->unread_bytes ));
- smb_panic("push_deferred_smb_message: "
+ smb_panic("push_deferred_open_message_smb: "
"logic error unread_bytes != 0" );
}
end_time = timeval_sum(&request_time, &timeout);
- DEBUG(10,("push_deferred_open_smb_message: pushing message len %u mid %u "
- "timeout time [%u.%06u]\n",
- (unsigned int) smb_len(req->inbuf)+4, (unsigned int)req->mid,
- (unsigned int)end_time.tv_sec,
- (unsigned int)end_time.tv_usec));
+ DEBUG(10,("push_deferred_open_message_smb: pushing message "
+ "len %u mid %llu timeout time [%u.%06u]\n",
+ (unsigned int) smb_len(req->inbuf)+4,
+ (unsigned long long)req->mid,
+ (unsigned int)end_time.tv_sec,
+ (unsigned int)end_time.tv_usec));
return push_queued_message(req, request_time, end_time,
private_data, priv_len);
void *siginfo,
void *private_data)
{
+ struct messaging_context *msg_ctx = talloc_get_type_abort(
+ private_data, struct messaging_context);
change_to_root_user();
DEBUG(1,("Reloading services after SIGHUP\n"));
- reload_services(False);
+ reload_services(msg_ctx, False);
}
-void smbd_setup_sig_hup_handler(void)
+void smbd_setup_sig_hup_handler(struct tevent_context *ev,
+ struct messaging_context *msg_ctx)
{
struct tevent_signal *se;
- se = tevent_add_signal(smbd_event_context(),
- smbd_event_context(),
- SIGHUP, 0,
- smbd_sig_hup_handler,
- NULL);
+ se = tevent_add_signal(ev, ev, SIGHUP, 0, smbd_sig_hup_handler,
+ msg_ctx);
if (!se) {
exit_server("failed to setup SIGHUP handler");
}
errno = sav;
}
+ if ((conn->smb1.echo_handler.trusted_fd != -1)
+ && FD_ISSET(smbd_server_fd(), &r_fds)
+ && FD_ISSET(conn->smb1.echo_handler.trusted_fd, &r_fds)) {
+ /*
+ * Prefer to read pending requests from the echo handler. To
+ * quote Jeremy (da70f8ab1): This is a hack of monstrous
+ * proportions...
+ */
+ FD_CLR(smbd_server_fd(), &r_fds);
+ }
+
if (run_events(smbd_event_context(), selrtn, &r_fds, &w_fds)) {
return NT_STATUS_RETRY;
}
* prevent a DoS.
*/
-NTSTATUS allow_new_trans(struct trans_state *list, int mid)
+NTSTATUS allow_new_trans(struct trans_state *list, uint64_t mid)
{
int count = 0;
for (; list != NULL; list = list->next) {
int flags;
uint16 session_tag;
connection_struct *conn = NULL;
+ struct smbd_server_connection *sconn = req->sconn;
errno = 0;
* 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_domain(vuser->server_info
- ->sam_account));
+ vuser->server_info->info3->base.domain.string);
}
}
}
if (type == SMBntcreateX) {
reply_nterror(req, NT_STATUS_INVALID_HANDLE);
} else {
- reply_doserror(req, ERRSRV, ERRinvnid);
+ reply_nterror(req, NT_STATUS_NETWORK_NAME_DELETED);
}
return NULL;
}
if (!change_to_user(conn,session_tag)) {
- reply_nterror(req, NT_STATUS_DOS(ERRSRV, ERRbaduid));
- remove_deferred_open_smb_message(req->mid);
+ DEBUG(0, ("Error: Could not change to user. Removing "
+ "deferred open, mid=%llu.\n",
+ (unsigned long long)req->mid));
+ reply_force_doserror(req, ERRSRV, ERRbaduid);
return conn;
}
/* IPC services are limited */
if (IS_IPC(conn) && !(flags & CAN_IPC)) {
- reply_doserror(req, ERRSRV,ERRaccess);
+ reply_nterror(req, NT_STATUS_ACCESS_DENIED);
return conn;
}
} else {
if (!set_current_service(conn,SVAL(req->inbuf,smb_flg),
(flags & (AS_USER|DO_CHDIR)
?True:False))) {
- reply_doserror(req, ERRSRV, ERRaccess);
+ reply_nterror(req, NT_STATUS_ACCESS_DENIED);
return conn;
}
conn->num_smb_operations++;
&& (!change_to_guest() ||
!check_access(smbd_server_fd(), lp_hostsallow(-1),
lp_hostsdeny(-1)))) {
- reply_doserror(req, ERRSRV, ERRaccess);
+ reply_nterror(req, NT_STATUS_ACCESS_DENIED);
return conn;
}
smb_panic("could not allocate smb_request");
}
- init_smb_request(req, (uint8 *)inbuf, unread_bytes, encrypted);
+ if (!init_smb_request(req, smbd_server_conn, (uint8 *)inbuf,
+ unread_bytes, encrypted, seqnum)) {
+ exit_server_cleanly("Invalid SMB request");
+ }
+
req->inbuf = (uint8_t *)talloc_move(req, &inbuf);
- req->seqnum = seqnum;
/* we popped this message off the queue - keep original perf data */
if (deferred_pcd)
req->unread_bytes = 0;
}
+ if (req->done) {
+ TALLOC_FREE(req);
+ return;
+ }
+
if (req->outbuf == NULL) {
return;
}
/*
* NetBIOS session request, keepalive, etc.
*/
- reply_special((char *)inbuf);
+ reply_special(conn, (char *)inbuf);
goto done;
}
- if (smbd_server_conn->allow_smb2) {
+ if (smbd_server_conn->using_smb2) {
+ /* At this point we're not really using smb2,
+ * we make the decision here.. */
if (smbd_is_smb2_header(inbuf, nread)) {
smbd_smb2_first_negprot(smbd_server_conn, inbuf, nread);
return;
+ } else if (nread >= smb_size && valid_smb_header(inbuf)
+ && CVAL(inbuf, smb_com) != 0x72) {
+ /* This is a non-negprot SMB1 packet.
+ Disable SMB2 from now on. */
+ smbd_server_conn->using_smb2 = false;
}
- smbd_server_conn->allow_smb2 = false;
}
show_msg((char *)inbuf);
char *outbuf)
{
srv_set_message(outbuf,0,0,false);
-
+
SCVAL(outbuf, smb_com, req->cmd);
SIVAL(outbuf,smb_rcls,0);
SCVAL(outbuf,smb_flg, FLAG_REPLY | (CVAL(inbuf,smb_flg) & FLAG_CASELESS_PATHNAMES));
SCVAL(req->outbuf, smb_vwv0, 0xff);
}
+/**
+ * @brief Find the smb_cmd offset of the last command pushed
+ * @param[in] buf The buffer we're building up
+ * @retval Where can we put our next andx cmd?
+ *
+ * While chaining requests, the "next" request we're looking at needs to put
+ * its SMB_Command before the data the previous request already built up added
+ * to the chain. Find the offset to the place where we have to put our cmd.
+ */
+
+static bool find_andx_cmd_ofs(uint8_t *buf, size_t *pofs)
+{
+ uint8_t cmd;
+ size_t ofs;
+
+ cmd = CVAL(buf, smb_com);
+
+ SMB_ASSERT(is_andx_req(cmd));
+
+ ofs = smb_vwv0;
+
+ while (CVAL(buf, ofs) != 0xff) {
+
+ if (!is_andx_req(CVAL(buf, ofs))) {
+ return false;
+ }
+
+ /*
+ * ofs is from start of smb header, so add the 4 length
+ * bytes. The next cmd is right after the wct field.
+ */
+ ofs = SVAL(buf, ofs+2) + 4 + 1;
+
+ SMB_ASSERT(ofs+4 < talloc_get_size(buf));
+ }
+
+ *pofs = ofs;
+ return true;
+}
+
+/**
+ * @brief Do the smb chaining at a buffer level
+ * @param[in] poutbuf Pointer to the talloc'ed buffer to be modified
+ * @param[in] smb_command The command that we want to issue
+ * @param[in] wct How many words?
+ * @param[in] vwv The words, already in network order
+ * @param[in] bytes_alignment How shall we align "bytes"?
+ * @param[in] num_bytes How many bytes?
+ * @param[in] bytes The data the request ships
+ *
+ * smb_splice_chain() adds the vwv and bytes to the request already present in
+ * *poutbuf.
+ */
+
+static bool smb_splice_chain(uint8_t **poutbuf, uint8_t smb_command,
+ uint8_t wct, const uint16_t *vwv,
+ size_t bytes_alignment,
+ uint32_t num_bytes, const uint8_t *bytes)
+{
+ uint8_t *outbuf;
+ size_t old_size, new_size;
+ size_t ofs;
+ size_t chain_padding = 0;
+ size_t bytes_padding = 0;
+ bool first_request;
+
+ old_size = talloc_get_size(*poutbuf);
+
+ /*
+ * old_size == smb_wct means we're pushing the first request in for
+ * libsmb/
+ */
+
+ first_request = (old_size == smb_wct);
+
+ if (!first_request && ((old_size % 4) != 0)) {
+ /*
+ * Align the wct field of subsequent requests to a 4-byte
+ * boundary
+ */
+ chain_padding = 4 - (old_size % 4);
+ }
+
+ /*
+ * After the old request comes the new wct field (1 byte), the vwv's
+ * and the num_bytes field. After at we might need to align the bytes
+ * given to us to "bytes_alignment", increasing the num_bytes value.
+ */
+
+ new_size = old_size + chain_padding + 1 + wct * sizeof(uint16_t) + 2;
+
+ if ((bytes_alignment != 0) && ((new_size % bytes_alignment) != 0)) {
+ bytes_padding = bytes_alignment - (new_size % bytes_alignment);
+ }
+
+ new_size += bytes_padding + num_bytes;
+
+ if ((smb_command != SMBwriteX) && (new_size > 0xffff)) {
+ DEBUG(1, ("splice_chain: %u bytes won't fit\n",
+ (unsigned)new_size));
+ return false;
+ }
+
+ outbuf = TALLOC_REALLOC_ARRAY(NULL, *poutbuf, uint8_t, new_size);
+ if (outbuf == NULL) {
+ DEBUG(0, ("talloc failed\n"));
+ return false;
+ }
+ *poutbuf = outbuf;
+
+ if (first_request) {
+ SCVAL(outbuf, smb_com, smb_command);
+ } else {
+ size_t andx_cmd_ofs;
+
+ if (!find_andx_cmd_ofs(outbuf, &andx_cmd_ofs)) {
+ DEBUG(1, ("invalid command chain\n"));
+ *poutbuf = TALLOC_REALLOC_ARRAY(
+ NULL, *poutbuf, uint8_t, old_size);
+ return false;
+ }
+
+ if (chain_padding != 0) {
+ memset(outbuf + old_size, 0, chain_padding);
+ old_size += chain_padding;
+ }
+
+ SCVAL(outbuf, andx_cmd_ofs, smb_command);
+ SSVAL(outbuf, andx_cmd_ofs + 2, old_size - 4);
+ }
+
+ ofs = old_size;
+
+ /*
+ * Push the chained request:
+ *
+ * wct field
+ */
+
+ SCVAL(outbuf, ofs, wct);
+ ofs += 1;
+
+ /*
+ * vwv array
+ */
+
+ memcpy(outbuf + ofs, vwv, sizeof(uint16_t) * wct);
+ ofs += sizeof(uint16_t) * wct;
+
+ /*
+ * bcc (byte count)
+ */
+
+ SSVAL(outbuf, ofs, num_bytes + bytes_padding);
+ ofs += sizeof(uint16_t);
+
+ /*
+ * padding
+ */
+
+ if (bytes_padding != 0) {
+ memset(outbuf + ofs, 0, bytes_padding);
+ ofs += bytes_padding;
+ }
+
+ /*
+ * The bytes field
+ */
+
+ memcpy(outbuf + ofs, bytes, num_bytes);
+
+ return true;
+}
+
/****************************************************************************
Construct a chained reply and add it to the already made reply
****************************************************************************/
*/
if ((req->wct < 2) || (CVAL(req->outbuf, smb_wct) < 2)) {
+ if (req->chain_outbuf == NULL) {
+ req->chain_outbuf = TALLOC_REALLOC_ARRAY(
+ req, req->outbuf, uint8_t,
+ smb_len(req->outbuf) + 4);
+ if (req->chain_outbuf == NULL) {
+ smb_panic("talloc failed");
+ }
+ }
+ req->outbuf = NULL;
goto error;
}
req->chain_outbuf = TALLOC_REALLOC_ARRAY(
req, req->outbuf, uint8_t, smb_len(req->outbuf) + 4);
if (req->chain_outbuf == NULL) {
- goto error;
+ smb_panic("talloc failed");
}
req->outbuf = NULL;
} else {
exit_server_cleanly("chain_reply: srv_send_smb "
"failed.");
}
- TALLOC_FREE(req);
-
+ TALLOC_FREE(req->chain_outbuf);
+ req->done = true;
return;
}
* We end up here if there's any error in the chain syntax. Report a
* DOS error, just like Windows does.
*/
- reply_nterror(req, NT_STATUS_DOS(ERRSRV, ERRerror));
+ reply_force_doserror(req, ERRSRV, ERRerror);
fixup_chain_error_packet(req);
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));
+
+ /*
+ * 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));
+
if (!smb_splice_chain(&req->chain_outbuf,
CVAL(req->outbuf, smb_com),
CVAL(req->outbuf, smb_wct),
&req->pcd)) {
exit_server_cleanly("construct_reply: srv_send_smb failed.");
}
- TALLOC_FREE(req);
+ TALLOC_FREE(req->chain_outbuf);
+ req->done = true;
}
/****************************************************************************
Check if services need reloading.
****************************************************************************/
-void check_reload(time_t t)
+static void check_reload(struct messaging_context *msg_ctx, time_t t)
{
time_t printcap_cache_time = (time_t)lp_printcap_cache_time();
}
if (t >= last_smb_conf_reload_time+SMBD_RELOAD_CHECK) {
- reload_services(True);
+ reload_services(msg_ctx, True);
last_smb_conf_reload_time = t;
}
/* 'printcap cache time = 0' disable the feature */
-
+
if ( printcap_cache_time != 0 )
{
/* see if it's time to reload or if the clock has been set back */
-
+
if ( (t >= last_printer_reload_time+printcap_cache_time)
|| (t-last_printer_reload_time < 0) )
{
DEBUG( 3,( "Printcap cache time expired.\n"));
- reload_printers();
+ reload_printers(msg_ctx);
last_printer_reload_time = t;
}
}
}
+static bool fd_is_readable(int fd)
+{
+ fd_set fds;
+ struct timeval timeout = {0, };
+ int ret;
+
+ FD_ZERO(&fds);
+ FD_SET(fd, &fds);
+
+ ret = sys_select(fd+1, &fds, NULL, NULL, &timeout);
+ if (ret == -1) {
+ return false;
+ }
+ return FD_ISSET(fd, &fds);
+}
+
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)
+static void smbd_server_connection_read_handler(
+ struct smbd_server_connection *conn, int fd)
{
uint8_t *inbuf = NULL;
size_t inbuf_len = 0;
NTSTATUS status;
uint32_t seqnum;
- /* TODO: make this completely nonblocking */
+ bool from_client = (smbd_server_fd() == fd)?true:false;
+
+ if (from_client) {
+ smbd_lock_socket(conn);
+
+ if (!fd_is_readable(smbd_server_fd())) {
+ DEBUG(10,("the echo listener was faster\n"));
+ smbd_unlock_socket(conn);
+ return;
+ }
+
+ /* TODO: make this completely nonblocking */
+ status = receive_smb_talloc(mem_ctx, fd,
+ (char **)(void *)&inbuf,
+ 0, /* timeout */
+ &unread_bytes,
+ &encrypted,
+ &inbuf_len, &seqnum,
+ false /* trusted channel */);
+ smbd_unlock_socket(conn);
+ } else {
+ /* TODO: make this completely nonblocking */
+ status = receive_smb_talloc(mem_ctx, fd,
+ (char **)(void *)&inbuf,
+ 0, /* timeout */
+ &unread_bytes,
+ &encrypted,
+ &inbuf_len, &seqnum,
+ true /* trusted channel */);
+ }
- 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 (flags & EVENT_FD_WRITE) {
smbd_server_connection_write_handler(conn);
} else if (flags & EVENT_FD_READ) {
- smbd_server_connection_read_handler(conn);
+ smbd_server_connection_read_handler(conn, smbd_server_fd());
}
}
+static void smbd_server_echo_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, conn->smb1.echo_handler.trusted_fd);
+ }
+}
/****************************************************************************
received when we should release a specific IP
*/
static bool keepalive_fn(const struct timeval *now, void *private_data)
{
- if (!send_keepalive(smbd_server_fd())) {
+ struct smbd_server_connection *sconn = smbd_server_conn;
+ bool ret;
+
+ if (sconn->using_smb2) {
+ /* Don't do keepalives on an SMB2 connection. */
+ return false;
+ }
+
+ smbd_lock_socket(smbd_server_conn);
+ ret = send_keepalive(smbd_server_fd());
+ smbd_unlock_socket(smbd_server_conn);
+
+ if (!ret) {
DEBUG( 2, ( "Keepalive failed - exiting.\n" ) );
return False;
}
*/
static bool deadtime_fn(const struct timeval *now, void *private_data)
{
- if ((conn_num_open() == 0)
- || (conn_idle_all(now->tv_sec))) {
- DEBUG( 2, ( "Closing idle connection\n" ) );
- messaging_send(smbd_messaging_context(), procid_self(),
+ struct smbd_server_connection *sconn =
+ (struct smbd_server_connection *)private_data;
+
+ if (sconn->using_smb2) {
+ /* TODO: implement real idle check */
+ if (sconn->smb2.sessions.list) {
+ return true;
+ }
+ DEBUG( 2, ( "Closing idle SMB2 connection\n" ) );
+ messaging_send(sconn->msg_ctx, procid_self(),
+ MSG_SHUTDOWN, &data_blob_null);
+ return false;
+ }
+
+ if ((conn_num_open(sconn) == 0)
+ || (conn_idle_all(sconn, now->tv_sec))) {
+ DEBUG( 2, ( "Closing idle SMB1 connection\n" ) );
+ messaging_send(sconn->msg_ctx, procid_self(),
MSG_SHUTDOWN, &data_blob_null);
return False;
}
static bool housekeeping_fn(const struct timeval *now, void *private_data)
{
+ struct messaging_context *msg_ctx = talloc_get_type_abort(
+ private_data, struct messaging_context);
change_to_root_user();
/* update printer queue caches if necessary */
- update_monitored_printq_cache();
+ update_monitored_printq_cache(msg_ctx);
/* check if we need to reload services */
- check_reload(time(NULL));
+ check_reload(msg_ctx, time(NULL));
/* Change machine password if neccessary. */
attempt_machine_password_change();
return true;
}
+static int create_unlink_tmp(const char *dir)
+{
+ char *fname;
+ int fd;
+
+ fname = talloc_asprintf(talloc_tos(), "%s/listenerlock_XXXXXX", dir);
+ if (fname == NULL) {
+ errno = ENOMEM;
+ return -1;
+ }
+ fd = mkstemp(fname);
+ if (fd == -1) {
+ TALLOC_FREE(fname);
+ return -1;
+ }
+ if (unlink(fname) == -1) {
+ int sys_errno = errno;
+ close(fd);
+ TALLOC_FREE(fname);
+ errno = sys_errno;
+ return -1;
+ }
+ TALLOC_FREE(fname);
+ return fd;
+}
+
+struct smbd_echo_state {
+ struct tevent_context *ev;
+ struct iovec *pending;
+ struct smbd_server_connection *sconn;
+ int parent_pipe;
+
+ struct tevent_fd *parent_fde;
+
+ struct tevent_fd *read_fde;
+ struct tevent_req *write_req;
+};
+
+static void smbd_echo_writer_done(struct tevent_req *req);
+
+static void smbd_echo_activate_writer(struct smbd_echo_state *state)
+{
+ int num_pending;
+
+ if (state->write_req != NULL) {
+ return;
+ }
+
+ num_pending = talloc_array_length(state->pending);
+ if (num_pending == 0) {
+ return;
+ }
+
+ state->write_req = writev_send(state, state->ev, NULL,
+ state->parent_pipe, false,
+ state->pending, num_pending);
+ if (state->write_req == NULL) {
+ DEBUG(1, ("writev_send failed\n"));
+ exit(1);
+ }
+
+ talloc_steal(state->write_req, state->pending);
+ state->pending = NULL;
+
+ tevent_req_set_callback(state->write_req, smbd_echo_writer_done,
+ state);
+}
+
+static void smbd_echo_writer_done(struct tevent_req *req)
+{
+ struct smbd_echo_state *state = tevent_req_callback_data(
+ req, struct smbd_echo_state);
+ ssize_t written;
+ int err;
+
+ written = writev_recv(req, &err);
+ TALLOC_FREE(req);
+ state->write_req = NULL;
+ if (written == -1) {
+ DEBUG(1, ("writev to parent failed: %s\n", strerror(err)));
+ exit(1);
+ }
+ DEBUG(10,("echo_handler[%d]: forwarded pdu to main\n", (int)sys_getpid()));
+ smbd_echo_activate_writer(state);
+}
+
+static bool smbd_echo_reply(int fd,
+ uint8_t *inbuf, size_t inbuf_len,
+ uint32_t seqnum)
+{
+ struct smb_request req;
+ uint16_t num_replies;
+ size_t out_len;
+ char *outbuf;
+ bool ok;
+
+ if (inbuf_len < smb_size) {
+ DEBUG(10, ("Got short packet: %d bytes\n", (int)inbuf_len));
+ return false;
+ }
+ if (!valid_smb_header(inbuf)) {
+ DEBUG(10, ("Got invalid SMB header\n"));
+ return false;
+ }
+
+ if (!init_smb_request(&req, smbd_server_conn, inbuf, 0, false,
+ seqnum)) {
+ return false;
+ }
+ req.inbuf = inbuf;
+
+ DEBUG(10, ("smbecho handler got cmd %d (%s)\n", (int)req.cmd,
+ smb_messages[req.cmd].name
+ ? smb_messages[req.cmd].name : "unknown"));
+
+ if (req.cmd != SMBecho) {
+ return false;
+ }
+ if (req.wct < 1) {
+ return false;
+ }
+
+ num_replies = SVAL(req.vwv+0, 0);
+ if (num_replies != 1) {
+ /* Not a Windows "Hey, you're still there?" request */
+ return false;
+ }
+
+ if (!create_outbuf(talloc_tos(), &req, (char *)req.inbuf, &outbuf,
+ 1, req.buflen)) {
+ DEBUG(10, ("create_outbuf failed\n"));
+ return false;
+ }
+ req.outbuf = (uint8_t *)outbuf;
+
+ SSVAL(req.outbuf, smb_vwv0, num_replies);
+
+ if (req.buflen > 0) {
+ memcpy(smb_buf(req.outbuf), req.buf, req.buflen);
+ }
+
+ out_len = smb_len(req.outbuf) + 4;
+
+ ok = srv_send_smb(smbd_server_fd(),
+ (char *)outbuf,
+ true, seqnum+1,
+ false, &req.pcd);
+ TALLOC_FREE(outbuf);
+ if (!ok) {
+ exit(1);
+ }
+
+ return true;
+}
+
+static void smbd_echo_exit(struct tevent_context *ev,
+ struct tevent_fd *fde, uint16_t flags,
+ void *private_data)
+{
+ DEBUG(2, ("smbd_echo_exit: lost connection to parent\n"));
+ exit(0);
+}
+
+static void smbd_echo_reader(struct tevent_context *ev,
+ struct tevent_fd *fde, uint16_t flags,
+ void *private_data)
+{
+ struct smbd_echo_state *state = talloc_get_type_abort(
+ private_data, struct smbd_echo_state);
+ struct smbd_server_connection *sconn = state->sconn;
+ size_t unread, num_pending;
+ NTSTATUS status;
+ struct iovec *tmp;
+ uint32_t seqnum = 0;
+ bool reply;
+ bool ok;
+ bool encrypted = false;
+
+ ok = smbd_lock_socket_internal(sconn);
+ if (!ok) {
+ DEBUG(0, ("%s: failed to lock socket\n",
+ __location__));
+ exit(1);
+ }
+
+ if (!fd_is_readable(smbd_server_fd())) {
+ DEBUG(10,("echo_handler[%d] the parent smbd was faster\n",
+ (int)sys_getpid()));
+ ok = smbd_unlock_socket_internal(sconn);
+ if (!ok) {
+ DEBUG(1, ("%s: failed to unlock socket in\n",
+ __location__));
+ exit(1);
+ }
+ return;
+ }
+
+ num_pending = talloc_array_length(state->pending);
+ tmp = talloc_realloc(state, state->pending, struct iovec,
+ num_pending+1);
+ if (tmp == NULL) {
+ DEBUG(1, ("talloc_realloc failed\n"));
+ exit(1);
+ }
+ state->pending = tmp;
+
+ DEBUG(10,("echo_handler[%d]: reading pdu\n", (int)sys_getpid()));
+
+ status = receive_smb_talloc(state->pending, smbd_server_fd(),
+ (char **)(void *)&state->pending[num_pending].iov_base,
+ 0 /* timeout */,
+ &unread,
+ &encrypted,
+ &state->pending[num_pending].iov_len,
+ &seqnum,
+ false /* trusted_channel*/);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("echo_handler[%d]: receive_smb_raw_talloc failed: %s\n",
+ (int)sys_getpid(), nt_errstr(status)));
+ exit(1);
+ }
+
+ ok = smbd_unlock_socket_internal(sconn);
+ if (!ok) {
+ DEBUG(1, ("%s: failed to unlock socket in\n",
+ __location__));
+ exit(1);
+ }
+
+ /*
+ * place the seqnum in the packet so that the main process can reply
+ * with signing
+ */
+ SIVAL((uint8_t *)state->pending[num_pending].iov_base, smb_ss_field, seqnum);
+ SIVAL((uint8_t *)state->pending[num_pending].iov_base, smb_ss_field+4, NT_STATUS_V(NT_STATUS_OK));
+
+ reply = smbd_echo_reply(smbd_server_fd(),
+ (uint8_t *)state->pending[num_pending].iov_base,
+ state->pending[num_pending].iov_len,
+ seqnum);
+ if (reply) {
+ DEBUG(10,("echo_handler[%d]: replied to client\n", (int)sys_getpid()));
+ /* no check, shrinking by some bytes does not fail */
+ state->pending = talloc_realloc(state, state->pending,
+ struct iovec,
+ num_pending);
+ } else {
+ DEBUG(10,("echo_handler[%d]: forward to main\n", (int)sys_getpid()));
+ smbd_echo_activate_writer(state);
+ }
+}
+
+static void smbd_echo_loop(struct smbd_server_connection *sconn,
+ int parent_pipe)
+{
+ struct smbd_echo_state *state;
+
+ state = talloc_zero(sconn, struct smbd_echo_state);
+ if (state == NULL) {
+ DEBUG(1, ("talloc failed\n"));
+ return;
+ }
+ state->sconn = sconn;
+ state->parent_pipe = parent_pipe;
+ state->ev = s3_tevent_context_init(state);
+ if (state->ev == NULL) {
+ DEBUG(1, ("tevent_context_init failed\n"));
+ TALLOC_FREE(state);
+ return;
+ }
+ state->parent_fde = tevent_add_fd(state->ev, state, parent_pipe,
+ TEVENT_FD_READ, smbd_echo_exit,
+ state);
+ if (state->parent_fde == NULL) {
+ DEBUG(1, ("tevent_add_fd failed\n"));
+ TALLOC_FREE(state);
+ return;
+ }
+ state->read_fde = tevent_add_fd(state->ev, state, smbd_server_fd(),
+ TEVENT_FD_READ, smbd_echo_reader,
+ state);
+ if (state->read_fde == NULL) {
+ DEBUG(1, ("tevent_add_fd failed\n"));
+ TALLOC_FREE(state);
+ return;
+ }
+
+ while (true) {
+ if (tevent_loop_once(state->ev) == -1) {
+ DEBUG(1, ("tevent_loop_once failed: %s\n",
+ strerror(errno)));
+ break;
+ }
+ }
+ TALLOC_FREE(state);
+}
+
+/*
+ * Handle SMBecho requests in a forked child process
+ */
+static bool fork_echo_handler(struct smbd_server_connection *sconn)
+{
+ int listener_pipe[2];
+ int res;
+ pid_t child;
+
+ res = pipe(listener_pipe);
+ if (res == -1) {
+ DEBUG(1, ("pipe() failed: %s\n", strerror(errno)));
+ return false;
+ }
+ sconn->smb1.echo_handler.socket_lock_fd = create_unlink_tmp(lp_lockdir());
+ if (sconn->smb1.echo_handler.socket_lock_fd == -1) {
+ DEBUG(1, ("Could not create lock fd: %s\n", strerror(errno)));
+ goto fail;
+ }
+
+ child = sys_fork();
+ if (child == 0) {
+ NTSTATUS status;
+
+ close(listener_pipe[0]);
+
+ status = reinit_after_fork(sconn->msg_ctx,
+ smbd_event_context(),
+ procid_self(), false);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("reinit_after_fork failed: %s\n",
+ nt_errstr(status)));
+ exit(1);
+ }
+ smbd_echo_loop(sconn, listener_pipe[1]);
+ exit(0);
+ }
+ close(listener_pipe[1]);
+ listener_pipe[1] = -1;
+ sconn->smb1.echo_handler.trusted_fd = listener_pipe[0];
+
+ DEBUG(10,("fork_echo_handler: main[%d] echo_child[%d]\n", (int)sys_getpid(), child));
+
+ /*
+ * Without smb signing this is the same as the normal smbd
+ * listener. This needs to change once signing comes in.
+ */
+ sconn->smb1.echo_handler.trusted_fde = event_add_fd(smbd_event_context(),
+ sconn,
+ sconn->smb1.echo_handler.trusted_fd,
+ EVENT_FD_READ,
+ smbd_server_echo_handler,
+ sconn);
+ if (sconn->smb1.echo_handler.trusted_fde == NULL) {
+ DEBUG(1, ("event_add_fd failed\n"));
+ goto fail;
+ }
+
+ return true;
+
+fail:
+ if (listener_pipe[0] != -1) {
+ close(listener_pipe[0]);
+ }
+ if (listener_pipe[1] != -1) {
+ close(listener_pipe[1]);
+ }
+ sconn->smb1.echo_handler.trusted_fd = -1;
+ if (sconn->smb1.echo_handler.socket_lock_fd != -1) {
+ close(sconn->smb1.echo_handler.socket_lock_fd);
+ }
+ sconn->smb1.echo_handler.trusted_fd = -1;
+ sconn->smb1.echo_handler.socket_lock_fd = -1;
+ return false;
+}
+
+static bool spoolss_init_cb(void *ptr)
+{
+ return nt_printing_tdb_migrate(smbd_messaging_context());
+}
+
/****************************************************************************
Process commands from the client
****************************************************************************/
-void smbd_process(void)
+void smbd_process(struct smbd_server_connection *sconn)
{
TALLOC_CTX *frame = talloc_stackframe();
- char remaddr[INET6_ADDRSTRLEN];
-
- smbd_server_conn = talloc_zero(smbd_event_context(), struct smbd_server_connection);
- if (!smbd_server_conn) {
- exit_server("failed to create smbd_server_connection");
- }
-
- if (lp_maxprotocol() == PROTOCOL_SMB2) {
- smbd_server_conn->allow_smb2 = true;
+ struct sockaddr_storage ss;
+ struct sockaddr *sa = NULL;
+ socklen_t sa_len;
+ struct tsocket_address *local_address = NULL;
+ struct tsocket_address *remote_address = NULL;
+ const char *remaddr = NULL;
+ int ret;
+ struct rpc_srv_callbacks spoolss_cb;
+
+ if (lp_maxprotocol() == PROTOCOL_SMB2 &&
+ lp_security() != SEC_SHARE &&
+ !lp_async_smb_echo_handler()) {
+ /*
+ * We're not making the desion here,
+ * we're just allowing the client
+ * to decide between SMB1 and SMB2
+ * with the first negprot
+ * packet.
+ */
+ sconn->using_smb2 = true;
}
/* Ensure child is set to blocking mode */
set_socket_options(smbd_server_fd(),"SO_KEEPALIVE");
set_socket_options(smbd_server_fd(), lp_socket_options());
+ sa = (struct sockaddr *)(void *)&ss;
+ sa_len = sizeof(ss);
+ ret = getpeername(smbd_server_fd(), sa, &sa_len);
+ if (ret != 0) {
+ int level = (errno == ENOTCONN)?2:0;
+ DEBUG(level,("getpeername() failed - %s\n", strerror(errno)));
+ exit_server_cleanly("getpeername() failed.\n");
+ }
+ ret = tsocket_address_bsd_from_sockaddr(sconn,
+ sa, sa_len,
+ &remote_address);
+ if (ret != 0) {
+ DEBUG(0,("%s: tsocket_address_bsd_from_sockaddr remote failed - %s\n",
+ __location__, strerror(errno)));
+ exit_server_cleanly("tsocket_address_bsd_from_sockaddr remote failed.\n");
+ }
+
+ sa = (struct sockaddr *)(void *)&ss;
+ sa_len = sizeof(ss);
+ ret = getsockname(smbd_server_fd(), sa, &sa_len);
+ if (ret != 0) {
+ int level = (errno == ENOTCONN)?2:0;
+ DEBUG(level,("getsockname() failed - %s\n", strerror(errno)));
+ exit_server_cleanly("getsockname() failed.\n");
+ }
+ ret = tsocket_address_bsd_from_sockaddr(sconn,
+ sa, sa_len,
+ &local_address);
+ if (ret != 0) {
+ DEBUG(0,("%s: tsocket_address_bsd_from_sockaddr remote failed - %s\n",
+ __location__, strerror(errno)));
+ exit_server_cleanly("tsocket_address_bsd_from_sockaddr remote failed.\n");
+ }
+
+ sconn->local_address = local_address;
+ sconn->remote_address = remote_address;
+
+ if (tsocket_address_is_inet(remote_address, "ip")) {
+ remaddr = tsocket_address_inet_addr_string(
+ sconn->remote_address,
+ talloc_tos());
+ if (remaddr == NULL) {
+
+ }
+ } else {
+ remaddr = "0.0.0.0";
+ }
+
/* 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);
+ set_remote_machine_name(remaddr, false);
+ reload_services(sconn->msg_ctx, true);
/*
* Before the first packet, check the global hosts allow/ hosts deny
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)) ) );
+ DEBUG( 1, ("Connection denied from %s to %s\n",
+ tsocket_address_string(remote_address, talloc_tos()),
+ tsocket_address_string(local_address, talloc_tos())));
(void)srv_send_smb(smbd_server_fd(),(char *)buf, false,
0, false, NULL);
exit_server_cleanly("connection denied");
}
- static_init_rpc;
+ DEBUG(10, ("Connection allowed from %s to %s\n",
+ tsocket_address_string(remote_address, talloc_tos()),
+ tsocket_address_string(local_address, talloc_tos())));
init_modules();
DEBUG(0,("Changed root to %s\n", lp_rootdir()));
}
- if (!srv_init_signing(smbd_server_conn)) {
+ if (!srv_init_signing(sconn)) {
exit_server("Failed to init smb_signing");
}
+ if (lp_async_smb_echo_handler() && !fork_echo_handler(sconn)) {
+ exit_server("Failed to fork echo handler");
+ }
+
/* Setup oplocks */
- if (!init_oplocks(smbd_messaging_context()))
+ if (!init_oplocks(sconn->msg_ctx))
exit_server("Failed to init oplocks");
- /* Setup aio signal handler. */
- initialize_async_io_handler();
-
/* register our message handlers */
- messaging_register(smbd_messaging_context(), NULL,
+ messaging_register(sconn->msg_ctx, NULL,
MSG_SMB_FORCE_TDIS, msg_force_tdis);
- messaging_register(smbd_messaging_context(), NULL,
+ messaging_register(sconn->msg_ctx, NULL,
MSG_SMB_RELEASE_IP, msg_release_ip);
- messaging_register(smbd_messaging_context(), NULL,
+ messaging_register(sconn->msg_ctx, NULL,
MSG_SMB_CLOSE_FILE, msg_close_file);
+ /*
+ * Use the default MSG_DEBUG handler to avoid rebroadcasting
+ * MSGs to all child processes
+ */
+ messaging_deregister(sconn->msg_ctx,
+ MSG_DEBUG, NULL);
+ messaging_register(sconn->msg_ctx, NULL,
+ MSG_DEBUG, debug_message);
+
if ((lp_keepalive() != 0)
&& !(event_add_idle(smbd_event_context(), NULL,
timeval_set(lp_keepalive(), 0),
if (!(event_add_idle(smbd_event_context(), NULL,
timeval_set(IDLE_CLOSED_TIMEOUT, 0),
- "deadtime", deadtime_fn, NULL))) {
+ "deadtime", deadtime_fn, sconn))) {
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))) {
+ "housekeeping", housekeeping_fn,
+ sconn->msg_ctx))) {
DEBUG(0, ("Could not add housekeeping event\n"));
exit(1);
}
NTSTATUS status;
status = ctdbd_register_ips(
- messaging_ctdbd_connection(),
+ messaging_ctdbd_connection(procid_self()),
&srv, &clnt, release_ip, NULL);
if (!NT_STATUS_IS_OK(status)) {
#endif
- max_recv = MIN(lp_maxxmit(),BUFFER_SIZE);
+ sconn->nbt.got_session = false;
+
+ sconn->smb1.negprot.max_recv = MIN(lp_maxxmit(),BUFFER_SIZE);
+
+ sconn->smb1.sessions.done_sesssetup = false;
+ sconn->smb1.sessions.max_send = BUFFER_SIZE;
+ sconn->smb1.sessions.last_session_tag = UID_FIELD_INVALID;
+ /* users from session setup */
+ sconn->smb1.sessions.session_userlist = NULL;
+ /* workgroup from session setup. */
+ sconn->smb1.sessions.session_workgroup = NULL;
+ /* this holds info on user ids that are already validated for this VC */
+ sconn->smb1.sessions.validated_users = NULL;
+ sconn->smb1.sessions.next_vuid = VUID_OFFSET;
+ sconn->smb1.sessions.num_validated_vuids = 0;
+
+ conn_init(sconn);
+ if (!init_dptrs(sconn)) {
+ exit_server("init_dptrs() failed");
+ }
- smbd_server_conn->smb1.fde = event_add_fd(smbd_event_context(),
- smbd_server_conn,
+ sconn->smb1.fde = event_add_fd(smbd_event_context(),
+ sconn,
smbd_server_fd(),
EVENT_FD_READ,
smbd_server_connection_handler,
- smbd_server_conn);
- if (!smbd_server_conn->smb1.fde) {
+ sconn);
+ if (!sconn->smb1.fde) {
exit_server("failed to create smbd_server_connection fde");
}
+ /*
+ * Initialize spoolss with an init function to convert printers first.
+ * static_init_rpc will try to initialize the spoolss server too but you
+ * can't register it twice.
+ */
+ spoolss_cb.init = spoolss_init_cb;
+ spoolss_cb.shutdown = NULL;
+
+
+ if (!NT_STATUS_IS_OK(rpc_winreg_init(NULL))) {
+ exit(1);
+ }
+
+ if (!NT_STATUS_IS_OK(rpc_spoolss_init(&spoolss_cb))) {
+ exit(1);
+ }
+
+ static_init_rpc;
+
TALLOC_FREE(frame);
while (True) {
errno = 0;
- status = smbd_server_connection_loop_once(smbd_server_conn);
+ status = smbd_server_connection_loop_once(sconn);
if (!NT_STATUS_EQUAL(status, NT_STATUS_RETRY) &&
!NT_STATUS_IS_OK(status)) {
DEBUG(3, ("smbd_server_connection_loop_once failed: %s,"