mail-process.c revision b6d3625636be481e28ad8f39d138344fe751b76c
/* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
#include "common.h"
#include "array.h"
#include "hash.h"
#include "fd-close-on-exec.h"
#include "env-util.h"
#include "base64.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 "settings-parser.h"
#include "mail-process.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("user %s: Logins with login_user's UID %s "
"not permitted (see http://wiki.dovecot.org/UserIds).",
return FALSE;
}
i_error("user %s: Logins with UID %s not permitted "
"(see first_valid_uid in config file).",
return FALSE;
}
i_error("user %s: Logins for users with primary group ID %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,
{
#define VAR_EXPAND_HOME_IDX 4
static struct var_expand_table static_tab[] = {
};
struct var_expand_table *tab;
return tab;
}
{
/* we don't know all the settings, so since we can't expand all of
them just let the mail process expand all of them internally. */
}
{
const struct var_expand_table *var_expand_table;
struct master_settings *set;
const char *executable;
/* external binary. section contains path for it. */
i_fatal("External binary parameter not given");
executable = *args;
} else {
else
}
getenv("TCPLOCALIP"),
getenv("TCPREMOTEIP"),
/* set up logging */
env_put("USE_SYSLOG=1");
else
}
else
}
{
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 mail_nfs_index=yes to avoid index corruptions. "
"If you're sure this check was wrong, set nfs_check=no.", path);
}
enum master_login_status
const struct mail_login_request *request,
const unsigned char *data, bool dump_capability,
{
const struct var_expand_table *var_expand_table;
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 "
"(see max_mail_processes setting)");
}
home_given = FALSE;
home_given = TRUE;
const char *arg;
i_error("uid specified multiple times for %s",
user);
}
} else {
}
}
/* check process limit for this user, but not if this is a master
user login. */
set->mail_max_userip_connections != 0 &&
master_user == NULL)
i_error("User %s is missing UID (see mail_uid setting)",
user);
}
}
i_error("User %s is missing GID (see mail_gid setting)",
user);
}
}
/* wu-ftpd like <chroot>/./<home> - check only if there's even
a possibility of using them (non-empty valid_chroot_dirs)*/
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 setting)",
chroot_dir, user);
}
/* mail_chroot setting's value doesn't need to be in
valid_chroot_dirs. */
}
i_error("Can't chroot to directory '%s' (user %s) "
"with mail_drop_priv_before_exec=yes",
chroot_dir, user);
}
/* strip chroot dir from home dir */
}
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) {
}
}
return MASTER_LOGIN_STATUS_OK;
}
#ifdef HAVE_SETPRIORITY
if (nice_value != 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");
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 (*home_dir != '\0')
if (request->cmd_tag_size > 0) {
}
str_truncate(str, 0);
}
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();
if (set->mail_drop_priv_before_exec) {
/* privileged GID is now only in saved-GID. if we want to
preserve it accross exec, it needs to be temporarily
in effective gid */
}
/* 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;
}
}