service-process.c revision b2ed2b25c4c457ec1c99ebe5e9bd66a2e2f89cfd
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen/* Copyright (c) 2005-2009 Dovecot authors, see the included COPYING file */
6c2ce1d5bf17b21e804a079eb0f973b7ab83e0d8Timo Sirainenservice_dup_fds(struct service *service, int auth_fd, int std_fd,
7487ff578435377bbeefffdbfb78ca09ed1292dfTimo Sirainen unsigned int i, count, n = 0, socket_listener_count, ssl_socket_count;
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen /* stdin/stdout is already redirected to /dev/null. Other master fds
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen should have been opened with fd_close_on_exec() so we don't have to
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen worry about them.
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen because the destination fd might be another one's source fd we have
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen to be careful not to overwrite anything. dup() the fd when needed */
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen listeners = array_get(&service->listeners, &count);
78fa3c578c14ee8a612f86cf73b6181c7f16463fTimo Sirainen services_log_dup2(&dups, service->list, MASTER_LISTEN_FD_FIRST,
6c2ce1d5bf17b21e804a079eb0f973b7ab83e0d8Timo Sirainen /* nonblocking anvil fd must be the first one. anvil treats it
6c2ce1d5bf17b21e804a079eb0f973b7ab83e0d8Timo Sirainen as the master's fd */
6c2ce1d5bf17b21e804a079eb0f973b7ab83e0d8Timo Sirainen dup2_append(&dups, service->list->nonblocking_anvil_fd[0],
6c2ce1d5bf17b21e804a079eb0f973b7ab83e0d8Timo Sirainen dup2_append(&dups, service->list->blocking_anvil_fd[0],
7487ff578435377bbeefffdbfb78ca09ed1292dfTimo Sirainen /* first add non-ssl listeners */
78fa3c578c14ee8a612f86cf73b6181c7f16463fTimo Sirainen for (i = 0; i < count; i++) {
617e13833c798435e2be425b99c27ecaad1b8393Timo Sirainen (listeners[i]->type != SERVICE_LISTENER_INET ||
7487ff578435377bbeefffdbfb78ca09ed1292dfTimo Sirainen /* then ssl-listeners */
7487ff578435377bbeefffdbfb78ca09ed1292dfTimo Sirainen for (i = 0; i < count; i++) {
617e13833c798435e2be425b99c27ecaad1b8393Timo Sirainen listeners[i]->type == SERVICE_LISTENER_INET &&
6c2ce1d5bf17b21e804a079eb0f973b7ab83e0d8Timo Sirainen dup2_append(&dups, service->list->blocking_anvil_fd[1],
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen dup2_append(&dups, service->status_fd[1], MASTER_STATUS_FD);
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen env_put(t_strdup_printf("MASTER_AUTH_FD=%d", MASTER_AUTH_FD));
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen /* set log file to stderr. dup2() here immediately so that
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen we can set up logging to it without causing any log messages
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen to be lost. */
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen if (dup2(service->log_fd[1], STDERR_FILENO) < 0)
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen /* make sure we don't leak syslog fd. try to do it as late as possible,
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen but also before dup2()s in case syslog fd is one of them. */
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen env_put(t_strdup_printf("SOCKET_COUNT=%d", socket_listener_count));
7487ff578435377bbeefffdbfb78ca09ed1292dfTimo Sirainen env_put(t_strdup_printf("SSL_SOCKET_COUNT=%d", ssl_socket_count));
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainenstatic int validate_uid_gid(struct master_settings *set, uid_t uid, gid_t gid,
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen i_error("Logins with UID 0 not permitted (user %s)", user);
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen (set->last_valid_uid != 0 && uid > (uid_t)set->last_valid_uid)) {
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen i_error("Logins with UID %s (user %s) not permitted "
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen "(see first_valid_uid in config file)",
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen (set->last_valid_gid != 0 && gid > (gid_t)set->last_valid_gid)) {
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen i_error("Logins for users with primary group ID %s (user %s) "
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen "not permitted (see first_valid_gid in config file).",
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainenstatic void auth_args_apply(const char *const *args,
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen const char **home)
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen rset->uid = (uid_t)strtoul(*args + 4, NULL, 10);
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen rset->gid = (gid_t)strtoul(*args + 4, NULL, 10);
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen env_put(t_strconcat("HOME=", *args + 5, NULL));
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen else if (strncmp(*args, "system_groups_user=", 19) == 0)
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen else if (strncmp(*args, "mail_access_groups=", 19) == 0) {
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen /* unknown, set as environment */
e3ae2ac7a93b418cf46e829c94973b6e962a7830Timo Sirainen /* boolean */
e3ae2ac7a93b418cf46e829c94973b6e962a7830Timo Sirainen /* FIXME: kind of ugly to have it
e3ae2ac7a93b418cf46e829c94973b6e962a7830Timo Sirainen env_put(t_strconcat(t_str_ucase(key), value, NULL));
617e13833c798435e2be425b99c27ecaad1b8393Timo Sirainenstatic void auth_success_write(void)
617e13833c798435e2be425b99c27ecaad1b8393Timo Sirainen i_error("creat(%s) failed: %m", AUTH_SUCCESS_PATH);
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainenstatic void drop_privileges(struct service *service,
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen const char *const *auth_args)
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen struct master_settings *master_set = service->set->master_set;
e154d8764089896a693cbb83d6831b2398f22ee8Timo Sirainen if (auth_args != NULL && service->set->master_set->mail_debug)
6ec925f52d04ec8700e47cb005bd7ddc65ac5614Timo Sirainen restrict_process_size(service->set->vsz_limit, -1U);
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen rset.privileged_gid = service->privileged_gid;
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen rset.chroot_dir = *service->set->chroot == '\0' ? NULL :
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen /* non-authenticating service. don't use *_valid_gid checks */
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen rset.first_valid_gid = master_set->first_valid_gid;
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen rset.last_valid_gid = master_set->last_valid_gid;
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen if (!validate_uid_gid(master_set, rset.uid, rset.gid, user))
72cc352b25ad401b923436c6ed0f1f3adaffa737Timo Sirainen disallow_root = service->type == SERVICE_TYPE_AUTH_SERVER ||
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainenservice_process_setup_environment(struct service *service, unsigned int uid)
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen const char *const *p;
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen /* remove all environment, and put back what we need */
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen for (p = service->list->child_process_env; *p != NULL; p++)
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen env_put(t_strconcat(MASTER_CONFIG_FILE_ENV"=",
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen /* give the log's configuration directly, so it won't depend
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen on config process */
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen set = master_service_settings_get(master_service);
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen env_put(t_strconcat("LOG_PATH=", set->log_path, NULL));
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen env_put(t_strconcat("INFO_LOG_PATH=", set->info_log_path, NULL));
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen env_put(t_strconcat("LOG_TIMESTAMP=", set->log_timestamp, NULL));
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen env_put(t_strconcat("SYSLOG_FACILITY=", set->syslog_facility, NULL));
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen listeners = array_get(&service->list->config->listeners,
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen env_put(t_strconcat(MASTER_CONFIG_FILE_ENV"=",
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen /* fallback to default limit */
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen limit = service->set->master_set->default_client_limit;
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen env_put(t_strdup_printf(MASTER_CLIENT_LIMIT_ENV"=%u", limit));
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen env_put(t_strdup_printf(MASTER_UID_ENV"=%u", uid));
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen if (!service->set->master_set->version_ignore)
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen env_put(MASTER_DOVECOT_VERSION_ENV"="PACKAGE_VERSION);
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainenstatic void service_process_status_timeout(struct service_process *process)
55bc6a7a0940ec48a68558ef70838991c5d301d2Timo Sirainen "Initial status notification not received in %d "
55bc6a7a0940ec48a68558ef70838991c5d301d2Timo Sirainen "seconds, killing the process",
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen if (kill(process->pid, SIGKILL) < 0 && errno != ESRCH) {
55bc6a7a0940ec48a68558ef70838991c5d301d2Timo Sirainen service_error(process->service, "kill(%s, SIGKILL) failed: %m",
6c2ce1d5bf17b21e804a079eb0f973b7ab83e0d8Timo Sirainenhandle_request(const struct service_process_auth_request *request)
6c2ce1d5bf17b21e804a079eb0f973b7ab83e0d8Timo Sirainen base64_encode(request->data, request->data_size, str);
6c2ce1d5bf17b21e804a079eb0f973b7ab83e0d8Timo Sirainen env_put(t_strconcat("LOCAL_IP=", net_ip2addr(&request->local_ip), NULL));
6c2ce1d5bf17b21e804a079eb0f973b7ab83e0d8Timo Sirainen env_put(t_strconcat("IP=", net_ip2addr(&request->remote_ip), NULL));
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainenservice_process_create(struct service *service, const char *const *auth_args,
6c2ce1d5bf17b21e804a079eb0f973b7ab83e0d8Timo Sirainen const struct service_process_auth_request *request)
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen static unsigned int uid_counter = 0;
b2ed2b25c4c457ec1c99ebe5e9bd66a2e2f89cfdTimo Sirainen /* probably throttling service, don't create new processes */
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen if (socketpair(AF_UNIX, SOCK_STREAM, 0, fd) < 0) {
55bc6a7a0940ec48a68558ef70838991c5d301d2Timo Sirainen service_error(service, "socketpair() failed: %m");
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen service_process_setup_environment(service, uid);
6c2ce1d5bf17b21e804a079eb0f973b7ab83e0d8Timo Sirainen service_dup_fds(service, fd[1], request == NULL ? -1 :
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen process = i_malloc(sizeof(struct service_process_auth_server));
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen service_process_auth_server_init(process, fd[0]);
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen process = i_malloc(sizeof(struct service_process_auth_source));
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen service_process_auth_source_init(process, fd[0]);
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen timeout_add(SERVICE_FIRST_STATUS_TIMEOUT_SECS * 1000,
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen process->available_count = service->set->client_limit;
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen /* fallback to default limit */
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen service->set->master_set->default_client_limit;
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen hash_table_insert(service->list->pids, &process->pid, process);
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainenvoid service_process_destroy(struct service_process *process)
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen hash_table_remove(service->list->pids, &process->pid);
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen i_assert(service->process_avail <= service->process_count);
6c2ce1d5bf17b21e804a079eb0f973b7ab83e0d8Timo Sirainen service_process_notify_add(service->list->log_byes, process);
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainenvoid service_process_ref(struct service_process *process)
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainenint service_process_unref(struct service_process *process)
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainenstatic const char *
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainenget_exit_status_message(struct service *service, enum fatal_exit_status status)
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen return "Can't open log file";
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen return "Can't write to log file";
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen return "Internal logging error";
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen return "Out of memory";
a3dcda4b01461c7690c655a013ec12851cdb78d4Timo Sirainen return t_strdup_printf("Out of memory (vsz_limit=%u MB, "
a3dcda4b01461c7690c655a013ec12851cdb78d4Timo Sirainen "you may need to increase it)",
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen return "exec() failed";
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen return "Fatal failure";
5fc02738b38ac2b0c21db0854d7a5ad452b1177fTimo Sirainenstatic void log_coredump(struct service *service ATTR_UNUSED,
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen if (signum != SIGABRT && signum != SIGSEGV && signum != SIGBUS)
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen /* let's try to figure out why we didn't get a core dump */
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen str_append(str, " (core not dumped - set drop_priv_before_exec=yes)");
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen if (*service->set->privileged_group != '\0') {
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen str_append(str, " (core not dumped - privileged_group prevented it)");
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainenservice_process_get_status_error(string_t *str, struct service_process *process,
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen const char *msg;
55bc6a7a0940ec48a68558ef70838991c5d301d2Timo Sirainen str_printfa(str, "service(%s): child %s ", service->set->name,
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen str_printfa(str, "killed with signal %d", WTERMSIG(status));
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen str_printfa(str, "died with status %d", status);
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen str_printfa(str, "returned error %d", status);
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen msg = get_exit_status_message(service, status);
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainenstatic void service_process_log(struct service_process *process,
9c8f854d95d8d895022a75f140a0a500eb200d39Timo Sirainen if (!default_fatal || process->service->log_fd[1] == -1) {
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen /* log it via the log process in charge of handling
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen this process's logging */
78fa3c578c14ee8a612f86cf73b6181c7f16463fTimo Sirainen data = t_strdup_printf("%d %s DEFAULT-FATAL %s\n",
78fa3c578c14ee8a612f86cf73b6181c7f16463fTimo Sirainen if (write(process->service->list->master_log_fd[1],
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainenvoid service_process_log_status_error(struct service_process *process,
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen /* fast path */
9c8f854d95d8d895022a75f140a0a500eb200d39Timo Sirainen service_process_get_status_error(str, process, status,