mail-storage-service.c revision 367e28a16854ee9f7247b2518f36f5e9163fcc10
89a126810703c666309310d0f3189e9834d70b5bTimo Sirainen/* Copyright (c) 2009 Dovecot authors, see the included COPYING file */
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainenstatic void set_keyval(struct setting_parser_context *set_parser,
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen const char *str;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen if (settings_parse_line(set_parser, str) < 0) {
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainenstatic bool validate_chroot(const struct mail_user_settings *user_set,
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen const char *dir)
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen const char *const *chroot_dirs;
20344c0e814139e3c365fbb9287478f91512089eTimo Sirainen chroot_dirs = t_strsplit(user_set->valid_chroot_dirs, ":");
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen strncmp(dir, *chroot_dirs, strlen(*chroot_dirs)) == 0)
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainenuser_reply_handle(struct setting_parser_context *set_parser,
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen const char **system_groups_user_r, const char **error_r)
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen unsigned int i, count;
601f5f14c6cde28f0e0c6ca7c5d735315d3d48dfTimo Sirainen set_keyval(set_parser, "mail_uid", dec2str(reply->uid));
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen set_keyval(set_parser, "mail_gid", dec2str(reply->gid));
602a0434db30d8e3292d1c161a803d96a879a74fTimo Sirainen set_keyval(set_parser, "mail_home", reply->home);
602a0434db30d8e3292d1c161a803d96a879a74fTimo Sirainen if (!validate_chroot(user_set, reply->chroot)) {
602a0434db30d8e3292d1c161a803d96a879a74fTimo Sirainen "userdb returned invalid chroot directory: %s "
602a0434db30d8e3292d1c161a803d96a879a74fTimo Sirainen "(see valid_chroot_dirs setting)",
602a0434db30d8e3292d1c161a803d96a879a74fTimo Sirainen set_keyval(set_parser, "mail_chroot", reply->chroot);
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen str = array_get(&reply->extra_fields, &count);
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen for (i = 0; i < count && ret == 0; i++) T_BEGIN {
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen if (strncmp(line, "system_groups_user=", 19) == 0) {
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen line = t_strconcat("mail_location=", line + 5, NULL);
7d207b1e77a7b5e3fda640e353acfc86d261fedfTimo Sirainen if (!settings_parse_is_valid_key(set_parser, key)) {
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen /* assume it's a plugin setting */
7e1f68ad71d3485f1882142837b01f7a98ca8467Timo Sirainen *error_r = t_strdup_printf("Invalid userdb input '%s': %s",
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen str[i], settings_parser_get_error(set_parser));
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainenservice_auth_userdb_lookup(struct setting_parser_context *set_parser,
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen const char **user, const char **system_groups_user_r,
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen const char **error_r)
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen const char *system_groups_user, *orig_user = *user;
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen unsigned int len;
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen mail_set = mail_user_set_get_storage_set(user_set);
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen pool = pool_alloconly_create("userdb lookup", 1024);
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen conn = auth_master_init(user_set->auth_socket_path,
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen ret = auth_master_user_lookup(conn, *user, name, pool, &reply);
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen len = reply.chroot == NULL ? 0 : strlen(reply.chroot);
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen if (user_reply_handle(set_parser, user_set, &reply,
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen *system_groups_user_r = t_strdup(system_groups_user);
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen if (ret > 0 && strcmp(*user, orig_user) != 0) {
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen if (mail_user_set_get_storage_set(user_set)->mail_debug)
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen i_set_failure_prefix(t_strdup_printf("%s(%s): ", name, *user));
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainenstatic bool parse_uid(const char *str, uid_t *uid_r)
89e195dfb5c4b0efd9b9f459771a4467674e5b1fTimo Sirainen if (*p == '\0')
c06f4017027263cf3a08becc551f5126409e2a83Timo Sirainenstatic bool parse_gid(const char *str, gid_t *gid_r)
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen if (*p == '\0')
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainenservice_drop_privileges(const struct mail_user_settings *set,
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen const char *system_groups_user, const char *home,
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen i_fatal("Unknown mail_uid user: %s", set->mail_uid);
37ab3cde96bfa4bc5304c0c348fc420aec79572dTimo Sirainen "not permitted (see first_valid_uid in config file).",
abe8754852e70763e92f74caabbcc13d0917714cTimo Sirainen i_fatal("Unknown mail_gid group: %s", set->mail_gid);
c06f4017027263cf3a08becc551f5126409e2a83Timo Sirainen "not permitted (see first_valid_gid in config file).",
c06f4017027263cf3a08becc551f5126409e2a83Timo Sirainen if (!parse_uid(set->mail_privileged_group, &rset.privileged_gid))
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen i_fatal("Unknown mail_gid group: %s", set->mail_gid);
904f9d5654b9c39edcdf32883e5e88771faf4d69Timo Sirainen /* we can't chroot if we want to switch between users. there's not
904f9d5654b9c39edcdf32883e5e88771faf4d69Timo Sirainen much point either (from security point of view) */
904f9d5654b9c39edcdf32883e5e88771faf4d69Timo Sirainen rset.chroot_dir = *set->mail_chroot == '\0' || keep_setuid_root ?
7ef5ca6fb59a318c821a852ae48a2edbb671d7ddTimo Sirainen (rset.uid == 0 || (rset.uid == (uid_t)-1 && current_euid == 0)))
7ef5ca6fb59a318c821a852ae48a2edbb671d7ddTimo Sirainen if (keep_setuid_root && current_euid != rset.uid) {
7ef5ca6fb59a318c821a852ae48a2edbb671d7ddTimo Sirainen /* we're changing the UID, switch back to root first */
7ef5ca6fb59a318c821a852ae48a2edbb671d7ddTimo Sirainen restrict_access(&rset, *home == '\0' ? NULL : home, disallow_root);
7ef5ca6fb59a318c821a852ae48a2edbb671d7ddTimo Sirainen i_fatal("seteuid(%s) failed: %m", dec2str(setuid_uid));
14175321ddb88619015866978c05a27786ca4814Timo Sirainenmail_storage_service_init_settings(struct master_service *service,
14175321ddb88619015866978c05a27786ca4814Timo Sirainen const struct setting_parser_info *set_roots[],
14175321ddb88619015866978c05a27786ca4814Timo Sirainen ARRAY_DEFINE(all_set_roots, const struct setting_parser_info *);
14175321ddb88619015866978c05a27786ca4814Timo Sirainen const struct setting_parser_info *info = &mail_user_setting_parser_info;
14175321ddb88619015866978c05a27786ca4814Timo Sirainen unsigned int i;
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen array_append(&all_set_roots, &set_roots[i], 1);
7ef5ca6fb59a318c821a852ae48a2edbb671d7ddTimo Sirainen /* read settings after registering storages so they can have their
7ef5ca6fb59a318c821a852ae48a2edbb671d7ddTimo Sirainen own setting definitions too */
7ef5ca6fb59a318c821a852ae48a2edbb671d7ddTimo Sirainen set_roots = array_idx_modifiable(&all_set_roots, 0);
7ef5ca6fb59a318c821a852ae48a2edbb671d7ddTimo Sirainen if (master_service_settings_read(service, set_roots,
7ef5ca6fb59a318c821a852ae48a2edbb671d7ddTimo Sirainen i_fatal("Error reading configuration: %s", error);
fe363b433b8038a69b55169da9dca27892ad7d18Timo Sirainenmail_storage_service_init_post(struct master_service *service,
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen const char **error_r)
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen mail_set = mail_user_set_get_storage_set(user_set);
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen /* If possible chdir to home directory, so that core file
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen could be written in case we crash. */
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen mail_user_set_home(mail_user, *home == '\0' ? NULL : home);
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen mail_user_set_vars(mail_user, geteuid(), service->name, NULL, NULL);
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen if (mail_namespaces_init(mail_user, error_r) < 0) {
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainenstatic const struct var_expand_table *
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainenget_var_expand_table(struct master_service *service, const char *user)
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen static struct var_expand_table static_tab[] = {
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainenstatic const char *
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainenuser_expand_varstr(struct master_service *service, const char *user,
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen const char *str)
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen i_assert(*str == SETTING_STRVAR_UNEXPANDED[0]);
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen var_expand(ret, str + 1, get_var_expand_table(service, user));
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainenmail_storage_service_init_user(struct master_service *service, const char *user,
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen const struct setting_parser_info *set_roots[],
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen const char *orig_user, *home, *system_groups_user, *error;
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen unsigned int len;
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen userdb_lookup = (flags & MAIL_STORAGE_SERVICE_FLAG_USERDB_LOOKUP) != 0;
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen mail_storage_service_init_settings(service, set_roots, !userdb_lookup);
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen if ((flags & MAIL_STORAGE_SERVICE_FLAG_DEBUG) != 0)
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen set_keyval(service->set_parser, "mail_debug", "yes");
fe363b433b8038a69b55169da9dca27892ad7d18Timo Sirainen /* now that we've read settings, we can set up logging */
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen t_strdup_printf("%s(%s): ", service->name, user));
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen sets = master_service_settings_get_others(service);
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen mail_set = mail_user_set_get_storage_set(user_set);
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen /* userdb lookup may change settings, do it as soon as
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen if (service_auth_userdb_lookup(service->set_parser,
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen /* variable strings are expanded in mail_user_init(),
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen but we need the home sooner so do it separately here. */
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen home = user_expand_varstr(service, user, user_set->mail_home);
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen if (*home == '\0' && getenv("HOME") != NULL) {
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen set_keyval(service->set_parser, "mail_home", home);
FALSE);
return mail_user;
void mail_storage_service_deinit_user(void)
struct mail_storage_service_multi_ctx *
void **sets;
return ctx;
const char **error_r)
const char *orig_user;
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) {
void **sets;