service-process.c revision 617e13833c798435e2be425b99c27ecaad1b8393
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen/* Copyright (c) 2005-2009 Dovecot authors, see the included COPYING file */
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen#include "common.h"
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen#include "array.h"
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen#include "aqueue.h"
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen#include "ioloop.h"
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen#include "istream.h"
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen#include "ostream.h"
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen#include "write-full.h"
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen#include "base64.h"
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen#include "hash.h"
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen#include "str.h"
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen#include "hostpid.h"
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen#include "env-util.h"
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen#include "fd-close-on-exec.h"
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen#include "restrict-access.h"
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen#include "master-service-settings.h"
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen#include "dup2-array.h"
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen#include "service.h"
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen#include "service-log.h"
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen#include "service-auth-server.h"
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen#include "service-auth-source.h"
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen#include "service-process.h"
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen#include <stdlib.h>
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen#include <unistd.h>
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen#include <fcntl.h>
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen#include <syslog.h>
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen#include <signal.h>
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen#include <sys/wait.h>
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainenstatic void
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainenservice_dup_fds(struct service *service, int auth_fd, int std_fd)
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen{
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen struct service_listener *const *listeners;
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen ARRAY_TYPE(dup2) dups;
01f54478a7c69b88ab13840c99bbab19a0d7d754Timo Sirainen unsigned int i, count, n = 0, socket_listener_count, ssl_socket_count;
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen /* stdin/stdout is already redirected to /dev/null. Other master fds
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen should have been opened with fd_close_on_exec() so we don't have to
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen worry about them.
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen because the destination fd might be another one's source fd we have
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen to be careful not to overwrite anything. dup() the fd when needed */
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen socket_listener_count = 0;
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen listeners = array_get(&service->listeners, &count);
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen t_array_init(&dups, count + 10);
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen if (service->type == SERVICE_TYPE_LOG) {
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen i_assert(n == 0);
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen services_log_dup2(&dups, service->list, MASTER_LISTEN_FD_FIRST,
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen &socket_listener_count);
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen n += socket_listener_count;
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen }
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen /* first add non-ssl listeners */
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen for (i = 0; i < count; i++) {
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen if (listeners[i]->fd != -1 &&
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen (listeners[i]->type != SERVICE_LISTENER_INET ||
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen !listeners[i]->set.inetset.set->ssl)) {
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen dup2_append(&dups, listeners[i]->fd,
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen MASTER_LISTEN_FD_FIRST + n);
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen n++; socket_listener_count++;
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen }
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen }
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen /* then ssl-listeners */
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen ssl_socket_count = 0;
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen for (i = 0; i < count; i++) {
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen if (listeners[i]->fd != -1 &&
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen listeners[i]->type == SERVICE_LISTENER_INET &&
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen listeners[i]->set.inetset.set->ssl) {
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen dup2_append(&dups, listeners[i]->fd,
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen MASTER_LISTEN_FD_FIRST + n);
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen n++; socket_listener_count++;
21484e16293b2fb12f026d2ad533fa32ebebf641Timo Sirainen ssl_socket_count++;
01f54478a7c69b88ab13840c99bbab19a0d7d754Timo Sirainen }
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen }
01f54478a7c69b88ab13840c99bbab19a0d7d754Timo Sirainen
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen dup2_append(&dups, null_fd, MASTER_RESERVED_FD);
01f54478a7c69b88ab13840c99bbab19a0d7d754Timo Sirainen dup2_append(&dups, service->status_fd[1], MASTER_STATUS_FD);
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen
c59ce1887df061441a31f8243c730d4ec6a62a07Timo Sirainen switch (service->type) {
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen case SERVICE_TYPE_AUTH_SOURCE:
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen case SERVICE_TYPE_AUTH_SERVER:
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen i_assert(auth_fd != -1);
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen dup2_append(&dups, auth_fd, MASTER_AUTH_FD);
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen env_put(t_strdup_printf("MASTER_AUTH_FD=%d", MASTER_AUTH_FD));
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen break;
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen default:
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen i_assert(auth_fd == -1);
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen dup2_append(&dups, null_fd, MASTER_AUTH_FD);
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen break;
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen }
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen if (std_fd != -1) {
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen dup2_append(&dups, std_fd, STDIN_FILENO);
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen dup2_append(&dups, std_fd, STDOUT_FILENO);
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen env_put("LOGGED_IN=1");
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen }
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen if (service->type != SERVICE_TYPE_LOG) {
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen /* set log file to stderr. dup2() here immediately so that
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen we can set up logging to it without causing any log messages
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen to be lost. */
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen i_assert(service->log_fd[1] != -1);
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen env_put("LOG_SERVICE=1");
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen if (dup2(service->log_fd[1], STDERR_FILENO) < 0)
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen i_fatal("dup2(log fd) failed: %m");
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen i_set_failure_internal();
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen } else {
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen dup2_append(&dups, null_fd, STDERR_FILENO);
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen }
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen /* make sure we don't leak syslog fd. try to do it as late as possible,
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen but also before dup2()s in case syslog fd is one of them. */
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen closelog();
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen if (dup2_array(&dups) < 0)
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen service_error(service, "dup2s failed");
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen env_put(t_strdup_printf("SOCKET_COUNT=%d", socket_listener_count));
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen env_put(t_strdup_printf("SSL_SOCKET_COUNT=%d", ssl_socket_count));
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen}
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainenstatic int validate_uid_gid(struct master_settings *set, uid_t uid, gid_t gid,
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen const char *user)
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen{
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen if (uid == 0) {
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen i_error("Logins with UID 0 not permitted (user %s)", user);
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen return FALSE;
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen }
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen if (uid < (uid_t)set->first_valid_uid ||
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen (set->last_valid_uid != 0 && uid > (uid_t)set->last_valid_uid)) {
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen i_error("Logins with UID %s (user %s) not permitted "
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen "(see first_valid_uid in config file)",
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen dec2str(uid), user);
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen return FALSE;
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen }
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen if (gid < (gid_t)set->first_valid_gid ||
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen (set->last_valid_gid != 0 && gid > (gid_t)set->last_valid_gid)) {
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen i_error("Logins for users with primary group ID %s (user %s) "
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen "not permitted (see first_valid_gid in config file).",
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen dec2str(gid), user);
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen return FALSE;
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen }
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen return TRUE;
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen}
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainenstatic void auth_args_apply(const char *const *args,
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen struct restrict_access_settings *rset,
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen const char **home)
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen{
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen const char *key, *value;
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen string_t *expanded_vars;
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen
01f54478a7c69b88ab13840c99bbab19a0d7d754Timo Sirainen expanded_vars = t_str_new(128);
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen str_append(expanded_vars, "VARS_EXPANDED=");
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen for (; *args != NULL; args++) {
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen if (strncmp(*args, "uid=", 4) == 0)
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen rset->uid = (uid_t)strtoul(*args + 4, NULL, 10);
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen else if (strncmp(*args, "gid=", 4) == 0)
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen rset->gid = (gid_t)strtoul(*args + 4, NULL, 10);
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen else if (strncmp(*args, "home=", 5) == 0) {
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen *home = *args + 5;
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen env_put(t_strconcat("HOME=", *args + 5, NULL));
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen } else if (strncmp(*args, "chroot=", 7) == 0)
01f54478a7c69b88ab13840c99bbab19a0d7d754Timo Sirainen rset->chroot_dir = *args + 7;
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen else if (strncmp(*args, "system_groups_user=", 19) == 0)
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen rset->system_groups_user = *args + 19;
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen else if (strncmp(*args, "mail_access_groups=", 19) == 0) {
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen rset->extra_groups =
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen rset->extra_groups == NULL ? *args + 19 :
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen t_strconcat(*args + 19, ",",
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen rset->extra_groups, NULL);
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen } else {
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen /* unknown, set as environment */
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen value = strchr(*args, '=');
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen if (value == NULL) {
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen /* boolean */
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen key = *args;
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen value = "=1";
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen } else {
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen key = t_strdup_until(*args, value);
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen if (strcmp(key, "mail") == 0) {
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen /* FIXME: kind of ugly to have it
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen here.. */
c59ce1887df061441a31f8243c730d4ec6a62a07Timo Sirainen key = "mail_location";
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen }
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen }
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen str_append(expanded_vars, key);
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen str_append_c(expanded_vars, ' ');
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen env_put(t_strconcat(t_str_ucase(key), value, NULL));
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen }
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen }
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen env_put(str_c(expanded_vars));
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen}
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainenstatic void auth_success_write(void)
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen{
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen int fd;
77a8c99da71844aaf0fa3036960473024d19f471Timo Sirainen
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen if (auth_success_written)
77a8c99da71844aaf0fa3036960473024d19f471Timo Sirainen return;
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen fd = creat(AUTH_SUCCESS_PATH, 0666);
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen if (fd == -1)
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen i_error("creat(%s) failed: %m", AUTH_SUCCESS_PATH);
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen else
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen (void)close(fd);
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen auth_success_written = TRUE;
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen}
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainenstatic void drop_privileges(struct service *service,
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen const char *const *auth_args)
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen{
77a8c99da71844aaf0fa3036960473024d19f471Timo Sirainen struct master_settings *master_set = service->set->master_set;
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen struct restrict_access_settings rset;
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen const char *user, *home = NULL;
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen bool disallow_root;
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen restrict_access_init(&rset);
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen rset.uid = service->uid;
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen rset.gid = service->gid;
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen rset.privileged_gid = service->privileged_gid;
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen rset.chroot_dir = *service->set->chroot == '\0' ? NULL :
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen service->set->chroot;
01f54478a7c69b88ab13840c99bbab19a0d7d754Timo Sirainen rset.extra_groups = service->extra_gids;
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen
01f54478a7c69b88ab13840c99bbab19a0d7d754Timo Sirainen if (auth_args == NULL) {
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen /* non-authenticating service. don't use *_valid_gid checks */
01f54478a7c69b88ab13840c99bbab19a0d7d754Timo Sirainen } else {
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen i_assert(auth_args[0] != NULL);
c59ce1887df061441a31f8243c730d4ec6a62a07Timo Sirainen
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen rset.first_valid_gid = master_set->first_valid_gid;
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen rset.last_valid_gid = master_set->last_valid_gid;
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen user = auth_args[0];
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen env_put(t_strconcat("USER=", user, NULL));
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen auth_success_write();
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen auth_args_apply(auth_args + 1, &rset, &home);
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen if (!validate_uid_gid(master_set, rset.uid, rset.gid, user))
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen exit(FATAL_DEFAULT);
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen }
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen if (home != NULL) {
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen if (chdir(home) < 0 && errno != ENOENT)
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen i_error("chdir(%s) failed: %m", home);
01f54478a7c69b88ab13840c99bbab19a0d7d754Timo Sirainen }
9625595c47c665f5aee57ebfcb1fcbe9ad1bf3a0Martti Rannanjärvi
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen if (service->set->drop_priv_before_exec) {
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen disallow_root = service->type == SERVICE_TYPE_AUTH_SERVER ||
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen service->type == SERVICE_TYPE_AUTH_SOURCE;
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen restrict_access(&rset, home, disallow_root);
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen } else {
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen restrict_access_set_env(&rset);
c1252a5812eb11fcb81508b9ed37597a5bc84100Timo Sirainen }
74ae32512357bdd4872bf160dc697ff7b54b54c5Timo Sirainen}
74ae32512357bdd4872bf160dc697ff7b54b54c5Timo Sirainen
74ae32512357bdd4872bf160dc697ff7b54b54c5Timo Sirainenstatic void
74ae32512357bdd4872bf160dc697ff7b54b54c5Timo Sirainenservice_process_setup_environment(struct service *service, unsigned int uid)
74ae32512357bdd4872bf160dc697ff7b54b54c5Timo Sirainen{
74ae32512357bdd4872bf160dc697ff7b54b54c5Timo Sirainen const struct master_service_settings *set;
74ae32512357bdd4872bf160dc697ff7b54b54c5Timo Sirainen struct service_listener *const *listeners;
74ae32512357bdd4872bf160dc697ff7b54b54c5Timo Sirainen const char *const *p;
74ae32512357bdd4872bf160dc697ff7b54b54c5Timo Sirainen unsigned int limit, count;
74ae32512357bdd4872bf160dc697ff7b54b54c5Timo Sirainen
74ae32512357bdd4872bf160dc697ff7b54b54c5Timo Sirainen /* remove all environment, and put back what we need */
74ae32512357bdd4872bf160dc697ff7b54b54c5Timo Sirainen env_clean();
74ae32512357bdd4872bf160dc697ff7b54b54c5Timo Sirainen for (p = service->list->child_process_env; *p != NULL; p++)
74ae32512357bdd4872bf160dc697ff7b54b54c5Timo Sirainen env_put(*p);
74ae32512357bdd4872bf160dc697ff7b54b54c5Timo Sirainen
74ae32512357bdd4872bf160dc697ff7b54b54c5Timo Sirainen switch (service->type) {
74ae32512357bdd4872bf160dc697ff7b54b54c5Timo Sirainen case SERVICE_TYPE_CONFIG:
74ae32512357bdd4872bf160dc697ff7b54b54c5Timo Sirainen env_put(t_strconcat(MASTER_CONFIG_FILE_ENV"=",
74ae32512357bdd4872bf160dc697ff7b54b54c5Timo Sirainen service->config_file_path, NULL));
74ae32512357bdd4872bf160dc697ff7b54b54c5Timo Sirainen break;
74ae32512357bdd4872bf160dc697ff7b54b54c5Timo Sirainen case SERVICE_TYPE_LOG:
74ae32512357bdd4872bf160dc697ff7b54b54c5Timo Sirainen /* give the log's configuration directly, so it won't depend
74ae32512357bdd4872bf160dc697ff7b54b54c5Timo Sirainen on config process */
74ae32512357bdd4872bf160dc697ff7b54b54c5Timo Sirainen set = master_service_settings_get(master_service);
env_put("DOVECONF_ENV=1");
env_put(t_strconcat("LOG_PATH=", set->log_path, NULL));
env_put(t_strconcat("INFO_LOG_PATH=", set->info_log_path, NULL));
env_put(t_strconcat("LOG_TIMESTAMP=", set->log_timestamp, NULL));
env_put(t_strconcat("SYSLOG_FACILITY=", set->syslog_facility, NULL));
break;
default:
listeners = array_get(&service->list->config->listeners,
&count);
i_assert(count > 0);
env_put(t_strconcat(MASTER_CONFIG_FILE_ENV"=",
listeners[0]->set.fileset.set->path, NULL));
break;
}
limit = service->set->client_limit;
if (limit == 0) {
/* fallback to default limit */
limit = service->set->master_set->default_client_limit;
}
env_put(t_strdup_printf(MASTER_CLIENT_LIMIT_ENV"=%u", limit));
env_put(t_strdup_printf(MASTER_UID_ENV"=%u", uid));
if (!service->set->master_set->version_ignore)
env_put(MASTER_DOVECOT_VERSION_ENV"="PACKAGE_VERSION);
}
static void service_process_status_timeout(struct service_process *process)
{
service_error(process->service,
"Initial status notification not received in %d "
"seconds, killing the process",
SERVICE_FIRST_STATUS_TIMEOUT_SECS);
if (kill(process->pid, SIGKILL) < 0 && errno != ESRCH) {
service_error(process->service, "kill(%s, SIGKILL) failed: %m",
dec2str(process->pid));
}
timeout_remove(&process->to_status);
}
struct service_process *
service_process_create(struct service *service, const char *const *auth_args,
int std_fd, const unsigned char *data, size_t data_size)
{
static unsigned int uid_counter = 0;
struct service_process *process;
unsigned int uid = ++uid_counter;
string_t *str;
int fd[2];
pid_t pid;
switch (service->type) {
case SERVICE_TYPE_AUTH_SOURCE:
case SERVICE_TYPE_AUTH_SERVER:
if (socketpair(AF_UNIX, SOCK_STREAM, 0, fd) < 0) {
service_error(service, "socketpair() failed: %m");
return NULL;
}
fd_close_on_exec(fd[0], TRUE);
fd_close_on_exec(fd[1], TRUE);
break;
default:
fd[0] = fd[1] = -1;
break;
}
pid = fork();
if (pid < 0) {
service_error(service, "fork() failed: %m");
if (fd[0] != -1) {
(void)close(fd[0]);
(void)close(fd[1]);
}
return NULL;
}
if (pid == 0) {
/* child */
if (fd[0] != -1)
(void)close(fd[0]);
service_process_setup_environment(service, uid);
if (data_size > 0) {
str = t_str_new(data_size*3);
str_append(str, "CLIENT_INPUT=");
base64_encode(data, data_size, str);
env_put(str_c(str));
}
service_dup_fds(service, fd[1], std_fd);
drop_privileges(service, auth_args);
process_exec(service->executable, NULL);
}
switch (service->type) {
case SERVICE_TYPE_AUTH_SERVER:
process = i_malloc(sizeof(struct service_process_auth_server));
process->service = service;
service_process_auth_server_init(process, fd[0]);
(void)close(fd[1]);
break;
case SERVICE_TYPE_AUTH_SOURCE:
process = i_malloc(sizeof(struct service_process_auth_source));
process->service = service;
service_process_auth_source_init(process, fd[0]);
(void)close(fd[1]);
break;
default:
process = i_new(struct service_process, 1);
process->service = service;
i_assert(fd[0] == -1);
break;
}
process->refcount = 1;
process->pid = pid;
process->uid = uid;
process->to_status =
timeout_add(SERVICE_FIRST_STATUS_TIMEOUT_SECS * 1000,
service_process_status_timeout, process);
process->available_count = service->set->client_limit;
if (process->available_count == 0) {
/* fallback to default limit */
process->available_count =
service->set->master_set->default_client_limit;
}
service->process_count++;
service->process_avail++;
hash_table_insert(service->list->pids, &process->pid, process);
return process;
}
static int service_process_write_bye(struct service_process *process)
{
const char *data;
data = t_strdup_printf("%d %s BYE\n",
process->service->log_process_internal_fd,
dec2str(process->pid));
if (write(process->service->list->master_log_fd[1],
data, strlen(data)) < 0) {
if (errno != EAGAIN)
i_error("write(log process) failed: %m");
return -1;
}
return 0;
}
static void service_list_log_flush_byes(struct service_list *service_list)
{
struct service_process *const *processes, *process;
while (aqueue_count(service_list->bye_queue) > 0) {
processes = array_idx_modifiable(&service_list->bye_arr, 0);
process = processes[aqueue_idx(service_list->bye_queue, 0)];
if (service_process_write_bye(process) < 0) {
if (errno != EAGAIN)
services_log_clear_byes(service_list);
return;
}
service_process_unref(process);
aqueue_delete_tail(service_list->bye_queue);
}
io_remove(&service_list->io_log_write);
}
static void service_process_log_bye(struct service_process *process)
{
struct service_list *service_list = process->service->list;
if (process->service->log_fd[1] == -1) {
/* stopping all services */
return;
}
if (service_process_write_bye(process) < 0) {
if (errno != EAGAIN)
return;
if (service_list->io_log_write == NULL) {
service_list->io_log_write =
io_add(service_list->master_log_fd[1], IO_WRITE,
service_list_log_flush_byes,
service_list);
}
aqueue_append(service_list->bye_queue, &process);
service_process_ref(process);
}
}
void service_process_destroy(struct service_process *process)
{
struct service *service = process->service;
hash_table_remove(service->list->pids, &process->pid);
if (process->available_count > 0)
service->process_avail--;
service->process_count--;
i_assert(service->process_avail <= service->process_count);
if (process->to_status != NULL)
timeout_remove(&process->to_status);
switch (process->service->type) {
case SERVICE_TYPE_AUTH_SERVER:
service_process_auth_server_deinit(process);
break;
case SERVICE_TYPE_AUTH_SOURCE:
service_process_auth_source_deinit(process);
break;
default:
break;
}
service_process_log_bye(process);
process->destroyed = TRUE;
service_process_unref(process);
}
void service_process_ref(struct service_process *process)
{
i_assert(process->refcount > 0);
process->refcount++;
}
int service_process_unref(struct service_process *process)
{
i_assert(process->refcount > 0);
if (--process->refcount > 0)
return TRUE;
i_assert(process->destroyed);
i_free(process);
return FALSE;
}
static const char *
get_exit_status_message(struct service *service, enum fatal_exit_status status)
{
switch (status) {
case FATAL_LOGOPEN:
return "Can't open log file";
case FATAL_LOGWRITE:
return "Can't write to log file";
case FATAL_LOGERROR:
return "Internal logging error";
case FATAL_OUTOFMEM:
if (service->set->vsz_limit == 0)
return "Out of memory";
return t_strdup_printf("Out of memory (vsz_limit=%u MB)",
service->set->vsz_limit);
case FATAL_EXEC:
return "exec() failed";
case FATAL_DEFAULT:
return "Fatal failure";
}
return NULL;
}
static void log_coredump(struct service *service, string_t *str, int status)
{
#ifdef WCOREDUMP
int signum = WTERMSIG(status);
if (WCOREDUMP(status)) {
str_append(str, " (core dumped)");
return;
}
if (signum != SIGABRT && signum != SIGSEGV && signum != SIGBUS)
return;
/* let's try to figure out why we didn't get a core dump */
if (core_dumps_disabled) {
str_printfa(str, " (core dumps disabled)");
return;
}
#ifdef HAVE_PR_SET_DUMPABLE
if (!service->set->drop_priv_before_exec) {
str_append(str, " (core not dumped - set drop_priv_before_exec=yes)");
return;
}
if (*service->set->privileged_group != '\0') {
str_append(str, " (core not dumped - privileged_group prevented it)");
return;
}
#endif
str_append(str, " (core not dumped)");
#endif
}
static void
service_process_get_status_error(string_t *str, struct service_process *process,
int status, bool *default_fatal_r)
{
struct service *service = process->service;
const char *msg;
*default_fatal_r = FALSE;
str_printfa(str, "service(%s): child %s ", service->set->name,
dec2str(process->pid));
if (WIFSIGNALED(status)) {
str_printfa(str, "killed with signal %d", WTERMSIG(status));
log_coredump(service, str, status);
return;
}
if (!WIFEXITED(status)) {
str_printfa(str, "died with status %d", status);
return;
}
status = WEXITSTATUS(status);
if (status == 0) {
str_truncate(str, 0);
return;
}
str_printfa(str, "returned error %d", status);
msg = get_exit_status_message(service, status);
if (msg != NULL)
str_printfa(str, " (%s)", msg);
if (status == FATAL_DEFAULT)
*default_fatal_r = TRUE;
}
static void service_process_log(struct service_process *process,
bool default_fatal, const char *str)
{
const char *data;
if (!default_fatal || process->service->log_fd[1] == -1) {
i_error("%s", str);
return;
}
/* log it via the log process in charge of handling
this process's logging */
data = t_strdup_printf("%d %s DEFAULT-FATAL %s\n",
process->service->log_process_internal_fd,
dec2str(process->pid), str);
if (write(process->service->list->master_log_fd[1],
data, strlen(data)) < 0) {
i_error("write(log process) failed: %m");
i_error("%s", str);
}
}
void service_process_log_status_error(struct service_process *process,
int status)
{
if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
/* fast path */
return;
}
T_BEGIN {
string_t *str = t_str_new(256);
bool default_fatal;
service_process_get_status_error(str, process, status,
&default_fatal);
if (str_len(str) > 0)
service_process_log(process, default_fatal, str_c(str));
} T_END;
}