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.socket_lock_fd = -1;
Construct a reply to the incoming packet.
****************************************************************************/
-static 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 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)
{
struct smbd_server_connection *sconn = xconn->client->sconn;
struct smb_request *req;
/* 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_smb1_server_connection_read_handler(
- struct smbXsrv_connection *xconn, int fd)
+void smbd_smb1_server_connection_read_handler(struct smbXsrv_connection *xconn,
+ int fd)
{
uint8_t *inbuf = NULL;
size_t inbuf_len = 0;
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,
struct tevent_fd *fde,
uint16_t flags,
}
}
-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,
void *private_data, uint32_t msg_type,
struct server_id server_id, DATA_BLOB *data)
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
****************************************************************************/
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;
+}