mirror of
https://github.com/samba-team/samba.git
synced 2024-12-24 21:34:56 +03:00
12f1d94a4e
Looks larger than it is, "git clang-format" added a few lines Signed-off-by: Volker Lendecke <vl@samba.org> Reviewed-by: Jeremy Allison <jra@samba.org>
2024 lines
53 KiB
C
2024 lines
53 KiB
C
/*
|
|
Unix SMB/CIFS implementation.
|
|
process incoming packets - main loop
|
|
Copyright (C) Andrew Tridgell 1992-1998
|
|
Copyright (C) Volker Lendecke 2005-2007
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "includes.h"
|
|
#include "../lib/tsocket/tsocket.h"
|
|
#include "system/filesys.h"
|
|
#include "smbd/smbd.h"
|
|
#include "smbd/globals.h"
|
|
#include "smbd/smbXsrv_open.h"
|
|
#include "librpc/gen_ndr/netlogon.h"
|
|
#include "../lib/async_req/async_sock.h"
|
|
#include "ctdbd_conn.h"
|
|
#include "../lib/util/select.h"
|
|
#include "printing/queue_process.h"
|
|
#include "system/select.h"
|
|
#include "passdb.h"
|
|
#include "auth.h"
|
|
#include "messages.h"
|
|
#include "lib/messages_ctdb.h"
|
|
#include "smbprofile.h"
|
|
#include "rpc_server/spoolss/srv_spoolss_nt.h"
|
|
#include "../lib/util/tevent_ntstatus.h"
|
|
#include "../libcli/security/dom_sid.h"
|
|
#include "../libcli/security/security_token.h"
|
|
#include "lib/id_cache.h"
|
|
#include "lib/util/sys_rw_data.h"
|
|
#include "system/threads.h"
|
|
#include "lib/pthreadpool/pthreadpool_tevent.h"
|
|
#include "util_event.h"
|
|
#include "libcli/smb/smbXcli_base.h"
|
|
#include "lib/util/time_basic.h"
|
|
#include "source3/lib/substitute.h"
|
|
|
|
/* Internal message queue for deferred opens. */
|
|
struct pending_message_list {
|
|
struct pending_message_list *next, *prev;
|
|
struct timeval request_time; /* When was this first issued? */
|
|
struct smbd_server_connection *sconn;
|
|
struct smbXsrv_connection *xconn;
|
|
struct tevent_timer *te;
|
|
struct smb_perfcount_data pcd;
|
|
uint32_t seqnum;
|
|
bool encrypted;
|
|
bool processed;
|
|
DATA_BLOB buf;
|
|
struct deferred_open_record *open_rec;
|
|
};
|
|
|
|
static struct pending_message_list *get_deferred_open_message_smb(
|
|
struct smbd_server_connection *sconn, uint64_t mid);
|
|
|
|
#if !defined(WITH_SMB1SERVER)
|
|
bool smb1_srv_send(struct smbXsrv_connection *xconn,
|
|
char *buffer,
|
|
bool do_signing,
|
|
uint32_t seqnum,
|
|
bool do_encrypt)
|
|
{
|
|
size_t len = 0;
|
|
ssize_t ret;
|
|
len = smb_len_large(buffer) + 4;
|
|
ret = write_data(xconn->transport.sock, buffer, len);
|
|
return (ret > 0);
|
|
}
|
|
#endif
|
|
|
|
/*******************************************************************
|
|
Setup the word count and byte count for a smb1 message.
|
|
********************************************************************/
|
|
|
|
size_t srv_smb1_set_message(char *buf,
|
|
size_t num_words,
|
|
size_t num_bytes,
|
|
bool zero)
|
|
{
|
|
if (zero && (num_words || num_bytes)) {
|
|
memset(buf + smb_size,'\0',num_words*2 + num_bytes);
|
|
}
|
|
SCVAL(buf,smb_wct,num_words);
|
|
SSVAL(buf,smb_vwv + num_words*SIZEOFWORD,num_bytes);
|
|
smb_setlen(buf,(smb_size + num_words*2 + num_bytes - 4));
|
|
return (smb_size + num_words*2 + num_bytes);
|
|
}
|
|
|
|
NTSTATUS read_packet_remainder(int fd, char *buffer,
|
|
unsigned int timeout, ssize_t len)
|
|
{
|
|
NTSTATUS status;
|
|
|
|
if (len <= 0) {
|
|
return NT_STATUS_OK;
|
|
}
|
|
|
|
status = read_fd_with_timeout(fd, buffer, len, len, timeout, NULL);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
char addr[INET6_ADDRSTRLEN];
|
|
DEBUG(0, ("read_fd_with_timeout failed for client %s read "
|
|
"error = %s.\n",
|
|
get_peer_addr(fd, addr, sizeof(addr)),
|
|
nt_errstr(status)));
|
|
}
|
|
return status;
|
|
}
|
|
|
|
#if !defined(WITH_SMB1SERVER)
|
|
static NTSTATUS smb2_receive_raw_talloc(TALLOC_CTX *mem_ctx,
|
|
struct smbXsrv_connection *xconn,
|
|
int sock,
|
|
char **buffer, unsigned int timeout,
|
|
size_t *p_unread, size_t *plen)
|
|
{
|
|
char lenbuf[4];
|
|
size_t len;
|
|
NTSTATUS status;
|
|
|
|
*p_unread = 0;
|
|
|
|
status = read_smb_length_return_keepalive(sock, lenbuf, timeout,
|
|
&len);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
return status;
|
|
}
|
|
|
|
/*
|
|
* The +4 here can't wrap, we've checked the length above already.
|
|
*/
|
|
|
|
*buffer = talloc_array(mem_ctx, char, len+4);
|
|
|
|
if (*buffer == NULL) {
|
|
DEBUG(0, ("Could not allocate inbuf of length %d\n",
|
|
(int)len+4));
|
|
return NT_STATUS_NO_MEMORY;
|
|
}
|
|
|
|
memcpy(*buffer, lenbuf, sizeof(lenbuf));
|
|
|
|
status = read_packet_remainder(sock, (*buffer)+4, timeout, len);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
return status;
|
|
}
|
|
|
|
*plen = len + 4;
|
|
return NT_STATUS_OK;
|
|
}
|
|
|
|
static NTSTATUS smb2_receive_talloc(TALLOC_CTX *mem_ctx,
|
|
struct smbXsrv_connection *xconn,
|
|
int sock,
|
|
char **buffer, unsigned int timeout,
|
|
size_t *p_unread, bool *p_encrypted,
|
|
size_t *p_len,
|
|
uint32_t *seqnum,
|
|
bool trusted_channel)
|
|
{
|
|
size_t len = 0;
|
|
NTSTATUS status;
|
|
|
|
*p_encrypted = false;
|
|
|
|
status = smb2_receive_raw_talloc(mem_ctx, xconn, sock, buffer, timeout,
|
|
p_unread, &len);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
DEBUG(NT_STATUS_EQUAL(status, NT_STATUS_END_OF_FILE)?5:1,
|
|
("smb2_receive_raw_talloc failed for client %s "
|
|
"read error = %s.\n",
|
|
smbXsrv_connection_dbg(xconn),
|
|
nt_errstr(status)) );
|
|
return status;
|
|
}
|
|
|
|
*p_len = len;
|
|
return NT_STATUS_OK;
|
|
}
|
|
#endif
|
|
|
|
NTSTATUS receive_smb_talloc(TALLOC_CTX *mem_ctx,
|
|
struct smbXsrv_connection *xconn,
|
|
int sock,
|
|
char **buffer, unsigned int timeout,
|
|
size_t *p_unread, bool *p_encrypted,
|
|
size_t *p_len,
|
|
uint32_t *seqnum,
|
|
bool trusted_channel)
|
|
{
|
|
#if defined(WITH_SMB1SERVER)
|
|
return smb1_receive_talloc(mem_ctx, xconn, sock, buffer, timeout,
|
|
p_unread, p_encrypted, p_len, seqnum,
|
|
trusted_channel);
|
|
#else
|
|
return smb2_receive_talloc(mem_ctx, xconn, sock, buffer, timeout,
|
|
p_unread, p_encrypted, p_len, seqnum,
|
|
trusted_channel);
|
|
#endif
|
|
}
|
|
|
|
/****************************************************************************
|
|
Function to delete a sharing violation open message by mid.
|
|
****************************************************************************/
|
|
|
|
void remove_deferred_open_message_smb(struct smbXsrv_connection *xconn,
|
|
uint64_t mid)
|
|
{
|
|
struct smbd_server_connection *sconn = xconn->client->sconn;
|
|
struct pending_message_list *pml;
|
|
|
|
if (sconn->using_smb2) {
|
|
remove_deferred_open_message_smb2(xconn, mid);
|
|
return;
|
|
}
|
|
|
|
for (pml = sconn->deferred_open_queue; pml; pml = pml->next) {
|
|
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(sconn->deferred_open_queue, pml);
|
|
TALLOC_FREE(pml);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void smbd_deferred_open_timer(struct tevent_context *ev,
|
|
struct tevent_timer *te,
|
|
struct timeval _tval,
|
|
void *private_data)
|
|
{
|
|
struct pending_message_list *msg = talloc_get_type(private_data,
|
|
struct pending_message_list);
|
|
struct smbd_server_connection *sconn = msg->sconn;
|
|
struct smbXsrv_connection *xconn = msg->xconn;
|
|
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,
|
|
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 %llu.\n",
|
|
(unsigned long long)mid ));
|
|
|
|
/* Mark the message as processed so this is not
|
|
* re-processed in error. */
|
|
msg->processed = true;
|
|
|
|
process_smb(xconn, 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(sconn, mid);
|
|
if (msg && msg->processed) {
|
|
remove_deferred_open_message_smb(xconn, mid);
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
Move a sharing violation open retry message to the front of the list and
|
|
schedule it for immediate processing.
|
|
****************************************************************************/
|
|
|
|
bool schedule_deferred_open_message_smb(struct smbXsrv_connection *xconn,
|
|
uint64_t mid)
|
|
{
|
|
struct smbd_server_connection *sconn = xconn->client->sconn;
|
|
struct pending_message_list *pml;
|
|
int i = 0;
|
|
|
|
if (sconn->using_smb2) {
|
|
return schedule_deferred_open_message_smb2(xconn, mid);
|
|
}
|
|
|
|
for (pml = sconn->deferred_open_queue; pml; pml = pml->next) {
|
|
uint64_t msg_mid = (uint64_t)SVAL(pml->buf.data,smb_mid);
|
|
|
|
DEBUG(10,("schedule_deferred_open_message_smb: [%d] "
|
|
"msg_mid = %llu\n",
|
|
i++,
|
|
(unsigned long long)msg_mid ));
|
|
|
|
if (mid == msg_mid) {
|
|
struct tevent_timer *te;
|
|
|
|
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 ));
|
|
|
|
/*
|
|
* smbd_deferred_open_timer() calls
|
|
* process_smb() to redispatch the request
|
|
* including the required impersonation.
|
|
*
|
|
* So we can just use the raw tevent_context.
|
|
*/
|
|
te = tevent_add_timer(xconn->client->raw_ev_ctx,
|
|
pml,
|
|
timeval_zero(),
|
|
smbd_deferred_open_timer,
|
|
pml);
|
|
if (!te) {
|
|
DEBUG(10,("schedule_deferred_open_message_smb: "
|
|
"event_add_timed() failed, "
|
|
"skipping mid %llu\n",
|
|
(unsigned long long)msg_mid ));
|
|
}
|
|
|
|
TALLOC_FREE(pml->te);
|
|
pml->te = te;
|
|
DLIST_PROMOTE(sconn->deferred_open_queue, pml);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
DEBUG(10,("schedule_deferred_open_message_smb: failed to "
|
|
"find message mid %llu\n",
|
|
(unsigned long long)mid ));
|
|
|
|
return false;
|
|
}
|
|
|
|
/****************************************************************************
|
|
Return true if this mid is on the deferred queue and was not yet processed.
|
|
****************************************************************************/
|
|
|
|
bool open_was_deferred(struct smbXsrv_connection *xconn, uint64_t mid)
|
|
{
|
|
struct smbd_server_connection *sconn = xconn->client->sconn;
|
|
struct pending_message_list *pml;
|
|
|
|
if (sconn->using_smb2) {
|
|
return open_was_deferred_smb2(xconn, mid);
|
|
}
|
|
|
|
for (pml = sconn->deferred_open_queue; pml; pml = pml->next) {
|
|
if (((uint64_t)SVAL(pml->buf.data,smb_mid)) == mid && !pml->processed) {
|
|
return True;
|
|
}
|
|
}
|
|
return False;
|
|
}
|
|
|
|
/****************************************************************************
|
|
Return the message queued by this mid.
|
|
****************************************************************************/
|
|
|
|
static struct pending_message_list *get_deferred_open_message_smb(
|
|
struct smbd_server_connection *sconn, uint64_t mid)
|
|
{
|
|
struct pending_message_list *pml;
|
|
|
|
for (pml = sconn->deferred_open_queue; pml; pml = pml->next) {
|
|
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,
|
|
struct deferred_open_record **open_rec)
|
|
{
|
|
struct pending_message_list *pml;
|
|
|
|
if (smbreq->sconn->using_smb2) {
|
|
return get_deferred_open_message_state_smb2(smbreq->smb2req,
|
|
p_request_time,
|
|
open_rec);
|
|
}
|
|
|
|
pml = get_deferred_open_message_smb(smbreq->sconn, smbreq->mid);
|
|
if (!pml) {
|
|
return false;
|
|
}
|
|
if (p_request_time) {
|
|
*p_request_time = pml->request_time;
|
|
}
|
|
if (open_rec != NULL) {
|
|
*open_rec = pml->open_rec;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool push_deferred_open_message_smb(struct smb_request *req,
|
|
struct timeval timeout,
|
|
struct file_id id,
|
|
struct deferred_open_record *open_rec)
|
|
{
|
|
#if defined(WITH_SMB1SERVER)
|
|
if (req->smb2req) {
|
|
#endif
|
|
return push_deferred_open_message_smb2(req->smb2req,
|
|
req->request_time,
|
|
timeout,
|
|
id,
|
|
open_rec);
|
|
#if defined(WITH_SMB1SERVER)
|
|
} else {
|
|
return push_deferred_open_message_smb1(req, timeout,
|
|
id, open_rec);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static void construct_smb1_reply_common(uint8_t cmd, const uint8_t *inbuf,
|
|
char *outbuf)
|
|
{
|
|
uint16_t in_flags2 = SVAL(inbuf,smb_flg2);
|
|
uint16_t out_flags2 = common_flags2;
|
|
|
|
out_flags2 |= in_flags2 & FLAGS2_UNICODE_STRINGS;
|
|
out_flags2 |= in_flags2 & FLAGS2_SMB_SECURITY_SIGNATURES;
|
|
out_flags2 |= in_flags2 & FLAGS2_SMB_SECURITY_SIGNATURES_REQUIRED;
|
|
|
|
srv_smb1_set_message(outbuf,0,0,false);
|
|
|
|
SCVAL(outbuf, smb_com, cmd);
|
|
SIVAL(outbuf,smb_rcls,0);
|
|
SCVAL(outbuf,smb_flg, FLAG_REPLY | (CVAL(inbuf,smb_flg) & FLAG_CASELESS_PATHNAMES));
|
|
SSVAL(outbuf,smb_flg2, out_flags2);
|
|
memset(outbuf+smb_pidhigh,'\0',(smb_tid-smb_pidhigh));
|
|
memcpy(outbuf+smb_ss_field, inbuf+smb_ss_field, 8);
|
|
|
|
SSVAL(outbuf,smb_tid,SVAL(inbuf,smb_tid));
|
|
SSVAL(outbuf,smb_pid,SVAL(inbuf,smb_pid));
|
|
SSVAL(outbuf,smb_pidhigh,SVAL(inbuf,smb_pidhigh));
|
|
SSVAL(outbuf,smb_uid,SVAL(inbuf,smb_uid));
|
|
SSVAL(outbuf,smb_mid,SVAL(inbuf,smb_mid));
|
|
}
|
|
|
|
void construct_smb1_reply_common_req(struct smb_request *req, char *outbuf)
|
|
{
|
|
construct_smb1_reply_common(req->cmd, req->inbuf, outbuf);
|
|
}
|
|
|
|
/*******************************************************************
|
|
allocate and initialize a reply packet
|
|
********************************************************************/
|
|
|
|
bool create_smb1_outbuf(TALLOC_CTX *mem_ctx, struct smb_request *req,
|
|
const uint8_t *inbuf, char **outbuf,
|
|
uint8_t num_words, uint32_t num_bytes)
|
|
{
|
|
size_t smb_len = MIN_SMB_SIZE + VWV(num_words) + num_bytes;
|
|
|
|
/*
|
|
* Protect against integer wrap.
|
|
* The SMB layer reply can be up to 0xFFFFFF bytes.
|
|
*/
|
|
if ((num_bytes > 0xffffff) || (smb_len > 0xffffff)) {
|
|
char *msg;
|
|
if (asprintf(&msg, "num_bytes too large: %u",
|
|
(unsigned)num_bytes) == -1) {
|
|
msg = discard_const_p(char, "num_bytes too large");
|
|
}
|
|
smb_panic(msg);
|
|
}
|
|
|
|
/*
|
|
* Here we include the NBT header for now.
|
|
*/
|
|
*outbuf = talloc_array(mem_ctx, char,
|
|
NBT_HDR_SIZE + smb_len);
|
|
if (*outbuf == NULL) {
|
|
return false;
|
|
}
|
|
|
|
construct_smb1_reply_common(req->cmd, inbuf, *outbuf);
|
|
srv_smb1_set_message(*outbuf, num_words, num_bytes, false);
|
|
/*
|
|
* Zero out the word area, the caller has to take care of the bcc area
|
|
* himself
|
|
*/
|
|
if (num_words != 0) {
|
|
memset(*outbuf + (NBT_HDR_SIZE + HDR_VWV), 0, VWV(num_words));
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void reply_smb1_outbuf(struct smb_request *req, uint8_t num_words, uint32_t num_bytes)
|
|
{
|
|
char *outbuf;
|
|
if (!create_smb1_outbuf(req, req, req->inbuf, &outbuf, num_words,
|
|
num_bytes)) {
|
|
smb_panic("could not allocate output buffer\n");
|
|
}
|
|
req->outbuf = (uint8_t *)outbuf;
|
|
}
|
|
|
|
bool valid_smb1_header(const uint8_t *inbuf)
|
|
{
|
|
if (is_encrypted_packet(inbuf)) {
|
|
return true;
|
|
}
|
|
/*
|
|
* 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);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Process an smb from the client
|
|
****************************************************************************/
|
|
|
|
static void process_smb2(struct smbXsrv_connection *xconn,
|
|
uint8_t *inbuf, size_t nread, size_t unread_bytes,
|
|
uint32_t seqnum, bool encrypted,
|
|
struct smb_perfcount_data *deferred_pcd)
|
|
{
|
|
const uint8_t *inpdu = inbuf + NBT_HDR_SIZE;
|
|
size_t pdulen = nread - NBT_HDR_SIZE;
|
|
NTSTATUS status = smbd_smb2_process_negprot(xconn, 0, inpdu, pdulen);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
exit_server_cleanly("SMB2 negprot fail");
|
|
}
|
|
}
|
|
|
|
void process_smb(struct smbXsrv_connection *xconn,
|
|
uint8_t *inbuf, size_t nread, size_t unread_bytes,
|
|
uint32_t seqnum, bool encrypted,
|
|
struct smb_perfcount_data *deferred_pcd)
|
|
{
|
|
struct smbd_server_connection *sconn = xconn->client->sconn;
|
|
int msg_type = CVAL(inbuf,0);
|
|
|
|
DO_PROFILE_INC(request);
|
|
|
|
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",
|
|
sconn->trans_num, (int)nread, (unsigned int)unread_bytes));
|
|
|
|
if (msg_type != NBSSmessage) {
|
|
/*
|
|
* NetBIOS session request, keepalive, etc.
|
|
*/
|
|
reply_special(xconn, (char *)inbuf, nread);
|
|
goto done;
|
|
}
|
|
|
|
#if defined(WITH_SMB1SERVER)
|
|
if (sconn->using_smb2) {
|
|
/* At this point we're not really using smb2,
|
|
* we make the decision here.. */
|
|
if (smbd_is_smb2_header(inbuf, nread)) {
|
|
#endif
|
|
process_smb2(xconn, inbuf, nread, unread_bytes, seqnum,
|
|
encrypted, deferred_pcd);
|
|
return;
|
|
#if defined(WITH_SMB1SERVER)
|
|
}
|
|
if (nread >= smb_size && valid_smb1_header(inbuf)
|
|
&& CVAL(inbuf, smb_com) != 0x72) {
|
|
/* This is a non-negprot SMB1 packet.
|
|
Disable SMB2 from now on. */
|
|
sconn->using_smb2 = false;
|
|
}
|
|
}
|
|
process_smb1(xconn, inbuf, nread, unread_bytes, seqnum, encrypted,
|
|
deferred_pcd);
|
|
#endif
|
|
|
|
done:
|
|
sconn->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 ((sconn->num_requests % 50) == 0 &&
|
|
need_to_check_log_size()) {
|
|
change_to_root_user();
|
|
check_log_size();
|
|
}
|
|
}
|
|
|
|
NTSTATUS smbXsrv_connection_init_tables(struct smbXsrv_connection *conn,
|
|
enum protocol_types protocol)
|
|
{
|
|
NTSTATUS status;
|
|
|
|
conn->protocol = protocol;
|
|
|
|
if (conn->client->session_table != NULL) {
|
|
return NT_STATUS_OK;
|
|
}
|
|
|
|
if (protocol >= PROTOCOL_SMB2_02) {
|
|
status = smb2srv_session_table_init(conn);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
conn->protocol = PROTOCOL_NONE;
|
|
return status;
|
|
}
|
|
|
|
status = smb2srv_open_table_init(conn);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
conn->protocol = PROTOCOL_NONE;
|
|
return status;
|
|
}
|
|
} else {
|
|
#if defined(WITH_SMB1SERVER)
|
|
status = smb1srv_session_table_init(conn);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
conn->protocol = PROTOCOL_NONE;
|
|
return status;
|
|
}
|
|
|
|
status = smb1srv_tcon_table_init(conn);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
conn->protocol = PROTOCOL_NONE;
|
|
return status;
|
|
}
|
|
|
|
status = smb1srv_open_table_init(conn);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
conn->protocol = PROTOCOL_NONE;
|
|
return status;
|
|
}
|
|
#else
|
|
conn->protocol = PROTOCOL_NONE;
|
|
return NT_STATUS_INVALID_NETWORK_RESPONSE;
|
|
#endif
|
|
}
|
|
|
|
set_Protocol(protocol);
|
|
return NT_STATUS_OK;
|
|
}
|
|
|
|
/**
|
|
* Create a debug string for the connection
|
|
*
|
|
* This is allocated to talloc_tos() or a string constant
|
|
* in certain corner cases. The returned string should
|
|
* hence not be free'd directly but only via the talloc stack.
|
|
*/
|
|
const char *smbXsrv_connection_dbg(const struct smbXsrv_connection *xconn)
|
|
{
|
|
const char *ret;
|
|
char *addr;
|
|
/*
|
|
* TODO: this can be improved later
|
|
* maybe including the client guid or more
|
|
*/
|
|
addr = tsocket_address_string(xconn->remote_address, talloc_tos());
|
|
if (addr == NULL) {
|
|
return "<tsocket_address_string() failed>";
|
|
}
|
|
|
|
ret = talloc_asprintf(talloc_tos(), "ptr=%p,id=%llu,addr=%s",
|
|
xconn, (unsigned long long)xconn->channel_id, addr);
|
|
TALLOC_FREE(addr);
|
|
if (ret == NULL) {
|
|
return "<talloc_asprintf() failed>";
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Initialize a struct smb_request from an inbuf
|
|
*/
|
|
|
|
bool init_smb1_request(struct smb_request *req,
|
|
struct smbd_server_connection *sconn,
|
|
struct smbXsrv_connection *xconn,
|
|
const uint8_t *inbuf,
|
|
size_t unread_bytes, bool encrypted,
|
|
uint32_t seqnum)
|
|
{
|
|
struct smbXsrv_tcon *tcon;
|
|
NTSTATUS status;
|
|
NTTIME now;
|
|
size_t req_size = smb_len(inbuf) + 4;
|
|
|
|
/* Ensure we have at least smb_size bytes. */
|
|
if (req_size < smb_size) {
|
|
DEBUG(0,("init_smb1_request: invalid request size %u\n",
|
|
(unsigned int)req_size ));
|
|
return false;
|
|
}
|
|
|
|
req->request_time = timeval_current();
|
|
now = timeval_to_nttime(&req->request_time);
|
|
|
|
req->cmd = CVAL(inbuf, smb_com);
|
|
req->flags2 = SVAL(inbuf, smb_flg2);
|
|
req->smbpid = SVAL(inbuf, smb_pid);
|
|
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->vwv = (const uint16_t *)(inbuf+smb_vwv);
|
|
req->buflen = smb_buflen(inbuf);
|
|
req->buf = (const uint8_t *)smb_buf_const(inbuf);
|
|
req->unread_bytes = unread_bytes;
|
|
req->encrypted = encrypted;
|
|
req->sconn = sconn;
|
|
req->xconn = xconn;
|
|
req->conn = NULL;
|
|
if (xconn != NULL) {
|
|
status = smb1srv_tcon_lookup(xconn, req->tid, now, &tcon);
|
|
if (NT_STATUS_IS_OK(status)) {
|
|
req->conn = tcon->compat;
|
|
}
|
|
}
|
|
req->chain_fsp = NULL;
|
|
req->smb2req = NULL;
|
|
req->chain = NULL;
|
|
req->posix_pathnames = lp_posix_pathnames();
|
|
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) {
|
|
DEBUG(0,("init_smb1_request: invalid wct number %u (size %u)\n",
|
|
(unsigned int)req->wct,
|
|
(unsigned int)req_size));
|
|
return false;
|
|
}
|
|
/* Ensure bcc is correct. */
|
|
if (((const uint8_t *)smb_buf_const(inbuf)) + req->buflen > inbuf + req_size) {
|
|
DEBUG(0,("init_smb1_request: invalid bcc number %u "
|
|
"(wct = %u, size %u)\n",
|
|
(unsigned int)req->buflen,
|
|
(unsigned int)req->wct,
|
|
(unsigned int)req_size));
|
|
return false;
|
|
}
|
|
|
|
req->outbuf = NULL;
|
|
return true;
|
|
}
|
|
|
|
/****************************************************************************
|
|
Construct a reply to the incoming packet.
|
|
****************************************************************************/
|
|
|
|
static void construct_reply_smb1negprot(struct smbXsrv_connection *xconn,
|
|
char *inbuf, int size,
|
|
size_t unread_bytes)
|
|
{
|
|
struct smbd_server_connection *sconn = xconn->client->sconn;
|
|
struct smb_request *req;
|
|
NTSTATUS status;
|
|
|
|
if (!(req = talloc(talloc_tos(), struct smb_request))) {
|
|
smb_panic("could not allocate smb_request");
|
|
}
|
|
|
|
if (!init_smb1_request(req, sconn, xconn, (uint8_t *)inbuf, unread_bytes,
|
|
false, 0)) {
|
|
exit_server_cleanly("Invalid SMB request");
|
|
}
|
|
|
|
req->inbuf = (uint8_t *)talloc_move(req, &inbuf);
|
|
|
|
status = smb2_multi_protocol_reply_negprot(req);
|
|
if (req->outbuf == NULL) {
|
|
/*
|
|
* req->outbuf == NULL means we bootstrapped into SMB2.
|
|
*/
|
|
return;
|
|
}
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
if (!smb1_srv_send(req->xconn,
|
|
(char *)req->outbuf,
|
|
true,
|
|
req->seqnum + 1,
|
|
IS_CONN_ENCRYPTED(req->conn) ||
|
|
req->encrypted)) {
|
|
exit_server_cleanly("construct_reply_smb1negprot: "
|
|
"smb1_srv_send failed.");
|
|
}
|
|
TALLOC_FREE(req);
|
|
} else {
|
|
/* This code path should only *ever* bootstrap into SMB2. */
|
|
exit_server_cleanly("Internal error SMB1negprot didn't reply "
|
|
"with an SMB2 packet");
|
|
}
|
|
}
|
|
|
|
static void smbd_server_connection_write_handler(
|
|
struct smbXsrv_connection *xconn)
|
|
{
|
|
/* TODO: make write nonblocking */
|
|
}
|
|
|
|
static void smbd_smb2_server_connection_read_handler(
|
|
struct smbXsrv_connection *xconn, int fd)
|
|
{
|
|
char lenbuf[NBT_HDR_SIZE];
|
|
size_t len = 0;
|
|
uint8_t *buffer = NULL;
|
|
size_t bufferlen = 0;
|
|
NTSTATUS status;
|
|
uint8_t msg_type = 0;
|
|
|
|
/* Read the first 4 bytes - contains length of remainder. */
|
|
status = read_smb_length_return_keepalive(fd, lenbuf, 0, &len);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
exit_server_cleanly("failed to receive request length");
|
|
return;
|
|
}
|
|
|
|
/* Integer wrap check. */
|
|
if (len + NBT_HDR_SIZE < len) {
|
|
exit_server_cleanly("Invalid length on initial request");
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* The +4 here can't wrap, we've checked the length above already.
|
|
*/
|
|
bufferlen = len+NBT_HDR_SIZE;
|
|
|
|
buffer = talloc_array(talloc_tos(), uint8_t, bufferlen);
|
|
if (buffer == NULL) {
|
|
DBG_ERR("Could not allocate request inbuf of length %zu\n",
|
|
bufferlen);
|
|
exit_server_cleanly("talloc fail");
|
|
return;
|
|
}
|
|
|
|
/* Copy the NBT_HDR_SIZE length. */
|
|
memcpy(buffer, lenbuf, sizeof(lenbuf));
|
|
|
|
status = read_packet_remainder(fd, (char *)buffer+NBT_HDR_SIZE, 0, len);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
exit_server_cleanly("Failed to read remainder of initial request");
|
|
return;
|
|
}
|
|
|
|
/* Check the message type. */
|
|
msg_type = PULL_LE_U8(buffer,0);
|
|
if (msg_type == NBSSrequest) {
|
|
/*
|
|
* clients can send this request before
|
|
* bootstrapping into SMB2. Cope with this
|
|
* message only, don't allow any other strange
|
|
* NBSS types.
|
|
*/
|
|
reply_special(xconn, (char *)buffer, bufferlen);
|
|
xconn->client->sconn->num_requests++;
|
|
return;
|
|
}
|
|
|
|
/* Only a 'normal' message type allowed now. */
|
|
if (msg_type != NBSSmessage) {
|
|
DBG_ERR("Invalid message type %d\n", msg_type);
|
|
exit_server_cleanly("Invalid message type for initial request");
|
|
return;
|
|
}
|
|
|
|
/* Could this be an SMB1 negprot bootstrap into SMB2 ? */
|
|
if (bufferlen < smb_size) {
|
|
exit_server_cleanly("Invalid initial SMB1 or SMB2 packet");
|
|
return;
|
|
}
|
|
if (valid_smb1_header(buffer)) {
|
|
/* Can *only* allow an SMB1 negprot here. */
|
|
uint8_t cmd = PULL_LE_U8(buffer, smb_com);
|
|
if (cmd != SMBnegprot) {
|
|
DBG_ERR("Incorrect SMB1 command 0x%hhx, "
|
|
"should be SMBnegprot (0x72)\n",
|
|
cmd);
|
|
exit_server_cleanly("Invalid initial SMB1 packet");
|
|
}
|
|
/* Minimal process_smb(). */
|
|
show_msg((char *)buffer);
|
|
construct_reply_smb1negprot(xconn, (char *)buffer,
|
|
bufferlen, 0);
|
|
xconn->client->sconn->trans_num++;
|
|
xconn->client->sconn->num_requests++;
|
|
return;
|
|
|
|
} else if (!smbd_is_smb2_header(buffer, bufferlen)) {
|
|
exit_server_cleanly("Invalid initial SMB2 packet");
|
|
return;
|
|
}
|
|
|
|
/* Here we know we're a valid SMB2 packet. */
|
|
|
|
/*
|
|
* Point at the start of the SMB2 PDU.
|
|
* len is the length of the SMB2 PDU.
|
|
*/
|
|
|
|
status = smbd_smb2_process_negprot(xconn,
|
|
0,
|
|
(const uint8_t *)buffer+NBT_HDR_SIZE,
|
|
len);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
exit_server_cleanly("SMB2 negprot fail");
|
|
}
|
|
return;
|
|
}
|
|
|
|
static void smbd_server_connection_handler(struct tevent_context *ev,
|
|
struct tevent_fd *fde,
|
|
uint16_t flags,
|
|
void *private_data)
|
|
{
|
|
struct smbXsrv_connection *xconn =
|
|
talloc_get_type_abort(private_data,
|
|
struct smbXsrv_connection);
|
|
|
|
if (!NT_STATUS_IS_OK(xconn->transport.status)) {
|
|
/*
|
|
* we're not supposed to do any io
|
|
*/
|
|
TEVENT_FD_NOT_READABLE(xconn->transport.fde);
|
|
TEVENT_FD_NOT_WRITEABLE(xconn->transport.fde);
|
|
return;
|
|
}
|
|
|
|
if (flags & TEVENT_FD_WRITE) {
|
|
smbd_server_connection_write_handler(xconn);
|
|
return;
|
|
}
|
|
if (flags & TEVENT_FD_READ) {
|
|
#if defined(WITH_SMB1SERVER)
|
|
if (lp_server_min_protocol() > PROTOCOL_NT1) {
|
|
#endif
|
|
smbd_smb2_server_connection_read_handler(xconn,
|
|
xconn->transport.sock);
|
|
#if defined(WITH_SMB1SERVER)
|
|
} else {
|
|
smbd_smb1_server_connection_read_handler(xconn,
|
|
xconn->transport.sock);
|
|
}
|
|
#endif
|
|
return;
|
|
}
|
|
}
|
|
|
|
struct smbd_release_ip_state {
|
|
struct smbXsrv_connection *xconn;
|
|
struct tevent_immediate *im;
|
|
char addr[INET6_ADDRSTRLEN];
|
|
};
|
|
|
|
static void smbd_release_ip_immediate(struct tevent_context *ctx,
|
|
struct tevent_immediate *im,
|
|
void *private_data)
|
|
{
|
|
struct smbd_release_ip_state *state =
|
|
talloc_get_type_abort(private_data,
|
|
struct smbd_release_ip_state);
|
|
struct smbXsrv_connection *xconn = state->xconn;
|
|
|
|
if (!NT_STATUS_EQUAL(xconn->transport.status, NT_STATUS_ADDRESS_CLOSED)) {
|
|
/*
|
|
* smbd_server_connection_terminate() already triggered ?
|
|
*/
|
|
return;
|
|
}
|
|
|
|
smbd_server_connection_terminate(xconn, "CTDB_SRVID_RELEASE_IP");
|
|
}
|
|
|
|
/****************************************************************************
|
|
received when we should release a specific IP
|
|
****************************************************************************/
|
|
static int release_ip(struct tevent_context *ev,
|
|
uint32_t src_vnn, uint32_t dst_vnn,
|
|
uint64_t dst_srvid,
|
|
const uint8_t *msg, size_t msglen,
|
|
void *private_data)
|
|
{
|
|
struct smbd_release_ip_state *state =
|
|
talloc_get_type_abort(private_data,
|
|
struct smbd_release_ip_state);
|
|
struct smbXsrv_connection *xconn = state->xconn;
|
|
const char *ip;
|
|
const char *addr = state->addr;
|
|
const char *p = addr;
|
|
|
|
if (msglen == 0) {
|
|
return 0;
|
|
}
|
|
if (msg[msglen-1] != '\0') {
|
|
return 0;
|
|
}
|
|
|
|
ip = (const char *)msg;
|
|
|
|
if (!NT_STATUS_IS_OK(xconn->transport.status)) {
|
|
/* avoid recursion */
|
|
return 0;
|
|
}
|
|
|
|
if (strncmp("::ffff:", addr, 7) == 0) {
|
|
p = addr + 7;
|
|
}
|
|
|
|
DEBUG(10, ("Got release IP message for %s, "
|
|
"our address is %s\n", ip, p));
|
|
|
|
if ((strcmp(p, ip) == 0) || ((p != addr) && strcmp(addr, ip) == 0)) {
|
|
DEBUG(0,("Got release IP message for our IP %s - exiting immediately\n",
|
|
ip));
|
|
/*
|
|
* With SMB2 we should do a clean disconnect,
|
|
* the previous_session_id in the session setup
|
|
* will cleanup the old session, tcons and opens.
|
|
*
|
|
* A clean disconnect is needed in order to support
|
|
* durable handles.
|
|
*
|
|
* Note: typically this is never triggered
|
|
* as we got a TCP RST (triggered by ctdb event scripts)
|
|
* before we get CTDB_SRVID_RELEASE_IP.
|
|
*
|
|
* We used to call _exit(1) here, but as this was mostly never
|
|
* triggered and has implication on our process model,
|
|
* we can just use smbd_server_connection_terminate()
|
|
* (also for SMB1).
|
|
*
|
|
* We don't call smbd_server_connection_terminate() directly
|
|
* as we might be called from within ctdbd_migrate(),
|
|
* we need to defer our action to the next event loop
|
|
*/
|
|
tevent_schedule_immediate(state->im,
|
|
xconn->client->raw_ev_ctx,
|
|
smbd_release_ip_immediate,
|
|
state);
|
|
|
|
/*
|
|
* Make sure we don't get any io on the connection.
|
|
*/
|
|
xconn->transport.status = NT_STATUS_ADDRESS_CLOSED;
|
|
return EADDRNOTAVAIL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int match_cluster_movable_ip(uint32_t total_ip_count,
|
|
const struct sockaddr_storage *ip,
|
|
bool is_movable_ip,
|
|
void *private_data)
|
|
{
|
|
const struct sockaddr_storage *srv = private_data;
|
|
struct samba_sockaddr pub_ip = {
|
|
.u = {
|
|
.ss = *ip,
|
|
},
|
|
};
|
|
struct samba_sockaddr srv_ip = {
|
|
.u = {
|
|
.ss = *srv,
|
|
},
|
|
};
|
|
|
|
if (is_movable_ip && sockaddr_equal(&pub_ip.u.sa, &srv_ip.u.sa)) {
|
|
return EADDRNOTAVAIL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static NTSTATUS smbd_register_ips(struct smbXsrv_connection *xconn,
|
|
struct sockaddr_storage *srv,
|
|
struct sockaddr_storage *clnt)
|
|
{
|
|
struct smbd_release_ip_state *state;
|
|
struct ctdbd_connection *cconn;
|
|
int ret;
|
|
|
|
cconn = messaging_ctdb_connection();
|
|
if (cconn == NULL) {
|
|
return NT_STATUS_NO_MEMORY;
|
|
}
|
|
|
|
state = talloc_zero(xconn, struct smbd_release_ip_state);
|
|
if (state == NULL) {
|
|
return NT_STATUS_NO_MEMORY;
|
|
}
|
|
state->xconn = xconn;
|
|
state->im = tevent_create_immediate(state);
|
|
if (state->im == NULL) {
|
|
return NT_STATUS_NO_MEMORY;
|
|
}
|
|
if (print_sockaddr(state->addr, sizeof(state->addr), srv) == NULL) {
|
|
return NT_STATUS_NO_MEMORY;
|
|
}
|
|
|
|
if (xconn->client->server_multi_channel_enabled) {
|
|
ret = ctdbd_public_ip_foreach(cconn,
|
|
match_cluster_movable_ip,
|
|
srv);
|
|
if (ret == EADDRNOTAVAIL) {
|
|
xconn->has_cluster_movable_ip = true;
|
|
DBG_DEBUG("cluster movable IP on %s\n",
|
|
smbXsrv_connection_dbg(xconn));
|
|
} else if (ret != 0) {
|
|
DBG_ERR("failed to iterate cluster IPs: %s\n",
|
|
strerror(ret));
|
|
return NT_STATUS_INTERNAL_ERROR;
|
|
}
|
|
}
|
|
|
|
ret = ctdbd_register_ips(cconn, srv, clnt, release_ip, state);
|
|
if (ret != 0) {
|
|
return map_nt_error_from_unix(ret);
|
|
}
|
|
return NT_STATUS_OK;
|
|
}
|
|
|
|
static int smbXsrv_connection_destructor(struct smbXsrv_connection *xconn)
|
|
{
|
|
DBG_DEBUG("xconn[%s]\n", smbXsrv_connection_dbg(xconn));
|
|
return 0;
|
|
}
|
|
|
|
NTSTATUS smbd_add_connection(struct smbXsrv_client *client, int sock_fd,
|
|
NTTIME now, struct smbXsrv_connection **_xconn)
|
|
{
|
|
TALLOC_CTX *frame = talloc_stackframe();
|
|
struct smbXsrv_connection *xconn;
|
|
struct sockaddr_storage ss_srv;
|
|
void *sp_srv = (void *)&ss_srv;
|
|
struct sockaddr *sa_srv = (struct sockaddr *)sp_srv;
|
|
struct sockaddr_storage ss_clnt;
|
|
void *sp_clnt = (void *)&ss_clnt;
|
|
struct sockaddr *sa_clnt = (struct sockaddr *)sp_clnt;
|
|
socklen_t sa_socklen;
|
|
struct tsocket_address *local_address = NULL;
|
|
struct tsocket_address *remote_address = NULL;
|
|
const char *remaddr = NULL;
|
|
char *p;
|
|
const char *rhost = NULL;
|
|
int ret;
|
|
int tmp;
|
|
|
|
*_xconn = NULL;
|
|
|
|
DO_PROFILE_INC(connect);
|
|
|
|
xconn = talloc_zero(client, struct smbXsrv_connection);
|
|
if (xconn == NULL) {
|
|
DEBUG(0,("talloc_zero(struct smbXsrv_connection)\n"));
|
|
TALLOC_FREE(frame);
|
|
return NT_STATUS_NO_MEMORY;
|
|
}
|
|
talloc_set_destructor(xconn, smbXsrv_connection_destructor);
|
|
talloc_steal(frame, xconn);
|
|
xconn->client = client;
|
|
xconn->connect_time = now;
|
|
if (client->next_channel_id != 0) {
|
|
xconn->channel_id = client->next_channel_id++;
|
|
}
|
|
|
|
xconn->transport.sock = sock_fd;
|
|
#if defined(WITH_SMB1SERVER)
|
|
smbd_echo_init(xconn);
|
|
#endif
|
|
xconn->protocol = PROTOCOL_NONE;
|
|
|
|
/* Ensure child is set to blocking mode */
|
|
set_blocking(sock_fd,True);
|
|
|
|
set_socket_options(sock_fd, "SO_KEEPALIVE");
|
|
set_socket_options(sock_fd, lp_socket_options());
|
|
|
|
sa_socklen = sizeof(ss_clnt);
|
|
ret = getpeername(sock_fd, sa_clnt, &sa_socklen);
|
|
if (ret != 0) {
|
|
int saved_errno = errno;
|
|
int level = (errno == ENOTCONN)?2:0;
|
|
DEBUG(level,("getpeername() failed - %s\n",
|
|
strerror(saved_errno)));
|
|
TALLOC_FREE(frame);
|
|
return map_nt_error_from_unix_common(saved_errno);
|
|
}
|
|
ret = tsocket_address_bsd_from_sockaddr(xconn,
|
|
sa_clnt, sa_socklen,
|
|
&remote_address);
|
|
if (ret != 0) {
|
|
int saved_errno = errno;
|
|
DEBUG(0,("%s: tsocket_address_bsd_from_sockaddr remote failed - %s\n",
|
|
__location__, strerror(saved_errno)));
|
|
TALLOC_FREE(frame);
|
|
return map_nt_error_from_unix_common(saved_errno);
|
|
}
|
|
|
|
sa_socklen = sizeof(ss_srv);
|
|
ret = getsockname(sock_fd, sa_srv, &sa_socklen);
|
|
if (ret != 0) {
|
|
int saved_errno = errno;
|
|
int level = (errno == ENOTCONN)?2:0;
|
|
DEBUG(level,("getsockname() failed - %s\n",
|
|
strerror(saved_errno)));
|
|
TALLOC_FREE(frame);
|
|
return map_nt_error_from_unix_common(saved_errno);
|
|
}
|
|
ret = tsocket_address_bsd_from_sockaddr(xconn,
|
|
sa_srv, sa_socklen,
|
|
&local_address);
|
|
if (ret != 0) {
|
|
int saved_errno = errno;
|
|
DEBUG(0,("%s: tsocket_address_bsd_from_sockaddr remote failed - %s\n",
|
|
__location__, strerror(saved_errno)));
|
|
TALLOC_FREE(frame);
|
|
return map_nt_error_from_unix_common(saved_errno);
|
|
}
|
|
|
|
if (tsocket_address_is_inet(remote_address, "ip")) {
|
|
remaddr = tsocket_address_inet_addr_string(remote_address,
|
|
talloc_tos());
|
|
if (remaddr == NULL) {
|
|
DEBUG(0,("%s: tsocket_address_inet_addr_string remote failed - %s\n",
|
|
__location__, strerror(errno)));
|
|
TALLOC_FREE(frame);
|
|
return NT_STATUS_NO_MEMORY;
|
|
}
|
|
} else {
|
|
remaddr = "0.0.0.0";
|
|
}
|
|
|
|
/*
|
|
* 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.
|
|
*/
|
|
|
|
ret = get_remote_hostname(remote_address,
|
|
&p, talloc_tos());
|
|
if (ret < 0) {
|
|
int saved_errno = errno;
|
|
DEBUG(0,("%s: get_remote_hostname failed - %s\n",
|
|
__location__, strerror(saved_errno)));
|
|
TALLOC_FREE(frame);
|
|
return map_nt_error_from_unix_common(saved_errno);
|
|
}
|
|
rhost = p;
|
|
if (strequal(rhost, "UNKNOWN")) {
|
|
rhost = remaddr;
|
|
}
|
|
|
|
xconn->local_address = local_address;
|
|
xconn->remote_address = remote_address;
|
|
xconn->remote_hostname = talloc_strdup(xconn, rhost);
|
|
if (xconn->remote_hostname == NULL) {
|
|
return NT_STATUS_NO_MEMORY;
|
|
}
|
|
|
|
if (!srv_init_signing(xconn)) {
|
|
DEBUG(0, ("Failed to init smb_signing\n"));
|
|
TALLOC_FREE(frame);
|
|
return NT_STATUS_INTERNAL_ERROR;
|
|
}
|
|
|
|
if (!allow_access(lp_hosts_deny(-1), lp_hosts_allow(-1),
|
|
xconn->remote_hostname,
|
|
remaddr)) {
|
|
DEBUG( 1, ("Connection denied from %s to %s\n",
|
|
tsocket_address_string(remote_address, talloc_tos()),
|
|
tsocket_address_string(local_address, talloc_tos())));
|
|
|
|
/*
|
|
* We return a valid xconn
|
|
* so that the caller can return an error message
|
|
* to the client
|
|
*/
|
|
DLIST_ADD_END(client->connections, xconn);
|
|
talloc_steal(client, xconn);
|
|
|
|
*_xconn = xconn;
|
|
TALLOC_FREE(frame);
|
|
return NT_STATUS_NETWORK_ACCESS_DENIED;
|
|
}
|
|
|
|
DEBUG(10, ("Connection allowed from %s to %s\n",
|
|
tsocket_address_string(remote_address, talloc_tos()),
|
|
tsocket_address_string(local_address, talloc_tos())));
|
|
|
|
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.
|
|
*/
|
|
NTSTATUS status;
|
|
|
|
status = smbd_register_ips(xconn, &ss_srv, &ss_clnt);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
DEBUG(0, ("ctdbd_register_ips failed: %s\n",
|
|
nt_errstr(status)));
|
|
}
|
|
}
|
|
|
|
tmp = lp_max_xmit();
|
|
tmp = MAX(tmp, SMB_BUFFER_SIZE_MIN);
|
|
tmp = MIN(tmp, SMB_BUFFER_SIZE_MAX);
|
|
|
|
#if defined(WITH_SMB1SERVER)
|
|
xconn->smb1.negprot.max_recv = tmp;
|
|
|
|
xconn->smb1.sessions.done_sesssetup = false;
|
|
xconn->smb1.sessions.max_send = SMB_BUFFER_SIZE_MAX;
|
|
#endif
|
|
|
|
xconn->transport.fde = tevent_add_fd(client->raw_ev_ctx,
|
|
xconn,
|
|
sock_fd,
|
|
TEVENT_FD_READ,
|
|
smbd_server_connection_handler,
|
|
xconn);
|
|
if (!xconn->transport.fde) {
|
|
TALLOC_FREE(frame);
|
|
return NT_STATUS_NO_MEMORY;
|
|
}
|
|
tevent_fd_set_auto_close(xconn->transport.fde);
|
|
|
|
/* for now we only have one connection */
|
|
DLIST_ADD_END(client->connections, xconn);
|
|
talloc_steal(client, xconn);
|
|
|
|
*_xconn = xconn;
|
|
TALLOC_FREE(frame);
|
|
return NT_STATUS_OK;
|
|
}
|
|
|
|
static bool uid_in_use(struct auth_session_info *session_info,
|
|
uid_t uid)
|
|
{
|
|
if (session_info->unix_token->uid == uid) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static bool gid_in_use(struct auth_session_info *session_info,
|
|
gid_t gid)
|
|
{
|
|
uint32_t i;
|
|
struct security_unix_token *utok = NULL;
|
|
|
|
utok = session_info->unix_token;
|
|
if (utok->gid == gid) {
|
|
return true;
|
|
}
|
|
|
|
for(i = 0; i < utok->ngroups; i++) {
|
|
if (utok->groups[i] == gid) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static bool sid_in_use(struct auth_session_info *session_info,
|
|
const struct dom_sid *psid)
|
|
{
|
|
struct security_token *tok = NULL;
|
|
|
|
tok = session_info->security_token;
|
|
if (tok == NULL) {
|
|
/*
|
|
* Not sure session_info->security_token can
|
|
* ever be NULL. This check might be not
|
|
* necessary.
|
|
*/
|
|
return false;
|
|
}
|
|
if (security_token_has_sid(tok, psid)) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
struct id_in_use_state {
|
|
const struct id_cache_ref *id;
|
|
bool match;
|
|
};
|
|
|
|
static int id_in_use_cb(struct smbXsrv_session *session,
|
|
void *private_data)
|
|
{
|
|
struct id_in_use_state *state = (struct id_in_use_state *)
|
|
private_data;
|
|
struct auth_session_info *session_info =
|
|
session->global->auth_session_info;
|
|
|
|
switch(state->id->type) {
|
|
case UID:
|
|
state->match = uid_in_use(session_info, state->id->id.uid);
|
|
break;
|
|
case GID:
|
|
state->match = gid_in_use(session_info, state->id->id.gid);
|
|
break;
|
|
case SID:
|
|
state->match = sid_in_use(session_info, &state->id->id.sid);
|
|
break;
|
|
default:
|
|
state->match = false;
|
|
break;
|
|
}
|
|
if (state->match) {
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static bool id_in_use(struct smbd_server_connection *sconn,
|
|
const struct id_cache_ref *id)
|
|
{
|
|
struct id_in_use_state state;
|
|
NTSTATUS status;
|
|
|
|
state = (struct id_in_use_state) {
|
|
.id = id,
|
|
.match = false,
|
|
};
|
|
|
|
status = smbXsrv_session_local_traverse(sconn->client,
|
|
id_in_use_cb,
|
|
&state);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
return false;
|
|
}
|
|
|
|
return state.match;
|
|
}
|
|
|
|
/****************************************************************************
|
|
Check if services need reloading.
|
|
****************************************************************************/
|
|
|
|
static void check_reload(struct smbd_server_connection *sconn, time_t t)
|
|
{
|
|
|
|
if (last_smb_conf_reload_time == 0) {
|
|
last_smb_conf_reload_time = t;
|
|
}
|
|
|
|
if (t >= last_smb_conf_reload_time+SMBD_RELOAD_CHECK) {
|
|
reload_services(sconn, conn_snum_used, true);
|
|
last_smb_conf_reload_time = t;
|
|
}
|
|
}
|
|
|
|
static void msg_kill_client_ip(struct messaging_context *msg_ctx,
|
|
void *private_data, uint32_t msg_type,
|
|
struct server_id server_id, DATA_BLOB *data)
|
|
{
|
|
struct smbd_server_connection *sconn = talloc_get_type_abort(
|
|
private_data, struct smbd_server_connection);
|
|
const char *ip = (char *) data->data;
|
|
char *client_ip;
|
|
|
|
DBG_DEBUG("Got kill request for client IP %s\n", ip);
|
|
|
|
client_ip = tsocket_address_inet_addr_string(sconn->remote_address,
|
|
talloc_tos());
|
|
if (client_ip == NULL) {
|
|
return;
|
|
}
|
|
|
|
if (strequal(ip, client_ip)) {
|
|
DBG_WARNING("Got kill client message for %s - "
|
|
"exiting immediately\n", ip);
|
|
exit_server_cleanly("Forced disconnect for client");
|
|
}
|
|
|
|
TALLOC_FREE(client_ip);
|
|
}
|
|
|
|
/*
|
|
* Do the recurring check if we're idle
|
|
*/
|
|
static bool deadtime_fn(const struct timeval *now, void *private_data)
|
|
{
|
|
struct smbd_server_connection *sconn =
|
|
(struct smbd_server_connection *)private_data;
|
|
|
|
if ((conn_num_open(sconn) == 0)
|
|
|| (conn_idle_all(sconn, now->tv_sec))) {
|
|
DEBUG( 2, ( "Closing idle connection\n" ) );
|
|
messaging_send(sconn->msg_ctx,
|
|
messaging_server_id(sconn->msg_ctx),
|
|
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)
|
|
{
|
|
struct smbd_server_connection *sconn = talloc_get_type_abort(
|
|
private_data, struct smbd_server_connection);
|
|
|
|
DEBUG(5, ("housekeeping\n"));
|
|
|
|
change_to_root_user();
|
|
|
|
/* check if we need to reload services */
|
|
check_reload(sconn, time_mono(NULL));
|
|
|
|
/*
|
|
* Force a log file check.
|
|
*/
|
|
force_check_log_size();
|
|
check_log_size();
|
|
return true;
|
|
}
|
|
|
|
static void smbd_sig_term_handler(struct tevent_context *ev,
|
|
struct tevent_signal *se,
|
|
int signum,
|
|
int count,
|
|
void *siginfo,
|
|
void *private_data)
|
|
{
|
|
exit_server_cleanly("termination signal");
|
|
}
|
|
|
|
static void smbd_setup_sig_term_handler(struct smbd_server_connection *sconn)
|
|
{
|
|
struct tevent_signal *se;
|
|
|
|
se = tevent_add_signal(sconn->ev_ctx,
|
|
sconn,
|
|
SIGTERM, 0,
|
|
smbd_sig_term_handler,
|
|
sconn);
|
|
if (!se) {
|
|
exit_server("failed to setup SIGTERM handler");
|
|
}
|
|
}
|
|
|
|
static void smbd_sig_hup_handler(struct tevent_context *ev,
|
|
struct tevent_signal *se,
|
|
int signum,
|
|
int count,
|
|
void *siginfo,
|
|
void *private_data)
|
|
{
|
|
struct smbd_server_connection *sconn =
|
|
talloc_get_type_abort(private_data,
|
|
struct smbd_server_connection);
|
|
|
|
change_to_root_user();
|
|
DEBUG(1,("Reloading services after SIGHUP\n"));
|
|
reload_services(sconn, conn_snum_used, false);
|
|
}
|
|
|
|
static void smbd_setup_sig_hup_handler(struct smbd_server_connection *sconn)
|
|
{
|
|
struct tevent_signal *se;
|
|
|
|
se = tevent_add_signal(sconn->ev_ctx,
|
|
sconn,
|
|
SIGHUP, 0,
|
|
smbd_sig_hup_handler,
|
|
sconn);
|
|
if (!se) {
|
|
exit_server("failed to setup SIGHUP handler");
|
|
}
|
|
}
|
|
|
|
static void smbd_conf_updated(struct messaging_context *msg,
|
|
void *private_data,
|
|
uint32_t msg_type,
|
|
struct server_id server_id,
|
|
DATA_BLOB *data)
|
|
{
|
|
struct smbd_server_connection *sconn =
|
|
talloc_get_type_abort(private_data,
|
|
struct smbd_server_connection);
|
|
|
|
DEBUG(10,("smbd_conf_updated: Got message saying smb.conf was "
|
|
"updated. Reloading.\n"));
|
|
change_to_root_user();
|
|
reload_services(sconn, conn_snum_used, false);
|
|
}
|
|
|
|
static void smbd_id_cache_kill(struct messaging_context *msg_ctx,
|
|
void *private_data,
|
|
uint32_t msg_type,
|
|
struct server_id server_id,
|
|
DATA_BLOB* data)
|
|
{
|
|
const char *msg = (data && data->data)
|
|
? (const char *)data->data : "<NULL>";
|
|
struct id_cache_ref id;
|
|
struct smbd_server_connection *sconn =
|
|
talloc_get_type_abort(private_data,
|
|
struct smbd_server_connection);
|
|
|
|
if (!id_cache_ref_parse(msg, &id)) {
|
|
DEBUG(0, ("Invalid ?ID: %s\n", msg));
|
|
return;
|
|
}
|
|
|
|
if (id_in_use(sconn, &id)) {
|
|
exit_server_cleanly(msg);
|
|
}
|
|
id_cache_delete_from_cache(&id);
|
|
}
|
|
|
|
struct smbd_tevent_trace_state {
|
|
struct tevent_context *ev;
|
|
TALLOC_CTX *frame;
|
|
SMBPROFILE_BASIC_ASYNC_STATE(profile_idle);
|
|
};
|
|
|
|
static inline void smbd_tevent_trace_callback_before_loop_once(
|
|
struct smbd_tevent_trace_state *state)
|
|
{
|
|
talloc_free(state->frame);
|
|
state->frame = talloc_stackframe_pool(8192);
|
|
}
|
|
|
|
static inline void smbd_tevent_trace_callback_after_loop_once(
|
|
struct smbd_tevent_trace_state *state)
|
|
{
|
|
TALLOC_FREE(state->frame);
|
|
}
|
|
|
|
static void smbd_tevent_trace_callback(enum tevent_trace_point point,
|
|
void *private_data)
|
|
{
|
|
struct smbd_tevent_trace_state *state =
|
|
(struct smbd_tevent_trace_state *)private_data;
|
|
|
|
switch (point) {
|
|
case TEVENT_TRACE_BEFORE_WAIT:
|
|
break;
|
|
case TEVENT_TRACE_AFTER_WAIT:
|
|
break;
|
|
case TEVENT_TRACE_BEFORE_LOOP_ONCE:
|
|
smbd_tevent_trace_callback_before_loop_once(state);
|
|
break;
|
|
case TEVENT_TRACE_AFTER_LOOP_ONCE:
|
|
smbd_tevent_trace_callback_after_loop_once(state);
|
|
break;
|
|
}
|
|
|
|
errno = 0;
|
|
}
|
|
|
|
static void smbd_tevent_trace_callback_profile(enum tevent_trace_point point,
|
|
void *private_data)
|
|
{
|
|
struct smbd_tevent_trace_state *state =
|
|
(struct smbd_tevent_trace_state *)private_data;
|
|
|
|
switch (point) {
|
|
case TEVENT_TRACE_BEFORE_WAIT:
|
|
if (!smbprofile_dump_pending()) {
|
|
/*
|
|
* If there's no dump pending
|
|
* we don't want to schedule a new 1 sec timer.
|
|
*
|
|
* Instead we want to sleep as long as nothing happens.
|
|
*/
|
|
smbprofile_dump_setup(NULL);
|
|
}
|
|
SMBPROFILE_BASIC_ASYNC_START(idle, profile_p, state->profile_idle);
|
|
break;
|
|
case TEVENT_TRACE_AFTER_WAIT:
|
|
SMBPROFILE_BASIC_ASYNC_END(state->profile_idle);
|
|
if (!smbprofile_dump_pending()) {
|
|
/*
|
|
* We need to flush our state after sleeping
|
|
* (hopefully a long time).
|
|
*/
|
|
smbprofile_dump();
|
|
/*
|
|
* future profiling events should trigger timers
|
|
* on our main event context.
|
|
*/
|
|
smbprofile_dump_setup(state->ev);
|
|
}
|
|
break;
|
|
case TEVENT_TRACE_BEFORE_LOOP_ONCE:
|
|
smbd_tevent_trace_callback_before_loop_once(state);
|
|
break;
|
|
case TEVENT_TRACE_AFTER_LOOP_ONCE:
|
|
smbd_tevent_trace_callback_after_loop_once(state);
|
|
break;
|
|
}
|
|
|
|
errno = 0;
|
|
}
|
|
|
|
/****************************************************************************
|
|
Process commands from the client
|
|
****************************************************************************/
|
|
|
|
void smbd_process(struct tevent_context *ev_ctx,
|
|
struct messaging_context *msg_ctx,
|
|
int sock_fd,
|
|
bool interactive)
|
|
{
|
|
struct smbd_tevent_trace_state trace_state = {
|
|
.ev = ev_ctx,
|
|
.frame = talloc_stackframe(),
|
|
};
|
|
const struct loadparm_substitution *lp_sub =
|
|
loadparm_s3_global_substitution();
|
|
struct smbXsrv_client *client = NULL;
|
|
struct smbd_server_connection *sconn = NULL;
|
|
struct smbXsrv_connection *xconn = NULL;
|
|
const char *locaddr = NULL;
|
|
const char *remaddr = NULL;
|
|
int ret;
|
|
NTSTATUS status;
|
|
struct timeval tv = timeval_current();
|
|
NTTIME now = timeval_to_nttime(&tv);
|
|
char *chroot_dir = NULL;
|
|
int rc;
|
|
|
|
status = smbXsrv_client_create(ev_ctx, ev_ctx, msg_ctx, now, &client);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
DBG_ERR("smbXsrv_client_create(): %s\n", nt_errstr(status));
|
|
exit_server_cleanly("talloc_zero(struct smbXsrv_client).\n");
|
|
}
|
|
|
|
/*
|
|
* TODO: remove this...:-)
|
|
*/
|
|
global_smbXsrv_client = client;
|
|
|
|
sconn = talloc_zero(client, struct smbd_server_connection);
|
|
if (sconn == NULL) {
|
|
exit_server("failed to create smbd_server_connection");
|
|
}
|
|
|
|
client->sconn = sconn;
|
|
sconn->client = client;
|
|
|
|
sconn->ev_ctx = ev_ctx;
|
|
sconn->msg_ctx = msg_ctx;
|
|
|
|
ret = pthreadpool_tevent_init(sconn, lp_aio_max_threads(),
|
|
&sconn->pool);
|
|
if (ret != 0) {
|
|
exit_server("pthreadpool_tevent_init() failed.");
|
|
}
|
|
|
|
#if defined(WITH_SMB1SERVER)
|
|
if (lp_server_max_protocol() >= PROTOCOL_SMB2_02) {
|
|
#endif
|
|
/*
|
|
* We're not making the decision here,
|
|
* we're just allowing the client
|
|
* to decide between SMB1 and SMB2
|
|
* with the first negprot
|
|
* packet.
|
|
*/
|
|
sconn->using_smb2 = true;
|
|
#if defined(WITH_SMB1SERVER)
|
|
}
|
|
#endif
|
|
|
|
if (!interactive) {
|
|
smbd_setup_sig_term_handler(sconn);
|
|
smbd_setup_sig_hup_handler(sconn);
|
|
}
|
|
|
|
status = smbd_add_connection(client, sock_fd, now, &xconn);
|
|
if (NT_STATUS_EQUAL(status, NT_STATUS_NETWORK_ACCESS_DENIED)) {
|
|
/*
|
|
* send a negative session response "not listening on calling
|
|
* name"
|
|
*/
|
|
unsigned char buf[5] = {0x83, 0, 0, 1, 0x81};
|
|
(void)smb1_srv_send(xconn, (char *)buf, false, 0, false);
|
|
exit_server_cleanly("connection denied");
|
|
} else if (!NT_STATUS_IS_OK(status)) {
|
|
exit_server_cleanly(nt_errstr(status));
|
|
}
|
|
|
|
sconn->local_address =
|
|
tsocket_address_copy(xconn->local_address, sconn);
|
|
if (sconn->local_address == NULL) {
|
|
exit_server_cleanly("tsocket_address_copy() failed");
|
|
}
|
|
sconn->remote_address =
|
|
tsocket_address_copy(xconn->remote_address, sconn);
|
|
if (sconn->remote_address == NULL) {
|
|
exit_server_cleanly("tsocket_address_copy() failed");
|
|
}
|
|
sconn->remote_hostname =
|
|
talloc_strdup(sconn, xconn->remote_hostname);
|
|
if (sconn->remote_hostname == NULL) {
|
|
exit_server_cleanly("tsocket_strdup() failed");
|
|
}
|
|
|
|
client->global->local_address =
|
|
tsocket_address_string(sconn->local_address,
|
|
client->global);
|
|
if (client->global->local_address == NULL) {
|
|
exit_server_cleanly("tsocket_address_string() failed");
|
|
}
|
|
client->global->remote_address =
|
|
tsocket_address_string(sconn->remote_address,
|
|
client->global);
|
|
if (client->global->remote_address == NULL) {
|
|
exit_server_cleanly("tsocket_address_string() failed");
|
|
}
|
|
client->global->remote_name =
|
|
talloc_strdup(client->global, sconn->remote_hostname);
|
|
if (client->global->remote_name == NULL) {
|
|
exit_server_cleanly("tsocket_strdup() failed");
|
|
}
|
|
|
|
if (tsocket_address_is_inet(sconn->local_address, "ip")) {
|
|
locaddr = tsocket_address_inet_addr_string(
|
|
sconn->local_address,
|
|
talloc_tos());
|
|
if (locaddr == NULL) {
|
|
DEBUG(0,("%s: tsocket_address_inet_addr_string remote failed - %s\n",
|
|
__location__, strerror(errno)));
|
|
exit_server_cleanly("tsocket_address_inet_addr_string remote failed.\n");
|
|
}
|
|
} else {
|
|
locaddr = "0.0.0.0";
|
|
}
|
|
|
|
if (tsocket_address_is_inet(sconn->remote_address, "ip")) {
|
|
remaddr = tsocket_address_inet_addr_string(
|
|
sconn->remote_address,
|
|
talloc_tos());
|
|
if (remaddr == NULL) {
|
|
DEBUG(0,("%s: tsocket_address_inet_addr_string remote failed - %s\n",
|
|
__location__, strerror(errno)));
|
|
exit_server_cleanly("tsocket_address_inet_addr_string remote failed.\n");
|
|
}
|
|
} 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(remaddr, false);
|
|
reload_services(sconn, conn_snum_used, true);
|
|
sub_set_socket_ids(remaddr,
|
|
sconn->remote_hostname,
|
|
locaddr);
|
|
|
|
if (lp_preload_modules()) {
|
|
smb_load_all_modules_absoute_path(lp_preload_modules());
|
|
}
|
|
|
|
smb_perfcount_init();
|
|
|
|
if (!init_account_policy()) {
|
|
exit_server("Could not open account policy tdb.\n");
|
|
}
|
|
|
|
chroot_dir = lp_root_directory(talloc_tos(), lp_sub);
|
|
if (chroot_dir[0] != '\0') {
|
|
rc = chdir(chroot_dir);
|
|
if (rc != 0) {
|
|
DBG_ERR("Failed to chdir to %s\n", chroot_dir);
|
|
exit_server("Failed to chdir()");
|
|
}
|
|
|
|
rc = chroot(chroot_dir);
|
|
if (rc != 0) {
|
|
DBG_ERR("Failed to change root to %s\n", chroot_dir);
|
|
exit_server("Failed to chroot()");
|
|
}
|
|
DBG_WARNING("Changed root to %s\n", chroot_dir);
|
|
|
|
TALLOC_FREE(chroot_dir);
|
|
}
|
|
|
|
if (!file_init(sconn)) {
|
|
exit_server("file_init() failed");
|
|
}
|
|
|
|
/* Setup oplocks */
|
|
if (!init_oplocks(sconn))
|
|
exit_server("Failed to init oplocks");
|
|
|
|
/* register our message handlers */
|
|
messaging_register(sconn->msg_ctx, sconn,
|
|
MSG_SMB_FORCE_TDIS, msg_force_tdis);
|
|
messaging_register(
|
|
sconn->msg_ctx,
|
|
sconn,
|
|
MSG_SMB_FORCE_TDIS_DENIED,
|
|
msg_force_tdis_denied);
|
|
messaging_register(sconn->msg_ctx, sconn,
|
|
MSG_SMB_CLOSE_FILE, msg_close_file);
|
|
messaging_register(sconn->msg_ctx, sconn,
|
|
MSG_SMB_FILE_RENAME, msg_file_was_renamed);
|
|
|
|
id_cache_register_msgs(sconn->msg_ctx);
|
|
messaging_deregister(sconn->msg_ctx, ID_CACHE_KILL, NULL);
|
|
messaging_register(sconn->msg_ctx, sconn,
|
|
ID_CACHE_KILL, smbd_id_cache_kill);
|
|
|
|
messaging_deregister(sconn->msg_ctx,
|
|
MSG_SMB_CONF_UPDATED, sconn->ev_ctx);
|
|
messaging_register(sconn->msg_ctx, sconn,
|
|
MSG_SMB_CONF_UPDATED, smbd_conf_updated);
|
|
|
|
messaging_deregister(sconn->msg_ctx, MSG_SMB_KILL_CLIENT_IP,
|
|
NULL);
|
|
messaging_register(sconn->msg_ctx, sconn,
|
|
MSG_SMB_KILL_CLIENT_IP,
|
|
msg_kill_client_ip);
|
|
|
|
messaging_deregister(sconn->msg_ctx, MSG_SMB_TELL_NUM_CHILDREN, NULL);
|
|
|
|
/*
|
|
* 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 defined(WITH_SMB1SERVER)
|
|
if ((lp_keepalive() != 0)
|
|
&& !(event_add_idle(ev_ctx, NULL,
|
|
timeval_set(lp_keepalive(), 0),
|
|
"keepalive", keepalive_fn,
|
|
sconn))) {
|
|
DEBUG(0, ("Could not add keepalive event\n"));
|
|
exit(1);
|
|
}
|
|
#endif
|
|
|
|
if (!(event_add_idle(ev_ctx, NULL,
|
|
timeval_set(IDLE_CLOSED_TIMEOUT, 0),
|
|
"deadtime", deadtime_fn, sconn))) {
|
|
DEBUG(0, ("Could not add deadtime event\n"));
|
|
exit(1);
|
|
}
|
|
|
|
if (!(event_add_idle(ev_ctx, NULL,
|
|
timeval_set(SMBD_HOUSEKEEPING_INTERVAL, 0),
|
|
"housekeeping", housekeeping_fn, sconn))) {
|
|
DEBUG(0, ("Could not add housekeeping event\n"));
|
|
exit(1);
|
|
}
|
|
|
|
smbprofile_dump_setup(ev_ctx);
|
|
|
|
if (!init_dptrs(sconn)) {
|
|
exit_server("init_dptrs() failed");
|
|
}
|
|
|
|
TALLOC_FREE(trace_state.frame);
|
|
|
|
if (smbprofile_active()) {
|
|
tevent_set_trace_callback(ev_ctx,
|
|
smbd_tevent_trace_callback_profile,
|
|
&trace_state);
|
|
} else {
|
|
tevent_set_trace_callback(ev_ctx,
|
|
smbd_tevent_trace_callback,
|
|
&trace_state);
|
|
}
|
|
|
|
ret = tevent_loop_wait(ev_ctx);
|
|
if (ret != 0) {
|
|
DEBUG(1, ("tevent_loop_wait failed: %d, %s,"
|
|
" exiting\n", ret, strerror(errno)));
|
|
}
|
|
|
|
TALLOC_FREE(trace_state.frame);
|
|
|
|
exit_server_cleanly(NULL);
|
|
}
|