mail-storage-service.c revision 18c209a06941ef583b08b173dadfbe4571995bf9
c25356d5978632df6203437e1953bcb29e0c736fTimo Sirainen/* Copyright (c) 2009 Dovecot authors, see the included COPYING file */
589a9c6e8ee22071c14171c04bfc6bfe17121871Timo Sirainen/* If time moves backwards more than this, kill ourself instead of sleeping. */
5da1aa5197a43d83f0fb3eeb83125c7cd73d1b62Timo Sirainenstatic void set_keyval(struct setting_parser_context *set_parser,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen const char *str;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (settings_parse_line(set_parser, str) < 0) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstatic bool validate_chroot(const struct mail_user_settings *user_set,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen const char *dir)
024815ea2ffdda9ea79919f18e865663977f73eaTimo Sirainen const char *const *chroot_dirs;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen chroot_dirs = t_strsplit(user_set->valid_chroot_dirs, ":");
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen strncmp(dir, *chroot_dirs, strlen(*chroot_dirs)) == 0)
1d3f7c1278168d5b1cbfa9a2cc9929a0909056b4Timo Sirainenuser_reply_handle(struct setting_parser_context *set_parser,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen const char **system_groups_user_r, const char **error_r)
51795bfe9d05d92fe942cb451aec2b9d16d32a11Timo Sirainen unsigned int i, count;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen set_keyval(set_parser, "mail_uid", dec2str(reply->uid));
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen set_keyval(set_parser, "mail_gid", dec2str(reply->gid));
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen set_keyval(set_parser, "mail_home", reply->home);
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen if (!validate_chroot(user_set, reply->chroot)) {
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen "userdb returned invalid chroot directory: %s "
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen "(see valid_chroot_dirs setting)",
db87d16551d1081ada01f787ea21aa3ed1402c31Timo Sirainen set_keyval(set_parser, "mail_chroot", reply->chroot);
db87d16551d1081ada01f787ea21aa3ed1402c31Timo Sirainen str = array_get(&reply->extra_fields, &count);
c8adec8db635f5efb13b9879a5f3fb523abdc969Timo Sirainen if (strncmp(line, "system_groups_user=", 19) == 0)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (!settings_parse_is_valid_key(set_parser, key)) {
26ff8f8a4867bf8e9551a27a2de8c12cd138b065Timo Sirainen /* assume it's a plugin setting */
724b7fcf28c2547eb9c837d0e99241c0501dccf3Timo Sirainen *error_r = t_strdup_printf("Invalid userdb input '%s': %s",
724b7fcf28c2547eb9c837d0e99241c0501dccf3Timo Sirainen str[i], settings_parser_get_error(set_parser));
2d39dc1a453546892109b35c0d9770369011a13dTimo Sirainenservice_auth_userdb_lookup(struct auth_master_connection *conn,
c8adec8db635f5efb13b9879a5f3fb523abdc969Timo Sirainen const struct mail_storage_service_input *input,
d6a1fa1d65c6d1996937802c2482c0f14dd821a7Timo Sirainen const char **user, const char **system_groups_user_r,
807b48fe1f6a57b01ed2cc20247d5b5e3facc562Timo Sirainen const char **error_r)
dc912088f84c263db1609435c2f5d7cb29bf1a33Timo Sirainen const char *system_groups_user, *orig_user = *user;
3f91e60401495a4046c73992fabaa5e77200a451Timo Sirainen unsigned int len;
62f4a199b5c9a0862f486cbf18e195cc621bbe25Timo Sirainen pool = pool_alloconly_create("userdb lookup", 1024);
6bc98d3898c475ba7615ba2b016e5142c8b2c09fTimo Sirainen ret = auth_master_user_lookup(conn, *user, &info, pool, &reply);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen len = reply.chroot == NULL ? 0 : strlen(reply.chroot);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (user_reply_handle(set_parser, user_set, &reply,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen *system_groups_user_r = t_strdup(system_groups_user);
1f43c8ac132c153c224c4fffe34b2c3075d87ef7Timo Sirainen if (ret > 0 && strcmp(*user, orig_user) != 0) {
1f43c8ac132c153c224c4fffe34b2c3075d87ef7Timo Sirainen if (mail_user_set_get_storage_set(user_set)->mail_debug)
b9c44feadade0481b957f2978640afb3317bd1dfTimo Sirainenstatic bool parse_uid(const char *str, uid_t *uid_r)
9f19a50d5966643c4d1c5ca06868ac2ad31bc4d5Timo Sirainen if (*p == '\0')
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstatic bool parse_gid(const char *str, gid_t *gid_r)
b780aa272b742a43579cdb523cc79cc8d4521306Timo Sirainen if (*p == '\0')
47e0598840ecffa364ebed523e06939e22738f06Timo Sirainenservice_drop_privileges(const struct mail_user_settings *set,
47e0598840ecffa364ebed523e06939e22738f06Timo Sirainen const char *system_groups_user, const char *home,
4d4d585520538a752e9f0a4a1c019a2918f52e56Timo Sirainen i_fatal("Unknown mail_uid user: %s", set->mail_uid);
a045c3aba2610c6ed0bf1c346df1c6d8f7b9fbfdTimo Sirainen "not permitted (see first_valid_uid in config file).",
d30da25fb6be1f1c667d93767c9194000194b618Timo Sirainen i_fatal("Unknown mail_gid group: %s", set->mail_gid);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen "not permitted (see first_valid_gid in config file).",
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (!parse_uid(set->mail_privileged_group, &rset.privileged_gid))
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen i_fatal("Unknown mail_gid group: %s", set->mail_gid);
ec77cd41241208345efd51c1fcce9030be30aa9bTimo Sirainen /* we can't chroot if we want to switch between users. there's not
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen much point either (from security point of view) */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen rset.chroot_dir = *set->mail_chroot == '\0' || keep_setuid_root ?
7c95b03620a03a43dd72d39608cea5fc77393ad6Timo Sirainen (rset.uid == 0 || (rset.uid == (uid_t)-1 && current_euid == 0)))
eac3948d67eff8623d51aeaea9eca582f3aec677Timo Sirainen /* we're changing the UID,
2674b4f0cf8f3c203d8e56b29735f5e267038dafTimo Sirainen switch back to root first */
48136ae5a0eb49daa44e343553f3688a500307e2Timo Sirainen restrict_access(&rset, *home == '\0' ? NULL : home, disallow_root);
e376693bfa3985232c41df99c7010fca22612c89Timo Sirainen i_fatal("seteuid(%s) failed: %m", dec2str(setuid_uid));
97735b96d442568f65efa20f8c292a496498c17bTimo Sirainen /* enable core dumps only when we can be sure that the core
97735b96d442568f65efa20f8c292a496498c17bTimo Sirainen file is written to a safe directory. with chrooting we're
97735b96d442568f65efa20f8c292a496498c17bTimo Sirainen chrooting to user's home dir. */
d6a1fa1d65c6d1996937802c2482c0f14dd821a7Timo Sirainenmail_storage_service_init_settings(struct master_service *service,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen const struct mail_storage_service_input *input,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen const struct setting_parser_info *set_roots[],
b92813e2f96d4b28f989528ed5dd6115da7d9bdbTimo Sirainen ARRAY_DEFINE(all_set_roots, const struct setting_parser_info *);
b92813e2f96d4b28f989528ed5dd6115da7d9bdbTimo Sirainen const struct setting_parser_info *info = &mail_user_setting_parser_info;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct master_service_settings_input set_input;
d152ccd0d29fae1bc6092bf198ee7eb843202f96Timo Sirainen unsigned int i;
d152ccd0d29fae1bc6092bf198ee7eb843202f96Timo Sirainen array_append(&all_set_roots, &set_roots[i], 1);
aa0647f2debf0d48d504a321186f66c85596aaf4Timo Sirainen /* read settings after registering storages so they can have their
2d39dc1a453546892109b35c0d9770369011a13dTimo Sirainen own setting definitions too */
8f8315e4b4e27ead12dd1c3da65bf4dee3762f18Timo Sirainen set_input.roots = array_idx_modifiable(&all_set_roots, 0);
8f8315e4b4e27ead12dd1c3da65bf4dee3762f18Timo Sirainen set_input.dyn_parsers = mail_storage_get_dynamic_parsers();
768b7f5783c8de119d7386321e5d0c72d5c2d9f6Timo Sirainen if (master_service_settings_read(service, &set_input, &error) < 0)
768b7f5783c8de119d7386321e5d0c72d5c2d9f6Timo Sirainen i_fatal("Error reading configuration: %s", error);
079f54c97145a0a5daa36c37eead3eae91b67a1eTimo Sirainenmail_storage_service_init_post(struct master_service *service,
079f54c97145a0a5daa36c37eead3eae91b67a1eTimo Sirainen const struct mail_storage_service_input *input,
3955d6726c939b3b30527c22b70c879fbe78692eTimo Sirainen const char **error_r)
9f19a50d5966643c4d1c5ca06868ac2ad31bc4d5Timo Sirainen mail_set = mail_user_set_get_storage_set(user_set);
685393de106e55b61f754d420e378d05bd462ebbTimo Sirainen /* we don't want to write core files to any users' home
87712707722ef7d73acb065546e61afa4455cd9eTimo Sirainen directories since they could contain information about other
3b80595fcf2001cf7b2fcc6290823e38f4a142fcTimo Sirainen users' mails as well. so do no chdiring to home. */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen (flags & MAIL_STORAGE_SERVICE_FLAG_NO_CHDIR) == 0) {
8a3f549a3cb1d6dd980a4fa3db284653e256dae7Timo Sirainen /* If possible chdir to home directory, so that core file
8a3f549a3cb1d6dd980a4fa3db284653e256dae7Timo Sirainen could be written in case we crash. */
c979eeda1f46483d9c963e265786b701d7683d77Timo Sirainen mail_user = mail_user_alloc(input->username, user_set);
c979eeda1f46483d9c963e265786b701d7683d77Timo Sirainen mail_user_set_home(mail_user, *home == '\0' ? NULL : home);
94a8cb0ee1d85569ad1a2acacd92d3ce22f8a1cbTimo Sirainen mail_user_set_vars(mail_user, geteuid(), service->name,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (mail_namespaces_init(mail_user, error_r) < 0) {
73b50eecfc31750a312e2f940023f522eb07178cTimo Sirainenstatic const struct var_expand_table *
73b50eecfc31750a312e2f940023f522eb07178cTimo Sirainenget_var_expand_table(struct master_service *service,
73b50eecfc31750a312e2f940023f522eb07178cTimo Sirainen static struct var_expand_table static_tab[] = {
3da614c39dd29f536c485089e67839b4cf89fed3Timo Sirainen tab[1].value = t_strcut(input->username, '@');
3da614c39dd29f536c485089e67839b4cf89fed3Timo Sirainen tab[5].value = net_ip2addr(&input->remote_ip);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstatic const char *
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenuser_expand_varstr(struct master_service *service,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct mail_storage_service_input *input, const char *str)
8cf32443413f811d514123c5c74c95c87594b0e3Timo Sirainen i_assert(*str == SETTING_STRVAR_UNEXPANDED[0]);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen var_expand(ret, str + 1, get_var_expand_table(service, input));
de62ce819d59a529530da4b57be1b8d6dad13d6bTimo Sirainenmail_storage_service_init_log(struct master_service *service,
c251a38df327599a62d341bf5c2282f31352faa5Timo Sirainen sets = master_service_settings_get_others(service);
ad48319996942463675b53877092ab7e13a7a75aTimo Sirainenstatic void mail_storage_service_time_moved(time_t old_time, time_t new_time)
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen i_warning("Time jumped forwards %ld seconds", diff);
8872e5c991430f96138a46e36b7f3c2c40d8e5c2Timo Sirainen i_fatal("Time just moved backwards by %ld seconds. "
651fc0f1e43fef3e02e0e7b5f498973b05f641d7Timo Sirainen "This might cause a lot of problems, "
651fc0f1e43fef3e02e0e7b5f498973b05f641d7Timo Sirainen "so I'll just kill myself now. "
651fc0f1e43fef3e02e0e7b5f498973b05f641d7Timo Sirainen "http://wiki.dovecot.org/TimeMovedBackwards", diff);
6c2ddb9f586e6392552ddfb82ab55e57fcfc4110Timo Sirainen i_error("Time just moved backwards by %ld seconds. "
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen "I'll sleep now until we're back in present. "
6825360d446542046757b06064282301c4c6b27cTimo Sirainen "http://wiki.dovecot.org/TimeMovedBackwards", diff);
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen /* Sleep extra second to make sure usecs also grows. */
6825360d446542046757b06064282301c4c6b27cTimo Sirainen /* don't use sleep()'s return value, because
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen it could get us to a long loop in case
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen interrupts just keep coming */
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainenstatic struct mail_user *
a10ed8c47534b4c6b6bf2711ccfe577e720a47b4Timo Sirainen const struct mail_storage_service_input *_input,
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen const struct setting_parser_info *set_roots[],
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen struct mail_storage_service_input input = *_input;
41e1c7380edda701719d8ce1fb4d465d2ec4c84dTimo Sirainen const char *user, *orig_user, *home, *system_groups_user, *error;
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen unsigned int len;
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen io_loop_set_time_moved_callback(current_ioloop,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen userdb_lookup = (flags & MAIL_STORAGE_SERVICE_FLAG_USERDB_LOOKUP) != 0;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen mail_storage_service_init_settings(service, &input, set_roots,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if ((flags & MAIL_STORAGE_SERVICE_FLAG_DEBUG) != 0)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen set_keyval(service->set_parser, "mail_debug", "yes");
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen mail_storage_service_init_log(service, &input);
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen sets = master_service_settings_get_others(service);
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen mail_set = mail_user_set_get_storage_set(user_set);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* userdb lookup may change settings, do it as soon as
bbf796c17f02538058d7559bfe96d677e5b55015Timo Sirainen conn = auth_master_init(user_set->auth_socket_path,
bbf796c17f02538058d7559bfe96d677e5b55015Timo Sirainen if (service_auth_userdb_lookup(conn, service->set_parser,
bbf796c17f02538058d7559bfe96d677e5b55015Timo Sirainen /* set up logging again in case username changed */
c7fc0431b23dc8d29e12e09c8120e223fbee116bTimo Sirainen mail_storage_service_init_log(service, &input);
bbf796c17f02538058d7559bfe96d677e5b55015Timo Sirainen /* variable strings are expanded in mail_user_init(),
6e07b4251bf6a3cf34019c351a32a65c08392e58Timo Sirainen but we need the home sooner so do it separately here. */
6e07b4251bf6a3cf34019c351a32a65c08392e58Timo Sirainen home = user_expand_varstr(service, &input, user_set->mail_home);
6e07b4251bf6a3cf34019c351a32a65c08392e58Timo Sirainen if (*home == '\0' && getenv("HOME") != NULL) {
6e07b4251bf6a3cf34019c351a32a65c08392e58Timo Sirainen set_keyval(service->set_parser, "mail_home", home);
6e07b4251bf6a3cf34019c351a32a65c08392e58Timo Sirainen if (len > 2 && strcmp(user_set->mail_chroot + len - 2, "/.") == 0 &&
6e07b4251bf6a3cf34019c351a32a65c08392e58Timo Sirainen strncmp(home, user_set->mail_chroot, len - 2) == 0) {
6e07b4251bf6a3cf34019c351a32a65c08392e58Timo Sirainen /* If chroot ends with "/.", strip chroot dir from home dir */
893e5bbd5184ec5c21f47c67c8ea6efbea41f7d0Timo Sirainen set_keyval(service->set_parser, "mail_home", home);
44c5e644cb413a6559bf2d4179cbe48f9a82f366Timo Sirainen modules = *user_set->mail_plugins == '\0' ? NULL :
88187ee880b4829443e0d55ea7d145d9d5880217Timo Sirainen if ((flags & MAIL_STORAGE_SERVICE_FLAG_NO_RESTRICT_ACCESS) == 0) {
88187ee880b4829443e0d55ea7d145d9d5880217Timo Sirainen service_drop_privileges(user_set, system_groups_user, home,
88187ee880b4829443e0d55ea7d145d9d5880217Timo Sirainen (flags & MAIL_STORAGE_SERVICE_FLAG_DISALLOW_ROOT) != 0,
73b50eecfc31750a312e2f940023f522eb07178cTimo Sirainen /* privileges are now dropped */
73b50eecfc31750a312e2f940023f522eb07178cTimo Sirainen mail_users_init(user_set->auth_socket_path, mail_set->mail_debug);
73b50eecfc31750a312e2f940023f522eb07178cTimo Sirainen if (mail_storage_service_init_post(service, &input, home, user_set,
bbf796c17f02538058d7559bfe96d677e5b55015Timo Sirainenmail_storage_service_init_user(struct master_service *service,
bbf796c17f02538058d7559bfe96d677e5b55015Timo Sirainen const struct mail_storage_service_input *_input,
bbf796c17f02538058d7559bfe96d677e5b55015Timo Sirainen const struct setting_parser_info *set_roots[],
a10ed8c47534b4c6b6bf2711ccfe577e720a47b4Timo Sirainen user = init_user_real(service, _input, set_roots, flags);
struct mail_storage_service_multi_ctx *
void **sets;
return ctx;
struct auth_master_connection *
const char **error_r)
void **sets;
int ret;
error_r);
if (ret <= 0)
return ret;
const char **error_r)
const char *home;
unsigned int len;
TRUE);
} else if (len > 0) {
const char **username_r)
T_BEGIN {
} T_END;
return set;