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