mail-storage-service.c revision c56500d4363beba4ffa954069ab30f4401849156
bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2009-2012 Dovecot authors, see the included COPYING file */
402f9183489c1a75736b1e9068c33fe2741a366dTimo Sirainen
402f9183489c1a75736b1e9068c33fe2741a366dTimo Sirainen#include "lib.h"
402f9183489c1a75736b1e9068c33fe2741a366dTimo Sirainen#include "ioloop.h"
402f9183489c1a75736b1e9068c33fe2741a366dTimo Sirainen#include "array.h"
5dd05e966ffd69181ab3067f6939b03ced68ebc3Timo Sirainen#include "hostpid.h"
2ac5f36aa7c2e7a07ba8815d43a6d7483f62e74cTimo Sirainen#include "module-dir.h"
402f9183489c1a75736b1e9068c33fe2741a366dTimo Sirainen#include "restrict-access.h"
402f9183489c1a75736b1e9068c33fe2741a366dTimo Sirainen#include "eacces-error.h"
402f9183489c1a75736b1e9068c33fe2741a366dTimo Sirainen#include "ipwd.h"
2ac5f36aa7c2e7a07ba8815d43a6d7483f62e74cTimo Sirainen#include "str.h"
402f9183489c1a75736b1e9068c33fe2741a366dTimo Sirainen#include "var-expand.h"
5dd05e966ffd69181ab3067f6939b03ced68ebc3Timo Sirainen#include "dict.h"
1dcf22e98ba8310e8daa8c9297936c6f3a645e7aPhil Carmody#include "settings-parser.h"
402f9183489c1a75736b1e9068c33fe2741a366dTimo Sirainen#include "auth-master.h"
5dd05e966ffd69181ab3067f6939b03ced68ebc3Timo Sirainen#include "master-service-private.h"
402f9183489c1a75736b1e9068c33fe2741a366dTimo Sirainen#include "master-service-settings.h"
5dd05e966ffd69181ab3067f6939b03ced68ebc3Timo Sirainen#include "master-service-settings-cache.h"
5dd05e966ffd69181ab3067f6939b03ced68ebc3Timo Sirainen#include "mail-user.h"
1dcf22e98ba8310e8daa8c9297936c6f3a645e7aPhil Carmody#include "mail-namespace.h"
5dd05e966ffd69181ab3067f6939b03ced68ebc3Timo Sirainen#include "mail-storage.h"
5dd05e966ffd69181ab3067f6939b03ced68ebc3Timo Sirainen#include "mail-storage-service.h"
c61d9048580e49485fac676a0585f5303e9a844aTimo Sirainen
402f9183489c1a75736b1e9068c33fe2741a366dTimo Sirainen#include <stdlib.h>
5dd05e966ffd69181ab3067f6939b03ced68ebc3Timo Sirainen#include <sys/stat.h>
5dd05e966ffd69181ab3067f6939b03ced68ebc3Timo Sirainen
5dd05e966ffd69181ab3067f6939b03ced68ebc3Timo Sirainen#ifdef HAVE_SYS_TIME_H
5dd05e966ffd69181ab3067f6939b03ced68ebc3Timo Sirainen# include <sys/time.h>
c61d9048580e49485fac676a0585f5303e9a844aTimo Sirainen#endif
402f9183489c1a75736b1e9068c33fe2741a366dTimo Sirainen#ifdef HAVE_SYS_RESOURCE_H
5dd05e966ffd69181ab3067f6939b03ced68ebc3Timo Sirainen# include <sys/resource.h>
402f9183489c1a75736b1e9068c33fe2741a366dTimo Sirainen#endif
402f9183489c1a75736b1e9068c33fe2741a366dTimo Sirainen
402f9183489c1a75736b1e9068c33fe2741a366dTimo Sirainen/* If time moves backwards more than this, kill ourself instead of sleeping. */
686a3be710a464396cbfbc7b05eaa7fe16f3cd1cTimo Sirainen#define MAX_TIME_BACKWARDS_SLEEP 5
686a3be710a464396cbfbc7b05eaa7fe16f3cd1cTimo Sirainen#define MAX_NOWARN_FORWARD_SECS 10
686a3be710a464396cbfbc7b05eaa7fe16f3cd1cTimo Sirainen
686a3be710a464396cbfbc7b05eaa7fe16f3cd1cTimo Sirainen#define ERRSTR_INVALID_USER_SETTINGS \
686a3be710a464396cbfbc7b05eaa7fe16f3cd1cTimo Sirainen "Invalid user settings. Refer to server log for more information."
686a3be710a464396cbfbc7b05eaa7fe16f3cd1cTimo Sirainen
9bf221d642eba214b81346abd9ba2f9b9f2eb937Phil Carmodystruct mail_storage_service_privileges {
686a3be710a464396cbfbc7b05eaa7fe16f3cd1cTimo Sirainen uid_t uid;
686a3be710a464396cbfbc7b05eaa7fe16f3cd1cTimo Sirainen gid_t gid;
686a3be710a464396cbfbc7b05eaa7fe16f3cd1cTimo Sirainen const char *uid_source, *gid_source;
9bf221d642eba214b81346abd9ba2f9b9f2eb937Phil Carmody
9bf221d642eba214b81346abd9ba2f9b9f2eb937Phil Carmody const char *home;
686a3be710a464396cbfbc7b05eaa7fe16f3cd1cTimo Sirainen const char *chroot;
686a3be710a464396cbfbc7b05eaa7fe16f3cd1cTimo Sirainen};
686a3be710a464396cbfbc7b05eaa7fe16f3cd1cTimo Sirainen
686a3be710a464396cbfbc7b05eaa7fe16f3cd1cTimo Sirainenstruct mail_storage_service_ctx {
686a3be710a464396cbfbc7b05eaa7fe16f3cd1cTimo Sirainen pool_t pool;
686a3be710a464396cbfbc7b05eaa7fe16f3cd1cTimo Sirainen struct master_service *service;
686a3be710a464396cbfbc7b05eaa7fe16f3cd1cTimo Sirainen const char *default_log_prefix;
686a3be710a464396cbfbc7b05eaa7fe16f3cd1cTimo Sirainen
686a3be710a464396cbfbc7b05eaa7fe16f3cd1cTimo Sirainen struct auth_master_connection *conn;
686a3be710a464396cbfbc7b05eaa7fe16f3cd1cTimo Sirainen struct auth_master_user_list_ctx *auth_list;
5dd05e966ffd69181ab3067f6939b03ced68ebc3Timo Sirainen const struct setting_parser_info **set_roots;
2ac5f36aa7c2e7a07ba8815d43a6d7483f62e74cTimo Sirainen enum mail_storage_service_flags flags;
402f9183489c1a75736b1e9068c33fe2741a366dTimo Sirainen
c398eca6b0fc6583687bd6fe2ee2dbcca2ae9387Timo Sirainen const char *set_cache_module, *set_cache_service;
c398eca6b0fc6583687bd6fe2ee2dbcca2ae9387Timo Sirainen struct master_service_settings_cache *set_cache;
c398eca6b0fc6583687bd6fe2ee2dbcca2ae9387Timo Sirainen
c398eca6b0fc6583687bd6fe2ee2dbcca2ae9387Timo Sirainen unsigned int debug:1;
c398eca6b0fc6583687bd6fe2ee2dbcca2ae9387Timo Sirainen unsigned int log_initialized:1;
c398eca6b0fc6583687bd6fe2ee2dbcca2ae9387Timo Sirainen};
c398eca6b0fc6583687bd6fe2ee2dbcca2ae9387Timo Sirainen
c398eca6b0fc6583687bd6fe2ee2dbcca2ae9387Timo Sirainenstruct mail_storage_service_user {
c398eca6b0fc6583687bd6fe2ee2dbcca2ae9387Timo Sirainen pool_t pool;
c398eca6b0fc6583687bd6fe2ee2dbcca2ae9387Timo Sirainen struct mail_storage_service_ctx *service_ctx;
c398eca6b0fc6583687bd6fe2ee2dbcca2ae9387Timo Sirainen struct mail_storage_service_input input;
c398eca6b0fc6583687bd6fe2ee2dbcca2ae9387Timo Sirainen enum mail_storage_service_flags flags;
c398eca6b0fc6583687bd6fe2ee2dbcca2ae9387Timo Sirainen
c398eca6b0fc6583687bd6fe2ee2dbcca2ae9387Timo Sirainen struct ioloop_context *ioloop_ctx;
c398eca6b0fc6583687bd6fe2ee2dbcca2ae9387Timo Sirainen const char *log_prefix;
c398eca6b0fc6583687bd6fe2ee2dbcca2ae9387Timo Sirainen
51292e327a91a257b8baea83560b3a8323cac8c5Timo Sirainen const char *system_groups_user, *uid_source, *gid_source;
c398eca6b0fc6583687bd6fe2ee2dbcca2ae9387Timo Sirainen const struct mail_user_settings *user_set;
51292e327a91a257b8baea83560b3a8323cac8c5Timo Sirainen const struct setting_parser_info *user_info;
51292e327a91a257b8baea83560b3a8323cac8c5Timo Sirainen struct setting_parser_context *set_parser;
51292e327a91a257b8baea83560b3a8323cac8c5Timo Sirainen};
c398eca6b0fc6583687bd6fe2ee2dbcca2ae9387Timo Sirainen
c398eca6b0fc6583687bd6fe2ee2dbcca2ae9387Timo Sirainenstruct module *mail_storage_service_modules = NULL;
51292e327a91a257b8baea83560b3a8323cac8c5Timo Sirainen
c398eca6b0fc6583687bd6fe2ee2dbcca2ae9387Timo Sirainenstatic bool
c398eca6b0fc6583687bd6fe2ee2dbcca2ae9387Timo Sirainenmail_user_set_get_mail_debug(const struct setting_parser_info *user_info,
c398eca6b0fc6583687bd6fe2ee2dbcca2ae9387Timo Sirainen const struct mail_user_settings *user_set)
c398eca6b0fc6583687bd6fe2ee2dbcca2ae9387Timo Sirainen{
c398eca6b0fc6583687bd6fe2ee2dbcca2ae9387Timo Sirainen const struct mail_storage_settings *mail_set;
c398eca6b0fc6583687bd6fe2ee2dbcca2ae9387Timo Sirainen
c398eca6b0fc6583687bd6fe2ee2dbcca2ae9387Timo Sirainen mail_set = mail_user_set_get_driver_settings(user_info, user_set,
c398eca6b0fc6583687bd6fe2ee2dbcca2ae9387Timo Sirainen MAIL_STORAGE_SET_DRIVER_NAME);
c398eca6b0fc6583687bd6fe2ee2dbcca2ae9387Timo Sirainen return mail_set->mail_debug;
c398eca6b0fc6583687bd6fe2ee2dbcca2ae9387Timo Sirainen}
c398eca6b0fc6583687bd6fe2ee2dbcca2ae9387Timo Sirainen
c398eca6b0fc6583687bd6fe2ee2dbcca2ae9387Timo Sirainenstatic void set_keyval(struct mail_storage_service_ctx *ctx,
c398eca6b0fc6583687bd6fe2ee2dbcca2ae9387Timo Sirainen struct mail_storage_service_user *user,
c398eca6b0fc6583687bd6fe2ee2dbcca2ae9387Timo Sirainen const char *key, const char *value)
c398eca6b0fc6583687bd6fe2ee2dbcca2ae9387Timo Sirainen{
c398eca6b0fc6583687bd6fe2ee2dbcca2ae9387Timo Sirainen struct setting_parser_context *set_parser = user->set_parser;
c398eca6b0fc6583687bd6fe2ee2dbcca2ae9387Timo Sirainen const char *str;
c398eca6b0fc6583687bd6fe2ee2dbcca2ae9387Timo Sirainen
c398eca6b0fc6583687bd6fe2ee2dbcca2ae9387Timo Sirainen if (master_service_set_has_config_override(ctx->service, key)) {
c398eca6b0fc6583687bd6fe2ee2dbcca2ae9387Timo Sirainen /* this setting was already overridden with -o parameter */
4baf980b75800ad3595c39dc3b8d913f2686affdTimo Sirainen if (mail_user_set_get_mail_debug(user->user_info,
c398eca6b0fc6583687bd6fe2ee2dbcca2ae9387Timo Sirainen user->user_set)) {
c398eca6b0fc6583687bd6fe2ee2dbcca2ae9387Timo Sirainen i_debug("Ignoring overridden (-o) userdb setting: %s",
c398eca6b0fc6583687bd6fe2ee2dbcca2ae9387Timo Sirainen key);
686a3be710a464396cbfbc7b05eaa7fe16f3cd1cTimo Sirainen }
686a3be710a464396cbfbc7b05eaa7fe16f3cd1cTimo Sirainen return;
686a3be710a464396cbfbc7b05eaa7fe16f3cd1cTimo Sirainen }
c398eca6b0fc6583687bd6fe2ee2dbcca2ae9387Timo Sirainen
c398eca6b0fc6583687bd6fe2ee2dbcca2ae9387Timo Sirainen str = t_strconcat(key, "=", value, NULL);
c398eca6b0fc6583687bd6fe2ee2dbcca2ae9387Timo Sirainen if (settings_parse_line(set_parser, str) < 0) {
c398eca6b0fc6583687bd6fe2ee2dbcca2ae9387Timo Sirainen i_fatal("Invalid userdb input '%s': %s", str,
c398eca6b0fc6583687bd6fe2ee2dbcca2ae9387Timo Sirainen settings_parser_get_error(set_parser));
85174bd658f999cb3aa145885fd85674f8595fbbTimo Sirainen }
85174bd658f999cb3aa145885fd85674f8595fbbTimo Sirainen}
85174bd658f999cb3aa145885fd85674f8595fbbTimo Sirainen
c398eca6b0fc6583687bd6fe2ee2dbcca2ae9387Timo Sirainenstatic int set_line(struct mail_storage_service_ctx *ctx,
c398eca6b0fc6583687bd6fe2ee2dbcca2ae9387Timo Sirainen struct mail_storage_service_user *user,
c398eca6b0fc6583687bd6fe2ee2dbcca2ae9387Timo Sirainen const char *line)
c398eca6b0fc6583687bd6fe2ee2dbcca2ae9387Timo Sirainen{
c398eca6b0fc6583687bd6fe2ee2dbcca2ae9387Timo Sirainen struct setting_parser_context *set_parser = user->set_parser;
c398eca6b0fc6583687bd6fe2ee2dbcca2ae9387Timo Sirainen bool mail_debug;
c398eca6b0fc6583687bd6fe2ee2dbcca2ae9387Timo Sirainen const char *key, *orig_key, *append_value = NULL;
c398eca6b0fc6583687bd6fe2ee2dbcca2ae9387Timo Sirainen unsigned int len;
c398eca6b0fc6583687bd6fe2ee2dbcca2ae9387Timo Sirainen int ret;
c398eca6b0fc6583687bd6fe2ee2dbcca2ae9387Timo Sirainen
c398eca6b0fc6583687bd6fe2ee2dbcca2ae9387Timo Sirainen mail_debug = mail_user_set_get_mail_debug(user->user_info,
c398eca6b0fc6583687bd6fe2ee2dbcca2ae9387Timo Sirainen user->user_set);
c398eca6b0fc6583687bd6fe2ee2dbcca2ae9387Timo Sirainen if (strchr(line, '=') == NULL)
c398eca6b0fc6583687bd6fe2ee2dbcca2ae9387Timo Sirainen line = t_strconcat(line, "=yes", NULL);
c398eca6b0fc6583687bd6fe2ee2dbcca2ae9387Timo Sirainen orig_key = key = t_strcut(line, '=');
c398eca6b0fc6583687bd6fe2ee2dbcca2ae9387Timo Sirainen
c398eca6b0fc6583687bd6fe2ee2dbcca2ae9387Timo Sirainen len = strlen(key);
c398eca6b0fc6583687bd6fe2ee2dbcca2ae9387Timo Sirainen if (len > 0 && key[len-1] == '+') {
c398eca6b0fc6583687bd6fe2ee2dbcca2ae9387Timo Sirainen /* key+=value */
c398eca6b0fc6583687bd6fe2ee2dbcca2ae9387Timo Sirainen append_value = line + len + 1;
ebe00087d3c7f9706d4acb9017eaad912404516cTimo Sirainen key = t_strndup(key, len-1);
c398eca6b0fc6583687bd6fe2ee2dbcca2ae9387Timo Sirainen }
c398eca6b0fc6583687bd6fe2ee2dbcca2ae9387Timo Sirainen
ebe00087d3c7f9706d4acb9017eaad912404516cTimo Sirainen if (!settings_parse_is_valid_key(set_parser, key)) {
ebe00087d3c7f9706d4acb9017eaad912404516cTimo Sirainen /* assume it's a plugin setting */
ebe00087d3c7f9706d4acb9017eaad912404516cTimo Sirainen key = t_strconcat("plugin/", key, NULL);
ebe00087d3c7f9706d4acb9017eaad912404516cTimo Sirainen line = t_strconcat("plugin/", line, NULL);
ebe00087d3c7f9706d4acb9017eaad912404516cTimo Sirainen }
ebe00087d3c7f9706d4acb9017eaad912404516cTimo Sirainen
ebe00087d3c7f9706d4acb9017eaad912404516cTimo Sirainen if (master_service_set_has_config_override(ctx->service, key)) {
ebe00087d3c7f9706d4acb9017eaad912404516cTimo Sirainen /* this setting was already overridden with -o parameter */
c398eca6b0fc6583687bd6fe2ee2dbcca2ae9387Timo Sirainen if (mail_debug) {
c398eca6b0fc6583687bd6fe2ee2dbcca2ae9387Timo Sirainen i_debug("Ignoring overridden (-o) userdb setting: %s",
c398eca6b0fc6583687bd6fe2ee2dbcca2ae9387Timo Sirainen key);
ebe00087d3c7f9706d4acb9017eaad912404516cTimo Sirainen }
c398eca6b0fc6583687bd6fe2ee2dbcca2ae9387Timo Sirainen return 1;
c398eca6b0fc6583687bd6fe2ee2dbcca2ae9387Timo Sirainen }
ebe00087d3c7f9706d4acb9017eaad912404516cTimo Sirainen
402f9183489c1a75736b1e9068c33fe2741a366dTimo Sirainen if (append_value != NULL) {
51292e327a91a257b8baea83560b3a8323cac8c5Timo Sirainen const void *value;
402f9183489c1a75736b1e9068c33fe2741a366dTimo Sirainen enum setting_type type;
402f9183489c1a75736b1e9068c33fe2741a366dTimo Sirainen
1dcf22e98ba8310e8daa8c9297936c6f3a645e7aPhil Carmody value = settings_parse_get_value(set_parser, key, &type);
51292e327a91a257b8baea83560b3a8323cac8c5Timo Sirainen if (type == SET_STR) {
1dcf22e98ba8310e8daa8c9297936c6f3a645e7aPhil Carmody const char *const *strp = value;
5dd05e966ffd69181ab3067f6939b03ced68ebc3Timo Sirainen
5dd05e966ffd69181ab3067f6939b03ced68ebc3Timo Sirainen line = t_strdup_printf("%s=%s%s",
2ac5f36aa7c2e7a07ba8815d43a6d7483f62e74cTimo Sirainen key, *strp, append_value);
5dd05e966ffd69181ab3067f6939b03ced68ebc3Timo Sirainen } else {
5dd05e966ffd69181ab3067f6939b03ced68ebc3Timo Sirainen i_error("Ignoring %s userdb setting. "
5dd05e966ffd69181ab3067f6939b03ced68ebc3Timo Sirainen "'+' can only be used for strings.", orig_key);
5dd05e966ffd69181ab3067f6939b03ced68ebc3Timo Sirainen }
5dd05e966ffd69181ab3067f6939b03ced68ebc3Timo Sirainen }
5dd05e966ffd69181ab3067f6939b03ced68ebc3Timo Sirainen
5dd05e966ffd69181ab3067f6939b03ced68ebc3Timo Sirainen ret = settings_parse_line(set_parser, line);
5dd05e966ffd69181ab3067f6939b03ced68ebc3Timo Sirainen if (mail_debug && ret >= 0) {
5dd05e966ffd69181ab3067f6939b03ced68ebc3Timo Sirainen if (strstr(key, "pass") != NULL) {
5dd05e966ffd69181ab3067f6939b03ced68ebc3Timo Sirainen /* possibly a password field (e.g. imapc_password).
5dd05e966ffd69181ab3067f6939b03ced68ebc3Timo Sirainen hide the value. */
5dd05e966ffd69181ab3067f6939b03ced68ebc3Timo Sirainen line = t_strconcat(key, "=<hidden>", NULL);
2ac5f36aa7c2e7a07ba8815d43a6d7483f62e74cTimo Sirainen }
5dd05e966ffd69181ab3067f6939b03ced68ebc3Timo Sirainen i_debug(ret == 0 ?
5dd05e966ffd69181ab3067f6939b03ced68ebc3Timo Sirainen "Unknown userdb setting: %s" :
5dd05e966ffd69181ab3067f6939b03ced68ebc3Timo Sirainen "Added userdb setting: %s", line);
5dd05e966ffd69181ab3067f6939b03ced68ebc3Timo Sirainen }
5dd05e966ffd69181ab3067f6939b03ced68ebc3Timo Sirainen return ret;
402f9183489c1a75736b1e9068c33fe2741a366dTimo Sirainen}
402f9183489c1a75736b1e9068c33fe2741a366dTimo Sirainen
5dd05e966ffd69181ab3067f6939b03ced68ebc3Timo Sirainenstatic bool validate_chroot(const struct mail_user_settings *user_set,
5dd05e966ffd69181ab3067f6939b03ced68ebc3Timo Sirainen const char *dir)
2ac5f36aa7c2e7a07ba8815d43a6d7483f62e74cTimo Sirainen{
5dd05e966ffd69181ab3067f6939b03ced68ebc3Timo Sirainen const char *const *chroot_dirs;
5dd05e966ffd69181ab3067f6939b03ced68ebc3Timo Sirainen
5dd05e966ffd69181ab3067f6939b03ced68ebc3Timo Sirainen if (*dir == '\0')
return FALSE;
if (*user_set->valid_chroot_dirs == '\0')
return FALSE;
chroot_dirs = t_strsplit(user_set->valid_chroot_dirs, ":");
while (*chroot_dirs != NULL) {
if (**chroot_dirs != '\0' &&
strncmp(dir, *chroot_dirs, strlen(*chroot_dirs)) == 0)
return TRUE;
chroot_dirs++;
}
return FALSE;
}
static int
user_reply_handle(struct mail_storage_service_ctx *ctx,
struct mail_storage_service_user *user,
const struct auth_user_reply *reply,
const char **error_r)
{
const char *home = reply->home;
const char *chroot = reply->chroot;
const char *const *str, *line, *p;
unsigned int i, count;
int ret = 0;
if (reply->uid != (uid_t)-1) {
if (reply->uid == 0) {
*error_r = "userdb returned 0 as uid";
return -1;
}
user->uid_source = "userdb lookup";
set_keyval(ctx, user, "mail_uid", dec2str(reply->uid));
}
if (reply->gid != (uid_t)-1) {
user->gid_source = "userdb lookup";
set_keyval(ctx, user, "mail_gid", dec2str(reply->gid));
}
if (home != NULL && chroot == NULL &&
*user->user_set->valid_chroot_dirs != '\0' &&
(p = strstr(home, "/./")) != NULL) {
/* wu-ftpd like <chroot>/./<home> - check only if there's even
a possibility of using them (non-empty valid_chroot_dirs) */
chroot = t_strdup_until(home, p);
home = p + 2;
}
if (home != NULL)
set_keyval(ctx, user, "mail_home", home);
if (chroot != NULL) {
if (!validate_chroot(user->user_set, chroot)) {
*error_r = t_strdup_printf(
"userdb returned invalid chroot directory: %s "
"(see valid_chroot_dirs setting)", chroot);
return -1;
}
set_keyval(ctx, user, "mail_chroot", chroot);
}
str = array_get(&reply->extra_fields, &count);
for (i = 0; i < count; i++) {
line = str[i];
if (strncmp(line, "system_groups_user=", 19) == 0) {
user->system_groups_user =
p_strdup(user->pool, line + 19);
} else if (strncmp(line, "nice=", 5) == 0) {
#ifdef HAVE_SETPRIORITY
int n = atoi(line + 5);
if (n != 0) {
if (setpriority(PRIO_PROCESS, 0, n) < 0)
i_error("setpriority(%d) failed: %m", n);
}
#endif
} else T_BEGIN {
ret = set_line(ctx, user, line);
} T_END;
if (ret < 0)
break;
}
if (ret < 0) {
*error_r = t_strdup_printf("Invalid userdb input '%s': %s",
str[i], settings_parser_get_error(user->set_parser));
}
return ret;
}
static int
service_auth_userdb_lookup(struct mail_storage_service_ctx *ctx,
const struct mail_storage_service_input *input,
pool_t pool, const char **user,
const char *const **fields_r,
const char **error_r)
{
struct auth_user_info info;
const char *new_username;
int ret;
memset(&info, 0, sizeof(info));
info.service = ctx->service->name;
info.local_ip = input->local_ip;
info.remote_ip = input->remote_ip;
info.local_port = input->local_port;
info.remote_port = input->remote_port;
ret = auth_master_user_lookup(ctx->conn, *user, &info, pool,
&new_username, fields_r);
if (ret > 0) {
if (strcmp(*user, new_username) != 0) {
if (ctx->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 if (**fields_r != NULL) {
*error_r = t_strdup(**fields_r);
ret = -2;
} else {
*error_r = MAIL_ERRSTR_CRITICAL_MSG;
}
return ret;
}
static bool parse_uid(const char *str, uid_t *uid_r, const char **error_r)
{
struct passwd pw;
if (str_to_uid(str, uid_r) == 0)
return TRUE;
switch (i_getpwnam(str, &pw)) {
case -1:
*error_r = t_strdup_printf("getpwnam(%s) failed: %m", str);
return FALSE;
case 0:
*error_r = t_strconcat("Unknown UNIX UID user: ", str, NULL);
return FALSE;
default:
*uid_r = pw.pw_uid;
return TRUE;
}
}
static bool parse_gid(const char *str, gid_t *gid_r, const char **error_r)
{
struct group gr;
if (str_to_gid(str, gid_r) == 0)
return TRUE;
switch (i_getgrnam(str, &gr)) {
case -1:
*error_r = t_strdup_printf("getgrnam(%s) failed: %m", str);
return FALSE;
case 0:
*error_r = t_strconcat("Unknown UNIX GID group: ", str, NULL);
return FALSE;
default:
*gid_r = gr.gr_gid;
return TRUE;
}
}
static const struct var_expand_table *
get_var_expand_table(struct master_service *service,
struct mail_storage_service_input *input,
struct mail_storage_service_privileges *priv)
{
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, "gid" },
{ '\0', NULL, "session" },
{ '\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(priv->uid == (uid_t)-1 ? geteuid() : priv->uid);
tab[8].value = dec2str(priv->gid == (gid_t)-1 ? getegid() : priv->gid);
tab[9].value = input->session_id;
return tab;
}
const struct var_expand_table *
mail_storage_service_get_var_expand_table(struct mail_storage_service_ctx *ctx,
struct mail_storage_service_input *input)
{
struct mail_storage_service_privileges priv;
memset(&priv, 0, sizeof(priv));
priv.uid = (uid_t)-1;
priv.gid = (gid_t)-1;
return get_var_expand_table(ctx->service, input, &priv);
}
static const char *
user_expand_varstr(struct master_service *service,
struct mail_storage_service_input *input,
struct mail_storage_service_privileges *priv,
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, priv));
return str_c(ret);
}
static int
service_parse_privileges(struct mail_storage_service_ctx *ctx,
struct mail_storage_service_user *user,
struct mail_storage_service_privileges *priv_r,
const char **error_r)
{
const struct mail_user_settings *set = user->user_set;
uid_t uid = (uid_t)-1;
gid_t gid = (gid_t)-1;
memset(priv_r, 0, sizeof(*priv_r));
if (*set->mail_uid != '\0') {
if (!parse_uid(set->mail_uid, &uid, error_r)) {
*error_r = t_strdup_printf("%s (from %s)", *error_r,
user->uid_source);
return -1;
}
if (uid < (uid_t)set->first_valid_uid ||
(set->last_valid_uid != 0 &&
uid > (uid_t)set->last_valid_uid)) {
*error_r = t_strdup_printf(
"Mail access for users with UID %s not permitted "
"(see first_valid_uid in config file, uid from %s).",
dec2str(uid), user->uid_source);
return -1;
}
}
priv_r->uid = uid;
priv_r->uid_source = user->uid_source;
if (*set->mail_gid != '\0') {
if (!parse_gid(set->mail_gid, &gid, error_r)) {
*error_r = t_strdup_printf("%s (from %s)", *error_r,
user->gid_source);
return -1;
}
if (gid < (gid_t)set->first_valid_gid ||
(set->last_valid_gid != 0 &&
gid > (gid_t)set->last_valid_gid)) {
*error_r = t_strdup_printf(
"Mail access for users with GID %s not permitted "
"(see first_valid_gid in config file, gid from %s).",
dec2str(gid), user->gid_source);
return -1;
}
}
priv_r->gid = gid;
priv_r->gid_source = user->gid_source;
/* variable strings are expanded in mail_user_init(),
but we need the home and chroot sooner so do them separately here. */
priv_r->home = user_expand_varstr(ctx->service, &user->input, priv_r,
user->user_set->mail_home);
priv_r->chroot = user_expand_varstr(ctx->service, &user->input, priv_r,
user->user_set->mail_chroot);
return 0;
}
static int
service_drop_privileges(struct mail_storage_service_user *user,
struct mail_storage_service_privileges *priv,
bool disallow_root, bool keep_setuid_root,
bool setenv_only, const char **error_r)
{
const struct mail_user_settings *set = user->user_set;
struct restrict_access_settings rset;
uid_t current_euid, setuid_uid = 0;
const char *cur_chroot, *error;
current_euid = geteuid();
restrict_access_init(&rset);
restrict_access_get_env(&rset);
if (priv->uid != (uid_t)-1) {
rset.uid = priv->uid;
rset.uid_source = priv->uid_source;
} else if (rset.uid == (uid_t)-1 &&
disallow_root && current_euid == 0) {
*error_r = "User is missing UID (see mail_uid setting)";
return -1;
}
if (priv->gid != (gid_t)-1) {
rset.gid = priv->gid;
rset.gid_source = priv->gid_source;
} else if (rset.gid == (gid_t)-1 && disallow_root &&
set->first_valid_gid > 0 && getegid() == 0) {
*error_r = "User is missing GID (see mail_gid setting)";
return -1;
}
if (*set->mail_privileged_group != '\0') {
if (!parse_gid(set->mail_privileged_group, &rset.privileged_gid,
&error)) {
*error_r = t_strdup_printf(
"%s (in mail_privileged_group setting)", error);
return -1;
}
}
if (*set->mail_access_groups != '\0') {
rset.extra_groups = t_strconcat(set->mail_access_groups, ",",
rset.extra_groups, NULL);
}
rset.first_valid_gid = set->first_valid_gid;
rset.last_valid_gid = set->last_valid_gid;
rset.chroot_dir = *priv->chroot == '\0' ? NULL : priv->chroot;
rset.system_groups_user = user->system_groups_user;
cur_chroot = restrict_access_get_current_chroot();
if (cur_chroot != NULL) {
/* we're already chrooted. make sure the chroots are equal. */
if (rset.chroot_dir == NULL) {
*error_r = "Process is already chrooted, "
"can't un-chroot for this user";
return -1;
}
if (strcmp(rset.chroot_dir, cur_chroot) != 0) {
*error_r = t_strdup_printf(
"Process is already chrooted to %s, "
"can't chroot to %s", cur_chroot, priv->chroot);
return -1;
}
/* chrooting to same directory where we're already chrooted */
rset.chroot_dir = NULL;
}
if (disallow_root &&
(rset.uid == 0 || (rset.uid == (uid_t)-1 && current_euid == 0))) {
*error_r = "Mail access not allowed for root";
return -1;
}
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;
}
if (!setenv_only) {
restrict_access(&rset, *priv->home == '\0' ? NULL : priv->home,
disallow_root);
} else {
restrict_access_set_env(&rset);
}
if (setuid_uid != 0 && !setenv_only) {
if (seteuid(setuid_uid) < 0)
i_fatal("seteuid(%s) failed: %m", dec2str(setuid_uid));
}
return 0;
}
static int
mail_storage_service_init_post(struct mail_storage_service_ctx *ctx,
struct mail_storage_service_user *user,
struct mail_storage_service_privileges *priv,
struct mail_user **mail_user_r,
const char **error_r)
{
const struct mail_storage_settings *mail_set;
const char *home = priv->home;
struct mail_user *mail_user;
mail_user = mail_user_alloc(user->input.username, user->user_info,
user->user_set);
mail_user_set_home(mail_user, *home == '\0' ? NULL : home);
mail_user_set_vars(mail_user, ctx->service->name,
&user->input.local_ip, &user->input.remote_ip);
mail_user->uid = priv->uid == (uid_t)-1 ? geteuid() : priv->uid;
mail_user->gid = priv->gid == (gid_t)-1 ? getegid() : priv->gid;
mail_set = mail_user_set_get_storage_set(mail_user);
if (mail_set->mail_debug) {
string_t *str = t_str_new(64);
str_printfa(str, "Effective uid=%s, gid=%s, home=%s",
dec2str(geteuid()), dec2str(getegid()), home);
if (*priv->chroot != '\0')
str_printfa(str, ", chroot=%s", priv->chroot);
i_debug("%s", str_c(str));
}
if ((user->flags & MAIL_STORAGE_SERVICE_FLAG_TEMP_PRIV_DROP) != 0 &&
(user->flags & MAIL_STORAGE_SERVICE_FLAG_ENABLE_CORE_DUMPS) == 0) {
/* 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' &&
(user->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 == EACCES) {
i_error("%s", eacces_error_get("chdir",
t_strconcat(home, "/", NULL)));
} if (errno != ENOENT)
i_error("chdir(%s) failed: %m", home);
else if (mail_set->mail_debug)
i_debug("Home dir not found: %s", home);
}
}
if (mail_user_init(mail_user, error_r) < 0) {
mail_user_unref(&mail_user);
return -1;
}
if ((user->flags & MAIL_STORAGE_SERVICE_FLAG_NO_NAMESPACES) == 0) {
if (mail_namespaces_init(mail_user, error_r) < 0) {
mail_user_unref(&mail_user);
return -1;
}
}
*mail_user_r = mail_user;
return 0;
}
static void mail_storage_service_io_activate(void *context)
{
struct mail_storage_service_user *user = context;
i_set_failure_prefix(user->log_prefix);
}
static void mail_storage_service_io_deactivate(void *context)
{
struct mail_storage_service_user *user = context;
i_set_failure_prefix(user->service_ctx->default_log_prefix);
}
static void
mail_storage_service_init_log(struct mail_storage_service_ctx *ctx,
struct mail_storage_service_user *user,
struct mail_storage_service_privileges *priv)
{
ctx->log_initialized = TRUE;
T_BEGIN {
string_t *str;
str = t_str_new(256);
var_expand(str, user->user_set->mail_log_prefix,
get_var_expand_table(ctx->service, &user->input, priv));
user->log_prefix = p_strdup(user->pool, str_c(str));
} T_END;
master_service_init_log(ctx->service, user->log_prefix);
if (master_service_get_client_limit(master_service) == 1)
i_set_failure_send_prefix(user->log_prefix);
user->ioloop_ctx = io_loop_context_new(current_ioloop);
io_loop_context_add_callbacks(user->ioloop_ctx,
mail_storage_service_io_activate,
mail_storage_service_io_deactivate,
user);
}
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://wiki2.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://wiki2.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;
}
}
}
struct mail_storage_service_ctx *
mail_storage_service_init(struct master_service *service,
const struct setting_parser_info *set_roots[],
enum mail_storage_service_flags flags)
{
struct mail_storage_service_ctx *ctx;
pool_t pool;
unsigned int count;
(void)umask(0077);
io_loop_set_time_moved_callback(current_ioloop,
mail_storage_service_time_moved);
mail_storage_init();
mail_storage_register_all();
mailbox_list_register_all();
pool = pool_alloconly_create("mail storage service", 2048);
ctx = p_new(pool, struct mail_storage_service_ctx, 1);
ctx->pool = pool;
ctx->service = service;
ctx->flags = flags;
/* @UNSAFE */
if (set_roots == NULL)
count = 0;
else
for (count = 0; set_roots[count] != NULL; count++) ;
ctx->set_roots =
p_new(pool, const struct setting_parser_info *, count + 2);
ctx->set_roots[0] = &mail_user_setting_parser_info;
if (set_roots != NULL) {
memcpy(ctx->set_roots + 1, set_roots,
sizeof(*ctx->set_roots) * count);
}
/* do all the global initialization. delay initializing plugins until
we drop privileges the first time. */
if ((flags & MAIL_STORAGE_SERVICE_FLAG_NO_LOG_INIT) == 0) {
/* note: we may not have read any settings yet, so this logging
may still be going to wrong location */
ctx->default_log_prefix =
p_strconcat(pool, service->name, ": ", NULL);
master_service_init_log(service, ctx->default_log_prefix);
}
dict_drivers_register_builtin();
return ctx;
}
struct auth_master_connection *
mail_storage_service_get_auth_conn(struct mail_storage_service_ctx *ctx)
{
i_assert(ctx->conn != NULL);
return ctx->conn;
}
static enum mail_storage_service_flags
mail_storage_service_input_get_flags(struct mail_storage_service_ctx *ctx,
const struct mail_storage_service_input *input)
{
enum mail_storage_service_flags flags;
flags = (ctx->flags & ~input->flags_override_remove) |
input->flags_override_add;
if (input->no_userdb_lookup) {
/* FIXME: for API backwards compatibility only */
flags &= ~MAIL_STORAGE_SERVICE_FLAG_USERDB_LOOKUP;
}
return flags;
}
int mail_storage_service_read_settings(struct mail_storage_service_ctx *ctx,
const struct mail_storage_service_input *input,
pool_t pool,
const struct setting_parser_info **user_info_r,
const struct setting_parser_context **parser_r,
const char **error_r)
{
struct master_service_settings_input set_input;
const struct setting_parser_info *const *roots;
struct master_service_settings_output set_output;
const struct dynamic_settings_parser *dyn_parsers;
enum mail_storage_service_flags flags;
unsigned int i;
flags = input == NULL ? ctx->flags :
mail_storage_service_input_get_flags(ctx, input);
memset(&set_input, 0, sizeof(set_input));
set_input.roots = ctx->set_roots;
set_input.preserve_user = TRUE;
/* settings reader may exec doveconf, which is going to clear
environment, and if we're not doing a userdb lookup we want to
use $HOME */
set_input.preserve_home =
(flags & MAIL_STORAGE_SERVICE_FLAG_USERDB_LOOKUP) == 0;
set_input.use_sysexits =
(flags & MAIL_STORAGE_SERVICE_FLAG_USE_SYSEXITS) != 0;
if (input != NULL) {
set_input.module = input->module;
set_input.service = input->service;
set_input.username = input->username;
set_input.local_ip = input->local_ip;
set_input.remote_ip = input->remote_ip;
}
if (ctx->set_cache == NULL) {
ctx->set_cache_module = p_strdup(ctx->pool, set_input.module);
ctx->set_cache_service = p_strdup(ctx->pool, set_input.service);
ctx->set_cache = master_service_settings_cache_init(
ctx->service, set_input.module, set_input.service);
} else {
/* already looked up settings at least once.
we really shouldn't be execing anymore. */
set_input.never_exec = TRUE;
}
dyn_parsers = mail_storage_get_dynamic_parsers(pool);
if (null_strcmp(set_input.module, ctx->set_cache_module) == 0 &&
null_strcmp(set_input.service, ctx->set_cache_service) == 0) {
if (master_service_settings_cache_read(ctx->set_cache,
&set_input, dyn_parsers,
parser_r, error_r) < 0) {
*error_r = t_strdup_printf(
"Error reading configuration: %s", *error_r);
return -1;
}
} else {
settings_parser_dyn_update(pool, &set_input.roots, dyn_parsers);
if (master_service_settings_read(ctx->service, &set_input,
&set_output, error_r) < 0) {
*error_r = t_strdup_printf(
"Error reading configuration: %s", *error_r);
return -1;
}
*parser_r = ctx->service->set_parser;
}
roots = settings_parser_get_roots(*parser_r);
for (i = 0; roots[i] != NULL; i++) {
if (strcmp(roots[i]->module_name,
mail_user_setting_parser_info.module_name) == 0) {
*user_info_r = roots[i];
return 0;
}
}
i_unreached();
return -1;
}
static void
mail_storage_service_first_init(struct mail_storage_service_ctx *ctx,
const struct setting_parser_info *user_info,
const struct mail_user_settings *user_set)
{
enum auth_master_flags flags = 0;
i_assert(ctx->conn == NULL);
ctx->debug = mail_user_set_get_mail_debug(user_info, user_set);
if (ctx->debug)
flags |= AUTH_MASTER_FLAG_DEBUG;
if ((ctx->flags & MAIL_STORAGE_SERVICE_FLAG_NO_IDLE_TIMEOUT) != 0)
flags |= AUTH_MASTER_FLAG_NO_IDLE_TIMEOUT;
ctx->conn = auth_master_init(user_set->auth_socket_path, flags);
i_assert(mail_user_auth_master_conn == NULL);
mail_user_auth_master_conn = ctx->conn;
}
static void
mail_storage_service_load_modules(struct mail_storage_service_ctx *ctx,
const struct setting_parser_info *user_info,
const struct mail_user_settings *user_set)
{
struct module_dir_load_settings mod_set;
if (*user_set->mail_plugins == '\0')
return;
if ((ctx->flags & MAIL_STORAGE_SERVICE_FLAG_NO_PLUGINS) != 0)
return;
memset(&mod_set, 0, sizeof(mod_set));
mod_set.version = master_service_get_version_string(ctx->service);
mod_set.binary_name = master_service_get_name(ctx->service);
mod_set.setting_name = "mail_plugins";
mod_set.require_init_funcs = TRUE;
mod_set.debug = mail_user_set_get_mail_debug(user_info, user_set);
mail_storage_service_modules =
module_dir_load_missing(mail_storage_service_modules,
user_set->mail_plugin_dir,
user_set->mail_plugins, &mod_set);
}
static int extra_field_key_cmp_p(const char *const *s1, const char *const *s2)
{
const char *p1 = *s1, *p2 = *s2;
for (; *p1 == *p2; p1++, p2++) {
if (*p1 == '\0')
return 0;
}
if (*p1 == '=')
return -1;
if (*p2 == '=')
return 1;
return *p1 - *p2;
}
int mail_storage_service_lookup(struct mail_storage_service_ctx *ctx,
const struct mail_storage_service_input *input,
struct mail_storage_service_user **user_r,
const char **error_r)
{
enum mail_storage_service_flags flags;
struct mail_storage_service_user *user;
const char *username = input->username;
const struct setting_parser_info *user_info;
const struct mail_user_settings *user_set;
const char *const *userdb_fields, *error;
struct auth_user_reply reply;
const struct setting_parser_context *set_parser;
pool_t user_pool, temp_pool;
int ret = 1;
user_pool = pool_alloconly_create("mail storage service user", 1024*8);
if (mail_storage_service_read_settings(ctx, input, user_pool,
&user_info, &set_parser,
&error) < 0) {
i_error("user %s: %s", username, error);
pool_unref(&user_pool);
*error_r = MAIL_ERRSTR_CRITICAL_MSG;
return -1;
}
flags = mail_storage_service_input_get_flags(ctx, input);
if ((flags & MAIL_STORAGE_SERVICE_FLAG_NO_LOG_INIT) == 0 &&
!ctx->log_initialized) {
/* initialize logging again, in case we only read the
settings for the first above */
ctx->log_initialized = TRUE;
master_service_init_log(ctx->service,
t_strconcat(ctx->service->name, ": ", NULL));
}
user_set = settings_parser_get_list(set_parser)[1];
if (ctx->conn == NULL)
mail_storage_service_first_init(ctx, user_info, user_set);
/* load global plugins */
mail_storage_service_load_modules(ctx, user_info, user_set);
temp_pool = pool_alloconly_create("userdb lookup", 2048);
if ((flags & MAIL_STORAGE_SERVICE_FLAG_USERDB_LOOKUP) != 0) {
ret = service_auth_userdb_lookup(ctx, input, temp_pool,
&username, &userdb_fields,
error_r);
if (ret <= 0) {
pool_unref(&temp_pool);
pool_unref(&user_pool);
return ret;
}
} else {
userdb_fields = input->userdb_fields;
}
user = p_new(user_pool, struct mail_storage_service_user, 1);
user->service_ctx = ctx;
user->pool = user_pool;
user->input = *input;
user->input.userdb_fields = NULL;
user->input.username = p_strdup(user_pool, username);
user->user_info = user_info;
user->flags = flags;
user->set_parser = settings_parser_dup(set_parser, user_pool);
if (!settings_parser_check(user->set_parser, user_pool, &error))
i_panic("settings_parser_check() failed: %s", error);
user->user_set = settings_parser_get_list(user->set_parser)[1];
user->gid_source = "mail_gid setting";
user->uid_source = "mail_uid setting";
if ((flags & MAIL_STORAGE_SERVICE_FLAG_DEBUG) != 0)
(void)settings_parse_line(user->set_parser, "mail_debug=yes");
if ((flags & MAIL_STORAGE_SERVICE_FLAG_USERDB_LOOKUP) == 0) {
const char *home = getenv("HOME");
if (home != NULL)
set_keyval(ctx, user, "mail_home", home);
}
if (userdb_fields != NULL) {
auth_user_fields_parse(userdb_fields, temp_pool, &reply);
array_sort(&reply.extra_fields, extra_field_key_cmp_p);
if (user_reply_handle(ctx, user, &reply, &error) < 0) {
i_error("user %s: Invalid settings in userdb: %s",
username, error);
*error_r = ERRSTR_INVALID_USER_SETTINGS;
ret = -2;
}
}
pool_unref(&temp_pool);
/* load per-user plugins */
if (ret > 0) {
mail_storage_service_load_modules(ctx, user_info,
user->user_set);
}
*user_r = user;
return ret;
}
int mail_storage_service_next(struct mail_storage_service_ctx *ctx,
struct mail_storage_service_user *user,
struct mail_user **mail_user_r)
{
struct mail_storage_service_privileges priv;
const char *error;
unsigned int len;
bool disallow_root =
(user->flags & MAIL_STORAGE_SERVICE_FLAG_DISALLOW_ROOT) != 0;
bool temp_priv_drop =
(user->flags & MAIL_STORAGE_SERVICE_FLAG_TEMP_PRIV_DROP) != 0;
bool use_chroot;
if (service_parse_privileges(ctx, user, &priv, &error) < 0) {
i_error("user %s: %s", user->input.username, error);
return -2;
}
if (*priv.home != '/' && *priv.home != '\0') {
i_error("user %s: "
"Relative home directory paths not supported: %s",
user->input.username, priv.home);
return -2;
}
/* we can't chroot if we want to switch between users. there's
not much point either (from security point of view). but if we're
already chrooted, we'll just have to continue and hope that the
current chroot is the same as the wanted chroot */
use_chroot = !temp_priv_drop ||
restrict_access_get_current_chroot() != NULL;
len = strlen(priv.chroot);
if (len > 2 && strcmp(priv.chroot + len - 2, "/.") == 0 &&
strncmp(priv.home, priv.chroot, len - 2) == 0) {
/* mail_chroot = /chroot/. means that the home dir already
contains the chroot dir. remove it from home. */
if (use_chroot) {
priv.home += len - 2;
if (*priv.home == '\0')
priv.home = "/";
priv.chroot = t_strndup(priv.chroot, len - 2);
set_keyval(ctx, user, "mail_home", priv.home);
set_keyval(ctx, user, "mail_chroot", priv.chroot);
}
} else if (len > 0 && !use_chroot) {
/* we're not going to chroot. fix home directory so we can
access it. */
if (*priv.home == '\0' || strcmp(priv.home, "/") == 0)
priv.home = priv.chroot;
else
priv.home = t_strconcat(priv.chroot, priv.home, NULL);
priv.chroot = "";
set_keyval(ctx, user, "mail_home", priv.home);
}
if ((user->flags & MAIL_STORAGE_SERVICE_FLAG_NO_LOG_INIT) == 0)
mail_storage_service_init_log(ctx, user, &priv);
if ((user->flags & MAIL_STORAGE_SERVICE_FLAG_NO_RESTRICT_ACCESS) == 0) {
if (service_drop_privileges(user, &priv,
disallow_root, temp_priv_drop,
FALSE, &error) < 0) {
i_error("user %s: Couldn't drop privileges: %s",
user->input.username, error);
return -1;
}
if (!temp_priv_drop ||
(user->flags & MAIL_STORAGE_SERVICE_FLAG_ENABLE_CORE_DUMPS) != 0)
restrict_access_allow_coredumps(TRUE);
}
/* privileges are dropped. initialize plugins that haven't been
initialized yet. */
module_dir_init(mail_storage_service_modules);
if (mail_storage_service_init_post(ctx, user, &priv,
mail_user_r, &error) < 0) {
i_error("user %s: Initialization failed: %s",
user->input.username, error);
return -2;
}
return 0;
}
void mail_storage_service_restrict_setenv(struct mail_storage_service_ctx *ctx,
struct mail_storage_service_user *user)
{
struct mail_storage_service_privileges priv;
const char *error;
if (service_parse_privileges(ctx, user, &priv, &error) < 0)
i_fatal("user %s: %s", user->input.username, error);
if (service_drop_privileges(user, &priv,
FALSE, FALSE, TRUE, &error) < 0)
i_fatal("user %s: %s", user->input.username, error);
}
int mail_storage_service_lookup_next(struct mail_storage_service_ctx *ctx,
const struct mail_storage_service_input *input,
struct mail_storage_service_user **user_r,
struct mail_user **mail_user_r,
const char **error_r)
{
struct mail_storage_service_user *user;
int ret;
ret = mail_storage_service_lookup(ctx, input, &user, error_r);
if (ret <= 0)
return ret;
ret = mail_storage_service_next(ctx, user, mail_user_r);
if (ret < 0) {
mail_storage_service_user_free(&user);
*error_r = ret == -2 ? ERRSTR_INVALID_USER_SETTINGS :
MAIL_ERRSTR_CRITICAL_MSG;
return ret;
}
*user_r = user;
return 1;
}
void mail_storage_service_user_free(struct mail_storage_service_user **_user)
{
struct mail_storage_service_user *user = *_user;
*_user = NULL;
if (user->ioloop_ctx != NULL) {
io_loop_context_remove_callbacks(user->ioloop_ctx,
mail_storage_service_io_activate,
mail_storage_service_io_deactivate, user);
io_loop_context_unref(&user->ioloop_ctx);
}
settings_parser_deinit(&user->set_parser);
pool_unref(&user->pool);
}
void mail_storage_service_init_settings(struct mail_storage_service_ctx *ctx,
const struct mail_storage_service_input *input)
{
const struct setting_parser_info *user_info;
const struct mail_user_settings *user_set;
const struct setting_parser_context *set_parser;
const char *error;
pool_t temp_pool;
if (ctx->conn != NULL)
return;
temp_pool = pool_alloconly_create("service all settings", 4096);
if (mail_storage_service_read_settings(ctx, input, temp_pool,
&user_info, &set_parser,
&error) < 0)
i_fatal("%s", error);
user_set = settings_parser_get_list(set_parser)[1];
mail_storage_service_first_init(ctx, user_info, user_set);
pool_unref(&temp_pool);
}
unsigned int
mail_storage_service_all_init(struct mail_storage_service_ctx *ctx)
{
if (ctx->auth_list != NULL)
(void)auth_master_user_list_deinit(&ctx->auth_list);
mail_storage_service_init_settings(ctx, NULL);
ctx->auth_list = auth_master_user_list_init(ctx->conn, NULL, NULL);
return auth_master_user_list_count(ctx->auth_list);
}
int mail_storage_service_all_next(struct mail_storage_service_ctx *ctx,
const char **username_r)
{
i_assert((ctx->flags & MAIL_STORAGE_SERVICE_FLAG_USERDB_LOOKUP) != 0);
*username_r = auth_master_user_list_next(ctx->auth_list);
if (*username_r != NULL)
return 1;
return auth_master_user_list_deinit(&ctx->auth_list);
}
void mail_storage_service_deinit(struct mail_storage_service_ctx **_ctx)
{
struct mail_storage_service_ctx *ctx = *_ctx;
*_ctx = NULL;
if (ctx->auth_list != NULL)
(void)auth_master_user_list_deinit(&ctx->auth_list);
if (ctx->conn != NULL) {
if (mail_user_auth_master_conn == ctx->conn)
mail_user_auth_master_conn = NULL;
auth_master_deinit(&ctx->conn);
}
if (ctx->set_cache != NULL)
master_service_settings_cache_deinit(&ctx->set_cache);
pool_unref(&ctx->pool);
module_dir_unload(&mail_storage_service_modules);
mail_storage_deinit();
dict_drivers_unregister_builtin();
}
void **mail_storage_service_user_get_set(struct mail_storage_service_user *user)
{
return settings_parser_get_list(user->set_parser) + 1;
}
const struct mail_storage_service_input *
mail_storage_service_user_get_input(struct mail_storage_service_user *user)
{
return &user->input;
}
struct setting_parser_context *
mail_storage_service_user_get_settings_parser(struct mail_storage_service_user *user)
{
return user->set_parser;
}
void *mail_storage_service_get_settings(struct master_service *service)
{
void **sets, *set;
T_BEGIN {
sets = master_service_settings_get_others(service);
set = sets[1];
} T_END;
return set;
}