mail-process.c revision 36e556f8f4a320179dd680d261611e99a994a327
/* Copyright (C) 2002 Timo Sirainen */
#include "common.h"
#include "array.h"
#include "hash.h"
#include "fd-close-on-exec.h"
#include "env-util.h"
#include "str.h"
#include "network.h"
#include "mountpoint.h"
#include "restrict-access.h"
#include "restrict-process-size.h"
#include "home-expand.h"
#include "var-expand.h"
#include "mail-process.h"
#include "master-login-interface.h"
#include "login-process.h"
#include "log.h"
#include <stdlib.h>
#include <unistd.h>
#include <grp.h>
#include <syslog.h>
#ifdef HAVE_SYS_RESOURCE_H
# include <sys/resource.h>
#endif
/* Timeout chdir() completely after this many seconds */
#define CHDIR_TIMEOUT 30
/* Give a warning about chdir() taking a while if it took longer than this
many seconds to finish. */
#define CHDIR_WARN_SECS 10
struct mail_process_group {
/* process.type + user + remote_ip identifies this process group */
struct child_process process;
char *user;
/* processes array acts also as refcount */
};
/* type+user -> struct mail_process_group */
static struct hash_table *mail_process_groups;
static unsigned int mail_process_count = 0;
static unsigned int mail_process_group_hash(const void *p)
{
const struct mail_process_group *group = p;
}
{
int ret;
if (ret == 0)
ret = -1;
return ret;
}
static struct mail_process_group *
{
struct mail_process_group lookup_group;
}
static struct mail_process_group *
{
struct mail_process_group *group;
return group;
}
static void
{
}
{
}
const char *user)
{
if (uid == 0) {
return FALSE;
}
i_error("Logins with login process UID %s (user %s) "
"not permitted (see login_user in config file).",
}
i_error("Logins with UID %s (user %s) not permitted "
"(see first_valid_uid in config file)",
return FALSE;
}
i_error("Logins for users with primary group ID %s (user %s) "
"not permitted (see first_valid_gid in config file).",
return FALSE;
}
return TRUE;
}
{
const char *const *chroot_dirs;
if (*dir == '\0')
return FALSE;
return FALSE;
while (*chroot_dirs != NULL) {
if (**chroot_dirs != '\0' &&
return TRUE;
chroot_dirs++;
}
return FALSE;
}
static const struct var_expand_table *
get_var_expand_table(const char *protocol,
{
static struct var_expand_table static_tab[] = {
{ 'u', NULL },
{ 'n', NULL },
{ 'd', NULL },
{ 's', NULL },
{ 'h', NULL },
{ 'l', NULL },
{ 'r', NULL },
{ 'p', NULL },
{ 'i', NULL },
{ '\0', NULL }
};
struct var_expand_table *tab;
"/HOME_DIRECTORY_USED_BUT_NOT_GIVEN_BY_USERDB";
return tab;
}
static const char *
{
const char *p;
/* it's either type:data or just data */
if (p != NULL) {
while (env != p) {
env++;
}
}
if (env[0] == '~' &&
/* expand home */
}
/* expand %vars */
}
static void
const struct var_expand_table *table)
{
const char *location;
unsigned int i;
if (default_location == NULL)
default_location = "";
t_push();
}
}
/* expand variables, eg. ~%u/ can be useful */
}
t_pop();
}
}
static void
const struct var_expand_table *var_expand_table,
bool dump_capability)
{
const char *const *envs;
unsigned int i, count;
}
if (set->mail_save_crlf)
env_put("MAIL_SAVE_CRLF=1");
if (set->mmap_disable)
env_put("MMAP_DISABLE=1");
if (set->dotlock_use_excl)
env_put("DOTLOCK_USE_EXCL=1");
if (set->fsync_disable)
env_put("FSYNC_DISABLE=1");
env_put("MAILBOX_LIST_INDEX_DISABLE=1");
if (set->maildir_stat_dirs)
env_put("MAILDIR_STAT_DIRS=1");
env_put("MAILDIR_COPY_WITH_HARDLINKS=1");
env_put("MAILDIR_COPY_PRESERVE_FILENAME=1");
if (set->mail_debug)
env_put("DEBUG=1");
env_put("FULL_FILESYSTEM_ACCESS=1");
if (set->pop3_no_flag_updates)
env_put("POP3_NO_FLAG_UPDATES=1");
if (set->pop3_reuse_xuidl)
env_put("POP3_REUSE_XUIDL=1");
if (set->pop3_enable_last)
env_put("POP3_ENABLE_LAST=1");
if (set->pop3_lock_session)
env_put("POP3_LOCK_SESSION=1");
if (set->mbox_dirty_syncs)
env_put("MBOX_DIRTY_SYNCS=1");
if (set->mbox_very_dirty_syncs)
env_put("MBOX_VERY_DIRTY_SYNCS=1");
if (set->mbox_lazy_writes)
env_put("MBOX_LAZY_WRITES=1");
/* when running dump-capability log still points to stderr,
and io_add()ing it might break (epoll_ctl() gives EPERM) */
env_put("STDERR_CLOSE_SHUTDOWN=1");
set->mbox_lock_timeout));
set->dbox_rotate_size));
set->dbox_rotate_days));
}
/* user given environment - may be malicious. virtual_user comes from
auth process, but don't trust that too much either. Some auth
mechanism might allow leaving extra data there. */
}
for (i = 0; i < count; i += 2) {
str_truncate(str, 0);
}
}
{
const struct var_expand_table *var_expand_table;
const char *executable;
/* external binary. section contains path for it. */
i_fatal("External binary parameter not given");
} else {
break;
}
}
else
}
getenv("TCPLOCALIP"),
getenv("TCPREMOTEIP"),
/* set up logging */
env_put("USE_SYSLOG=1");
else
}
FALSE);
}
{
struct mountpoint point;
const char *path;
else {
/* indexes set separately */
path += 7;
return;
} else {
/* autodetection for path */
} else {
/* format:path */
path++;
}
}
}
return;
return;
i_fatal("Mailbox indexes in %s are in NFS mount. "
"You must set mmap_disable=yes to avoid index corruptions. "
"If you're sure this check was wrong, set nfs_check=no.", path);
}
enum master_login_status
bool dump_capability)
{
const struct var_expand_table *var_expand_table;
const char *system_user;
struct mail_process_group *process_group;
char title[1024];
ARRAY_DEFINE(extra_args, const char *);
bool home_given, nfs_check;
i_error("Maximum number of mail processes exceeded");
}
/* check process limit for this user */
set->mail_max_userip_connections != 0)
home_given = FALSE;
home_given = TRUE;
if (uid != 0) {
i_error("uid specified multiple times for %s",
user);
}
else {
}
}
/* wu-ftpd like <chroot>/./<home> */
home_dir = p + 2;
/* home directories should never be relative, but force this
with chroots. */
}
if (!dump_capability) {
}
if (*chroot_dir != '\0') {
i_error("Invalid chroot directory '%s' (user %s) "
"(see valid_chroot_dirs in config file)",
chroot_dir, user);
}
if (set->mail_drop_priv_before_exec) {
i_error("Can't chroot to directory '%s' (user %s) "
"with mail_drop_priv_before_exec=yes",
chroot_dir, user);
}
}
if (!dump_capability) {
if (log_fd == -1)
} else {
if (log_fd == -1) {
i_error("dup() failed: %m");
}
}
/* See if we need to do the initial NFS check. We want to do this only
once, so the check code needs to be before fork(). */
} else {
}
if (pid < 0) {
i_error("fork() failed: %m");
}
if (pid != 0) {
/* master */
if (!dump_capability)
if (process_group == NULL) {
user,
}
return MASTER_LOGIN_STATUS_OK;
}
#ifdef HAVE_SETPRIORITY
if (nice != 0) {
}
#endif
if (!dump_capability) {
}
/* move the client socket into stdin and stdout fds, log to stderr */
i_fatal("dup2(stdin) failed: %m");
i_fatal("dup2(stdout) failed: %m");
i_fatal("dup2(stderr) failed: %m");
for (i = 0; i < 3; i++)
fd_close_on_exec(i, FALSE);
/* setup environment - set the most important environment first
(paranoia about filling up environment without noticing) */
if (dump_capability)
env_put("DUMP_CAPABILITY=1");
if (*home_dir == '\0') {
full_home_dir = "";
ret = -1;
} else {
/* NOTE: if home directory is NFS-mounted, we might not
have access to it as root. Change the effective UID and GID
temporarily to make it work. */
if (uid != master_uid) {
}
chdir_errno = errno;
i_warning("chdir(%s) blocked for %u secs",
}
/* Change UID back. No need to change GID back, it doesn't
really matter. */
/* If user's home directory doesn't exist and we're not
trying to chroot anywhere, fallback to /tmp as the mails
could be stored elsewhere. The ENOTDIR check is mostly for
!(ENOTFOUND(chdir_errno) ||
chdir_errno == EINTR))) {
errno = chdir_errno;
i_fatal("chdir(%s) failed with uid %s: %m",
}
}
if (ret < 0) {
/* We still have to change to some directory where we have
rx-access. /tmp should exist everywhere. */
if (chdir("/tmp") < 0)
i_fatal("chdir(/tmp) failed: %m");
}
/* extra args. uppercase key value. */
for (i = 0; i < count; i++) {
if (*args[i] == '=') {
/* Should be caught by dovecot-auth already */
i_fatal("Userdb returned data with empty key (%s)",
args[i]);
}
if (p == NULL) {
/* boolean */
} else {
/* key=value */
}
}
if (nfs_check) {
/* ideally we should check all of the namespaces,
but for now don't bother. */
if (mail_location == NULL)
}
env_put("LOGGED_IN=1");
if (!set->verbose_proctitle)
title[0] = '\0';
else {
addr = "??";
}
/* make sure we don't leak syslog fd, but do it last so that
any errors above will be logged */
closelog();
/* not reached */
}
static void
{
unsigned int i, count;
if (count == 1) {
/* last process in this group */
} else {
for (i = 0; i < count; i++) {
break;
}
}
}
void mail_processes_init(void)
{
}
void mail_processes_deinit(void)
{
struct hash_iterate_context *iter;
}
}