mail-storage-service.c revision 48ada47cce07fb7195a3437224c7c25f542326b0
7cb128dc4cae2a03a742f63ba7afee23c78e3af0Phil Carmody/* Copyright (c) 2009 Dovecot authors, see the included COPYING file */
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen/* If time moves backwards more than this, kill ourself instead of sleeping. */
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainenstatic void set_keyval(struct setting_parser_context *set_parser,
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen const char *str;
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen if (settings_parse_line(set_parser, str) < 0) {
c2fbbf7515aa419dc8b2d62a3c2bb0471d51a391Timo Sirainenstatic bool validate_chroot(const struct mail_user_settings *user_set,
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen const char *dir)
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen const char *const *chroot_dirs;
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen chroot_dirs = t_strsplit(user_set->valid_chroot_dirs, ":");
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen strncmp(dir, *chroot_dirs, strlen(*chroot_dirs)) == 0)
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainenuser_reply_handle(struct mail_storage_service_user *user,
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen const char **error_r)
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen struct setting_parser_context *set_parser = user->set_parser;
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen unsigned int i, count;
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen set_keyval(set_parser, "mail_uid", dec2str(reply->uid));
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen set_keyval(set_parser, "mail_gid", dec2str(reply->gid));
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen set_keyval(set_parser, "mail_home", reply->home);
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen if (!validate_chroot(user->user_set, reply->chroot)) {
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen "userdb returned invalid chroot directory: %s "
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen "(see valid_chroot_dirs setting)",
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen set_keyval(set_parser, "mail_chroot", reply->chroot);
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen str = array_get(&reply->extra_fields, &count);
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen if (strncmp(line, "system_groups_user=", 19) == 0) {
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen if (n != 0) {
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen if (!settings_parse_is_valid_key(set_parser, key)) {
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen /* assume it's a plugin setting */
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen *error_r = t_strdup_printf("Invalid userdb input '%s': %s",
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen str[i], settings_parser_get_error(set_parser));
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainenservice_auth_userdb_lookup(struct mail_storage_service_ctx *ctx,
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen const struct mail_storage_service_input *input,
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen const char *const **fields_r,
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen const char **error_r)
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen ret = auth_master_user_lookup(ctx->conn, *user, &info, pool,
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen i_debug("changed username to %s", new_username);
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen } else if (ret == 0)
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainenstatic bool parse_uid(const char *str, uid_t *uid_r)
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen if (*p == '\0')
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainenstatic bool parse_gid(const char *str, gid_t *gid_r)
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen if (*p == '\0')
7da99e97d68f854b8726755d36dfb24b6cf08701Timo Sirainenservice_drop_privileges(const struct mail_user_settings *set,
7da99e97d68f854b8726755d36dfb24b6cf08701Timo Sirainen i_fatal("Unknown mail_uid user: %s", set->mail_uid);
7da99e97d68f854b8726755d36dfb24b6cf08701Timo Sirainen "not permitted (see first_valid_uid in config file).",
7da99e97d68f854b8726755d36dfb24b6cf08701Timo Sirainen i_fatal("Unknown mail_gid group: %s", set->mail_gid);
7da99e97d68f854b8726755d36dfb24b6cf08701Timo Sirainen "not permitted (see first_valid_gid in config file).",
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen if (!parse_gid(set->mail_privileged_group, &rset.privileged_gid)) {
da2f9bc5d477bdfee1773d7dcbcdd5a293c7d48cTimo Sirainen /* we can't chroot if we want to switch between users. there's not
da2f9bc5d477bdfee1773d7dcbcdd5a293c7d48cTimo Sirainen much point either (from security point of view) */
da2f9bc5d477bdfee1773d7dcbcdd5a293c7d48cTimo Sirainen rset.chroot_dir = *chroot == '\0' || keep_setuid_root ? NULL : chroot;
da2f9bc5d477bdfee1773d7dcbcdd5a293c7d48cTimo Sirainen (rset.uid == 0 || (rset.uid == (uid_t)-1 && current_euid == 0)))
da2f9bc5d477bdfee1773d7dcbcdd5a293c7d48cTimo Sirainen /* we're changing the UID,
da2f9bc5d477bdfee1773d7dcbcdd5a293c7d48cTimo Sirainen switch back to root first */
da2f9bc5d477bdfee1773d7dcbcdd5a293c7d48cTimo Sirainen restrict_access(&rset, *home == '\0' ? NULL : home, disallow_root);
da2f9bc5d477bdfee1773d7dcbcdd5a293c7d48cTimo Sirainen i_fatal("seteuid(%s) failed: %m", dec2str(setuid_uid));
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainenmail_storage_service_init_post(struct mail_storage_service_ctx *ctx,
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen const char *home, struct mail_user **mail_user_r,
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen const char **error_r)
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen mail_user = mail_user_alloc(user->input.username, user->user_info,
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen mail_user_set_home(mail_user, *home == '\0' ? NULL : home);
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen mail_user_set_vars(mail_user, geteuid(), ctx->service->name,
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen &user->input.local_ip, &user->input.remote_ip);
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen mail_set = mail_user_set_get_storage_set(mail_user);
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen dec2str(geteuid()), dec2str(getegid()), home);
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen if ((ctx->flags & MAIL_STORAGE_SERVICE_FLAG_TEMP_PRIV_DROP) != 0 &&
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen (ctx->flags & MAIL_STORAGE_SERVICE_FLAG_ENABLE_CORE_DUMPS) == 0) {
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen /* we don't want to write core files to any users' home
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen directories since they could contain information about other
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen users' mails as well. so do no chdiring to home. */
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen (ctx->flags & MAIL_STORAGE_SERVICE_FLAG_NO_CHDIR) == 0) {
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen /* If possible chdir to home directory, so that core file
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen could be written in case we crash. */
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen if (mail_namespaces_init(mail_user, error_r) < 0) {
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainenstatic const struct var_expand_table *
eef4ba0cc3e78f8c26804c1c9251a76580a41f0cTimo Sirainenget_var_expand_table(struct master_service *service,
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen static struct var_expand_table static_tab[] = {
da2f9bc5d477bdfee1773d7dcbcdd5a293c7d48cTimo Sirainen tab[1].value = t_strcut(input->username, '@');
da2f9bc5d477bdfee1773d7dcbcdd5a293c7d48cTimo Sirainen tab[5].value = net_ip2addr(&input->remote_ip);
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainenstatic const char *
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainenuser_expand_varstr(struct master_service *service,
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen struct mail_storage_service_input *input, const char *str)
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen i_assert(*str == SETTING_STRVAR_UNEXPANDED[0]);
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen var_expand(ret, str + 1, get_var_expand_table(service, input));
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainenmail_storage_service_init_log(struct master_service *service,
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen sets = master_service_settings_get_others(service);
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen var_expand(str, user->user_set->mail_log_prefix,
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainenstatic void mail_storage_service_time_moved(time_t old_time, time_t new_time)
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen i_warning("Time jumped forwards %ld seconds", diff);
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen i_fatal("Time just moved backwards by %ld seconds. "
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen "This might cause a lot of problems, "
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen "so I'll just kill myself now. "
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen "http://wiki.dovecot.org/TimeMovedBackwards", diff);
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen i_error("Time just moved backwards by %ld seconds. "
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen "I'll sleep now until we're back in present. "
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen "http://wiki.dovecot.org/TimeMovedBackwards", diff);
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen /* Sleep extra second to make sure usecs also grows. */
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen /* don't use sleep()'s return value, because
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen it could get us to a long loop in case
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen interrupts just keep coming */
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainenmail_storage_service_init(struct master_service *service,
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen const struct setting_parser_info *set_roots[],
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen unsigned int count;
eef4ba0cc3e78f8c26804c1c9251a76580a41f0cTimo Sirainen io_loop_set_time_moved_callback(current_ioloop,
eef4ba0cc3e78f8c26804c1c9251a76580a41f0cTimo Sirainen ctx = i_new(struct mail_storage_service_ctx, 1);
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen /* @UNSAFE */
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen for (count = 0; set_roots[count] != NULL; count++) ;
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen ctx->set_roots = i_new(const struct setting_parser_info *, count + 2);
eef4ba0cc3e78f8c26804c1c9251a76580a41f0cTimo Sirainen ctx->set_roots[0] = &mail_user_setting_parser_info;
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen memcpy(ctx->set_roots + 1, set_roots, sizeof(*ctx->set_roots) * count);
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen /* do all the global initialization. delay initializing plugins until
eef4ba0cc3e78f8c26804c1c9251a76580a41f0cTimo Sirainen we drop privileges the first time. */
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen if ((flags & MAIL_STORAGE_SERVICE_NO_LOG_INIT) == 0) {
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainenmail_storage_service_get_auth_conn(struct mail_storage_service_ctx *ctx)
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainensettings_parser_update_children_parent(struct setting_parser_info *parent,
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen unsigned int i, count;
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen for (count = 0; parent->defines[count].key != NULL; count++) ;
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen new_defs = p_new(pool, struct setting_define, count + 1);
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen memcpy(new_defs, parent->defines, sizeof(*new_defs) * count);
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen for (i = 0; i < count; i++) {
459f60325f94f486ef057241b42d8a9e9c376fb4Timo Sirainen new_info = p_new(pool, struct setting_parser_info, 1);
c2fbbf7515aa419dc8b2d62a3c2bb0471d51a391Timo Sirainen const struct setting_parser_info *old_parent, **new_roots;
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen struct setting_parser_info *new_parent, *new_info;
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen struct dynamic_settings_parser *new_dyn_parsers;
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen unsigned int i, count;
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen /* settings_parser_info_update() modifies the parent structure.
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen since we may be using the same structure later, we want it to be
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen in its original state, so we'll have to copy all structures. */
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen old_parent = input->dyn_parsers[0].info->parent;
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen new_parent = p_new(pool, struct setting_parser_info, 1);
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen settings_parser_update_children_parent(new_parent, pool);
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen /* update root */
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen for (count = 0; input->roots[count] != NULL; count++) ;
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen new_roots = p_new(pool, const struct setting_parser_info *, count + 1);
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen for (i = 0; i < count; i++) {
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen /* update parent in dyn_parsers */
c2fbbf7515aa419dc8b2d62a3c2bb0471d51a391Timo Sirainen for (count = 0; input->dyn_parsers[count].name != NULL; count++) ;
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen new_dyn_parsers = p_new(pool, struct dynamic_settings_parser, count + 1);
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen for (i = 0; i < count; i++) {
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen new_info = p_new(pool, struct setting_parser_info, 1);
51327f2489a4e0e615eb9f7d921473cf8512bb79Timo Sirainenint mail_storage_service_read_settings(struct mail_storage_service_ctx *ctx,
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen const struct mail_storage_service_input *input,
3a508ab3b10ff08889f3046a6bbf8553b55e3d44Timo Sirainen const struct setting_parser_info **user_info_r,
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen const char **error_r)
eef4ba0cc3e78f8c26804c1c9251a76580a41f0cTimo Sirainen struct master_service_settings_input set_input;
eef4ba0cc3e78f8c26804c1c9251a76580a41f0cTimo Sirainen unsigned int i;
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen set_input.dyn_parsers = mail_storage_get_dynamic_parsers();
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen /* settings reader may exec doveconf, which is going to clear
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen environment, and if we're not doing a userdb lookup we want to
da2f9bc5d477bdfee1773d7dcbcdd5a293c7d48cTimo Sirainen (ctx->flags & MAIL_STORAGE_SERVICE_FLAG_USERDB_LOOKUP) == 0;
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen if (master_service_settings_read(ctx->service, &set_input,
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen *error_r = t_strdup_printf("Error reading configuration: %s",
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen if (ctx->set_roots[i] == &mail_user_setting_parser_info) {
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainenmail_storage_service_first_init(struct mail_storage_service_ctx *ctx,
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen mail_set = mail_user_set_get_driver_settings(user_info, user_set,
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen ctx->conn = auth_master_init(user_set->auth_socket_path,
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainenmail_storage_service_load_modules(struct mail_storage_service_ctx *ctx,
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen version = master_service_get_version_string(ctx->service);
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen modules = module_dir_load_missing(modules, user_set->mail_plugin_dir,
const char **error_r)
const char *const *userdb_fields;
void **sets;
error_r);
if (ret <= 0) {
return ret;
return ret;
const char **error_r)
unsigned int len;
bool temp_priv_drop =
if (!temp_priv_drop ||
if (!temp_priv_drop) {
const char **error_r)
const char *error;
int ret;
if (ret <= 0) {
return ret;
const char *error;
void **sets;
const char **username_r)
const struct mail_storage_service_input *
struct setting_parser_context *
T_BEGIN {
} T_END;
return set;