diff --git a/source3/smbd/process.c b/source3/smbd/process.c index 071f400ca15..0aa41f4695e 100644 --- a/source3/smbd/process.c +++ b/source3/smbd/process.c @@ -708,76 +708,6 @@ bool push_deferred_open_message_smb1(struct smb_request *req, return push_queued_message(req, req->request_time, end_time, open_rec); } -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); -} - /* * Only allow 5 outstanding trans requests. We're allocating memory, so * prevent a DoS. @@ -2117,23 +2047,6 @@ bool smb1_parse_chain(TALLOC_CTX *mem_ctx, const uint8_t *buf, return true; } -/**************************************************************************** - 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 bool fd_is_readable(int fd) { int ret, revents; @@ -2244,36 +2157,10 @@ static void smbd_server_echo_handler(struct tevent_context *ev, } } -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); -} - /* * Send keepalive packets to our client */ -static bool keepalive_fn(const struct timeval *now, void *private_data) +bool keepalive_fn(const struct timeval *now, void *private_data) { struct smbd_server_connection *sconn = talloc_get_type_abort( private_data, struct smbd_server_connection); @@ -2309,50 +2196,6 @@ static bool keepalive_fn(const struct timeval *now, void *private_data) return True; } -/* - * Do the recurring check if we're idle - */ -static bool deadtime_fn(const struct timeval *now, void *private_data) -{ - struct smbd_server_connection *sconn = - (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; -} - /* * Read an smb packet in the echo handler child, giving the parent * smbd one second to react once the socket becomes readable. @@ -2903,464 +2746,6 @@ fail: return false; } -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; -} - -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 : ""; - 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 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: - 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: - TALLOC_FREE(state->frame); - state->frame = talloc_stackframe_pool(8192); - break; - case TEVENT_TRACE_AFTER_LOOP_ONCE: - TALLOC_FREE(state->frame); - 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)srv_send_smb(xconn,(char *)buf, false, - 0, false, NULL); - 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); - - 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); -} - bool req_is_in_chain(const struct smb_request *req) { if (req->vwv != (const uint16_t *)(req->inbuf+smb_vwv)) { diff --git a/source3/smbd/proto.h b/source3/smbd/proto.h index 86aa2ae0a13..41218e24d67 100644 --- a/source3/smbd/proto.h +++ b/source3/smbd/proto.h @@ -870,10 +870,6 @@ bool smb1_parse_chain(TALLOC_CTX *mem_ctx, const uint8_t *buf, bool encrypted, uint32_t seqnum, struct smb_request ***reqs, unsigned *num_reqs); bool req_is_in_chain(const struct smb_request *req); -void smbd_process(struct tevent_context *ev_ctx, - struct messaging_context *msg_ctx, - int sock_fd, - bool interactive); bool fork_echo_handler(struct smbXsrv_connection *xconn); NTSTATUS smb1_receive_talloc(TALLOC_CTX *mem_ctx, struct smbXsrv_connection *xconn, @@ -899,6 +895,7 @@ void construct_reply(struct smbXsrv_connection *xconn, struct smb_perfcount_data *deferred_pcd); void smbd_smb1_server_connection_read_handler(struct smbXsrv_connection *xconn, int fd); +bool keepalive_fn(const struct timeval *now, void *private_data); /* The following definitions come from smbd/smb2_process.c */ @@ -941,6 +938,10 @@ 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); +void smbd_process(struct tevent_context *ev_ctx, + struct messaging_context *msg_ctx, + int sock_fd, + bool interactive); /* The following definitions come from smbd/quotas.c */ diff --git a/source3/smbd/smb2_process.c b/source3/smbd/smb2_process.c index c97e188e232..d9d35574a7b 100644 --- a/source3/smbd/smb2_process.c +++ b/source3/smbd/smb2_process.c @@ -1284,3 +1284,618 @@ NTSTATUS smbd_add_connection(struct smbXsrv_client *client, int sock_fd, 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 : ""; + 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 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: + 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: + TALLOC_FREE(state->frame); + state->frame = talloc_stackframe_pool(8192); + break; + case TEVENT_TRACE_AFTER_LOOP_ONCE: + TALLOC_FREE(state->frame); + 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)srv_send_smb(xconn,(char *)buf, false, + 0, false, NULL); + 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); + + 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); +}