1
0
mirror of https://github.com/samba-team/samba.git synced 2024-12-24 21:34:56 +03:00

smbd: Move smbd_add_connection to smb2_process.c

Signed-off-by: David Mulder <dmulder@suse.com>
Reviewed-by: Jeremy Allison <jra@samba.org>
This commit is contained in:
David Mulder 2022-03-18 15:37:17 -06:00 committed by Jeremy Allison
parent c43c9ef3ee
commit 6f792afe43
3 changed files with 567 additions and 554 deletions

View File

@ -66,7 +66,7 @@ struct pending_message_list {
static bool smb_splice_chain(uint8_t **poutbuf, const uint8_t *andx_buf); static bool smb_splice_chain(uint8_t **poutbuf, const uint8_t *andx_buf);
static void smbd_echo_init(struct smbXsrv_connection *xconn) void smbd_echo_init(struct smbXsrv_connection *xconn)
{ {
xconn->smb1.echo_handler.trusted_fd = -1; xconn->smb1.echo_handler.trusted_fd = -1;
xconn->smb1.echo_handler.socket_lock_fd = -1; xconn->smb1.echo_handler.socket_lock_fd = -1;
@ -1414,7 +1414,7 @@ static connection_struct *switch_message(uint8_t type, struct smb_request *req)
Construct a reply to the incoming packet. Construct a reply to the incoming packet.
****************************************************************************/ ****************************************************************************/
static void construct_reply(struct smbXsrv_connection *xconn, void construct_reply(struct smbXsrv_connection *xconn,
char *inbuf, int size, size_t unread_bytes, char *inbuf, int size, size_t unread_bytes,
uint32_t seqnum, bool encrypted, uint32_t seqnum, bool encrypted,
struct smb_perfcount_data *deferred_pcd) struct smb_perfcount_data *deferred_pcd)
@ -2150,126 +2150,8 @@ static void smbd_server_connection_write_handler(
/* TODO: make write nonblocking */ /* TODO: make write nonblocking */
} }
static void smbd_smb2_server_connection_read_handler( void smbd_smb1_server_connection_read_handler(struct smbXsrv_connection *xconn,
struct smbXsrv_connection *xconn, int fd) 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 defined(WITH_SMB1SERVER)
if (valid_smb_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(xconn,
(char *)buffer,
bufferlen,
0,
0,
false,
NULL);
xconn->client->sconn->trans_num++;
xconn->client->sconn->num_requests++;
return;
} else
#endif
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_smb1_server_connection_read_handler(
struct smbXsrv_connection *xconn, int fd)
{ {
uint8_t *inbuf = NULL; uint8_t *inbuf = NULL;
size_t inbuf_len = 0; size_t inbuf_len = 0;
@ -2333,44 +2215,6 @@ process:
seqnum, encrypted, NULL); seqnum, encrypted, NULL);
} }
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;
}
}
static void smbd_server_echo_handler(struct tevent_context *ev, static void smbd_server_echo_handler(struct tevent_context *ev,
struct tevent_fd *fde, struct tevent_fd *fde,
uint16_t flags, uint16_t flags,
@ -2400,180 +2244,6 @@ static void smbd_server_echo_handler(struct tevent_context *ev,
} }
} }
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 void msg_kill_client_ip(struct messaging_context *msg_ctx, static void msg_kill_client_ip(struct messaging_context *msg_ctx,
void *private_data, uint32_t msg_type, void *private_data, uint32_t msg_type,
struct server_id server_id, DATA_BLOB *data) struct server_id server_id, DATA_BLOB *data)
@ -3411,223 +3081,6 @@ static void smbd_tevent_trace_callback(enum tevent_trace_point point,
errno = 0; errno = 0;
} }
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;
}
/**************************************************************************** /****************************************************************************
Process commands from the client Process commands from the client
****************************************************************************/ ****************************************************************************/

View File

@ -892,6 +892,13 @@ void process_smb1(struct smbXsrv_connection *xconn,
uint32_t seqnum, bool encrypted, uint32_t seqnum, bool encrypted,
struct smb_perfcount_data *deferred_pcd); struct smb_perfcount_data *deferred_pcd);
bool valid_smb_header(const uint8_t *inbuf); bool valid_smb_header(const uint8_t *inbuf);
void smbd_echo_init(struct smbXsrv_connection *xconn);
void construct_reply(struct smbXsrv_connection *xconn,
char *inbuf, int size, size_t unread_bytes,
uint32_t seqnum, bool encrypted,
struct smb_perfcount_data *deferred_pcd);
void smbd_smb1_server_connection_read_handler(struct smbXsrv_connection *xconn,
int fd);
/* The following definitions come from smbd/smb2_process.c */ /* The following definitions come from smbd/smb2_process.c */

View File

@ -731,3 +731,556 @@ const char *smbXsrv_connection_dbg(const struct smbXsrv_connection *xconn)
return ret; return ret;
} }
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 defined(WITH_SMB1SERVER)
if (valid_smb_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(xconn,
(char *)buffer,
bufferlen,
0,
0,
false,
NULL);
xconn->client->sconn->trans_num++;
xconn->client->sconn->num_requests++;
return;
} else
#endif
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;
}