mail-storage-service.c revision b34fdb68d376d85b4880da4a4bdf67ae726a381b
45312f52ff3a3d4c137447be4c7556500c2f8bf2Timo Sirainen/* Copyright (c) 2009 Dovecot authors, see the included COPYING file */
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen#include "lib.h"
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen#include "array.h"
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen#include "hostpid.h"
cbc61fcb33b370d049c16a3c44568b4deb4e2b33Timo Sirainen#include "module-dir.h"
a1607f6001a9949e1cf9d49eb0aa936dbb4c77ffTimo Sirainen#include "restrict-access.h"
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen#include "str.h"
00d58fcfe8191d6ce7efa801d289a5c0fe88d1aeTimo Sirainen#include "var-expand.h"
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen#include "dict.h"
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen#include "settings-parser.h"
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen#include "auth-master.h"
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen#include "master-service-private.h"
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen#include "master-service-settings.h"
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen#include "mail-user.h"
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen#include "mail-namespace.h"
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen#include "mail-storage.h"
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen#include "mail-storage-service.h"
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen#include <stdlib.h>
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen#include <sys/stat.h>
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen#include <pwd.h>
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen#include <grp.h>
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainenstruct mail_storage_service_multi_ctx {
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen struct master_service *service;
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen enum mail_storage_service_flags flags;
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen unsigned int modules_initialized:1;
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen};
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainenstatic struct module *modules = NULL;
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen
e07bf3772a2bc075de4915ad0961beb8d083c22dTimo Sirainenstatic void
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainenmaster_service_set(struct master_service *service,
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen const char *key, const char *value)
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen{
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen const char *str;
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen
cbc61fcb33b370d049c16a3c44568b4deb4e2b33Timo Sirainen str = t_strconcat(key, "=", value, NULL);
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen if (settings_parse_line(service->set_parser, str) < 0) {
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen i_fatal("Invalid userdb input '%s': %s", str,
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen settings_parser_get_error(service->set_parser));
f6c1297c26b355c4aec2a08978f51ec3efecb351Timo Sirainen }
f6c1297c26b355c4aec2a08978f51ec3efecb351Timo Sirainen}
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainen
9514dbd41faad453406fc3a10aca791417a9ce75Timo Sirainenstatic bool validate_chroot(const struct mail_user_settings *user_set,
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen const char *dir)
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen{
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen const char *const *chroot_dirs;
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen if (*dir == '\0')
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen return FALSE;
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen if (*user_set->valid_chroot_dirs == '\0')
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen return FALSE;
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen chroot_dirs = t_strsplit(user_set->valid_chroot_dirs, ":");
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen while (*chroot_dirs != NULL) {
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen if (**chroot_dirs != '\0' &&
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen strncmp(dir, *chroot_dirs, strlen(*chroot_dirs)) == 0)
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen return TRUE;
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen chroot_dirs++;
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen }
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen return FALSE;
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen}
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainenstatic int
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainenuser_reply_handle(struct master_service *service,
73b50eecfc31750a312e2f940023f522eb07178cTimo Sirainen const struct mail_user_settings *user_set,
73b50eecfc31750a312e2f940023f522eb07178cTimo Sirainen const struct auth_user_reply *reply,
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen const char **system_groups_user_r, const char **error_r)
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen{
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen const char *const *str, *p, *line, *key;
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen unsigned int i, count;
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen int ret = 0;
cbc61fcb33b370d049c16a3c44568b4deb4e2b33Timo Sirainen
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen *system_groups_user_r = NULL;
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen
cbc61fcb33b370d049c16a3c44568b4deb4e2b33Timo Sirainen if (reply->uid != (uid_t)-1) {
cbc61fcb33b370d049c16a3c44568b4deb4e2b33Timo Sirainen if (reply->uid == 0) {
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen *error_r = "userdb returned 0 as uid";
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen return -1;
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen }
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen master_service_set(service, "mail_uid", dec2str(reply->uid));
73b50eecfc31750a312e2f940023f522eb07178cTimo Sirainen }
cbc61fcb33b370d049c16a3c44568b4deb4e2b33Timo Sirainen if (reply->gid != (uid_t)-1)
e07bf3772a2bc075de4915ad0961beb8d083c22dTimo Sirainen master_service_set(service, "mail_gid", dec2str(reply->gid));
e07bf3772a2bc075de4915ad0961beb8d083c22dTimo Sirainen
73b50eecfc31750a312e2f940023f522eb07178cTimo Sirainen if (reply->home != NULL)
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen master_service_set(service, "mail_home", reply->home);
cbc61fcb33b370d049c16a3c44568b4deb4e2b33Timo Sirainen
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen if (reply->chroot != NULL) {
e07bf3772a2bc075de4915ad0961beb8d083c22dTimo Sirainen if (!validate_chroot(user_set, reply->chroot)) {
e07bf3772a2bc075de4915ad0961beb8d083c22dTimo Sirainen *error_r = t_strdup_printf(
e07bf3772a2bc075de4915ad0961beb8d083c22dTimo Sirainen "userdb returned invalid chroot directory: %s "
e07bf3772a2bc075de4915ad0961beb8d083c22dTimo Sirainen "(see valid_chroot_dirs setting)",
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen reply->chroot);
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen return -1;
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen }
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen master_service_set(service, "mail_chroot", reply->chroot);
96308127e006bb3b1108093bcf4cc1fd9481cb7aTimo Sirainen }
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen str = array_get(&reply->extra_fields, &count);
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen for (i = 0; i < count && ret == 0; i++) T_BEGIN {
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen line = str[i];
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen if (strncmp(line, "system_groups_user=", 19) == 0) {
d66be2bebfa96e7d3d20e2153f60e6e25dcc9a18Timo Sirainen *system_groups_user_r = line + 19;
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen continue;
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen }
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen if (strncmp(line, "mail=", 5) == 0)
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen line = t_strconcat("mail_location=", line + 5, NULL);
e07bf3772a2bc075de4915ad0961beb8d083c22dTimo Sirainen else if ((p = strchr(str[i], '=')) == NULL)
e07bf3772a2bc075de4915ad0961beb8d083c22dTimo Sirainen line = t_strconcat(str[i], "=yes", NULL);
e07bf3772a2bc075de4915ad0961beb8d083c22dTimo Sirainen else
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen line = str[i];
73b50eecfc31750a312e2f940023f522eb07178cTimo Sirainen
73b50eecfc31750a312e2f940023f522eb07178cTimo Sirainen key = t_strcut(line, '=');
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen if (!settings_parse_is_valid_key(service->set_parser, key)) {
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen /* assume it's a plugin setting */
96308127e006bb3b1108093bcf4cc1fd9481cb7aTimo Sirainen line = t_strconcat("plugin/", line, NULL);
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen }
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen ret = settings_parse_line(service->set_parser, line);
d66be2bebfa96e7d3d20e2153f60e6e25dcc9a18Timo Sirainen } T_END;
d66be2bebfa96e7d3d20e2153f60e6e25dcc9a18Timo Sirainen
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen if (ret < 0) {
cbc61fcb33b370d049c16a3c44568b4deb4e2b33Timo Sirainen *error_r = t_strdup_printf("Invalid userdb input '%s': %s",
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen str[i], settings_parser_get_error(service->set_parser));
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen }
783278c012551bc2f25f065a8d3ec1a3cfc0d296Timo Sirainen return ret;
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen}
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainenstatic int
ce3faaaaab3e2d45b023396287e02f88e5c76e74Timo Sirainenservice_auth_userdb_lookup(struct master_service *service, bool debug,
ce3faaaaab3e2d45b023396287e02f88e5c76e74Timo Sirainen const struct mail_user_settings *user_set,
ce3faaaaab3e2d45b023396287e02f88e5c76e74Timo Sirainen const char **user, const char **system_groups_user_r,
ce3faaaaab3e2d45b023396287e02f88e5c76e74Timo Sirainen const char **error_r)
ce3faaaaab3e2d45b023396287e02f88e5c76e74Timo Sirainen{
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen struct auth_master_connection *conn;
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen struct auth_user_reply reply;
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen const char *system_groups_user, *orig_user = *user;
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen unsigned int len;
d3ba2af6def6f42a9e2dd78ee03b97b5dff84e73Timo Sirainen pool_t pool;
d3ba2af6def6f42a9e2dd78ee03b97b5dff84e73Timo Sirainen int ret;
cbc61fcb33b370d049c16a3c44568b4deb4e2b33Timo Sirainen
cbc61fcb33b370d049c16a3c44568b4deb4e2b33Timo Sirainen pool = pool_alloconly_create("userdb lookup", 1024);
cbc61fcb33b370d049c16a3c44568b4deb4e2b33Timo Sirainen conn = auth_master_init(user_set->auth_socket_path, debug);
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen ret = auth_master_user_lookup(conn, *user, service->name,
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen pool, &reply);
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen if (ret > 0) {
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen len = reply.chroot == NULL ? 0 : strlen(reply.chroot);
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen *user = t_strdup(reply.user);
cbc61fcb33b370d049c16a3c44568b4deb4e2b33Timo Sirainen if (user_reply_handle(service, user_set, &reply,
cbc61fcb33b370d049c16a3c44568b4deb4e2b33Timo Sirainen &system_groups_user, error_r) < 0)
cbc61fcb33b370d049c16a3c44568b4deb4e2b33Timo Sirainen ret = -1;
cbc61fcb33b370d049c16a3c44568b4deb4e2b33Timo Sirainen *system_groups_user_r = t_strdup(system_groups_user);
cbc61fcb33b370d049c16a3c44568b4deb4e2b33Timo Sirainen } else {
cbc61fcb33b370d049c16a3c44568b4deb4e2b33Timo Sirainen if (ret == 0)
cbc61fcb33b370d049c16a3c44568b4deb4e2b33Timo Sirainen *error_r = "unknown user";
1dc6f277f5ac6a3dd5cd6aa75a7ef691de9acb7aTimo Sirainen else
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen *error_r = "userdb lookup failed";
19e8adccba16ff419f5675b1575358c2956dce83Timo Sirainen *system_groups_user_r = NULL;
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen }
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen if (ret > 0 && strcmp(*user, orig_user) != 0) {
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen if (mail_user_set_get_storage_set(user_set)->mail_debug)
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen i_info("changed username to %s", *user);
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen i_set_failure_prefix(t_strdup_printf("%s(%s): ", service->name,
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen *user));
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen }
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen auth_master_deinit(&conn);
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen pool_unref(&pool);
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen return ret;
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen}
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen
73b50eecfc31750a312e2f940023f522eb07178cTimo Sirainenstatic bool parse_uid(const char *str, uid_t *uid_r)
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen{
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen struct passwd *pw;
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen char *p;
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen if (*str >= '0' && *str <= '9') {
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen *uid_r = (uid_t)strtoul(str, &p, 10);
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen if (*p == '\0')
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen return TRUE;
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen }
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen
73b50eecfc31750a312e2f940023f522eb07178cTimo Sirainen pw = getpwnam(str);
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen if (pw == NULL)
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen return FALSE;
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen *uid_r = pw->pw_uid;
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen return TRUE;
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen}
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainenstatic bool parse_gid(const char *str, gid_t *gid_r)
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen{
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen struct group *gr;
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen char *p;
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen if (*str >= '0' && *str <= '9') {
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen *gid_r = (gid_t)strtoul(str, &p, 10);
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen if (*p == '\0')
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen return TRUE;
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen }
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen gr = getgrnam(str);
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen if (gr == NULL)
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen return FALSE;
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen *gid_r = gr->gr_gid;
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen return TRUE;
cbc61fcb33b370d049c16a3c44568b4deb4e2b33Timo Sirainen}
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen
cbc61fcb33b370d049c16a3c44568b4deb4e2b33Timo Sirainenstatic void
cbc61fcb33b370d049c16a3c44568b4deb4e2b33Timo Sirainenservice_drop_privileges(const struct mail_user_settings *set,
cbc61fcb33b370d049c16a3c44568b4deb4e2b33Timo Sirainen const char *system_groups_user, const char *home,
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen bool disallow_root, bool keep_setuid_root)
cbc61fcb33b370d049c16a3c44568b4deb4e2b33Timo Sirainen{
cbc61fcb33b370d049c16a3c44568b4deb4e2b33Timo Sirainen struct restrict_access_settings rset;
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen uid_t current_euid, setuid_uid = 0;
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen current_euid = geteuid();
bd4d0a1a7c0626452b8d82f37e3ec07267ac9896Timo Sirainen restrict_access_init(&rset);
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen if (*set->mail_uid != '\0') {
bd4d0a1a7c0626452b8d82f37e3ec07267ac9896Timo Sirainen if (!parse_uid(set->mail_uid, &rset.uid))
bd4d0a1a7c0626452b8d82f37e3ec07267ac9896Timo Sirainen i_fatal("Unknown mail_uid user: %s", set->mail_uid);
bd4d0a1a7c0626452b8d82f37e3ec07267ac9896Timo Sirainen if (rset.uid < (uid_t)set->first_valid_uid ||
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen (set->last_valid_uid != 0 &&
cbc61fcb33b370d049c16a3c44568b4deb4e2b33Timo Sirainen rset.uid > (uid_t)set->last_valid_uid)) {
bd4d0a1a7c0626452b8d82f37e3ec07267ac9896Timo Sirainen i_fatal("Mail access for users with UID %s "
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen "not permitted (see first_valid_uid in config file).",
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen dec2str(rset.uid));
e07bf3772a2bc075de4915ad0961beb8d083c22dTimo Sirainen }
e07bf3772a2bc075de4915ad0961beb8d083c22dTimo Sirainen }
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen if (*set->mail_gid != '\0') {
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen if (!parse_gid(set->mail_gid, &rset.gid))
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen i_fatal("Unknown mail_gid group: %s", set->mail_gid);
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen if (rset.gid < (gid_t)set->first_valid_gid ||
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen (set->last_valid_gid != 0 &&
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen rset.gid > (gid_t)set->last_valid_gid)) {
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen i_fatal("Mail access for users with GID %s "
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen "not permitted (see first_valid_gid in config file).",
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen dec2str(rset.gid));
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen }
cbc61fcb33b370d049c16a3c44568b4deb4e2b33Timo Sirainen }
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen if (*set->mail_privileged_group != '\0') {
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen if (!parse_uid(set->mail_privileged_group, &rset.privileged_gid))
e07bf3772a2bc075de4915ad0961beb8d083c22dTimo Sirainen i_fatal("Unknown mail_gid group: %s", set->mail_gid);
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen }
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen if (*set->mail_access_groups != '\0')
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen rset.extra_groups = set->mail_access_groups;
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen rset.first_valid_gid = set->first_valid_gid;
56963ffad65b860c827553dfaf09fb766cb7e20eTimo Sirainen rset.last_valid_gid = set->last_valid_gid;
56963ffad65b860c827553dfaf09fb766cb7e20eTimo Sirainen /* we can't chroot if we want to switch between users. there's not
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen much point either (from security point of view) */
cbc61fcb33b370d049c16a3c44568b4deb4e2b33Timo Sirainen rset.chroot_dir = *set->mail_chroot == '\0' || keep_setuid_root ?
e07bf3772a2bc075de4915ad0961beb8d083c22dTimo Sirainen NULL : set->mail_chroot;
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen rset.system_groups_user = system_groups_user;
56963ffad65b860c827553dfaf09fb766cb7e20eTimo Sirainen
56963ffad65b860c827553dfaf09fb766cb7e20eTimo Sirainen if (disallow_root &&
56963ffad65b860c827553dfaf09fb766cb7e20eTimo Sirainen (rset.uid == 0 || (rset.uid == (uid_t)-1 && current_euid == 0)))
56963ffad65b860c827553dfaf09fb766cb7e20eTimo Sirainen i_fatal("Mail access not allowed for root");
56963ffad65b860c827553dfaf09fb766cb7e20eTimo Sirainen
56963ffad65b860c827553dfaf09fb766cb7e20eTimo Sirainen if (keep_setuid_root && current_euid != rset.uid) {
56963ffad65b860c827553dfaf09fb766cb7e20eTimo Sirainen if (current_euid != 0) {
56963ffad65b860c827553dfaf09fb766cb7e20eTimo Sirainen /* we're changing the UID, switch back to root first */
56963ffad65b860c827553dfaf09fb766cb7e20eTimo Sirainen if (seteuid(0) < 0)
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen i_fatal("seteuid(0) failed: %m");
56963ffad65b860c827553dfaf09fb766cb7e20eTimo Sirainen }
56963ffad65b860c827553dfaf09fb766cb7e20eTimo Sirainen setuid_uid = rset.uid;
56963ffad65b860c827553dfaf09fb766cb7e20eTimo Sirainen rset.uid = (uid_t)-1;
56963ffad65b860c827553dfaf09fb766cb7e20eTimo Sirainen }
56963ffad65b860c827553dfaf09fb766cb7e20eTimo Sirainen restrict_access(&rset, *home == '\0' ? NULL : home, disallow_root);
56963ffad65b860c827553dfaf09fb766cb7e20eTimo Sirainen if (keep_setuid_root) {
56963ffad65b860c827553dfaf09fb766cb7e20eTimo Sirainen if (seteuid(setuid_uid) < 0)
56963ffad65b860c827553dfaf09fb766cb7e20eTimo Sirainen i_fatal("seteuid(%s) failed: %m", dec2str(setuid_uid));
56963ffad65b860c827553dfaf09fb766cb7e20eTimo Sirainen }
56963ffad65b860c827553dfaf09fb766cb7e20eTimo Sirainen}
56963ffad65b860c827553dfaf09fb766cb7e20eTimo Sirainen
e726bf74fcc8d24f4c9d0d83217b3db4314d9d1fTimo Sirainenstatic void
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainenmail_storage_service_init_settings(struct master_service *service,
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainen const struct setting_parser_info *set_root,
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen bool preserve_home)
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen{
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen const struct setting_parser_info *set_roots[3];
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen const char *error;
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen (void)umask(0077);
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen
9514dbd41faad453406fc3a10aca791417a9ce75Timo Sirainen mail_storage_init();
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen mail_storage_register_all();
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen mailbox_list_register_all();
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen set_roots[0] = &mail_user_setting_parser_info;
9514dbd41faad453406fc3a10aca791417a9ce75Timo Sirainen set_roots[1] = set_root;
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen set_roots[2] = NULL;
9514dbd41faad453406fc3a10aca791417a9ce75Timo Sirainen
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen /* read settings after registering storages so they can have their
a1607f6001a9949e1cf9d49eb0aa936dbb4c77ffTimo Sirainen own setting definitions too */
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen if (master_service_settings_read(service, set_roots,
a1607f6001a9949e1cf9d49eb0aa936dbb4c77ffTimo Sirainen mail_storage_get_dynamic_parsers(),
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen preserve_home, &error) < 0)
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen i_fatal("Error reading configuration: %s", error);
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen}
a1607f6001a9949e1cf9d49eb0aa936dbb4c77ffTimo Sirainen
a1607f6001a9949e1cf9d49eb0aa936dbb4c77ffTimo Sirainenstatic int
a1607f6001a9949e1cf9d49eb0aa936dbb4c77ffTimo Sirainenmail_storage_service_init_post(struct master_service *service,
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen const char *user, const char *home,
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen const struct mail_user_settings *user_set,
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen struct mail_user **mail_user_r,
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen const char **error_r)
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen{
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen const struct mail_storage_settings *mail_set;
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen struct mail_user *mail_user;
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen mail_set = mail_user_set_get_storage_set(user_set);
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen if (mail_set->mail_debug) {
9514dbd41faad453406fc3a10aca791417a9ce75Timo Sirainen i_info("Effective uid=%s, gid=%s, home=%s",
783278c012551bc2f25f065a8d3ec1a3cfc0d296Timo Sirainen dec2str(geteuid()), dec2str(getegid()),
783278c012551bc2f25f065a8d3ec1a3cfc0d296Timo Sirainen home != NULL ? home : "(none)");
4073f0dbf3277f981a8fcee3b89ea15aaf380a7fTimo Sirainen }
4073f0dbf3277f981a8fcee3b89ea15aaf380a7fTimo Sirainen
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen /* If possible chdir to home directory, so that core file
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen could be written in case we crash. */
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen if (*home != '\0') {
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen if (chdir(home) < 0) {
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen if (errno != ENOENT)
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen i_error("chdir(%s) failed: %m", home);
9514dbd41faad453406fc3a10aca791417a9ce75Timo Sirainen else if (mail_set->mail_debug)
9514dbd41faad453406fc3a10aca791417a9ce75Timo Sirainen i_info("Home dir not found: %s", home);
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen }
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen }
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen mail_user = mail_user_alloc(user, user_set);
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainen if (*home != '\0')
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainen mail_user_set_home(mail_user, home);
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen mail_user_set_vars(mail_user, geteuid(), service->name, NULL, NULL);
9514dbd41faad453406fc3a10aca791417a9ce75Timo Sirainen if (mail_user_init(mail_user, error_r) < 0 ||
9514dbd41faad453406fc3a10aca791417a9ce75Timo Sirainen mail_namespaces_init(mail_user, error_r) < 0) {
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen *error_r = t_strdup(*error_r);
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen mail_user_unref(&mail_user);
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen return -1;
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen }
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainen *mail_user_r = mail_user;
9514dbd41faad453406fc3a10aca791417a9ce75Timo Sirainen return 0;
cf7164ece50797a67fc4bfb5889022ac93a36a8aTimo Sirainen}
static const struct var_expand_table *
get_var_expand_table(struct master_service *service, const char *user)
{
static struct var_expand_table static_tab[] = {
{ 'u', NULL, "user" },
{ 'n', NULL, "username" },
{ 'd', NULL, "domain" },
{ 's', NULL, "service" },
{ '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 = user;
tab[1].value = t_strcut(user, '@');
tab[2].value = strchr(user, '@');
if (tab[2].value != NULL) tab[2].value++;
tab[3].value = service->name;
tab[4].value = my_pid;
tab[5].value = dec2str(geteuid());
return tab;
}
static const char *
user_expand_varstr(struct master_service *service, const char *user,
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, user));
return str_c(ret);
}
struct mail_user *
mail_storage_service_init_user(struct master_service *service, const char *user,
const struct setting_parser_info *set_root,
enum mail_storage_service_flags flags)
{
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;
void **sets;
const char *orig_user, *home, *system_groups_user, *error;
unsigned int len;
bool userdb_lookup;
userdb_lookup = (flags & MAIL_STORAGE_SERVICE_FLAG_USERDB_LOOKUP) != 0;
mail_storage_service_init_settings(service, set_root, !userdb_lookup);
if ((flags & MAIL_STORAGE_SERVICE_FLAG_DEBUG) != 0)
master_service_set(service, "mail_debug", "yes");
/* now that we've read settings, we can set up logging */
master_service_init_log(service,
t_strdup_printf("%s(%s): ", service->name, user));
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;
if (service_auth_userdb_lookup(service, mail_set->mail_debug,
user_set, &user,
&system_groups_user,
&error) <= 0)
i_fatal("%s", error);
}
/* variable strings are expanded in mail_user_init(),
but we need the home sooner so do it separately here. */
home = user_expand_varstr(service, user, user_set->mail_home);
if (!userdb_lookup) {
system_groups_user = NULL;
if (*home == '\0' && getenv("HOME") != NULL) {
home = getenv("HOME");
master_service_set(service, "mail_home", 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;
master_service_set(service, "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));
service_drop_privileges(user_set, system_groups_user, home,
(flags & MAIL_STORAGE_SERVICE_FLAG_DISALLOW_ROOT) != 0, FALSE);
/* privileges are now dropped */
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, user, home, user_set,
&mail_user, &error) < 0)
i_fatal("%s", error);
return mail_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_root,
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;
ctx = i_new(struct mail_storage_service_multi_ctx, 1);
ctx->service = service;
ctx->flags = flags;
mail_storage_service_init_settings(service, set_root, FALSE);
/* do all the global initialization. delay initializing plugins until
we drop privileges the first time. */
master_service_init_log(service,
t_strdup_printf("%s: ", service->name));
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);
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));
dict_drivers_register_builtin();
mail_users_init(user_set->auth_socket_path, mail_set->mail_debug);
return ctx;
}
int mail_storage_service_multi_next(struct mail_storage_service_multi_ctx *ctx,
const char *user,
struct mail_user **mail_user_r,
const char **error_r)
{
const struct mail_user_settings *user_set;
const struct mail_storage_settings *mail_set;
const char *orig_user, *system_groups_user, *home;
void **sets;
unsigned int len;
int ret;
sets = master_service_settings_get_others(ctx->service);
user_set = sets[0];
mail_set = mail_user_set_get_storage_set(user_set);
if ((ctx->flags & MAIL_STORAGE_SERVICE_FLAG_USERDB_LOOKUP) != 0) {
orig_user = user;
ret = service_auth_userdb_lookup(ctx->service,
mail_set->mail_debug,
user_set, &user,
&system_groups_user,
error_r);
if (ret <= 0)
return ret;
} else {
system_groups_user = NULL;
}
/* 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, user_set->mail_home);
service_drop_privileges(user_set, 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) {
master_service_set(ctx->service, "mail_home",
t_strconcat(user_set->mail_chroot, "/", home, NULL));
}
if (mail_storage_service_init_post(ctx->service, user, home,
user_set, mail_user_r, error_r) < 0)
return -1;
return 1;
}
void mail_storage_service_multi_deinit(struct mail_storage_service_multi_ctx **_ctx)
{
struct mail_storage_service_multi_ctx *ctx = *_ctx;
*_ctx = NULL;
i_free(ctx);
mail_storage_service_deinit_user();
}
void *mail_storage_service_get_settings(struct master_service *service)
{
void **sets;
sets = master_service_settings_get_others(service);
return sets[1];
}