mail-storage-service.c revision 5d2a93f2dd0392193b790bf94b8c22b0c2feae3a
a6ab8f00351265e35b79f3a22b1f5a4978ae5c35Timo Sirainen/* Copyright (c) 2009 Dovecot authors, see the included COPYING file */
a6ab8f00351265e35b79f3a22b1f5a4978ae5c35Timo Sirainen
a6ab8f00351265e35b79f3a22b1f5a4978ae5c35Timo Sirainen#include "lib.h"
767431e5084a037c4dbefdf30ebfa03c84b1f449Timo Sirainen#include "ioloop.h"
5fbccc935e3f7b916aa7c6e302a212821072e83aTimo Sirainen#include "array.h"
1c633f71ec2060e5bfa500a97f34cd881a958ecdTimo Sirainen#include "hostpid.h"
1db62753d9e3b5d71018889c8ef0a3722a307455Timo Sirainen#include "module-dir.h"
a8fe899601735459641edae975c0fa08be8482e2Timo Sirainen#include "restrict-access.h"
885a3c2287ae3e5827aa580ea06b231de38abb47Timo Sirainen#include "str.h"
5fb3f13537dffd15a31e997da133a721c0728af8Timo Sirainen#include "var-expand.h"
97437f768d1a3e6134fed1971202803fd250eef2Timo Sirainen#include "dict.h"
bb25bed75eefd011138ebf1b8e033fc8ef55ca74Timo Sirainen#include "settings-parser.h"
5fbccc935e3f7b916aa7c6e302a212821072e83aTimo Sirainen#include "auth-master.h"
a6ab8f00351265e35b79f3a22b1f5a4978ae5c35Timo Sirainen#include "master-service-private.h"
cf63dc8723b971cc80638fccbf494d961cbafc7fTimo Sirainen#include "master-service-settings.h"
a6ab8f00351265e35b79f3a22b1f5a4978ae5c35Timo Sirainen#include "mail-user.h"
1db62753d9e3b5d71018889c8ef0a3722a307455Timo Sirainen#include "mail-namespace.h"
e09c7dc961cb9cab04ec7cc79215c2f6318fbde0Timo Sirainen#include "mail-storage.h"
885a3c2287ae3e5827aa580ea06b231de38abb47Timo Sirainen#include "mail-storage-service.h"
23878bd03d1de531e3261a25598beec621351910Timo Sirainen
23878bd03d1de531e3261a25598beec621351910Timo Sirainen#include <stdlib.h>
1db62753d9e3b5d71018889c8ef0a3722a307455Timo Sirainen#include <sys/stat.h>
1db62753d9e3b5d71018889c8ef0a3722a307455Timo Sirainen#include <pwd.h>
62d0db14d2c5008758983c28d242ec158baabf9eTimo Sirainen#include <grp.h>
62d0db14d2c5008758983c28d242ec158baabf9eTimo Sirainen
62d0db14d2c5008758983c28d242ec158baabf9eTimo Sirainen/* If time moves backwards more than this, kill ourself instead of sleeping. */
5fbccc935e3f7b916aa7c6e302a212821072e83aTimo Sirainen#define MAX_TIME_BACKWARDS_SLEEP 5
5fbccc935e3f7b916aa7c6e302a212821072e83aTimo Sirainen#define MAX_NOWARN_FORWARD_SECS 10
cf63dc8723b971cc80638fccbf494d961cbafc7fTimo Sirainen
cf63dc8723b971cc80638fccbf494d961cbafc7fTimo Sirainenstruct mail_storage_service_multi_ctx {
a6ab8f00351265e35b79f3a22b1f5a4978ae5c35Timo Sirainen struct master_service *service;
1db62753d9e3b5d71018889c8ef0a3722a307455Timo Sirainen struct auth_master_connection *conn;
1db62753d9e3b5d71018889c8ef0a3722a307455Timo Sirainen struct auth_master_user_list_ctx *auth_list;
1db62753d9e3b5d71018889c8ef0a3722a307455Timo Sirainen enum mail_storage_service_flags flags;
1db62753d9e3b5d71018889c8ef0a3722a307455Timo Sirainen
1db62753d9e3b5d71018889c8ef0a3722a307455Timo Sirainen unsigned int modules_initialized:1;
1db62753d9e3b5d71018889c8ef0a3722a307455Timo Sirainen};
1db62753d9e3b5d71018889c8ef0a3722a307455Timo Sirainen
1db62753d9e3b5d71018889c8ef0a3722a307455Timo Sirainenstruct mail_storage_service_multi_user {
1db62753d9e3b5d71018889c8ef0a3722a307455Timo Sirainen pool_t pool;
1db62753d9e3b5d71018889c8ef0a3722a307455Timo Sirainen struct mail_storage_service_input input;
1db62753d9e3b5d71018889c8ef0a3722a307455Timo Sirainen
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen const char *system_groups_user;
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen const struct mail_user_settings *user_set;
10b8040903b1d1591f1d44552ff466c8789b8814Timo Sirainen struct setting_parser_context *set_parser;
10b8040903b1d1591f1d44552ff466c8789b8814Timo Sirainen};
0a9cb42cbb135e3200cbfbb657820304cca8ecb8Timo Sirainen
0a9cb42cbb135e3200cbfbb657820304cca8ecb8Timo Sirainenstatic struct module *modules = NULL;
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen
1db62753d9e3b5d71018889c8ef0a3722a307455Timo Sirainenstatic void set_keyval(struct setting_parser_context *set_parser,
72bc08129fb0aaec8144cc183a998ccc426fef9eTimo Sirainen const char *key, const char *value)
885a3c2287ae3e5827aa580ea06b231de38abb47Timo Sirainen{
1db62753d9e3b5d71018889c8ef0a3722a307455Timo Sirainen const char *str;
cdfdb67422891a44fc7d9ace6bc1a00185fd3528Timo Sirainen
1db62753d9e3b5d71018889c8ef0a3722a307455Timo Sirainen str = t_strconcat(key, "=", value, NULL);
1db62753d9e3b5d71018889c8ef0a3722a307455Timo Sirainen if (settings_parse_line(set_parser, str) < 0) {
1db62753d9e3b5d71018889c8ef0a3722a307455Timo Sirainen i_fatal("Invalid userdb input '%s': %s", str,
1d082a46e1676e7ec13928d588c4a25e062713ccTimo Sirainen settings_parser_get_error(set_parser));
885a3c2287ae3e5827aa580ea06b231de38abb47Timo Sirainen }
885a3c2287ae3e5827aa580ea06b231de38abb47Timo Sirainen}
7358272563d8ef77366447708ab0e58c0cff4151Timo Sirainen
1db62753d9e3b5d71018889c8ef0a3722a307455Timo Sirainenstatic bool validate_chroot(const struct mail_user_settings *user_set,
1db62753d9e3b5d71018889c8ef0a3722a307455Timo Sirainen const char *dir)
4ee00532a265bdfb38539d811fcd12d51210ac35Timo Sirainen{
7358272563d8ef77366447708ab0e58c0cff4151Timo Sirainen const char *const *chroot_dirs;
885a3c2287ae3e5827aa580ea06b231de38abb47Timo Sirainen
885a3c2287ae3e5827aa580ea06b231de38abb47Timo Sirainen if (*dir == '\0')
885a3c2287ae3e5827aa580ea06b231de38abb47Timo Sirainen return FALSE;
a8281b7c770f4a9a842b19303083fc7f6859e756Timo Sirainen
a8281b7c770f4a9a842b19303083fc7f6859e756Timo Sirainen if (*user_set->valid_chroot_dirs == '\0')
29f138b4b9bc037b21dfaa6b8e458943a99d5db2Timo Sirainen return FALSE;
29f138b4b9bc037b21dfaa6b8e458943a99d5db2Timo Sirainen
29f138b4b9bc037b21dfaa6b8e458943a99d5db2Timo Sirainen chroot_dirs = t_strsplit(user_set->valid_chroot_dirs, ":");
7358272563d8ef77366447708ab0e58c0cff4151Timo Sirainen while (*chroot_dirs != NULL) {
1db62753d9e3b5d71018889c8ef0a3722a307455Timo Sirainen if (**chroot_dirs != '\0' &&
1db62753d9e3b5d71018889c8ef0a3722a307455Timo Sirainen strncmp(dir, *chroot_dirs, strlen(*chroot_dirs)) == 0)
a6ab8f00351265e35b79f3a22b1f5a4978ae5c35Timo Sirainen return TRUE;
23878bd03d1de531e3261a25598beec621351910Timo Sirainen chroot_dirs++;
a6ab8f00351265e35b79f3a22b1f5a4978ae5c35Timo Sirainen }
a6ab8f00351265e35b79f3a22b1f5a4978ae5c35Timo Sirainen return FALSE;
a6ab8f00351265e35b79f3a22b1f5a4978ae5c35Timo Sirainen}
a6ab8f00351265e35b79f3a22b1f5a4978ae5c35Timo Sirainen
a6ab8f00351265e35b79f3a22b1f5a4978ae5c35Timo Sirainenstatic int
a6ab8f00351265e35b79f3a22b1f5a4978ae5c35Timo Sirainenuser_reply_handle(struct setting_parser_context *set_parser,
1db62753d9e3b5d71018889c8ef0a3722a307455Timo Sirainen const struct mail_user_settings *user_set,
1db62753d9e3b5d71018889c8ef0a3722a307455Timo Sirainen const struct auth_user_reply *reply,
ad58b50aef8125981ebdbc89513236558bcccf60Timo Sirainen const char **system_groups_user_r, const char **error_r)
a6ab8f00351265e35b79f3a22b1f5a4978ae5c35Timo Sirainen{
a6ab8f00351265e35b79f3a22b1f5a4978ae5c35Timo Sirainen const char *const *str, *p, *line, *key;
a6ab8f00351265e35b79f3a22b1f5a4978ae5c35Timo Sirainen unsigned int i, count;
0779e926687b319fe1bcc0f1010ba7f88023e789Timo Sirainen int ret = 0;
0f9a8663b0ff6fe30389d02284a2b002c40914ebTimo Sirainen
bd417d416988d11a6b555b9aa57779e7ed976951Timo Sirainen *system_groups_user_r = NULL;
a9efdb661eb7a8a33aacfdcc3486dcc675a21543Timo Sirainen
a9efdb661eb7a8a33aacfdcc3486dcc675a21543Timo Sirainen if (reply->uid != (uid_t)-1) {
fab850a6aee4aaef4f4795bd7946807a3ba45041Timo Sirainen if (reply->uid == 0) {
bd417d416988d11a6b555b9aa57779e7ed976951Timo Sirainen *error_r = "userdb returned 0 as uid";
a6ab8f00351265e35b79f3a22b1f5a4978ae5c35Timo Sirainen return -1;
a6ab8f00351265e35b79f3a22b1f5a4978ae5c35Timo Sirainen }
a6ab8f00351265e35b79f3a22b1f5a4978ae5c35Timo Sirainen set_keyval(set_parser, "mail_uid", dec2str(reply->uid));
a6ab8f00351265e35b79f3a22b1f5a4978ae5c35Timo Sirainen }
5685e60e62a8e0d368bd28a1526056f97bbba022Timo Sirainen if (reply->gid != (uid_t)-1)
5685e60e62a8e0d368bd28a1526056f97bbba022Timo Sirainen set_keyval(set_parser, "mail_gid", dec2str(reply->gid));
5685e60e62a8e0d368bd28a1526056f97bbba022Timo Sirainen
0779e926687b319fe1bcc0f1010ba7f88023e789Timo Sirainen if (reply->home != NULL)
72bc08129fb0aaec8144cc183a998ccc426fef9eTimo Sirainen set_keyval(set_parser, "mail_home", reply->home);
72bc08129fb0aaec8144cc183a998ccc426fef9eTimo Sirainen
c14c5561e85853d91280235a7611b6050feaebb2Timo Sirainen if (reply->chroot != NULL) {
c14c5561e85853d91280235a7611b6050feaebb2Timo Sirainen if (!validate_chroot(user_set, reply->chroot)) {
c14c5561e85853d91280235a7611b6050feaebb2Timo Sirainen *error_r = t_strdup_printf(
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen "userdb returned invalid chroot directory: %s "
72bc08129fb0aaec8144cc183a998ccc426fef9eTimo Sirainen "(see valid_chroot_dirs setting)",
2cc88ff507e244faa63683f804833b321a62c665Timo Sirainen reply->chroot);
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen return -1;
0779e926687b319fe1bcc0f1010ba7f88023e789Timo Sirainen }
51327f2489a4e0e615eb9f7d921473cf8512bb79Timo Sirainen set_keyval(set_parser, "mail_chroot", reply->chroot);
51327f2489a4e0e615eb9f7d921473cf8512bb79Timo Sirainen }
97437f768d1a3e6134fed1971202803fd250eef2Timo Sirainen
97437f768d1a3e6134fed1971202803fd250eef2Timo Sirainen str = array_get(&reply->extra_fields, &count);
97afa073e3e1e0301dc41173ec34beb08edcce50Timo Sirainen for (i = 0; i < count && ret == 0; i++) {
97afa073e3e1e0301dc41173ec34beb08edcce50Timo Sirainen line = str[i];
636f017be100bce67d66fd3ae1544a47681efd33Timo Sirainen if (strncmp(line, "system_groups_user=", 19) == 0)
b8b085f7bc6f1c0367802a9f00062bbbd981690dTimo Sirainen *system_groups_user_r = t_strdup(line + 19);
b8b085f7bc6f1c0367802a9f00062bbbd981690dTimo Sirainen else T_BEGIN {
94ba4820927b906b333e39445c1508a29387c3aaTimo Sirainen if (strncmp(line, "mail=", 5) == 0) {
b932ee7fbbec6e79b777dcc7ba613b9e99f8337bTimo Sirainen line = t_strconcat("mail_location=",
b932ee7fbbec6e79b777dcc7ba613b9e99f8337bTimo Sirainen line + 5, NULL);
cf63dc8723b971cc80638fccbf494d961cbafc7fTimo Sirainen } else if ((p = strchr(str[i], '=')) == NULL)
23878bd03d1de531e3261a25598beec621351910Timo Sirainen line = t_strconcat(str[i], "=yes", NULL);
23878bd03d1de531e3261a25598beec621351910Timo Sirainen else
23878bd03d1de531e3261a25598beec621351910Timo Sirainen line = str[i];
23878bd03d1de531e3261a25598beec621351910Timo Sirainen
5fbccc935e3f7b916aa7c6e302a212821072e83aTimo Sirainen key = t_strcut(line, '=');
5fbccc935e3f7b916aa7c6e302a212821072e83aTimo Sirainen if (!settings_parse_is_valid_key(set_parser, key)) {
5fbccc935e3f7b916aa7c6e302a212821072e83aTimo Sirainen /* assume it's a plugin setting */
5fbccc935e3f7b916aa7c6e302a212821072e83aTimo Sirainen line = t_strconcat("plugin/", line, NULL);
5fbccc935e3f7b916aa7c6e302a212821072e83aTimo Sirainen }
5fbccc935e3f7b916aa7c6e302a212821072e83aTimo Sirainen
bb25bed75eefd011138ebf1b8e033fc8ef55ca74Timo Sirainen ret = settings_parse_line(set_parser, line);
bb25bed75eefd011138ebf1b8e033fc8ef55ca74Timo Sirainen } T_END;
5fbccc935e3f7b916aa7c6e302a212821072e83aTimo Sirainen }
2a15ce3abe14099b94535f6dfc2d4ee023a7c455Timo Sirainen
2a15ce3abe14099b94535f6dfc2d4ee023a7c455Timo Sirainen if (ret < 0) {
2a15ce3abe14099b94535f6dfc2d4ee023a7c455Timo Sirainen *error_r = t_strdup_printf("Invalid userdb input '%s': %s",
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen str[i], settings_parser_get_error(set_parser));
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen }
6eb7938cd366fc087b39fc9a901e7de426131384Timo Sirainen return ret;
2a15ce3abe14099b94535f6dfc2d4ee023a7c455Timo Sirainen}
2a15ce3abe14099b94535f6dfc2d4ee023a7c455Timo Sirainen
2a15ce3abe14099b94535f6dfc2d4ee023a7c455Timo Sirainenstatic int
2a15ce3abe14099b94535f6dfc2d4ee023a7c455Timo Sirainenservice_auth_userdb_lookup(struct auth_master_connection *conn,
138495d02aa177230a9f1eaf90b720b4ce0f6544Timo Sirainen const char *service_name,
2a15ce3abe14099b94535f6dfc2d4ee023a7c455Timo Sirainen const struct mail_storage_service_input *input,
2a15ce3abe14099b94535f6dfc2d4ee023a7c455Timo Sirainen const struct mail_user_settings *user_set,
2a15ce3abe14099b94535f6dfc2d4ee023a7c455Timo Sirainen pool_t pool, const char **user,
2a15ce3abe14099b94535f6dfc2d4ee023a7c455Timo Sirainen const char *const **fields_r,
2a15ce3abe14099b94535f6dfc2d4ee023a7c455Timo Sirainen const char **error_r)
2a15ce3abe14099b94535f6dfc2d4ee023a7c455Timo Sirainen{
2a15ce3abe14099b94535f6dfc2d4ee023a7c455Timo Sirainen struct auth_user_info info;
2a15ce3abe14099b94535f6dfc2d4ee023a7c455Timo Sirainen const char *new_username;
2c57ebc900742bd1119ef011b77b4910c4660cfaTimo Sirainen int ret;
2c57ebc900742bd1119ef011b77b4910c4660cfaTimo Sirainen
2c57ebc900742bd1119ef011b77b4910c4660cfaTimo Sirainen memset(&info, 0, sizeof(info));
2c57ebc900742bd1119ef011b77b4910c4660cfaTimo Sirainen info.service = service_name;
eed1ec3ac96fddb8d9e4fa2af6e760ee42801fb8Timo Sirainen info.local_ip = input->local_ip;
94ba4820927b906b333e39445c1508a29387c3aaTimo Sirainen info.remote_ip = input->remote_ip;
a6ab8f00351265e35b79f3a22b1f5a4978ae5c35Timo Sirainen
ret = auth_master_user_lookup(conn, *user, &info, pool,
&new_username, fields_r);
if (ret > 0) {
if (strcmp(*user, new_username) != 0) {
if (mail_user_set_get_storage_set(user_set)->mail_debug)
i_debug("changed username to %s", new_username);
*user = t_strdup(new_username);
}
*user = new_username;
} else if (ret == 0)
*error_r = "unknown user";
else
*error_r = "userdb lookup failed";
return ret;
}
static bool parse_uid(const char *str, uid_t *uid_r)
{
struct passwd *pw;
char *p;
if (*str >= '0' && *str <= '9') {
*uid_r = (uid_t)strtoul(str, &p, 10);
if (*p == '\0')
return TRUE;
}
pw = getpwnam(str);
if (pw == NULL)
return FALSE;
*uid_r = pw->pw_uid;
return TRUE;
}
static bool parse_gid(const char *str, gid_t *gid_r)
{
struct group *gr;
char *p;
if (*str >= '0' && *str <= '9') {
*gid_r = (gid_t)strtoul(str, &p, 10);
if (*p == '\0')
return TRUE;
}
gr = getgrnam(str);
if (gr == NULL)
return FALSE;
*gid_r = gr->gr_gid;
return TRUE;
}
static void
service_drop_privileges(const struct mail_user_settings *set,
const char *system_groups_user, const char *home,
bool disallow_root, bool keep_setuid_root)
{
struct restrict_access_settings rset;
uid_t current_euid, setuid_uid = 0;
current_euid = geteuid();
restrict_access_init(&rset);
if (*set->mail_uid != '\0') {
if (!parse_uid(set->mail_uid, &rset.uid))
i_fatal("Unknown mail_uid user: %s", set->mail_uid);
if (rset.uid < (uid_t)set->first_valid_uid ||
(set->last_valid_uid != 0 &&
rset.uid > (uid_t)set->last_valid_uid)) {
i_fatal("Mail access for users with UID %s "
"not permitted (see first_valid_uid in config file).",
dec2str(rset.uid));
}
}
if (*set->mail_gid != '\0') {
if (!parse_gid(set->mail_gid, &rset.gid))
i_fatal("Unknown mail_gid group: %s", set->mail_gid);
if (rset.gid < (gid_t)set->first_valid_gid ||
(set->last_valid_gid != 0 &&
rset.gid > (gid_t)set->last_valid_gid)) {
i_fatal("Mail access for users with GID %s "
"not permitted (see first_valid_gid in config file).",
dec2str(rset.gid));
}
}
if (*set->mail_privileged_group != '\0') {
if (!parse_uid(set->mail_privileged_group, &rset.privileged_gid))
i_fatal("Unknown mail_gid group: %s", set->mail_gid);
}
if (*set->mail_access_groups != '\0')
rset.extra_groups = set->mail_access_groups;
rset.first_valid_gid = set->first_valid_gid;
rset.last_valid_gid = set->last_valid_gid;
/* we can't chroot if we want to switch between users. there's not
much point either (from security point of view) */
rset.chroot_dir = *set->mail_chroot == '\0' || keep_setuid_root ?
NULL : set->mail_chroot;
rset.system_groups_user = system_groups_user;
if (disallow_root &&
(rset.uid == 0 || (rset.uid == (uid_t)-1 && current_euid == 0)))
i_fatal("Mail access not allowed for root");
if (keep_setuid_root) {
if (current_euid != rset.uid) {
if (current_euid != 0) {
/* we're changing the UID,
switch back to root first */
if (seteuid(0) < 0)
i_fatal("seteuid(0) failed: %m");
}
setuid_uid = rset.uid;
}
rset.uid = (uid_t)-1;
disallow_root = FALSE;
}
restrict_access(&rset, *home == '\0' ? NULL : home, disallow_root);
if (setuid_uid != 0) {
if (seteuid(setuid_uid) < 0)
i_fatal("seteuid(%s) failed: %m", dec2str(setuid_uid));
}
}
static void
mail_storage_service_init_settings(struct master_service *service,
const struct mail_storage_service_input *input,
const struct setting_parser_info *set_roots[],
bool preserve_home)
{
ARRAY_DEFINE(all_set_roots, const struct setting_parser_info *);
const struct setting_parser_info *info = &mail_user_setting_parser_info;
struct master_service_settings_input set_input;
const char *error;
unsigned int i;
(void)umask(0077);
mail_storage_init();
mail_storage_register_all();
mailbox_list_register_all();
t_array_init(&all_set_roots, 5);
array_append(&all_set_roots, &info, 1);
if (set_roots != NULL) {
for (i = 0; set_roots[i] != NULL; i++)
array_append(&all_set_roots, &set_roots[i], 1);
}
(void)array_append_space(&all_set_roots);
/* read settings after registering storages so they can have their
own setting definitions too */
memset(&set_input, 0, sizeof(set_input));
set_input.roots = array_idx_modifiable(&all_set_roots, 0);
set_input.dyn_parsers = mail_storage_get_dynamic_parsers();
set_input.preserve_home = preserve_home;
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 (master_service_settings_read(service, &set_input, &error) < 0)
i_fatal("Error reading configuration: %s", error);
}
static int
mail_storage_service_init_post(struct master_service *service,
const struct mail_storage_service_input *input,
const char *home,
const struct mail_user_settings *user_set,
bool setuid_root,
enum mail_storage_service_flags flags,
struct mail_user **mail_user_r,
const char **error_r)
{
const struct mail_storage_settings *mail_set;
struct mail_user *mail_user;
mail_set = mail_user_set_get_storage_set(user_set);
if (mail_set->mail_debug) {
i_debug("Effective uid=%s, gid=%s, home=%s",
dec2str(geteuid()), dec2str(getegid()),
home != NULL ? home : "(none)");
}
if (setuid_root) {
/* we don't want to write core files to any users' home
directories since they could contain information about other
users' mails as well. so do no chdiring to home. */
} else if (*home != '\0' &&
(flags & MAIL_STORAGE_SERVICE_FLAG_NO_CHDIR) == 0) {
/* If possible chdir to home directory, so that core file
could be written in case we crash. */
if (chdir(home) < 0) {
if (errno != ENOENT)
i_error("chdir(%s) failed: %m", home);
else if (mail_set->mail_debug)
i_debug("Home dir not found: %s", home);
}
}
mail_user = mail_user_alloc(input->username, user_set);
mail_user_set_home(mail_user, *home == '\0' ? NULL : home);
mail_user_set_vars(mail_user, geteuid(), service->name,
&input->local_ip, &input->remote_ip);
if (mail_user_init(mail_user, error_r) < 0) {
mail_user_unref(&mail_user);
return -1;
}
if (mail_namespaces_init(mail_user, error_r) < 0) {
mail_user_unref(&mail_user);
return -1;
}
*mail_user_r = mail_user;
return 0;
}
static const struct var_expand_table *
get_var_expand_table(struct master_service *service,
struct mail_storage_service_input *input)
{
static struct var_expand_table static_tab[] = {
{ 'u', NULL, "user" },
{ 'n', NULL, "username" },
{ 'd', NULL, "domain" },
{ 's', NULL, "service" },
{ 'l', NULL, "lip" },
{ 'r', NULL, "rip" },
{ 'p', NULL, "pid" },
{ 'i', NULL, "uid" },
{ '\0', NULL, NULL }
};
struct var_expand_table *tab;
tab = t_malloc(sizeof(static_tab));
memcpy(tab, static_tab, sizeof(static_tab));
tab[0].value = input->username;
tab[1].value = t_strcut(input->username, '@');
tab[2].value = strchr(input->username, '@');
if (tab[2].value != NULL) tab[2].value++;
tab[3].value = service->name;
tab[4].value = net_ip2addr(&input->local_ip);
tab[5].value = net_ip2addr(&input->remote_ip);
tab[6].value = my_pid;
tab[7].value = dec2str(geteuid());
return tab;
}
static const char *
user_expand_varstr(struct master_service *service,
struct mail_storage_service_input *input, const char *str)
{
string_t *ret;
if (*str == SETTING_STRVAR_EXPANDED[0])
return str + 1;
i_assert(*str == SETTING_STRVAR_UNEXPANDED[0]);
ret = t_str_new(256);
var_expand(ret, str + 1, get_var_expand_table(service, input));
return str_c(ret);
}
static void
mail_storage_service_init_log(struct master_service *service,
struct mail_storage_service_input *input)
{
const struct mail_user_settings *user_set;
void **sets;
sets = master_service_settings_get_others(service);
user_set = sets[0];
T_BEGIN {
string_t *str;
str = t_str_new(256);
var_expand(str, user_set->mail_log_prefix,
get_var_expand_table(service, input));
master_service_init_log(service, str_c(str));
} T_END;
}
static void mail_storage_service_time_moved(time_t old_time, time_t new_time)
{
long diff = new_time - old_time;
if (diff > 0) {
if (diff > MAX_NOWARN_FORWARD_SECS)
i_warning("Time jumped forwards %ld seconds", diff);
return;
}
diff = -diff;
if (diff > MAX_TIME_BACKWARDS_SLEEP) {
i_fatal("Time just moved backwards by %ld seconds. "
"This might cause a lot of problems, "
"so I'll just kill myself now. "
"http://wiki.dovecot.org/TimeMovedBackwards", diff);
} else {
i_error("Time just moved backwards by %ld seconds. "
"I'll sleep now until we're back in present. "
"http://wiki.dovecot.org/TimeMovedBackwards", diff);
/* Sleep extra second to make sure usecs also grows. */
diff++;
while (diff > 0 && sleep(diff) != 0) {
/* don't use sleep()'s return value, because
it could get us to a long loop in case
interrupts just keep coming */
diff = old_time - time(NULL) + 1;
}
}
}
static struct mail_user *
init_user_real(struct master_service *service,
const struct mail_storage_service_input *_input,
const struct setting_parser_info *set_roots[],
enum mail_storage_service_flags flags)
{
struct mail_storage_service_input input = *_input;
const struct master_service_settings *set;
const struct mail_user_settings *user_set;
const struct mail_storage_settings *mail_set;
struct mail_user *mail_user;
struct auth_master_connection *conn;
void **sets;
const char *user, *orig_user, *home, *error;
const char *system_groups_user = NULL, *const *userdb_fields = NULL;
unsigned int len;
bool userdb_lookup;
pool_t userdb_pool = NULL;
io_loop_set_time_moved_callback(current_ioloop,
mail_storage_service_time_moved);
userdb_lookup = (flags & MAIL_STORAGE_SERVICE_FLAG_USERDB_LOOKUP) != 0;
mail_storage_service_init_settings(service, &input, set_roots,
!userdb_lookup);
if ((flags & MAIL_STORAGE_SERVICE_FLAG_DEBUG) != 0)
set_keyval(service->set_parser, "mail_debug", "yes");
mail_storage_service_init_log(service, &input);
set = master_service_settings_get(service);
sets = master_service_settings_get_others(service);
user_set = sets[0];
mail_set = mail_user_set_get_storage_set(user_set);
if (userdb_lookup) {
/* userdb lookup may change settings, do it as soon as
possible. */
orig_user = user = input.username;
conn = auth_master_init(user_set->auth_socket_path,
mail_set->mail_debug);
userdb_pool = pool_alloconly_create("userdb lookup", 1024);
if (service_auth_userdb_lookup(conn, service->name, &input,
user_set, userdb_pool, &user,
&userdb_fields, &error) <= 0)
i_fatal("%s", error);
auth_master_deinit(&conn);
input.username = user;
/* set up logging again in case username changed */
mail_storage_service_init_log(service, &input);
} else if (input.userdb_fields != NULL) {
userdb_fields = input.userdb_fields;
userdb_pool = pool_alloconly_create("userdb fields", 1024);
}
if (userdb_fields != NULL) {
struct auth_user_reply reply;
auth_user_fields_parse(userdb_fields, userdb_pool, &reply);
if (user_reply_handle(service->set_parser, user_set, &reply,
&system_groups_user, &error) < 0)
i_fatal("%s", error);
}
if (userdb_pool != NULL)
pool_unref(&userdb_pool);
/* variable strings are expanded in mail_user_init(),
but we need the home sooner so do it separately here. */
home = user_expand_varstr(service, &input, user_set->mail_home);
if (!userdb_lookup) {
if (*home == '\0' && getenv("HOME") != NULL) {
home = getenv("HOME");
set_keyval(service->set_parser, "mail_home", home);
}
}
if (*home != '/') {
i_fatal("user %s: Relative home directory paths not supported: "
"%s", input.username, home);
}
len = strlen(user_set->mail_chroot);
if (len > 2 && strcmp(user_set->mail_chroot + len - 2, "/.") == 0 &&
strncmp(home, user_set->mail_chroot, len - 2) == 0) {
/* If chroot ends with "/.", strip chroot dir from home dir */
home += len - 2;
set_keyval(service->set_parser, "mail_home", home);
}
modules = *user_set->mail_plugins == '\0' ? NULL :
module_dir_load(user_set->mail_plugin_dir,
user_set->mail_plugins, TRUE,
master_service_get_version_string(service));
if ((flags & MAIL_STORAGE_SERVICE_FLAG_NO_RESTRICT_ACCESS) != 0) {
/* no changes */
} else if ((flags & MAIL_STORAGE_SERVICE_FLAG_RESTRICT_BY_ENV) != 0) {
restrict_access_by_env(home,
(flags & MAIL_STORAGE_SERVICE_FLAG_DISALLOW_ROOT) != 0);
} else {
service_drop_privileges(user_set, system_groups_user, home,
(flags & MAIL_STORAGE_SERVICE_FLAG_DISALLOW_ROOT) != 0,
FALSE);
}
/* privileges are now dropped */
restrict_access_allow_coredumps(TRUE);
dict_drivers_register_builtin();
module_dir_init(modules);
mail_users_init(user_set->auth_socket_path, mail_set->mail_debug);
if (mail_storage_service_init_post(service, &input, home, user_set,
FALSE, flags,
&mail_user, &error) < 0)
i_fatal("%s", error);
return mail_user;
}
struct mail_user *
mail_storage_service_init_user(struct master_service *service,
const struct mail_storage_service_input *_input,
const struct setting_parser_info *set_roots[],
enum mail_storage_service_flags flags)
{
struct mail_user *user;
T_BEGIN {
user = init_user_real(service, _input, set_roots, flags);
} T_END;
return user;
}
void mail_storage_service_deinit_user(void)
{
module_dir_unload(&modules);
mail_storage_deinit();
mail_users_deinit();
dict_drivers_unregister_builtin();
}
struct mail_storage_service_multi_ctx *
mail_storage_service_multi_init(struct master_service *service,
const struct setting_parser_info *set_roots[],
enum mail_storage_service_flags flags)
{
struct mail_storage_service_multi_ctx *ctx;
const struct master_service_settings *set;
const struct mail_user_settings *user_set;
const struct mail_storage_settings *mail_set;
void **sets;
io_loop_set_time_moved_callback(current_ioloop,
mail_storage_service_time_moved);
ctx = i_new(struct mail_storage_service_multi_ctx, 1);
ctx->service = service;
ctx->flags = flags;
mail_storage_service_init_settings(service, NULL, set_roots, FALSE);
set = master_service_settings_get(service);
sets = master_service_settings_get_others(service);
user_set = sets[0];
mail_set = mail_user_set_get_storage_set(user_set);
/* do all the global initialization. delay initializing plugins until
we drop privileges the first time. */
master_service_init_log(service, t_strconcat(service->name, ": ", NULL));
modules = *user_set->mail_plugins == '\0' ? NULL :
module_dir_load(user_set->mail_plugin_dir,
user_set->mail_plugins, TRUE,
master_service_get_version_string(service));
if ((ctx->flags & MAIL_STORAGE_SERVICE_FLAG_USERDB_LOOKUP) != 0) {
ctx->conn = auth_master_init(user_set->auth_socket_path,
mail_set->mail_debug);
}
dict_drivers_register_builtin();
mail_users_init(user_set->auth_socket_path, mail_set->mail_debug);
return ctx;
}
struct auth_master_connection *
mail_storage_service_multi_get_auth_conn(struct mail_storage_service_multi_ctx *ctx)
{
return ctx->conn;
}
static int multi_userdb_lookup(struct mail_storage_service_multi_ctx *ctx,
struct mail_storage_service_multi_user *user,
pool_t userdb_pool, const char **error_r)
{
struct auth_user_reply reply;
const char *username, *system_groups_user, *const *userdb_fields;
int ret;
username = user->input.username;
ret = service_auth_userdb_lookup(ctx->conn, ctx->service->name,
&user->input, user->user_set,
userdb_pool, &username,
&userdb_fields, error_r);
if (ret <= 0)
return ret;
auth_user_fields_parse(userdb_fields, userdb_pool, &reply);
ret = user_reply_handle(ctx->service->set_parser, user->user_set,
&reply, &system_groups_user, error_r);
if (ret <= 0)
return ret;
user->input.username = p_strdup(user->pool, username);
user->system_groups_user = p_strdup(user->pool, system_groups_user);
return 1;
}
int mail_storage_service_multi_lookup(struct mail_storage_service_multi_ctx *ctx,
const struct mail_storage_service_input *input,
pool_t pool,
struct mail_storage_service_multi_user **user_r,
const char **error_r)
{
struct mail_storage_service_multi_user *user;
void **sets;
pool_t userdb_pool;
int ret = 1;
user = p_new(pool, struct mail_storage_service_multi_user, 1);
memset(user_r, 0, sizeof(user_r));
user->pool = pool;
user->input = *input;
user->input.username = p_strdup(pool, input->username);
user->set_parser = settings_parser_dup(ctx->service->set_parser, pool);
sets = settings_parser_get_list(user->set_parser);
user->user_set = sets[1];
if ((ctx->flags & MAIL_STORAGE_SERVICE_FLAG_USERDB_LOOKUP) != 0) {
userdb_pool = pool_alloconly_create("userdb lookup", 1024);
ret = multi_userdb_lookup(ctx, user, userdb_pool, error_r);
pool_unref(&userdb_pool);
}
*user_r = user;
return 1;
}
int mail_storage_service_multi_next(struct mail_storage_service_multi_ctx *ctx,
struct mail_storage_service_multi_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;
unsigned int len;
/* variable strings are expanded in mail_user_init(),
but we need the home sooner so do it separately here. */
home = user_expand_varstr(ctx->service, &user->input,
user_set->mail_home);
mail_storage_service_init_log(ctx->service, &user->input);
if ((ctx->flags & MAIL_STORAGE_SERVICE_FLAG_NO_RESTRICT_ACCESS) == 0) {
service_drop_privileges(user_set, user->system_groups_user, home,
(ctx->flags & MAIL_STORAGE_SERVICE_FLAG_DISALLOW_ROOT) != 0,
TRUE);
}
if (!ctx->modules_initialized) {
/* privileges dropped for the first time. initialize the
modules now to avoid code running as root. */
module_dir_init(modules);
ctx->modules_initialized = TRUE;
}
/* we couldn't do chrooting, so if chrooting was enabled fix
the home directory */
len = strlen(user_set->mail_chroot);
if (len > 2 && strcmp(user_set->mail_chroot + len - 2, "/.") == 0 &&
strncmp(home, user_set->mail_chroot, len - 2) == 0) {
/* home dir already contains the chroot dir */
} else if (len > 0) {
set_keyval(user->set_parser, "mail_home",
t_strconcat(user_set->mail_chroot, "/", home, NULL));
}
if (mail_storage_service_init_post(ctx->service, &user->input,
home, user_set, TRUE, ctx->flags,
mail_user_r, error_r) < 0)
return -1;
return 0;
}
void mail_storage_service_multi_user_free(struct mail_storage_service_multi_user *user)
{
settings_parser_deinit(&user->set_parser);
}
unsigned int
mail_storage_service_multi_all_init(struct mail_storage_service_multi_ctx *ctx)
{
if (ctx->auth_list != NULL)
(void)auth_master_user_list_deinit(&ctx->auth_list);
ctx->auth_list = auth_master_user_list_init(ctx->conn);
return auth_master_user_list_count(ctx->auth_list);
}
int mail_storage_service_multi_all_next(struct mail_storage_service_multi_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_multi_deinit(struct mail_storage_service_multi_ctx **_ctx)
{
struct mail_storage_service_multi_ctx *ctx = *_ctx;
*_ctx = NULL;
if (ctx->auth_list != NULL)
(void)auth_master_user_list_deinit(&ctx->auth_list);
if (ctx->conn != NULL)
auth_master_deinit(&ctx->conn);
i_free(ctx);
mail_storage_service_deinit_user();
}
void *mail_storage_service_multi_user_get_set(struct mail_storage_service_multi_user *user)
{
return settings_parser_get_list(user->set_parser) + 1;
}
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;
}