mail-storage-service.c revision 19cadcc25c26af7afea1355d78e20ad64eaad263
/* Copyright (c) 2009-2011 Dovecot authors, see the included COPYING file */
#include "lib.h"
#include "ioloop.h"
#include "array.h"
#include "hostpid.h"
#include "module-dir.h"
#include "restrict-access.h"
#include "eacces-error.h"
#include "ipwd.h"
#include "str.h"
#include "var-expand.h"
#include "dict.h"
#include "settings-parser.h"
#include "auth-master.h"
#include "master-service-private.h"
#include "master-service-settings.h"
#include "master-service-settings-cache.h"
#include "mail-user.h"
#include "mail-namespace.h"
#include "mail-storage.h"
#include "mail-storage-service.h"
#include <stdlib.h>
#ifdef HAVE_SYS_TIME_H
#endif
#ifdef HAVE_SYS_RESOURCE_H
# include <sys/resource.h>
#endif
/* If time moves backwards more than this, kill ourself instead of sleeping. */
#define MAX_TIME_BACKWARDS_SLEEP 5
#define MAX_NOWARN_FORWARD_SECS 10
#define ERRSTR_INVALID_USER_SETTINGS \
"Invalid user settings. Refer to server log for more information."
struct mail_storage_service_privileges {
const char *uid_source, *gid_source;
const char *home;
const char *chroot;
};
struct mail_storage_service_ctx {
struct master_service *service;
const char *default_log_prefix;
struct auth_master_connection *conn;
struct auth_master_user_list_ctx *auth_list;
const struct setting_parser_info **set_roots;
const char *set_cache_module, *set_cache_service;
struct master_service_settings_cache *set_cache;
unsigned int debug:1;
unsigned int log_initialized:1;
};
struct mail_storage_service_user {
struct mail_storage_service_ctx *service_ctx;
struct mail_storage_service_input input;
struct ioloop_context *ioloop_ctx;
const char *log_prefix;
const struct mail_user_settings *user_set;
const struct setting_parser_info *user_info;
struct setting_parser_context *set_parser;
};
static bool
const struct mail_user_settings *user_set)
{
const struct mail_storage_settings *mail_set;
return mail_set->mail_debug;
}
struct mail_storage_service_user *user,
{
const char *str;
/* this setting was already overridden with -o parameter */
i_debug("Ignoring overridden (-o) userdb setting: %s",
key);
}
return;
}
}
}
struct mail_storage_service_user *user,
const char *line)
{
bool mail_debug;
unsigned int len;
int ret;
/* key+=value */
}
/* assume it's a plugin setting */
}
/* this setting was already overridden with -o parameter */
if (mail_debug) {
i_debug("Ignoring overridden (-o) userdb setting: %s",
key);
}
return 1;
}
if (append_value != NULL) {
const void *value;
enum setting_type type;
} else {
i_error("Ignoring %s userdb setting. "
"'+' can only be used for strings.", orig_key);
}
}
if (mail_debug && ret >= 0) {
/* possibly a password field (e.g. imapc_password).
hide the value. */
}
"Unknown userdb setting: %s" :
"Added userdb setting: %s", line);
}
return ret;
}
const char *dir)
{
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 int
struct mail_storage_service_user *user,
const struct auth_user_reply *reply,
const char **error_r)
{
unsigned int i, count;
int ret = 0;
*error_r = "userdb returned 0 as uid";
return -1;
}
}
}
/* wu-ftpd like <chroot>/./<home> - check only if there's even
a possibility of using them (non-empty valid_chroot_dirs) */
home = p + 2;
}
"userdb returned invalid chroot directory: %s "
"(see valid_chroot_dirs setting)", chroot);
return -1;
}
}
#ifdef HAVE_SETPRIORITY
if (n != 0) {
if (setpriority(PRIO_PROCESS, 0, n) < 0)
i_error("setpriority(%d) failed: %m", n);
}
#endif
} else T_BEGIN {
} T_END;
}
if (ret < 0) {
}
return ret;
}
static int
const struct mail_storage_service_input *input,
const char *const **fields_r,
const char **error_r)
{
struct auth_user_info info;
const char *new_username;
int ret;
&new_username, fields_r);
if (ret > 0) {
}
*user = new_username;
} else if (ret == 0)
*error_r = "Unknown user";
ret = -2;
} else {
}
return ret;
}
{
return TRUE;
case -1:
return FALSE;
case 0:
return FALSE;
default:
return TRUE;
}
}
{
return TRUE;
case -1:
return FALSE;
case 0:
return FALSE;
default:
return TRUE;
}
}
static const struct var_expand_table *
struct mail_storage_service_input *input,
struct mail_storage_service_privileges *priv)
{
static struct var_expand_table static_tab[] = {
};
struct var_expand_table *tab;
return tab;
}
const struct var_expand_table *
struct mail_storage_service_input *input)
{
struct mail_storage_service_privileges priv;
}
static const char *
struct mail_storage_service_input *input,
struct mail_storage_service_privileges *priv,
const char *str)
{
if (*str == SETTING_STRVAR_EXPANDED[0])
return str + 1;
}
static int
struct mail_storage_service_user *user,
struct mail_storage_service_privileges *priv_r,
const char **error_r)
{
user->uid_source);
return -1;
}
(set->last_valid_uid != 0 &&
"Mail access for users with UID %s not permitted "
"(see first_valid_uid in config file, uid from %s).",
return -1;
}
}
user->gid_source);
return -1;
}
(set->last_valid_gid != 0 &&
"Mail access for users with GID %s not permitted "
"(see first_valid_gid in config file, gid from %s).",
return -1;
}
}
/* variable strings are expanded in mail_user_init(),
but we need the home and chroot sooner so do them separately here. */
return 0;
}
static int
struct mail_storage_service_privileges *priv,
bool disallow_root, bool keep_setuid_root,
bool setenv_only, const char **error_r)
{
struct restrict_access_settings rset;
const char *cur_chroot, *error;
current_euid = geteuid();
disallow_root && current_euid == 0) {
*error_r = "User is missing UID (see mail_uid setting)";
return -1;
}
*error_r = "User is missing GID (see mail_gid setting)";
return -1;
}
&error)) {
"%s (in mail_privileged_group setting)", error);
return -1;
}
}
}
if (cur_chroot != NULL) {
/* we're already chrooted. make sure the chroots are equal. */
*error_r = "Process is already chrooted, "
"can't un-chroot for this user";
return -1;
}
"Process is already chrooted to %s, "
return -1;
}
/* chrooting to same directory where we're already chrooted */
}
if (disallow_root &&
*error_r = "Mail access not allowed for root";
return -1;
}
if (keep_setuid_root) {
if (current_euid != 0) {
/* we're changing the UID,
switch back to root first */
if (seteuid(0) < 0)
i_fatal("seteuid(0) failed: %m");
}
}
}
if (!setenv_only) {
} else {
}
if (setuid_uid != 0 && !setenv_only) {
if (seteuid(setuid_uid) < 0)
}
return 0;
}
static int
struct mail_storage_service_user *user,
struct mail_storage_service_privileges *priv,
struct mail_user **mail_user_r,
const char **error_r)
{
const struct mail_storage_settings *mail_set;
if (mail_set->mail_debug) {
i_debug("Effective uid=%s, gid=%s, home=%s",
}
/* we don't want to write core files to any users' home
directories since they could contain information about other
users' mails as well. so do no chdiring to home. */
} else if (*home != '\0' &&
/* If possible chdir to home directory, so that core file
could be written in case we crash. */
else if (mail_set->mail_debug)
}
}
return -1;
}
return -1;
}
}
*mail_user_r = mail_user;
return 0;
}
static void mail_storage_service_io_activate(void *context)
{
}
static void mail_storage_service_io_deactivate(void *context)
{
}
static void
struct mail_storage_service_user *user,
struct mail_storage_service_privileges *priv)
{
T_BEGIN {
} T_END;
user);
}
{
if (diff > 0) {
if (diff > MAX_NOWARN_FORWARD_SECS)
return;
}
if (diff > MAX_TIME_BACKWARDS_SLEEP) {
i_fatal("Time just moved backwards by %ld seconds. "
"This might cause a lot of problems, "
"so I'll just kill myself now. "
} else {
i_error("Time just moved backwards by %ld seconds. "
"I'll sleep now until we're back in present. "
/* Sleep extra second to make sure usecs also grows. */
diff++;
/* don't use sleep()'s return value, because
it could get us to a long loop in case
interrupts just keep coming */
}
}
}
struct mail_storage_service_ctx *
const struct setting_parser_info *set_roots[],
{
struct mail_storage_service_ctx *ctx;
unsigned int count;
(void)umask(0077);
/* @UNSAFE */
count = 0;
else
}
/* do all the global initialization. delay initializing plugins until
we drop privileges the first time. */
if ((flags & MAIL_STORAGE_SERVICE_FLAG_NO_LOG_INIT) == 0) {
/* note: we may not have read any settings yet, so this logging
may still be going to wrong location */
}
return ctx;
}
struct auth_master_connection *
{
}
static enum mail_storage_service_flags
const struct mail_storage_service_input *input)
{
if (input->no_userdb_lookup) {
/* FIXME: for API backwards compatibility only */
}
return flags;
}
const struct mail_storage_service_input *input,
const struct setting_parser_info **user_info_r,
const struct setting_parser_context **parser_r,
const char **error_r)
{
const struct setting_parser_info *const *roots;
const struct dynamic_settings_parser *dyn_parsers;
unsigned int i;
/* settings reader may exec doveconf, which is going to clear
environment, and if we're not doing a userdb lookup we want to
use $HOME */
(flags & MAIL_STORAGE_SERVICE_FLAG_USERDB_LOOKUP) == 0;
(flags & MAIL_STORAGE_SERVICE_FLAG_USE_SYSEXITS) != 0;
}
} else {
/* already looked up settings at least once.
we really shouldn't be execing anymore. */
}
"Error reading configuration: %s", *error_r);
return -1;
}
} else {
&set_output, error_r) < 0) {
"Error reading configuration: %s", *error_r);
return -1;
}
}
mail_user_setting_parser_info.module_name) == 0) {
*user_info_r = roots[i];
return 0;
}
}
i_unreached();
return -1;
}
static void
const struct setting_parser_info *user_info,
const struct mail_user_settings *user_set)
{
enum auth_master_flags flags = 0;
}
static void
const struct setting_parser_info *user_info,
const struct mail_user_settings *user_set)
{
struct module_dir_load_settings mod_set;
return;
return;
}
const struct mail_storage_service_input *input,
struct mail_storage_service_user **user_r,
const char **error_r)
{
struct mail_storage_service_user *user;
const struct setting_parser_info *user_info;
const struct mail_user_settings *user_set;
const char *const *userdb_fields, *error;
struct auth_user_reply reply;
const struct setting_parser_context *set_parser;
int ret = 1;
&user_info, &set_parser,
&error) < 0) {
return -1;
}
if ((flags & MAIL_STORAGE_SERVICE_FLAG_NO_LOG_INIT) == 0 &&
!ctx->log_initialized) {
/* initialize logging again, in case we only read the
settings for the first above */
}
/* load global plugins */
if ((flags & MAIL_STORAGE_SERVICE_FLAG_USERDB_LOOKUP) != 0) {
error_r);
if (ret <= 0) {
return ret;
}
} else {
}
if ((flags & MAIL_STORAGE_SERVICE_FLAG_DEBUG) != 0)
if ((flags & MAIL_STORAGE_SERVICE_FLAG_USERDB_LOOKUP) == 0) {
}
if (userdb_fields != NULL) {
i_error("user %s: Invalid settings in userdb: %s",
ret = -2;
}
}
/* load per-user plugins */
if (ret > 0) {
}
return ret;
}
struct mail_storage_service_user *user,
struct mail_user **mail_user_r)
{
struct mail_storage_service_privileges priv;
const char *error;
unsigned int len;
bool disallow_root =
bool temp_priv_drop =
bool use_chroot;
return -2;
}
i_error("user %s: "
"Relative home directory paths not supported: %s",
return -2;
}
/* we can't chroot if we want to switch between users. there's
not much point either (from security point of view). but if we're
already chrooted, we'll just have to continue and hope that the
current chroot is the same as the wanted chroot */
use_chroot = !temp_priv_drop ||
/* mail_chroot = /chroot/. means that the home dir already
contains the chroot dir. remove it from home. */
if (use_chroot) {
}
} else if (len > 0 && !use_chroot) {
/* we're not going to chroot. fix home directory so we can
access it. */
else
}
i_error("user %s: Couldn't drop privileges: %s",
return -1;
}
if (!temp_priv_drop ||
}
/* privileges are dropped. initialize plugins that haven't been
initialized yet. */
mail_user_r, &error) < 0) {
i_error("user %s: Initialization failed: %s",
return -2;
}
return 0;
}
struct mail_storage_service_user *user)
{
struct mail_storage_service_privileges priv;
const char *error;
}
const struct mail_storage_service_input *input,
struct mail_storage_service_user **user_r,
struct mail_user **mail_user_r,
const char **error_r)
{
struct mail_storage_service_user *user;
int ret;
if (ret <= 0)
return ret;
if (ret < 0) {
return ret;
}
return 1;
}
{
}
}
const struct mail_storage_service_input *input)
{
const struct setting_parser_info *user_info;
const struct mail_user_settings *user_set;
const struct setting_parser_context *set_parser;
const char *error;
return;
&user_info, &set_parser,
&error) < 0)
}
unsigned int
{
}
const char **username_r)
{
if (*username_r != NULL)
return 1;
}
{
}
}
{
}
const struct mail_storage_service_input *
{
}
struct setting_parser_context *
{
return user->set_parser;
}
{
T_BEGIN {
} T_END;
return set;
}