mail-storage-service.c revision 674f541b16689c0ed090dac32db94463c5af3977
76b43e4417bab52e913da39b5f5bc2a130d3f149Timo Sirainen/* Copyright (c) 2009-2010 Dovecot authors, see the included COPYING file */
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen#include "lib.h"
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen#include "ioloop.h"
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen#include "array.h"
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen#include "hostpid.h"
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen#include "module-dir.h"
16f816d3f3c32ae3351834253f52ddd0212bcbf3Timo Sirainen#include "restrict-access.h"
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen#include "eacces-error.h"
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen#include "str.h"
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen#include "var-expand.h"
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen#include "dict.h"
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen#include "settings-parser.h"
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen#include "auth-master.h"
181c1aff950e6f8e0556f8974e79d0747845ac0fTimo Sirainen#include "master-service-private.h"
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen#include "master-service-settings.h"
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen#include "master-service-settings-cache.h"
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen#include "mail-user.h"
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen#include "mail-namespace.h"
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen#include "mail-storage.h"
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen#include "mail-storage-service.h"
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen#include <stdlib.h>
6e1e9e341ffe21a69a23229c2b896d03066a071eTimo Sirainen#include <sys/stat.h>
6e1e9e341ffe21a69a23229c2b896d03066a071eTimo Sirainen#include <pwd.h>
6e1e9e341ffe21a69a23229c2b896d03066a071eTimo Sirainen#include <grp.h>
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen#ifdef HAVE_SYS_TIME_H
0ea17cea21df405e6e74a167d08e1ff0ee95dd13Timo Sirainen# include <sys/time.h>
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen#endif
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen#ifdef HAVE_SYS_RESOURCE_H
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen# include <sys/resource.h>
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen#endif
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen/* If time moves backwards more than this, kill ourself instead of sleeping. */
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen#define MAX_TIME_BACKWARDS_SLEEP 5
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen#define MAX_NOWARN_FORWARD_SECS 10
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainenstruct mail_storage_service_ctx {
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen pool_t pool;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen struct master_service *service;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen struct auth_master_connection *conn;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen struct auth_master_user_list_ctx *auth_list;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen const struct setting_parser_info **set_roots;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen enum mail_storage_service_flags flags;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen const char *set_cache_module, *set_cache_service;
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen struct master_service_settings_cache *set_cache;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen const struct dynamic_settings_parser *set_cache_dyn_parsers;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen struct setting_parser_info *set_cache_dyn_parsers_parent;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen const struct setting_parser_info **set_cache_roots;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen unsigned int debug:1;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen};
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen
0ea17cea21df405e6e74a167d08e1ff0ee95dd13Timo Sirainenstruct mail_storage_service_user {
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen pool_t pool;
0ea17cea21df405e6e74a167d08e1ff0ee95dd13Timo Sirainen struct mail_storage_service_input input;
0ea17cea21df405e6e74a167d08e1ff0ee95dd13Timo Sirainen
0ea17cea21df405e6e74a167d08e1ff0ee95dd13Timo Sirainen const char *system_groups_user;
0ea17cea21df405e6e74a167d08e1ff0ee95dd13Timo Sirainen const struct mail_user_settings *user_set;
0ea17cea21df405e6e74a167d08e1ff0ee95dd13Timo Sirainen const struct setting_parser_info *user_info;
0ea17cea21df405e6e74a167d08e1ff0ee95dd13Timo Sirainen struct setting_parser_context *set_parser;
0ea17cea21df405e6e74a167d08e1ff0ee95dd13Timo Sirainen};
0ea17cea21df405e6e74a167d08e1ff0ee95dd13Timo Sirainen
0ea17cea21df405e6e74a167d08e1ff0ee95dd13Timo Sirainenstatic struct module *modules = NULL;
0ea17cea21df405e6e74a167d08e1ff0ee95dd13Timo Sirainen
0ea17cea21df405e6e74a167d08e1ff0ee95dd13Timo Sirainenstatic void set_keyval(struct setting_parser_context *set_parser,
0ea17cea21df405e6e74a167d08e1ff0ee95dd13Timo Sirainen const char *key, const char *value)
0ea17cea21df405e6e74a167d08e1ff0ee95dd13Timo Sirainen{
0ea17cea21df405e6e74a167d08e1ff0ee95dd13Timo Sirainen const char *str;
0449e50c99f3fab4485f9b983e3936aaa003c79aTimo Sirainen
91dca97b367c54a139c268b56a0c67f564bd9197Timo Sirainen str = t_strconcat(key, "=", value, NULL);
3ccab0bac68040f179a7de45c516cec258e28fdbTimo Sirainen if (settings_parse_line(set_parser, str) < 0) {
3ccab0bac68040f179a7de45c516cec258e28fdbTimo Sirainen i_fatal("Invalid userdb input '%s': %s", str,
6e1e9e341ffe21a69a23229c2b896d03066a071eTimo Sirainen settings_parser_get_error(set_parser));
6e1e9e341ffe21a69a23229c2b896d03066a071eTimo Sirainen }
0ea17cea21df405e6e74a167d08e1ff0ee95dd13Timo Sirainen}
8da095519878426b012058e6f331a669f327f47fTimo Sirainen
8da095519878426b012058e6f331a669f327f47fTimo Sirainenstatic bool validate_chroot(const struct mail_user_settings *user_set,
8da095519878426b012058e6f331a669f327f47fTimo Sirainen const char *dir)
6e1e9e341ffe21a69a23229c2b896d03066a071eTimo Sirainen{
6e1e9e341ffe21a69a23229c2b896d03066a071eTimo Sirainen const char *const *chroot_dirs;
0ea17cea21df405e6e74a167d08e1ff0ee95dd13Timo Sirainen
0ea17cea21df405e6e74a167d08e1ff0ee95dd13Timo Sirainen if (*dir == '\0')
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen return FALSE;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen if (*user_set->valid_chroot_dirs == '\0')
9740d55b228a670047c854484d5cc979a056a9afTimo Sirainen return FALSE;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen
9740d55b228a670047c854484d5cc979a056a9afTimo Sirainen chroot_dirs = t_strsplit(user_set->valid_chroot_dirs, ":");
9740d55b228a670047c854484d5cc979a056a9afTimo Sirainen while (*chroot_dirs != NULL) {
9740d55b228a670047c854484d5cc979a056a9afTimo Sirainen if (**chroot_dirs != '\0' &&
3ccab0bac68040f179a7de45c516cec258e28fdbTimo Sirainen strncmp(dir, *chroot_dirs, strlen(*chroot_dirs)) == 0)
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen return TRUE;
648d24583c1574441c4fa0331a90bd4d6e7996c5Timo Sirainen chroot_dirs++;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen }
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen return FALSE;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen}
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainenstatic int
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainenuser_reply_handle(struct mail_storage_service_user *user,
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen const struct auth_user_reply *reply,
3ccab0bac68040f179a7de45c516cec258e28fdbTimo Sirainen const char **error_r)
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen{
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen struct setting_parser_context *set_parser = user->set_parser;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen const char *const *str, *p, *line, *key;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen unsigned int i, count;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen int ret = 0;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen if (reply->uid != (uid_t)-1) {
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen if (reply->uid == 0) {
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen *error_r = "userdb returned 0 as uid";
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen return -1;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen }
1e47cfede3a0b62654105daab00e97b5d660bc6bTimo Sirainen set_keyval(set_parser, "mail_uid", dec2str(reply->uid));
1e47cfede3a0b62654105daab00e97b5d660bc6bTimo Sirainen }
1e47cfede3a0b62654105daab00e97b5d660bc6bTimo Sirainen if (reply->gid != (uid_t)-1)
1e47cfede3a0b62654105daab00e97b5d660bc6bTimo Sirainen set_keyval(set_parser, "mail_gid", dec2str(reply->gid));
04b8a90af181cc4c7959266855e8ed50a22ed413Timo Sirainen
04b8a90af181cc4c7959266855e8ed50a22ed413Timo Sirainen if (reply->home != NULL)
04b8a90af181cc4c7959266855e8ed50a22ed413Timo Sirainen set_keyval(set_parser, "mail_home", reply->home);
04b8a90af181cc4c7959266855e8ed50a22ed413Timo Sirainen
04b8a90af181cc4c7959266855e8ed50a22ed413Timo Sirainen if (reply->chroot != NULL) {
181c1aff950e6f8e0556f8974e79d0747845ac0fTimo Sirainen if (!validate_chroot(user->user_set, reply->chroot)) {
181c1aff950e6f8e0556f8974e79d0747845ac0fTimo Sirainen *error_r = t_strdup_printf(
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen "userdb returned invalid chroot directory: %s "
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen "(see valid_chroot_dirs setting)",
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen reply->chroot);
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen return -1;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen }
0ea17cea21df405e6e74a167d08e1ff0ee95dd13Timo Sirainen set_keyval(set_parser, "mail_chroot", reply->chroot);
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen }
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen str = array_get(&reply->extra_fields, &count);
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen for (i = 0; i < count && ret == 0; i++) {
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen line = str[i];
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen if (strncmp(line, "system_groups_user=", 19) == 0) {
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen user->system_groups_user =
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen p_strdup(user->pool, line + 19);
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen } else if (strncmp(line, "nice=", 5) == 0) {
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen#ifdef HAVE_SETPRIORITY
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen int n = atoi(line + 5);
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen if (n != 0) {
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen if (setpriority(PRIO_PROCESS, 0, n) < 0)
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen i_error("setpriority(%d) failed: %m", n);
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen }
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen#endif
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen } else T_BEGIN {
0ea17cea21df405e6e74a167d08e1ff0ee95dd13Timo Sirainen if ((p = strchr(str[i], '=')) == NULL)
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen line = t_strconcat(str[i], "=yes", NULL);
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen else
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen line = str[i];
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen key = t_strcut(line, '=');
2526d52441ef368215ab6bf04fd0356d3b09d235Timo Sirainen if (!settings_parse_is_valid_key(set_parser, key)) {
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen /* assume it's a plugin setting */
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen line = t_strconcat("plugin/", line, NULL);
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen }
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen ret = settings_parse_line(set_parser, line);
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen } T_END;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen }
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen if (ret < 0) {
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen *error_r = t_strdup_printf("Invalid userdb input '%s': %s",
bd2e2500e64c00349ee00e28797809b4029db52fTimo Sirainen str[i], settings_parser_get_error(set_parser));
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen }
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen return ret;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen}
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainenstatic int
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainenservice_auth_userdb_lookup(struct mail_storage_service_ctx *ctx,
3ccab0bac68040f179a7de45c516cec258e28fdbTimo Sirainen const struct mail_storage_service_input *input,
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen pool_t pool, const char **user,
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen const char *const **fields_r,
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen const char **error_r)
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen{
cd56a23e21f1df3f79648cf07e2f4385e2fadebbTimo Sirainen struct auth_user_info info;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen const char *new_username;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen int ret;
0ea17cea21df405e6e74a167d08e1ff0ee95dd13Timo Sirainen
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen memset(&info, 0, sizeof(info));
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen info.service = ctx->service->name;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen info.local_ip = input->local_ip;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen info.remote_ip = input->remote_ip;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen ret = auth_master_user_lookup(ctx->conn, *user, &info, pool,
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen &new_username, fields_r);
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen if (ret > 0) {
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen if (strcmp(*user, new_username) != 0) {
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen if (ctx->debug)
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen i_debug("changed username to %s", new_username);
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen *user = t_strdup(new_username);
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen }
3ccab0bac68040f179a7de45c516cec258e28fdbTimo Sirainen *user = new_username;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen } else if (ret == 0)
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen *error_r = "unknown user";
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen else
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen *error_r = "userdb lookup failed";
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen return ret;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen}
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen
3ccab0bac68040f179a7de45c516cec258e28fdbTimo Sirainenstatic bool parse_uid(const char *str, uid_t *uid_r)
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen{
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen struct passwd *pw;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen char *p;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen if (*str >= '0' && *str <= '9') {
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen *uid_r = (uid_t)strtoul(str, &p, 10);
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen if (*p == '\0')
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen return TRUE;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen }
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen pw = getpwnam(str);
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen if (pw == NULL)
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen return FALSE;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen *uid_r = pw->pw_uid;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen return TRUE;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen}
3ccab0bac68040f179a7de45c516cec258e28fdbTimo Sirainen
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainenstatic bool parse_gid(const char *str, gid_t *gid_r)
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen{
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen struct group *gr;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen char *p;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen if (*str >= '0' && *str <= '9') {
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen *gid_r = (gid_t)strtoul(str, &p, 10);
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen if (*p == '\0')
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen return TRUE;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen }
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen gr = getgrnam(str);
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen if (gr == NULL)
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen return FALSE;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen *gid_r = gr->gr_gid;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen return TRUE;
3bc2b84acd8c5731a0bc9d69fbcd3103b68e9b52Timo Sirainen}
3bc2b84acd8c5731a0bc9d69fbcd3103b68e9b52Timo Sirainen
3bc2b84acd8c5731a0bc9d69fbcd3103b68e9b52Timo Sirainenstatic void
3bc2b84acd8c5731a0bc9d69fbcd3103b68e9b52Timo Sirainenservice_drop_privileges(const struct mail_user_settings *set,
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen const char *system_groups_user,
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen const char *home, const char *chroot,
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen bool disallow_root, bool keep_setuid_root,
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen bool setenv_only)
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen{
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen struct restrict_access_settings rset;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen uid_t current_euid, setuid_uid = 0;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen current_euid = geteuid();
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen restrict_access_init(&rset);
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen if (*set->mail_uid != '\0') {
ba3d9eeb0bec6ed8465d68fa2480ad085559b580Timo Sirainen if (!parse_uid(set->mail_uid, &rset.uid))
ba3d9eeb0bec6ed8465d68fa2480ad085559b580Timo Sirainen i_fatal("Unknown mail_uid user: %s", set->mail_uid);
ba3d9eeb0bec6ed8465d68fa2480ad085559b580Timo Sirainen if (rset.uid < (uid_t)set->first_valid_uid ||
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen (set->last_valid_uid != 0 &&
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen rset.uid > (uid_t)set->last_valid_uid)) {
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen i_fatal("Mail access for users with UID %s "
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen "not permitted (see first_valid_uid in config file).",
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen dec2str(rset.uid));
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen }
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen }
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen if (*set->mail_gid != '\0') {
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen if (!parse_gid(set->mail_gid, &rset.gid))
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen i_fatal("Unknown mail_gid group: %s", set->mail_gid);
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen if (rset.gid < (gid_t)set->first_valid_gid ||
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen (set->last_valid_gid != 0 &&
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen rset.gid > (gid_t)set->last_valid_gid)) {
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen i_fatal("Mail access for users with GID %s "
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen "not permitted (see first_valid_gid in config file).",
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen dec2str(rset.gid));
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen }
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen }
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen if (*set->mail_privileged_group != '\0') {
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen if (!parse_gid(set->mail_privileged_group, &rset.privileged_gid)) {
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen i_fatal("Unknown mail_privileged_group: %s",
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen set->mail_gid);
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen }
8a6fc472a12c5ec09e2bd19f1b3acd553d75ff91Timo Sirainen }
8a6fc472a12c5ec09e2bd19f1b3acd553d75ff91Timo Sirainen if (*set->mail_access_groups != '\0')
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen rset.extra_groups = set->mail_access_groups;
8a6fc472a12c5ec09e2bd19f1b3acd553d75ff91Timo Sirainen
8a6fc472a12c5ec09e2bd19f1b3acd553d75ff91Timo Sirainen rset.first_valid_gid = set->first_valid_gid;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen rset.last_valid_gid = set->last_valid_gid;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen /* we can't chroot if we want to switch between users. there's not
8a6fc472a12c5ec09e2bd19f1b3acd553d75ff91Timo Sirainen much point either (from security point of view) */
8a6fc472a12c5ec09e2bd19f1b3acd553d75ff91Timo Sirainen rset.chroot_dir = *chroot == '\0' || keep_setuid_root ? NULL : chroot;
8a6fc472a12c5ec09e2bd19f1b3acd553d75ff91Timo Sirainen rset.system_groups_user = system_groups_user;
8a6fc472a12c5ec09e2bd19f1b3acd553d75ff91Timo Sirainen
8a6fc472a12c5ec09e2bd19f1b3acd553d75ff91Timo Sirainen if (disallow_root &&
8a6fc472a12c5ec09e2bd19f1b3acd553d75ff91Timo Sirainen (rset.uid == 0 || (rset.uid == (uid_t)-1 && current_euid == 0)))
8a6fc472a12c5ec09e2bd19f1b3acd553d75ff91Timo Sirainen i_fatal("Mail access not allowed for root");
8a6fc472a12c5ec09e2bd19f1b3acd553d75ff91Timo Sirainen
8a6fc472a12c5ec09e2bd19f1b3acd553d75ff91Timo Sirainen if (keep_setuid_root) {
0ea17cea21df405e6e74a167d08e1ff0ee95dd13Timo Sirainen if (current_euid != rset.uid) {
8a6fc472a12c5ec09e2bd19f1b3acd553d75ff91Timo Sirainen if (current_euid != 0) {
8a6fc472a12c5ec09e2bd19f1b3acd553d75ff91Timo Sirainen /* we're changing the UID,
8a6fc472a12c5ec09e2bd19f1b3acd553d75ff91Timo Sirainen switch back to root first */
8a6fc472a12c5ec09e2bd19f1b3acd553d75ff91Timo Sirainen if (seteuid(0) < 0)
8a6fc472a12c5ec09e2bd19f1b3acd553d75ff91Timo Sirainen i_fatal("seteuid(0) failed: %m");
0ea17cea21df405e6e74a167d08e1ff0ee95dd13Timo Sirainen }
8a6fc472a12c5ec09e2bd19f1b3acd553d75ff91Timo Sirainen setuid_uid = rset.uid;
8a6fc472a12c5ec09e2bd19f1b3acd553d75ff91Timo Sirainen }
8a6fc472a12c5ec09e2bd19f1b3acd553d75ff91Timo Sirainen rset.uid = (uid_t)-1;
8a6fc472a12c5ec09e2bd19f1b3acd553d75ff91Timo Sirainen disallow_root = FALSE;
47001341950b8588c5f3a96b75864dab48e279aeTimo Sirainen }
47001341950b8588c5f3a96b75864dab48e279aeTimo Sirainen if (!setenv_only) {
47001341950b8588c5f3a96b75864dab48e279aeTimo Sirainen restrict_access(&rset, *home == '\0' ? NULL : home,
0ea17cea21df405e6e74a167d08e1ff0ee95dd13Timo Sirainen disallow_root);
8a6fc472a12c5ec09e2bd19f1b3acd553d75ff91Timo Sirainen } else {
8a6fc472a12c5ec09e2bd19f1b3acd553d75ff91Timo Sirainen restrict_access_set_env(&rset);
8a6fc472a12c5ec09e2bd19f1b3acd553d75ff91Timo Sirainen }
8a6fc472a12c5ec09e2bd19f1b3acd553d75ff91Timo Sirainen if (setuid_uid != 0 && !setenv_only) {
8a6fc472a12c5ec09e2bd19f1b3acd553d75ff91Timo Sirainen if (seteuid(setuid_uid) < 0)
8a6fc472a12c5ec09e2bd19f1b3acd553d75ff91Timo Sirainen i_fatal("seteuid(%s) failed: %m", dec2str(setuid_uid));
8a6fc472a12c5ec09e2bd19f1b3acd553d75ff91Timo Sirainen }
8a6fc472a12c5ec09e2bd19f1b3acd553d75ff91Timo Sirainen}
8a6fc472a12c5ec09e2bd19f1b3acd553d75ff91Timo Sirainen
8a6fc472a12c5ec09e2bd19f1b3acd553d75ff91Timo Sirainenstatic int
8a6fc472a12c5ec09e2bd19f1b3acd553d75ff91Timo Sirainenmail_storage_service_init_post(struct mail_storage_service_ctx *ctx,
8a6fc472a12c5ec09e2bd19f1b3acd553d75ff91Timo Sirainen struct mail_storage_service_user *user,
8a6fc472a12c5ec09e2bd19f1b3acd553d75ff91Timo Sirainen const char *home, struct mail_user **mail_user_r,
0ea17cea21df405e6e74a167d08e1ff0ee95dd13Timo Sirainen const char **error_r)
8a6fc472a12c5ec09e2bd19f1b3acd553d75ff91Timo Sirainen{
8a6fc472a12c5ec09e2bd19f1b3acd553d75ff91Timo Sirainen const struct mail_storage_settings *mail_set;
8a6fc472a12c5ec09e2bd19f1b3acd553d75ff91Timo Sirainen struct mail_user *mail_user;
8a6fc472a12c5ec09e2bd19f1b3acd553d75ff91Timo Sirainen
728a9c39b63b48f17600e6a1e5ebfc93d0896a24Timo Sirainen mail_user = mail_user_alloc(user->input.username, user->user_info,
728a9c39b63b48f17600e6a1e5ebfc93d0896a24Timo Sirainen user->user_set);
0ea17cea21df405e6e74a167d08e1ff0ee95dd13Timo Sirainen mail_user_set_home(mail_user, *home == '\0' ? NULL : home);
728a9c39b63b48f17600e6a1e5ebfc93d0896a24Timo Sirainen mail_user_set_vars(mail_user, geteuid(), ctx->service->name,
728a9c39b63b48f17600e6a1e5ebfc93d0896a24Timo Sirainen &user->input.local_ip, &user->input.remote_ip);
728a9c39b63b48f17600e6a1e5ebfc93d0896a24Timo Sirainen
8a6fc472a12c5ec09e2bd19f1b3acd553d75ff91Timo Sirainen mail_set = mail_user_set_get_storage_set(mail_user);
8a6fc472a12c5ec09e2bd19f1b3acd553d75ff91Timo Sirainen
8a6fc472a12c5ec09e2bd19f1b3acd553d75ff91Timo Sirainen if (mail_set->mail_debug) {
8a6fc472a12c5ec09e2bd19f1b3acd553d75ff91Timo Sirainen i_debug("Effective uid=%s, gid=%s, home=%s",
8a6fc472a12c5ec09e2bd19f1b3acd553d75ff91Timo Sirainen dec2str(geteuid()), dec2str(getegid()), home);
8a6fc472a12c5ec09e2bd19f1b3acd553d75ff91Timo Sirainen }
8a6fc472a12c5ec09e2bd19f1b3acd553d75ff91Timo Sirainen
e34d4597fc4995a067caafdc27676f6cebb0aa28Timo Sirainen if ((ctx->flags & MAIL_STORAGE_SERVICE_FLAG_TEMP_PRIV_DROP) != 0 &&
32ee977e189266744ef69ac4e832fd3111d6f949Timo Sirainen (ctx->flags & MAIL_STORAGE_SERVICE_FLAG_ENABLE_CORE_DUMPS) == 0) {
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen /* we don't want to write core files to any users' home
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen directories since they could contain information about other
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen users' mails as well. so do no chdiring to home. */
5667b85ee2cc32b5e0320ed02d9f548c291538aaTimo Sirainen } else if (*home != '\0' &&
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen (ctx->flags & MAIL_STORAGE_SERVICE_FLAG_NO_CHDIR) == 0) {
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen /* If possible chdir to home directory, so that core file
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen could be written in case we crash. */
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen if (chdir(home) < 0) {
32ee977e189266744ef69ac4e832fd3111d6f949Timo Sirainen if (errno == EACCES) {
32ee977e189266744ef69ac4e832fd3111d6f949Timo Sirainen i_error("%s", eacces_error_get("chdir",
32ee977e189266744ef69ac4e832fd3111d6f949Timo Sirainen t_strconcat(home, "/", NULL)));
32ee977e189266744ef69ac4e832fd3111d6f949Timo Sirainen } if (errno != ENOENT)
32ee977e189266744ef69ac4e832fd3111d6f949Timo Sirainen i_error("chdir(%s) failed: %m", home);
32ee977e189266744ef69ac4e832fd3111d6f949Timo Sirainen else if (mail_set->mail_debug)
32ee977e189266744ef69ac4e832fd3111d6f949Timo Sirainen i_debug("Home dir not found: %s", home);
32ee977e189266744ef69ac4e832fd3111d6f949Timo Sirainen }
32ee977e189266744ef69ac4e832fd3111d6f949Timo Sirainen }
32ee977e189266744ef69ac4e832fd3111d6f949Timo Sirainen
32ee977e189266744ef69ac4e832fd3111d6f949Timo Sirainen if (mail_user_init(mail_user, error_r) < 0) {
32ee977e189266744ef69ac4e832fd3111d6f949Timo Sirainen mail_user_unref(&mail_user);
32ee977e189266744ef69ac4e832fd3111d6f949Timo Sirainen return -1;
32ee977e189266744ef69ac4e832fd3111d6f949Timo Sirainen }
32ee977e189266744ef69ac4e832fd3111d6f949Timo Sirainen if (mail_namespaces_init(mail_user, error_r) < 0) {
32ee977e189266744ef69ac4e832fd3111d6f949Timo Sirainen mail_user_unref(&mail_user);
32ee977e189266744ef69ac4e832fd3111d6f949Timo Sirainen return -1;
32ee977e189266744ef69ac4e832fd3111d6f949Timo Sirainen }
32ee977e189266744ef69ac4e832fd3111d6f949Timo Sirainen *mail_user_r = mail_user;
32ee977e189266744ef69ac4e832fd3111d6f949Timo Sirainen return 0;
32ee977e189266744ef69ac4e832fd3111d6f949Timo Sirainen}
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainenstatic const struct var_expand_table *
8a6fc472a12c5ec09e2bd19f1b3acd553d75ff91Timo Sirainenget_var_expand_table(struct master_service *service,
8a6fc472a12c5ec09e2bd19f1b3acd553d75ff91Timo Sirainen struct mail_storage_service_input *input)
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen{
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen static struct var_expand_table static_tab[] = {
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen { 'u', NULL, "user" },
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen { 'n', NULL, "username" },
0ea17cea21df405e6e74a167d08e1ff0ee95dd13Timo Sirainen { 'd', NULL, "domain" },
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen { 's', NULL, "service" },
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen { 'l', NULL, "lip" },
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen { 'r', NULL, "rip" },
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen { 'p', NULL, "pid" },
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen { 'i', NULL, "uid" },
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen { '\0', NULL, NULL }
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen };
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen struct var_expand_table *tab;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen tab = t_malloc(sizeof(static_tab));
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen memcpy(tab, static_tab, sizeof(static_tab));
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen tab[0].value = input->username;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen tab[1].value = t_strcut(input->username, '@');
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen tab[2].value = strchr(input->username, '@');
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen if (tab[2].value != NULL) tab[2].value++;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen tab[3].value = service->name;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen tab[4].value = net_ip2addr(&input->local_ip);
91dca97b367c54a139c268b56a0c67f564bd9197Timo Sirainen tab[5].value = net_ip2addr(&input->remote_ip);
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen tab[6].value = my_pid;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen tab[7].value = dec2str(geteuid());
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen return tab;
9740d55b228a670047c854484d5cc979a056a9afTimo Sirainen}
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen
9740d55b228a670047c854484d5cc979a056a9afTimo Sirainenstatic const char *
9740d55b228a670047c854484d5cc979a056a9afTimo Sirainenuser_expand_varstr(struct master_service *service,
9740d55b228a670047c854484d5cc979a056a9afTimo Sirainen struct mail_storage_service_input *input, const char *str)
9740d55b228a670047c854484d5cc979a056a9afTimo Sirainen{
19e8adccba16ff419f5675b1575358c2956dce83Timo Sirainen string_t *ret;
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen
19e8adccba16ff419f5675b1575358c2956dce83Timo Sirainen if (*str == SETTING_STRVAR_EXPANDED[0])
8a6fc472a12c5ec09e2bd19f1b3acd553d75ff91Timo Sirainen return str + 1;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen i_assert(*str == SETTING_STRVAR_UNEXPANDED[0]);
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen ret = t_str_new(256);
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen var_expand(ret, str + 1, get_var_expand_table(service, input));
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen return str_c(ret);
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen}
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainenstatic void
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainenmail_storage_service_init_log(struct master_service *service,
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen struct mail_storage_service_user *user)
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen{
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen const struct mail_user_settings *user_set;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen user_set = master_service_settings_get_others(service)[0];
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen T_BEGIN {
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen string_t *str;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen str = t_str_new(256);
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen var_expand(str, user->user_set->mail_log_prefix,
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen get_var_expand_table(service, &user->input));
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen master_service_init_log(service, str_c(str));
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen } T_END;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen}
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainenstatic void mail_storage_service_time_moved(time_t old_time, time_t new_time)
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen{
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen long diff = new_time - old_time;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen if (diff > 0) {
6825360d446542046757b06064282301c4c6b27cTimo Sirainen if (diff > MAX_NOWARN_FORWARD_SECS)
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen i_warning("Time jumped forwards %ld seconds", diff);
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen return;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen }
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen diff = -diff;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen if (diff > MAX_TIME_BACKWARDS_SLEEP) {
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen i_fatal("Time just moved backwards by %ld seconds. "
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen "This might cause a lot of problems, "
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen "so I'll just kill myself now. "
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen "http://wiki.dovecot.org/TimeMovedBackwards", diff);
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen } else {
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen i_error("Time just moved backwards by %ld seconds. "
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen "I'll sleep now until we're back in present. "
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen "http://wiki.dovecot.org/TimeMovedBackwards", diff);
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen /* Sleep extra second to make sure usecs also grows. */
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen diff++;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen while (diff > 0 && sleep(diff) != 0) {
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen /* don't use sleep()'s return value, because
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen it could get us to a long loop in case
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen interrupts just keep coming */
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen diff = old_time - time(NULL) + 1;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen }
}
}
struct mail_storage_service_ctx *
mail_storage_service_init(struct master_service *service,
const struct setting_parser_info *set_roots[],
enum mail_storage_service_flags flags)
{
struct mail_storage_service_ctx *ctx;
pool_t pool;
unsigned int count;
(void)umask(0077);
io_loop_set_time_moved_callback(current_ioloop,
mail_storage_service_time_moved);
mail_storage_init();
mail_storage_register_all();
mailbox_list_register_all();
pool = pool_alloconly_create("mail storage service", 2048);
ctx = p_new(pool, struct mail_storage_service_ctx, 1);
ctx->pool = pool;
ctx->service = service;
ctx->flags = flags;
/* @UNSAFE */
if (set_roots == NULL)
count = 0;
else
for (count = 0; set_roots[count] != NULL; count++) ;
ctx->set_roots =
p_new(pool, const struct setting_parser_info *, count + 2);
ctx->set_roots[0] = &mail_user_setting_parser_info;
memcpy(ctx->set_roots + 1, set_roots, sizeof(*ctx->set_roots) * count);
/* 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) {
master_service_init_log(service,
t_strconcat(service->name, ": ", NULL));
}
dict_drivers_register_builtin();
return ctx;
}
struct auth_master_connection *
mail_storage_service_get_auth_conn(struct mail_storage_service_ctx *ctx)
{
i_assert(ctx->conn != NULL);
return ctx->conn;
}
static void
settings_parser_update_children_parent(struct setting_parser_info *parent,
pool_t pool)
{
struct setting_define *new_defs;
struct setting_parser_info *new_info;
unsigned int i, count;
for (count = 0; parent->defines[count].key != NULL; count++) ;
new_defs = p_new(pool, struct setting_define, count + 1);
memcpy(new_defs, parent->defines, sizeof(*new_defs) * count);
parent->defines = new_defs;
for (i = 0; i < count; i++) {
if (new_defs[i].list_info == NULL ||
new_defs[i].list_info->parent == NULL)
continue;
new_info = p_new(pool, struct setting_parser_info, 1);
*new_info = *new_defs[i].list_info;
new_info->parent = parent;
new_defs[i].list_info = new_info;
}
}
static struct setting_parser_info *
dyn_parsers_update_parent(pool_t pool,
const struct setting_parser_info ***_roots,
const struct dynamic_settings_parser **_dyn_parsers)
{
const struct dynamic_settings_parser *dyn_parsers = *_dyn_parsers;
const const struct setting_parser_info **roots = *_roots;
const struct setting_parser_info *old_parent, **new_roots;
struct setting_parser_info *new_parent, *new_info;
struct dynamic_settings_parser *new_dyn_parsers;
unsigned int i, count;
/* settings_parser_info_update() modifies the parent structure.
since we may be using the same structure later, we want it to be
in its original state, so we'll have to copy all structures. */
old_parent = dyn_parsers[0].info->parent;
new_parent = p_new(pool, struct setting_parser_info, 1);
*new_parent = *old_parent;
settings_parser_update_children_parent(new_parent, pool);
/* update root */
for (count = 0; roots[count] != NULL; count++) ;
new_roots = p_new(pool, const struct setting_parser_info *, count + 1);
for (i = 0; i < count; i++) {
if (roots[i] == old_parent)
new_roots[i] = new_parent;
else
new_roots[i] = roots[i];
}
*_roots = new_roots;
/* update parent in dyn_parsers */
for (count = 0; dyn_parsers[count].name != NULL; count++) ;
new_dyn_parsers = p_new(pool, struct dynamic_settings_parser, count + 1);
for (i = 0; i < count; i++) {
new_dyn_parsers[i] = dyn_parsers[i];
new_info = p_new(pool, struct setting_parser_info, 1);
*new_info = *dyn_parsers[i].info;
new_info->parent = new_parent;
new_dyn_parsers[i].info = new_info;
}
*_dyn_parsers = new_dyn_parsers;
return new_parent;
}
int mail_storage_service_read_settings(struct mail_storage_service_ctx *ctx,
const struct mail_storage_service_input *input,
pool_t pool,
const struct setting_parser_info **user_info_r,
const struct setting_parser_context **parser_r,
const char **error_r)
{
struct master_service_settings_input set_input;
struct master_service_settings_output set_output;
unsigned int i;
memset(&set_input, 0, sizeof(set_input));
set_input.roots = ctx->set_roots;
/* 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 */
set_input.preserve_home =
(ctx->flags & MAIL_STORAGE_SERVICE_FLAG_USERDB_LOOKUP) == 0;
if (input != NULL) {
set_input.module = input->module;
set_input.service = input->service;
set_input.username = input->username;
set_input.local_ip = input->local_ip;
set_input.remote_ip = input->remote_ip;
}
if (ctx->set_cache == NULL) {
ctx->set_cache_module = p_strdup(ctx->pool, set_input.module);
ctx->set_cache_service = p_strdup(ctx->pool, set_input.service);
ctx->set_cache = master_service_settings_cache_init(
ctx->service, set_input.module, set_input.service);
ctx->set_cache_roots = ctx->set_roots;
ctx->set_cache_dyn_parsers =
mail_storage_get_dynamic_parsers(ctx->pool);
ctx->set_cache_dyn_parsers_parent =
dyn_parsers_update_parent(ctx->pool,
&ctx->set_cache_roots,
&ctx->set_cache_dyn_parsers);
}
if (null_strcmp(set_input.module, ctx->set_cache_module) == 0 &&
null_strcmp(set_input.service, ctx->set_cache_service) == 0) {
set_input.roots = ctx->set_cache_roots;
set_input.dyn_parsers = ctx->set_cache_dyn_parsers;
set_input.dyn_parsers_parent =
ctx->set_cache_dyn_parsers_parent;
if (master_service_settings_cache_read(ctx->set_cache,
&set_input,
parser_r, error_r) < 0)
return -1;
} else {
set_input.dyn_parsers = mail_storage_get_dynamic_parsers(pool);
set_input.dyn_parsers_parent =
dyn_parsers_update_parent(pool, &set_input.roots,
&set_input.dyn_parsers);
if (master_service_settings_read(ctx->service, &set_input,
&set_output, error_r) < 0) {
*error_r = t_strdup_printf(
"Error reading configuration: %s", *error_r);
return -1;
}
*parser_r = ctx->service->set_parser;
}
for (i = 0; ctx->set_roots[i] != NULL; i++) {
if (strcmp(ctx->set_roots[i]->module_name,
mail_user_setting_parser_info.module_name) == 0) {
*user_info_r = set_input.roots[i];
return 0;
}
}
i_unreached();
return -1;
}
static void
mail_storage_service_first_init(struct mail_storage_service_ctx *ctx,
const struct setting_parser_info *user_info,
const struct mail_user_settings *user_set)
{
const struct mail_storage_settings *mail_set;
enum auth_master_flags flags = 0;
i_assert(ctx->conn == NULL);
mail_set = mail_user_set_get_driver_settings(user_info, user_set,
MAIL_STORAGE_SET_DRIVER_NAME);
ctx->debug = mail_set->mail_debug;
if (ctx->debug)
flags |= AUTH_MASTER_FLAG_DEBUG;
if ((ctx->flags & MAIL_STORAGE_SERVICE_FLAG_NO_IDLE_TIMEOUT) != 0)
flags |= AUTH_MASTER_FLAG_NO_IDLE_TIMEOUT;
ctx->conn = auth_master_init(user_set->auth_socket_path, flags);
i_assert(mail_user_auth_master_conn == NULL);
mail_user_auth_master_conn = ctx->conn;
}
static void
mail_storage_service_load_modules(struct mail_storage_service_ctx *ctx,
const struct setting_parser_info *user_info,
const struct mail_user_settings *user_set)
{
const struct mail_storage_settings *mail_set;
struct module_dir_load_settings mod_set;
if (*user_set->mail_plugins == '\0')
return;
if ((ctx->flags & MAIL_STORAGE_SERVICE_FLAG_NO_PLUGINS) != 0)
return;
mail_set = mail_user_set_get_driver_settings(user_info, user_set,
MAIL_STORAGE_SET_DRIVER_NAME);
memset(&mod_set, 0, sizeof(mod_set));
mod_set.version = master_service_get_version_string(ctx->service);
mod_set.require_init_funcs = TRUE;
mod_set.debug = mail_set->mail_debug;
modules = module_dir_load_missing(modules, user_set->mail_plugin_dir,
user_set->mail_plugins, &mod_set);
}
int mail_storage_service_lookup(struct mail_storage_service_ctx *ctx,
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 char *username = input->username;
const struct setting_parser_info *user_info;
const struct mail_user_settings *user_set;
const char *const *userdb_fields;
struct auth_user_reply reply;
const struct setting_parser_context *set_parser;
pool_t user_pool, temp_pool;
int ret = 1;
user_pool = pool_alloconly_create("mail storage service user", 1024*5);
if (mail_storage_service_read_settings(ctx, input, user_pool,
&user_info, &set_parser,
error_r) < 0) {
pool_unref(&user_pool);
return -1;
}
user_set = settings_parser_get_list(set_parser)[1];
if (ctx->conn == NULL)
mail_storage_service_first_init(ctx, user_info, user_set);
/* load global plugins */
mail_storage_service_load_modules(ctx, user_info, user_set);
temp_pool = pool_alloconly_create("userdb lookup", 1024);
if ((ctx->flags & MAIL_STORAGE_SERVICE_FLAG_USERDB_LOOKUP) != 0) {
ret = service_auth_userdb_lookup(ctx, input, temp_pool,
&username, &userdb_fields,
error_r);
if (ret <= 0) {
pool_unref(&temp_pool);
pool_unref(&user_pool);
return ret;
}
} else {
userdb_fields = input->userdb_fields;
}
user = p_new(user_pool, struct mail_storage_service_user, 1);
memset(user_r, 0, sizeof(user_r));
user->pool = user_pool;
user->input = *input;
user->input.userdb_fields = NULL;
user->input.username = p_strdup(user_pool, username);
user->user_info = user_info;
user->set_parser = settings_parser_dup(set_parser, user_pool);
if (!settings_parser_check(user->set_parser, user_pool, error_r))
i_unreached();
user->user_set = settings_parser_get_list(user->set_parser)[1];
if ((ctx->flags & MAIL_STORAGE_SERVICE_FLAG_USERDB_LOOKUP) == 0) {
const char *home = getenv("HOME");
if (home != NULL)
set_keyval(user->set_parser, "mail_home", home);
}
if (userdb_fields != NULL) {
auth_user_fields_parse(userdb_fields, temp_pool, &reply);
if (user_reply_handle(user, &reply, error_r) < 0)
ret = -1;
}
pool_unref(&temp_pool);
/* load per-user plugins */
mail_storage_service_load_modules(ctx, user_info, user->user_set);
*user_r = user;
return ret;
}
int mail_storage_service_next(struct mail_storage_service_ctx *ctx,
struct mail_storage_service_user *user,
struct mail_user **mail_user_r,
const char **error_r)
{
const struct mail_user_settings *user_set = user->user_set;
const char *home, *chroot;
unsigned int len;
bool temp_priv_drop =
(ctx->flags & MAIL_STORAGE_SERVICE_FLAG_TEMP_PRIV_DROP);
/* variable strings are expanded in mail_user_init(),
but we need the home and chroot sooner so do them separately here. */
home = user_expand_varstr(ctx->service, &user->input,
user_set->mail_home);
chroot = user_expand_varstr(ctx->service, &user->input,
user_set->mail_chroot);
if (*home != '/' && *home != '\0') {
i_error("user %s: Relative home directory paths not supported: "
"%s", user->input.username, home);
return -1;
}
if ((ctx->flags & MAIL_STORAGE_SERVICE_FLAG_NO_LOG_INIT) == 0)
mail_storage_service_init_log(ctx->service, user);
if ((ctx->flags & MAIL_STORAGE_SERVICE_FLAG_NO_RESTRICT_ACCESS) == 0) {
service_drop_privileges(user_set, user->system_groups_user,
home, chroot,
(ctx->flags & MAIL_STORAGE_SERVICE_FLAG_DISALLOW_ROOT) != 0,
temp_priv_drop, FALSE);
if (!temp_priv_drop ||
(ctx->flags & MAIL_STORAGE_SERVICE_FLAG_ENABLE_CORE_DUMPS) != 0)
restrict_access_allow_coredumps(TRUE);
}
/* privileges are dropped. initialize plugins that haven't been
initialized yet. */
module_dir_init(modules);
/* we couldn't do chrooting, so if chrooting was enabled fix
the home directory */
len = strlen(chroot);
if (len > 2 && strcmp(chroot + len - 2, "/.") == 0 &&
strncmp(home, chroot, len - 2) == 0) {
/* home dir already contains the chroot dir */
if (!temp_priv_drop) {
home += len - 2;
if (*home == '\0')
home = "/";
set_keyval(user->set_parser, "mail_home", home);
chroot = t_strndup(chroot, len - 2);
}
} else if (len > 0 && temp_priv_drop) {
set_keyval(user->set_parser, "mail_home",
t_strconcat(chroot, "/", home, NULL));
}
if (mail_storage_service_init_post(ctx, user, home,
mail_user_r, error_r) < 0)
return -1;
return 0;
}
void mail_storage_service_restrict_setenv(struct mail_storage_service_ctx *ctx,
struct mail_storage_service_user *user)
{
const struct mail_user_settings *user_set = user->user_set;
const char *home, *chroot;
home = user_expand_varstr(ctx->service, &user->input,
user_set->mail_home);
chroot = user_expand_varstr(ctx->service, &user->input,
user_set->mail_chroot);
service_drop_privileges(user_set, user->system_groups_user,
home, chroot, FALSE, FALSE, TRUE);
}
int mail_storage_service_lookup_next(struct mail_storage_service_ctx *ctx,
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;
const char *error;
int ret;
ret = mail_storage_service_lookup(ctx, input, &user, &error);
if (ret <= 0) {
*error_r = t_strdup_printf("User lookup failed: %s", error);
return ret;
}
if (mail_storage_service_next(ctx, user, mail_user_r, &error) < 0) {
mail_storage_service_user_free(&user);
*error_r = t_strdup_printf("User init failed: %s", error);
return -1;
}
*user_r = user;
return 1;
}
void mail_storage_service_user_free(struct mail_storage_service_user **_user)
{
struct mail_storage_service_user *user = *_user;
*_user = NULL;
settings_parser_deinit(&user->set_parser);
pool_unref(&user->pool);
}
void mail_storage_service_init_settings(struct mail_storage_service_ctx *ctx,
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;
pool_t temp_pool;
if (ctx->conn != NULL)
return;
temp_pool = pool_alloconly_create("service all settings", 4096);
if (mail_storage_service_read_settings(ctx, input, temp_pool,
&user_info, &set_parser,
&error) < 0)
i_fatal("%s", error);
user_set = settings_parser_get_list(set_parser)[1];
mail_storage_service_first_init(ctx, user_info, user_set);
pool_unref(&temp_pool);
}
unsigned int
mail_storage_service_all_init(struct mail_storage_service_ctx *ctx)
{
if (ctx->auth_list != NULL)
(void)auth_master_user_list_deinit(&ctx->auth_list);
mail_storage_service_init_settings(ctx, NULL);
ctx->auth_list = auth_master_user_list_init(ctx->conn);
return auth_master_user_list_count(ctx->auth_list);
}
int mail_storage_service_all_next(struct mail_storage_service_ctx *ctx,
const char **username_r)
{
i_assert((ctx->flags & MAIL_STORAGE_SERVICE_FLAG_USERDB_LOOKUP) != 0);
*username_r = auth_master_user_list_next(ctx->auth_list);
if (*username_r != NULL)
return 1;
return auth_master_user_list_deinit(&ctx->auth_list);
}
void mail_storage_service_deinit(struct mail_storage_service_ctx **_ctx)
{
struct mail_storage_service_ctx *ctx = *_ctx;
*_ctx = NULL;
if (ctx->auth_list != NULL)
(void)auth_master_user_list_deinit(&ctx->auth_list);
if (ctx->conn != NULL) {
if (mail_user_auth_master_conn == ctx->conn)
mail_user_auth_master_conn = NULL;
auth_master_deinit(&ctx->conn);
}
if (ctx->set_cache != NULL)
master_service_settings_cache_deinit(&ctx->set_cache);
pool_unref(&ctx->pool);
module_dir_unload(&modules);
mail_storage_deinit();
dict_drivers_unregister_builtin();
}
void **mail_storage_service_user_get_set(struct mail_storage_service_user *user)
{
return settings_parser_get_list(user->set_parser) + 1;
}
const struct mail_storage_service_input *
mail_storage_service_user_get_input(struct mail_storage_service_user *user)
{
return &user->input;
}
struct setting_parser_context *
mail_storage_service_user_get_settings_parser(struct mail_storage_service_user *user)
{
return user->set_parser;
}
void *mail_storage_service_get_settings(struct master_service *service)
{
void **sets, *set;
T_BEGIN {
sets = master_service_settings_get_others(service);
set = sets[1];
} T_END;
return set;
}