mail-storage-service.c revision 38dac3030bdd74acba0a60edd54b2db6d914fde2
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen/* Copyright (c) 2009-2016 Dovecot authors, see the included COPYING file */
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "lib.h"
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen#include "ioloop.h"
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen#include "array.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "base64.h"
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen#include "hostpid.h"
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen#include "module-dir.h"
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen#include "restrict-access.h"
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen#include "eacces-error.h"
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen#include "ipwd.h"
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen#include "str.h"
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen#include "var-expand.h"
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen#include "dict.h"
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen#include "settings-parser.h"
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen#include "auth-master.h"
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen#include "master-service-private.h"
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen#include "master-service-settings.h"
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen#include "master-service-settings-cache.h"
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen#include "mail-user.h"
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen#include "mail-namespace.h"
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen#include "mail-storage.h"
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen#include "mail-storage-service.h"
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen#include <sys/stat.h>
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen#ifdef HAVE_SYS_TIME_H
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen# include <sys/time.h>
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen#endif
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen#ifdef HAVE_SYS_RESOURCE_H
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen# include <sys/resource.h>
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen#endif
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen/* If time moves backwards more than this, kill ourself instead of sleeping. */
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen#define MAX_TIME_BACKWARDS_SLEEP 5
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen#define MAX_NOWARN_FORWARD_SECS 10
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen#define ERRSTR_INVALID_USER_SETTINGS \
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen "Invalid user settings. Refer to server log for more information."
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainenstruct mail_storage_service_privileges {
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen uid_t uid;
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen gid_t gid;
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen const char *uid_source, *gid_source;
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen const char *home;
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen const char *chroot;
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen};
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainenstruct mail_storage_service_ctx {
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen pool_t pool;
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen struct master_service *service;
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen const char *default_log_prefix;
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen struct auth_master_connection *conn, *iter_conn;
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen struct auth_master_user_list_ctx *auth_list;
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen const struct setting_parser_info **set_roots;
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen enum mail_storage_service_flags flags;
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen const char *set_cache_module, *set_cache_service;
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen struct master_service_settings_cache *set_cache;
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen pool_t userdb_next_pool;
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen const char *const **userdb_next_fieldsp;
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen unsigned int debug:1;
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen unsigned int log_initialized:1;
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen unsigned int config_permission_denied:1;
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen};
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainenstruct mail_storage_service_user {
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen pool_t pool;
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen struct mail_storage_service_ctx *service_ctx;
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen struct mail_storage_service_input input;
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen enum mail_storage_service_flags flags;
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen struct ioloop_context *ioloop_ctx;
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen const char *log_prefix, *auth_token, *auth_user;
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen const char *system_groups_user, *uid_source, *gid_source;
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen const struct mail_user_settings *user_set;
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen const struct setting_parser_info *user_info;
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen struct setting_parser_context *set_parser;
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen unsigned int anonymous:1;
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen unsigned int admin:1;
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen};
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainenstruct module *mail_storage_service_modules = NULL;
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainenstatic void
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainenmail_storage_service_var_expand(struct mail_storage_service_ctx *ctx,
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen string_t *str, const char *format,
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen struct mail_storage_service_user *user,
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen const struct mail_storage_service_input *input,
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen const struct mail_storage_service_privileges *priv);
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainenstatic bool
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainenmail_user_set_get_mail_debug(const struct setting_parser_info *user_info,
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen const struct mail_user_settings *user_set)
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen{
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen const struct mail_storage_settings *mail_set;
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen mail_set = mail_user_set_get_driver_settings(user_info, user_set,
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen MAIL_STORAGE_SET_DRIVER_NAME);
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen return mail_set->mail_debug;
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen}
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainenstatic void set_keyval(struct mail_storage_service_ctx *ctx,
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen struct mail_storage_service_user *user,
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen const char *key, const char *value)
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen{
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen struct setting_parser_context *set_parser = user->set_parser;
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen const char *str;
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen if (master_service_set_has_config_override(ctx->service, key)) {
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen /* this setting was already overridden with -o parameter */
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen if (mail_user_set_get_mail_debug(user->user_info,
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen user->user_set)) {
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen i_debug("Ignoring overridden (-o) userdb setting: %s",
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen key);
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen }
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen return;
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen }
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen str = t_strconcat(key, "=", value, NULL);
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen if (settings_parse_line(set_parser, str) < 0) {
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen i_fatal("Invalid userdb input '%s': %s", str,
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen settings_parser_get_error(set_parser));
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen }
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen}
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainenstatic int set_line(struct mail_storage_service_ctx *ctx,
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen struct mail_storage_service_user *user,
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen const char *line)
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen{
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen struct setting_parser_context *set_parser = user->set_parser;
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen bool mail_debug;
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen const char *key, *orig_key, *append_value = NULL;
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen unsigned int len;
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen int ret;
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen mail_debug = mail_user_set_get_mail_debug(user->user_info,
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen user->user_set);
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen if (strchr(line, '=') == NULL)
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen line = t_strconcat(line, "=yes", NULL);
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen orig_key = key = t_strcut(line, '=');
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen len = strlen(key);
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen if (len > 0 && key[len-1] == '+') {
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen /* key+=value */
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen append_value = line + len + 1;
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen key = t_strndup(key, len-1);
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen }
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen if (!settings_parse_is_valid_key(set_parser, key)) {
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen /* assume it's a plugin setting */
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen key = t_strconcat("plugin/", key, NULL);
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen line = t_strconcat("plugin/", line, NULL);
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen }
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen if (master_service_set_has_config_override(ctx->service, key)) {
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen /* this setting was already overridden with -o parameter */
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen if (mail_debug) {
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen i_debug("Ignoring overridden (-o) userdb setting: %s",
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen key);
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen }
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen return 1;
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen }
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen if (append_value != NULL) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen const void *value;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen enum setting_type type;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen value = settings_parse_get_value(set_parser, key, &type);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (type == SET_STR) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen const char *const *strp = value;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen line = t_strdup_printf("%s=%s%s",
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen key, *strp, append_value);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen } else {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen i_error("Ignoring %s userdb setting. "
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen "'+' can only be used for strings.", orig_key);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen ret = settings_parse_line(set_parser, line);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (mail_debug && ret >= 0) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (strstr(key, "pass") != NULL) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* possibly a password field (e.g. imapc_password).
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen hide the value. */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen line = t_strconcat(key, "=<hidden>", NULL);
b7b81543899e306c71e6152516d8698416162bcbTimo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen i_debug(ret == 0 ?
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen "Unknown userdb setting: %s" :
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen "Added userdb setting: %s", line);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
fd3d711f219fd6813492acbe051e04327f0ca0f0Timo Sirainen return ret;
fd3d711f219fd6813492acbe051e04327f0ca0f0Timo Sirainen}
fd3d711f219fd6813492acbe051e04327f0ca0f0Timo Sirainen
fd3d711f219fd6813492acbe051e04327f0ca0f0Timo Sirainenstatic bool validate_chroot(const struct mail_user_settings *user_set,
fd3d711f219fd6813492acbe051e04327f0ca0f0Timo Sirainen const char *dir)
fd3d711f219fd6813492acbe051e04327f0ca0f0Timo Sirainen{
fd3d711f219fd6813492acbe051e04327f0ca0f0Timo Sirainen const char *const *chroot_dirs;
fd3d711f219fd6813492acbe051e04327f0ca0f0Timo Sirainen
fd3d711f219fd6813492acbe051e04327f0ca0f0Timo Sirainen if (*dir == '\0')
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return FALSE;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (*user_set->valid_chroot_dirs == '\0')
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen return FALSE;
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen chroot_dirs = t_strsplit(user_set->valid_chroot_dirs, ":");
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen while (*chroot_dirs != NULL) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (**chroot_dirs != '\0' &&
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen strncmp(dir, *chroot_dirs, strlen(*chroot_dirs)) == 0)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return TRUE;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen chroot_dirs++;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return FALSE;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen}
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstatic int
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenuser_reply_handle(struct mail_storage_service_ctx *ctx,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct mail_storage_service_user *user,
fd3d711f219fd6813492acbe051e04327f0ca0f0Timo Sirainen const struct auth_user_reply *reply,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen const char **error_r)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen{
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen const char *home = reply->home;
d8b77aef97e89f1ccc5cbdaef77be9052279e35fTimo Sirainen const char *chroot = reply->chroot;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen const char *const *str, *line, *p;
d8b77aef97e89f1ccc5cbdaef77be9052279e35fTimo Sirainen unsigned int i, count;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen int ret = 0;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
d8b77aef97e89f1ccc5cbdaef77be9052279e35fTimo Sirainen if (reply->uid != (uid_t)-1) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (reply->uid == 0) {
d8b77aef97e89f1ccc5cbdaef77be9052279e35fTimo Sirainen *error_r = "userdb returned 0 as uid";
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen return -1;
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen user->uid_source = "userdb lookup";
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen set_keyval(ctx, user, "mail_uid", dec2str(reply->uid));
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (reply->gid != (uid_t)-1) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen user->gid_source = "userdb lookup";
fd3d711f219fd6813492acbe051e04327f0ca0f0Timo Sirainen set_keyval(ctx, user, "mail_gid", dec2str(reply->gid));
fd3d711f219fd6813492acbe051e04327f0ca0f0Timo Sirainen }
fd3d711f219fd6813492acbe051e04327f0ca0f0Timo Sirainen
fd3d711f219fd6813492acbe051e04327f0ca0f0Timo Sirainen if (home != NULL && chroot == NULL &&
fd3d711f219fd6813492acbe051e04327f0ca0f0Timo Sirainen *user->user_set->valid_chroot_dirs != '\0' &&
fd3d711f219fd6813492acbe051e04327f0ca0f0Timo Sirainen (p = strstr(home, "/./")) != NULL) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* wu-ftpd like <chroot>/./<home> - check only if there's even
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen a possibility of using them (non-empty valid_chroot_dirs) */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen chroot = t_strdup_until(home, p);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen home = p + 2;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
fd3d711f219fd6813492acbe051e04327f0ca0f0Timo Sirainen if (home != NULL)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen set_keyval(ctx, user, "mail_home", home);
fd3d711f219fd6813492acbe051e04327f0ca0f0Timo Sirainen
fd3d711f219fd6813492acbe051e04327f0ca0f0Timo Sirainen if (chroot != NULL) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (!validate_chroot(user->user_set, chroot)) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen *error_r = t_strdup_printf(
fd3d711f219fd6813492acbe051e04327f0ca0f0Timo Sirainen "userdb returned invalid chroot directory: %s "
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen "(see valid_chroot_dirs setting)", chroot);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return -1;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
fd3d711f219fd6813492acbe051e04327f0ca0f0Timo Sirainen set_keyval(ctx, user, "mail_chroot", chroot);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen user->anonymous = reply->anonymous;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen str = array_get(&reply->extra_fields, &count);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen for (i = 0; i < count; i++) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen line = str[i];
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (strncmp(line, "system_groups_user=", 19) == 0) {
fd3d711f219fd6813492acbe051e04327f0ca0f0Timo Sirainen user->system_groups_user =
fd3d711f219fd6813492acbe051e04327f0ca0f0Timo Sirainen p_strdup(user->pool, line + 19);
fd3d711f219fd6813492acbe051e04327f0ca0f0Timo Sirainen } else if (strncmp(line, "nice=", 5) == 0) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#ifdef HAVE_SETPRIORITY
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen int n;
fd3d711f219fd6813492acbe051e04327f0ca0f0Timo Sirainen if (str_to_int(line + 5, &n) < 0) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen i_error("userdb returned invalid nice value %s",
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen line + 5);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen } else if (n != 0) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (setpriority(PRIO_PROCESS, 0, n) < 0)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen i_error("setpriority(%d) failed: %m", n);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#endif
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen } else if (strncmp(line, "auth_token=", 11) == 0) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen user->auth_token = p_strdup(user->pool, line+11);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen } else if (strncmp(line, "auth_user=", 10) == 0) {
fd3d711f219fd6813492acbe051e04327f0ca0f0Timo Sirainen user->auth_user = p_strdup(user->pool, line+10);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen } else if (strncmp(line, "admin=", 6) == 0) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen user->admin = line[6] == 'y' || line[6] == 'Y' ||
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen line[6] == '1';
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen } else T_BEGIN {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen ret = set_line(ctx, user, line);
fd3d711f219fd6813492acbe051e04327f0ca0f0Timo Sirainen } T_END;
fd3d711f219fd6813492acbe051e04327f0ca0f0Timo Sirainen if (ret < 0)
fd3d711f219fd6813492acbe051e04327f0ca0f0Timo Sirainen break;
fd3d711f219fd6813492acbe051e04327f0ca0f0Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (ret < 0) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen *error_r = t_strdup_printf("Invalid userdb input '%s': %s",
fd3d711f219fd6813492acbe051e04327f0ca0f0Timo Sirainen str[i], settings_parser_get_error(user->set_parser));
fd3d711f219fd6813492acbe051e04327f0ca0f0Timo Sirainen }
fd3d711f219fd6813492acbe051e04327f0ca0f0Timo Sirainen return ret;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen}
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstatic int
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenservice_auth_userdb_lookup(struct mail_storage_service_ctx *ctx,
fd3d711f219fd6813492acbe051e04327f0ca0f0Timo Sirainen const struct mail_storage_service_input *input,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen pool_t pool, const char **user,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen const char *const **fields_r,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen const char **error_r)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen{
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct auth_user_info info;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen const char *new_username;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen int ret;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen memset(&info, 0, sizeof(info));
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen info.service = input->service != NULL ? input->service :
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen ctx->service->name;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen info.local_ip = input->local_ip;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen info.remote_ip = input->remote_ip;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen info.local_port = input->local_port;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen info.remote_port = input->remote_port;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen ret = auth_master_user_lookup(ctx->conn, *user, &info, pool,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen &new_username, fields_r);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (ret > 0) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (strcmp(*user, new_username) != 0) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (ctx->debug)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen i_debug("changed username to %s", new_username);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen *user = t_strdup(new_username);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen *user = new_username;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen } else if (ret == 0)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen *error_r = "Unknown user";
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen else if (**fields_r != NULL) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen *error_r = t_strdup(**fields_r);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen ret = -2;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen } else {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen *error_r = MAIL_ERRSTR_CRITICAL_MSG;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return ret;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen}
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstatic bool parse_uid(const char *str, uid_t *uid_r, const char **error_r)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen{
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct passwd pw;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (str_to_uid(str, uid_r) == 0)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return TRUE;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen switch (i_getpwnam(str, &pw)) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen case -1:
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen *error_r = t_strdup_printf("getpwnam(%s) failed: %m", str);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return FALSE;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen case 0:
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen *error_r = t_strconcat("Unknown UNIX UID user: ", str, NULL);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return FALSE;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen default:
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen *uid_r = pw.pw_uid;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return TRUE;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen}
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstatic bool parse_gid(const char *str, gid_t *gid_r, const char **error_r)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen{
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct group gr;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (str_to_gid(str, gid_r) == 0)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return TRUE;
641f0c0900ee6e7cf9667f4b40ed95cec7d0cdcaTimo Sirainen
641f0c0900ee6e7cf9667f4b40ed95cec7d0cdcaTimo Sirainen switch (i_getgrnam(str, &gr)) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen case -1:
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen *error_r = t_strdup_printf("getgrnam(%s) failed: %m", str);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return FALSE;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen case 0:
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen *error_r = t_strconcat("Unknown UNIX GID group: ", str, NULL);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return FALSE;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen default:
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen *gid_r = gr.gr_gid;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return TRUE;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen}
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
641f0c0900ee6e7cf9667f4b40ed95cec7d0cdcaTimo Sirainenstatic const struct var_expand_table *
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenget_var_expand_table(struct master_service *service,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct mail_storage_service_user *user,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen const struct mail_storage_service_input *input,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen const struct mail_storage_service_privileges *priv)
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen{
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen static struct var_expand_table static_tab[] = {
d67f54632110cfb6aafe2d7cd1f99b031c0b208aTimo Sirainen { 'u', NULL, "user" },
d67f54632110cfb6aafe2d7cd1f99b031c0b208aTimo Sirainen { 'n', NULL, "username" },
d67f54632110cfb6aafe2d7cd1f99b031c0b208aTimo Sirainen { 'd', NULL, "domain" },
d67f54632110cfb6aafe2d7cd1f99b031c0b208aTimo Sirainen { 's', NULL, "service" },
d67f54632110cfb6aafe2d7cd1f99b031c0b208aTimo Sirainen { 'l', NULL, "lip" },
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen { 'r', NULL, "rip" },
d67f54632110cfb6aafe2d7cd1f99b031c0b208aTimo Sirainen { 'p', NULL, "pid" },
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen { 'i', NULL, "uid" },
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen { '\0', NULL, "gid" },
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen { '\0', NULL, "session" },
641f0c0900ee6e7cf9667f4b40ed95cec7d0cdcaTimo Sirainen { '\0', NULL, "auth_user" },
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen { '\0', NULL, "auth_username" },
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen { '\0', NULL, "auth_domain" },
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen { '\0', NULL, NULL }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen };
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct var_expand_table *tab;
ead79af955bccc360de08c150918474c1809748eTimo Sirainen
ead79af955bccc360de08c150918474c1809748eTimo Sirainen tab = t_malloc(sizeof(static_tab));
641f0c0900ee6e7cf9667f4b40ed95cec7d0cdcaTimo Sirainen memcpy(tab, static_tab, sizeof(static_tab));
d67f54632110cfb6aafe2d7cd1f99b031c0b208aTimo Sirainen
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen tab[0].value = input->username;
641f0c0900ee6e7cf9667f4b40ed95cec7d0cdcaTimo Sirainen tab[1].value = t_strcut(input->username, '@');
641f0c0900ee6e7cf9667f4b40ed95cec7d0cdcaTimo Sirainen tab[2].value = strchr(input->username, '@');
641f0c0900ee6e7cf9667f4b40ed95cec7d0cdcaTimo Sirainen if (tab[2].value != NULL) tab[2].value++;
641f0c0900ee6e7cf9667f4b40ed95cec7d0cdcaTimo Sirainen tab[3].value = service->name;
641f0c0900ee6e7cf9667f4b40ed95cec7d0cdcaTimo Sirainen tab[4].value = net_ip2addr(&input->local_ip);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen tab[5].value = net_ip2addr(&input->remote_ip);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen tab[6].value = my_pid;
641f0c0900ee6e7cf9667f4b40ed95cec7d0cdcaTimo Sirainen tab[7].value = priv == NULL ? NULL :
641f0c0900ee6e7cf9667f4b40ed95cec7d0cdcaTimo Sirainen dec2str(priv->uid == (uid_t)-1 ? geteuid() : priv->uid);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen tab[8].value = priv == NULL ? NULL :
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen dec2str(priv->gid == (gid_t)-1 ? getegid() : priv->gid);
641f0c0900ee6e7cf9667f4b40ed95cec7d0cdcaTimo Sirainen tab[9].value = input->session_id;
641f0c0900ee6e7cf9667f4b40ed95cec7d0cdcaTimo Sirainen if (user == NULL || user->auth_user == NULL) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen tab[10].value = tab[0].value;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen tab[11].value = tab[1].value;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen tab[12].value = tab[2].value;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen } else {
641f0c0900ee6e7cf9667f4b40ed95cec7d0cdcaTimo Sirainen tab[10].value = user->auth_user;
641f0c0900ee6e7cf9667f4b40ed95cec7d0cdcaTimo Sirainen tab[11].value = t_strcut(user->auth_user, '@');
641f0c0900ee6e7cf9667f4b40ed95cec7d0cdcaTimo Sirainen tab[12].value = strchr(user->auth_user, '@');
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return tab;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen}
641f0c0900ee6e7cf9667f4b40ed95cec7d0cdcaTimo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenconst struct var_expand_table *
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenmail_storage_service_get_var_expand_table(struct mail_storage_service_ctx *ctx,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct mail_storage_service_input *input)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen{
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct mail_storage_service_privileges priv;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen memset(&priv, 0, sizeof(priv));
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen priv.uid = (uid_t)-1;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen priv.gid = (gid_t)-1;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return get_var_expand_table(ctx->service, NULL, input, &priv);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen}
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstatic const char *
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenuser_expand_varstr(struct mail_storage_service_ctx *ctx,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct mail_storage_service_user *user,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct mail_storage_service_privileges *priv,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen const char *str)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen{
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen string_t *ret;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (*str == SETTING_STRVAR_EXPANDED[0])
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return str + 1;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
b7b81543899e306c71e6152516d8698416162bcbTimo Sirainen i_assert(*str == SETTING_STRVAR_UNEXPANDED[0]);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen ret = t_str_new(256);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen mail_storage_service_var_expand(ctx, ret, str + 1, user, &user->input, priv);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return str_c(ret);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen}
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstatic int
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenservice_parse_privileges(struct mail_storage_service_ctx *ctx,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct mail_storage_service_user *user,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct mail_storage_service_privileges *priv_r,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen const char **error_r)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen{
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen const struct mail_user_settings *set = user->user_set;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen uid_t uid = (uid_t)-1;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen gid_t gid = (gid_t)-1;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen memset(priv_r, 0, sizeof(*priv_r));
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (*set->mail_uid != '\0') {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (!parse_uid(set->mail_uid, &uid, error_r)) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen *error_r = t_strdup_printf("%s (from %s)", *error_r,
b7b81543899e306c71e6152516d8698416162bcbTimo Sirainen user->uid_source);
b7b81543899e306c71e6152516d8698416162bcbTimo Sirainen return -1;
b7b81543899e306c71e6152516d8698416162bcbTimo Sirainen }
b7b81543899e306c71e6152516d8698416162bcbTimo Sirainen if (uid < (uid_t)set->first_valid_uid ||
b7b81543899e306c71e6152516d8698416162bcbTimo Sirainen (set->last_valid_uid != 0 &&
b7b81543899e306c71e6152516d8698416162bcbTimo Sirainen uid > (uid_t)set->last_valid_uid)) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen *error_r = t_strdup_printf(
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen "Mail access for users with UID %s not permitted "
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen "(see first_valid_uid in config file, uid from %s).",
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen dec2str(uid), user->uid_source);
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen return -1;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
b7b81543899e306c71e6152516d8698416162bcbTimo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen priv_r->uid = uid;
033557e1c8ebec5ae31f2f24fab90226a1945168Timo Sirainen priv_r->uid_source = user->uid_source;
033557e1c8ebec5ae31f2f24fab90226a1945168Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (*set->mail_gid != '\0') {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (!parse_gid(set->mail_gid, &gid, error_r)) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen *error_r = t_strdup_printf("%s (from %s)", *error_r,
d67f54632110cfb6aafe2d7cd1f99b031c0b208aTimo Sirainen user->gid_source);
d67f54632110cfb6aafe2d7cd1f99b031c0b208aTimo Sirainen return -1;
d67f54632110cfb6aafe2d7cd1f99b031c0b208aTimo Sirainen }
d67f54632110cfb6aafe2d7cd1f99b031c0b208aTimo Sirainen if (gid < (gid_t)set->first_valid_gid ||
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen (set->last_valid_gid != 0 &&
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen gid > (gid_t)set->last_valid_gid)) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen *error_r = t_strdup_printf(
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen "Mail access for users with GID %s not permitted "
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen "(see first_valid_gid in config file, gid from %s).",
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen dec2str(gid), user->gid_source);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return -1;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
fd3d711f219fd6813492acbe051e04327f0ca0f0Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen priv_r->gid = gid;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen priv_r->gid_source = user->gid_source;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* variable strings are expanded in mail_user_init(),
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen but we need the home and chroot sooner so do them separately here. */
b7b81543899e306c71e6152516d8698416162bcbTimo Sirainen priv_r->home = user_expand_varstr(ctx, user, priv_r,
b7b81543899e306c71e6152516d8698416162bcbTimo Sirainen user->user_set->mail_home);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen priv_r->chroot = user_expand_varstr(ctx, user, priv_r,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen user->user_set->mail_chroot);
d8b77aef97e89f1ccc5cbdaef77be9052279e35fTimo Sirainen return 0;
b7b81543899e306c71e6152516d8698416162bcbTimo Sirainen}
31ddc75584c5cde53d2e78a737587f2e7fdcb0d2Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstatic void mail_storage_service_seteuid_root(void)
fd3d711f219fd6813492acbe051e04327f0ca0f0Timo Sirainen{
fd3d711f219fd6813492acbe051e04327f0ca0f0Timo Sirainen if (seteuid(0) < 0) {
fd3d711f219fd6813492acbe051e04327f0ca0f0Timo Sirainen i_fatal("mail-storage-service: "
fd3d711f219fd6813492acbe051e04327f0ca0f0Timo Sirainen "Failed to restore temporarily dropped root privileges: "
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen "seteuid(0) failed: %m");
0add8c99ca65e56dbf613595fc37c41aafff3f7fTimo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen}
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
fd3d711f219fd6813492acbe051e04327f0ca0f0Timo Sirainenstatic int
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenservice_drop_privileges(struct mail_storage_service_user *user,
31ddc75584c5cde53d2e78a737587f2e7fdcb0d2Timo Sirainen struct mail_storage_service_privileges *priv,
31ddc75584c5cde53d2e78a737587f2e7fdcb0d2Timo Sirainen bool disallow_root, bool keep_setuid_root,
31ddc75584c5cde53d2e78a737587f2e7fdcb0d2Timo Sirainen bool setenv_only, const char **error_r)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen{
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen const struct mail_user_settings *set = user->user_set;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct restrict_access_settings rset;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen uid_t current_euid, setuid_uid = 0;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen const char *cur_chroot, *error;
d8b77aef97e89f1ccc5cbdaef77be9052279e35fTimo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen current_euid = geteuid();
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen restrict_access_init(&rset);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen restrict_access_get_env(&rset);
b7b81543899e306c71e6152516d8698416162bcbTimo Sirainen if (priv->uid != (uid_t)-1) {
b7b81543899e306c71e6152516d8698416162bcbTimo Sirainen rset.uid = priv->uid;
b7b81543899e306c71e6152516d8698416162bcbTimo Sirainen rset.uid_source = priv->uid_source;
b7b81543899e306c71e6152516d8698416162bcbTimo Sirainen } else if (rset.uid == (uid_t)-1 &&
b7b81543899e306c71e6152516d8698416162bcbTimo Sirainen disallow_root && current_euid == 0) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen *error_r = "User is missing UID (see mail_uid setting)";
d67f54632110cfb6aafe2d7cd1f99b031c0b208aTimo Sirainen return -1;
d67f54632110cfb6aafe2d7cd1f99b031c0b208aTimo Sirainen }
d67f54632110cfb6aafe2d7cd1f99b031c0b208aTimo Sirainen if (priv->gid != (gid_t)-1) {
d67f54632110cfb6aafe2d7cd1f99b031c0b208aTimo Sirainen rset.gid = priv->gid;
d67f54632110cfb6aafe2d7cd1f99b031c0b208aTimo Sirainen rset.gid_source = priv->gid_source;
d67f54632110cfb6aafe2d7cd1f99b031c0b208aTimo Sirainen } else if (rset.gid == (gid_t)-1 && disallow_root &&
d67f54632110cfb6aafe2d7cd1f99b031c0b208aTimo Sirainen set->first_valid_gid > 0 && getegid() == 0) {
d67f54632110cfb6aafe2d7cd1f99b031c0b208aTimo Sirainen *error_r = "User is missing GID (see mail_gid setting)";
d67f54632110cfb6aafe2d7cd1f99b031c0b208aTimo Sirainen return -1;
d67f54632110cfb6aafe2d7cd1f99b031c0b208aTimo Sirainen }
d67f54632110cfb6aafe2d7cd1f99b031c0b208aTimo Sirainen if (*set->mail_privileged_group != '\0') {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (!parse_gid(set->mail_privileged_group, &rset.privileged_gid,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen &error)) {
d8b77aef97e89f1ccc5cbdaef77be9052279e35fTimo Sirainen *error_r = t_strdup_printf(
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen "%s (in mail_privileged_group setting)", error);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return -1;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (*set->mail_access_groups != '\0') {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen rset.extra_groups = t_strconcat(set->mail_access_groups, ",",
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen rset.extra_groups, NULL);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen rset.first_valid_gid = set->first_valid_gid;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen rset.last_valid_gid = set->last_valid_gid;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen rset.chroot_dir = *priv->chroot == '\0' ? NULL : priv->chroot;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen rset.system_groups_user = user->system_groups_user;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen cur_chroot = restrict_access_get_current_chroot();
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (cur_chroot != NULL) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* we're already chrooted. make sure the chroots are equal. */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (rset.chroot_dir == NULL) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen *error_r = "Process is already chrooted, "
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen "can't un-chroot for this user";
d67f54632110cfb6aafe2d7cd1f99b031c0b208aTimo Sirainen return -1;
d67f54632110cfb6aafe2d7cd1f99b031c0b208aTimo Sirainen }
685393de106e55b61f754d420e378d05bd462ebbTimo Sirainen if (strcmp(rset.chroot_dir, cur_chroot) != 0) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen *error_r = t_strdup_printf(
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen "Process is already chrooted to %s, "
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen "can't chroot to %s", cur_chroot, priv->chroot);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return -1;
fd3d711f219fd6813492acbe051e04327f0ca0f0Timo Sirainen }
fd3d711f219fd6813492acbe051e04327f0ca0f0Timo Sirainen /* chrooting to same directory where we're already chrooted */
fd3d711f219fd6813492acbe051e04327f0ca0f0Timo Sirainen rset.chroot_dir = NULL;
fd3d711f219fd6813492acbe051e04327f0ca0f0Timo Sirainen }
fd3d711f219fd6813492acbe051e04327f0ca0f0Timo Sirainen
fd3d711f219fd6813492acbe051e04327f0ca0f0Timo Sirainen if (disallow_root &&
d8b77aef97e89f1ccc5cbdaef77be9052279e35fTimo Sirainen (rset.uid == 0 || (rset.uid == (uid_t)-1 && current_euid == 0))) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen *error_r = "Mail access not allowed for root";
d8b77aef97e89f1ccc5cbdaef77be9052279e35fTimo Sirainen return -1;
d8b77aef97e89f1ccc5cbdaef77be9052279e35fTimo Sirainen }
d67f54632110cfb6aafe2d7cd1f99b031c0b208aTimo Sirainen
d67f54632110cfb6aafe2d7cd1f99b031c0b208aTimo Sirainen if (keep_setuid_root) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (current_euid != rset.uid) {
d8b77aef97e89f1ccc5cbdaef77be9052279e35fTimo Sirainen if (current_euid != 0) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* we're changing the UID,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen switch back to root first */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen mail_storage_service_seteuid_root();
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
b7b81543899e306c71e6152516d8698416162bcbTimo Sirainen setuid_uid = rset.uid;
b7b81543899e306c71e6152516d8698416162bcbTimo Sirainen }
b7b81543899e306c71e6152516d8698416162bcbTimo Sirainen rset.uid = (uid_t)-1;
b7b81543899e306c71e6152516d8698416162bcbTimo Sirainen disallow_root = FALSE;
b7b81543899e306c71e6152516d8698416162bcbTimo Sirainen }
b7b81543899e306c71e6152516d8698416162bcbTimo Sirainen if (!setenv_only) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen restrict_access(&rset, *priv->home == '\0' ? NULL : priv->home,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen disallow_root);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen } else {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen restrict_access_set_env(&rset);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (setuid_uid != 0 && !setenv_only) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (seteuid(setuid_uid) < 0)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen i_fatal("mail-storage-service: seteuid(%s) failed: %m",
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen dec2str(setuid_uid));
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return 0;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen}
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstatic int
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenmail_storage_service_init_post(struct mail_storage_service_ctx *ctx,
fd3d711f219fd6813492acbe051e04327f0ca0f0Timo Sirainen struct mail_storage_service_user *user,
fd3d711f219fd6813492acbe051e04327f0ca0f0Timo Sirainen struct mail_storage_service_privileges *priv,
fd3d711f219fd6813492acbe051e04327f0ca0f0Timo Sirainen struct mail_user **mail_user_r,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen const char **error_r)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen{
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen const struct mail_storage_settings *mail_set;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen const char *home = priv->home;
b7b81543899e306c71e6152516d8698416162bcbTimo Sirainen struct mail_user *mail_user;
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen
fd3d711f219fd6813492acbe051e04327f0ca0f0Timo Sirainen /* NOTE: if more user initialization is added, add it also to
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen mail_user_dup() */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen mail_user = mail_user_alloc(user->input.username, user->user_info,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen user->user_set);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen mail_user->_service_user = user;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen mail_user_set_home(mail_user, *home == '\0' ? NULL : home);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen mail_user_set_vars(mail_user, ctx->service->name,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen &user->input.local_ip, &user->input.remote_ip);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen mail_user->uid = priv->uid == (uid_t)-1 ? geteuid() : priv->uid;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen mail_user->gid = priv->gid == (gid_t)-1 ? getegid() : priv->gid;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen mail_user->anonymous = user->anonymous;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen mail_user->admin = user->admin;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen mail_user->auth_token = p_strdup(mail_user->pool, user->auth_token);
641f0c0900ee6e7cf9667f4b40ed95cec7d0cdcaTimo Sirainen mail_user->auth_user = p_strdup(mail_user->pool, user->auth_user);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen mail_user->session_id =
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen p_strdup(mail_user->pool, user->input.session_id);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen mail_user->userdb_fields = user->input.userdb_fields == NULL ? NULL :
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen p_strarray_dup(mail_user->pool, user->input.userdb_fields);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen mail_user->autoexpunge_enabled =
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen (user->flags & MAIL_STORAGE_SERVICE_FLAG_AUTOEXPUNGE) != 0;
641f0c0900ee6e7cf9667f4b40ed95cec7d0cdcaTimo Sirainen
641f0c0900ee6e7cf9667f4b40ed95cec7d0cdcaTimo Sirainen mail_set = mail_user_set_get_storage_set(mail_user);
641f0c0900ee6e7cf9667f4b40ed95cec7d0cdcaTimo Sirainen
b7b81543899e306c71e6152516d8698416162bcbTimo Sirainen if (mail_set->mail_debug) {
b7b81543899e306c71e6152516d8698416162bcbTimo Sirainen string_t *str = t_str_new(64);
b7b81543899e306c71e6152516d8698416162bcbTimo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen str_printfa(str, "Effective uid=%s, gid=%s, home=%s",
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen dec2str(geteuid()), dec2str(getegid()), home);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (*priv->chroot != '\0')
b7b81543899e306c71e6152516d8698416162bcbTimo Sirainen str_printfa(str, ", chroot=%s", priv->chroot);
b7b81543899e306c71e6152516d8698416162bcbTimo Sirainen i_debug("%s", str_c(str));
b7b81543899e306c71e6152516d8698416162bcbTimo Sirainen }
b7b81543899e306c71e6152516d8698416162bcbTimo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if ((user->flags & MAIL_STORAGE_SERVICE_FLAG_TEMP_PRIV_DROP) != 0 &&
d67f54632110cfb6aafe2d7cd1f99b031c0b208aTimo Sirainen (user->flags & MAIL_STORAGE_SERVICE_FLAG_ENABLE_CORE_DUMPS) == 0) {
d67f54632110cfb6aafe2d7cd1f99b031c0b208aTimo Sirainen /* we don't want to write core files to any users' home
d67f54632110cfb6aafe2d7cd1f99b031c0b208aTimo Sirainen directories since they could contain information about other
d67f54632110cfb6aafe2d7cd1f99b031c0b208aTimo Sirainen users' mails as well. so do no chdiring to home. */
d67f54632110cfb6aafe2d7cd1f99b031c0b208aTimo Sirainen } else if (*home != '\0' &&
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen (user->flags & MAIL_STORAGE_SERVICE_FLAG_NO_CHDIR) == 0) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* If possible chdir to home directory, so that core file
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen could be written in case we crash. */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (chdir(home) < 0) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (errno == EACCES) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen i_error("%s", eacces_error_get("chdir",
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen t_strconcat(home, "/", NULL)));
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen } if (errno != ENOENT)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen i_error("chdir(%s) failed: %m", home);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen else if (mail_set->mail_debug)
b7b81543899e306c71e6152516d8698416162bcbTimo Sirainen i_debug("Home dir not found: %s", home);
b7b81543899e306c71e6152516d8698416162bcbTimo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (mail_user_init(mail_user, error_r) < 0) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen mail_user_unref(&mail_user);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return -1;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if ((user->flags & MAIL_STORAGE_SERVICE_FLAG_NO_NAMESPACES) == 0) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (mail_namespaces_init(mail_user, error_r) < 0) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen mail_user_unref(&mail_user);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return -1;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen *mail_user_r = mail_user;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return 0;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen}
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenvoid mail_storage_service_io_activate_user(struct mail_storage_service_user *user)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen{
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen i_set_failure_prefix("%s", user->log_prefix);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen}
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenvoid mail_storage_service_io_deactivate_user(struct mail_storage_service_user *user)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen{
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen i_set_failure_prefix("%s", user->service_ctx->default_log_prefix);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen}
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenvoid mail_storage_service_io_deactivate(struct mail_storage_service_ctx *ctx)
641f0c0900ee6e7cf9667f4b40ed95cec7d0cdcaTimo Sirainen{
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen i_set_failure_prefix("%s", ctx->default_log_prefix);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen}
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstatic const char *field_get_default(const char *data)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen{
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen const char *p;
641f0c0900ee6e7cf9667f4b40ed95cec7d0cdcaTimo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen p = strchr(data, ':');
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (p == NULL)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return "";
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen else {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* default value given */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return p+1;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen}
const char *mail_storage_service_fields_var_expand(const char *data,
const char *const *fields)
{
const char *field_name = t_strcut(data, ':');
unsigned int i, field_name_len;
if (fields == NULL)
return field_get_default(data);
field_name_len = strlen(field_name);
for (i = 0; fields[i] != NULL; i++) {
if (strncmp(fields[i], field_name, field_name_len) == 0 &&
fields[i][field_name_len] == '=')
return fields[i] + field_name_len+1;
}
return field_get_default(data);
}
static const char *
mail_storage_service_input_var_userdb(const char *data, void *context)
{
struct mail_storage_service_user *user = context;
return mail_storage_service_fields_var_expand(data,
user == NULL ? NULL : user->input.userdb_fields);
}
static void
mail_storage_service_var_expand(struct mail_storage_service_ctx *ctx,
string_t *str, const char *format,
struct mail_storage_service_user *user,
const struct mail_storage_service_input *input,
const struct mail_storage_service_privileges *priv)
{
static const struct var_expand_func_table func_table[] = {
{ "userdb", mail_storage_service_input_var_userdb },
{ NULL, NULL }
};
var_expand_with_funcs(str, format,
get_var_expand_table(ctx->service, user, input, priv),
func_table, user);
}
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);
mail_storage_service_var_expand(ctx, str,
user->user_set->mail_log_prefix,
user, &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);
io_loop_context_add_callbacks(user->ioloop_ctx,
mail_storage_service_io_activate_user,
mail_storage_service_io_deactivate_user,
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;
const char *version;
pool_t pool;
unsigned int count;
version = master_service_get_version_string(service);
if (version != NULL && strcmp(version, PACKAGE_VERSION) != 0) {
i_fatal("Version mismatch: libdovecot-storage.so is '%s', "
"while the running Dovecot binary is '%s'",
PACKAGE_VERSION, version);
}
if ((flags & MAIL_STORAGE_SERVICE_FLAG_TEMP_PRIV_DROP) != 0 &&
geteuid() != 0) {
/* service { user } isn't root. the permission drop can't be
temporary. */
flags &= ~MAIL_STORAGE_SERVICE_FLAG_TEMP_PRIV_DROP;
}
(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;
ctx->config_permission_denied = FALSE;
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 (input == NULL) {
/* global settings read - don't create a cache for thi */
} else 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 &&
ctx->set_cache != NULL) {
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);
ctx->config_permission_denied =
set_output.permission_denied;
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;
}
void mail_storage_service_set_auth_conn(struct mail_storage_service_ctx *ctx,
struct auth_master_connection *conn)
{
i_assert(ctx->conn == NULL);
i_assert(mail_user_auth_master_conn == NULL);
ctx->conn = conn;
mail_user_auth_master_conn = conn;
}
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;
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;
mail_storage_service_set_auth_conn(ctx,
auth_master_init(user_set->auth_socket_path, flags));
}
static int
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,
const char **error_r)
{
struct module_dir_load_settings mod_set;
if (*user_set->mail_plugins == '\0')
return 0;
if ((ctx->flags & MAIL_STORAGE_SERVICE_FLAG_NO_PLUGINS) != 0)
return 0;
memset(&mod_set, 0, sizeof(mod_set));
mod_set.abi_version = DOVECOT_ABI_VERSION;
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);
return module_dir_try_load_missing(&mail_storage_service_modules,
user_set->mail_plugin_dir,
user_set->mail_plugins,
&mod_set, error_r);
}
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;
}
static void
mail_storage_service_set_log_prefix(struct mail_storage_service_ctx *ctx,
const struct mail_user_settings *user_set,
struct mail_storage_service_user *user,
const struct mail_storage_service_input *input,
const struct mail_storage_service_privileges *priv)
{
string_t *str;
str = t_str_new(256);
mail_storage_service_var_expand(ctx, str, user_set->mail_log_prefix,
user, input, priv);
i_set_failure_prefix("%s", str_c(str));
}
static const char *
mail_storage_service_generate_session_id(pool_t pool, const char *prefix)
{
guid_128_t guid;
unsigned int prefix_len = prefix == NULL ? 0 : strlen(prefix);
string_t *str = str_new(pool, MAX_BASE64_ENCODED_SIZE(prefix_len + 1 + sizeof(guid)));
if (prefix != NULL)
str_printfa(str, "%s-", prefix);
guid_128_generate(guid);
base64_encode(guid, sizeof(guid), str);
/* remove the trailing "==" */
i_assert(str_data(str)[str_len(str)-2] == '=');
str_truncate(str, str_len(str)-2);
return str_c(str);
}
static int
mail_storage_service_lookup_real(struct mail_storage_service_ctx *ctx,
const struct mail_storage_service_input *input,
bool update_log_prefix,
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;
void **sets;
pool_t user_pool, temp_pool;
int ret = 1;
user_pool = pool_alloconly_create(MEMPOOL_GROWING"mail storage service user", 1024*6);
flags = mail_storage_service_input_get_flags(ctx, input);
if ((flags & MAIL_STORAGE_SERVICE_FLAG_TEMP_PRIV_DROP) != 0 &&
geteuid() != 0) {
/* we dropped privileges only temporarily. switch back to root
before reading settings, so we'll definitely have enough
permissions to connect to the config socket. */
mail_storage_service_seteuid_root();
}
if (mail_storage_service_read_settings(ctx, input, user_pool,
&user_info, &set_parser,
&error) < 0) {
if (ctx->config_permission_denied) {
/* just restart and maybe next time we will open the
config socket before dropping privileges */
i_fatal("%s", error);
}
i_error("%s", error);
pool_unref(&user_pool);
*error_r = MAIL_ERRSTR_CRITICAL_MSG;
return -1;
}
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));
update_log_prefix = TRUE;
}
sets = master_service_settings_parser_get_others(master_service,
set_parser);
user_set = sets[0];
if (update_log_prefix)
mail_storage_service_set_log_prefix(ctx, user_set, NULL, input, NULL);
if (ctx->conn == NULL)
mail_storage_service_first_init(ctx, user_info, user_set);
/* load global plugins */
if (mail_storage_service_load_modules(ctx, user_info, user_set, &error) < 0) {
i_error("%s", error);
pool_unref(&user_pool);
*error_r = MAIL_ERRSTR_CRITICAL_MSG;
return -1;
}
if (ctx->userdb_next_pool == NULL)
temp_pool = pool_alloconly_create("userdb lookup", 2048);
else {
temp_pool = ctx->userdb_next_pool;
ctx->userdb_next_pool = NULL;
pool_ref(temp_pool);
}
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;
}
if (ctx->userdb_next_fieldsp != NULL)
*ctx->userdb_next_fieldsp = userdb_fields;
} 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 = userdb_fields == NULL ? NULL :
p_strarray_dup(user_pool, userdb_fields);
user->input.username = p_strdup(user_pool, username);
user->input.session_id = p_strdup(user_pool, input->session_id);
if (user->input.session_id == NULL) {
user->input.session_id =
mail_storage_service_generate_session_id(user_pool,
input->session_id_prefix);
}
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);
sets = master_service_settings_parser_get_others(master_service,
user->set_parser);
user->user_set = sets[0];
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("Invalid settings in userdb: %s", error);
*error_r = ERRSTR_INVALID_USER_SETTINGS;
ret = -2;
}
}
pool_unref(&temp_pool);
/* load per-user plugins */
if (ret > 0) {
if (mail_storage_service_load_modules(ctx, user_info,
user->user_set,
&error) < 0) {
i_error("%s", error);
*error_r = MAIL_ERRSTR_CRITICAL_MSG;
ret = -2;
}
}
*user_r = user;
return ret;
}
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)
{
char *old_log_prefix = i_strdup(i_get_failure_prefix());
bool update_log_prefix;
int ret;
if (io_loop_get_current_context(current_ioloop) == NULL) {
/* no user yet. log prefix should be just "imap:" or something
equally unhelpful. we don't know the proper log format yet,
but initialize it to something better until we know it. */
const char *session_id =
input->session_id != NULL ? input->session_id :
(input->session_id_prefix != NULL ?
input->session_id_prefix : NULL);
i_set_failure_prefix("%s(%s%s,%s)",
master_service_get_name(ctx->service), input->username,
session_id == NULL ? "" : t_strdup_printf(",%s", session_id),
input->remote_ip.family == 0 ? "" :
t_strdup_printf(",%s", net_ip2addr(&input->remote_ip)));
update_log_prefix = TRUE;
} else {
/* we might be here because we're doing a user lookup for a
shared user. the log prefix is likely already usable, so
just append our own without replacing the whole thing. */
i_set_failure_prefix("%suser-lookup(%s)",
old_log_prefix, input->username);
update_log_prefix = FALSE;
}
ret = mail_storage_service_lookup_real(ctx, input, update_log_prefix,
user_r, error_r);
i_set_failure_prefix("%s", old_log_prefix);
i_free(old_log_prefix);
return ret;
}
void mail_storage_service_save_userdb_fields(struct mail_storage_service_ctx *ctx,
pool_t pool, const char *const **userdb_fields_r)
{
i_assert(pool != NULL);
i_assert(userdb_fields_r != NULL);
ctx->userdb_next_pool = pool;
ctx->userdb_next_fieldsp = userdb_fields_r;
*userdb_fields_r = NULL;
}
static int
mail_storage_service_next_real(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("%s", error);
return -2;
}
if (*priv.home != '/' && *priv.home != '\0') {
i_error("Relative home directory paths not supported: %s",
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);
}
/* create ioloop context regardless of logging. it's also used by
stats plugin. */
user->ioloop_ctx = io_loop_context_new(current_ioloop);
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("Couldn't drop privileges: %s", 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 initialization failed: %s", error);
return -2;
}
return 0;
}
int mail_storage_service_next(struct mail_storage_service_ctx *ctx,
struct mail_storage_service_user *user,
struct mail_user **mail_user_r)
{
char *old_log_prefix = i_strdup(i_get_failure_prefix());
int ret;
mail_storage_service_set_log_prefix(ctx, user->user_set, user,
&user->input, NULL);
i_set_failure_prefix("%s", old_log_prefix);
ret = mail_storage_service_next_real(ctx, user, mail_user_r);
if ((user->flags & MAIL_STORAGE_SERVICE_FLAG_NO_LOG_INIT) != 0)
i_set_failure_prefix("%s", old_log_prefix);
i_free(old_log_prefix);
return ret;
}
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) {
if ((user->flags & MAIL_STORAGE_SERVICE_FLAG_NO_LOG_INIT) == 0) {
io_loop_context_remove_callbacks(user->ioloop_ctx,
mail_storage_service_io_activate_user,
mail_storage_service_io_deactivate_user, user);
if (io_loop_get_current_context(current_ioloop) == user->ioloop_ctx)
mail_storage_service_io_deactivate_user(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;
void **sets;
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);
sets = master_service_settings_parser_get_others(master_service,
set_parser);
user_set = sets[0];
mail_storage_service_first_init(ctx, user_info, user_set);
pool_unref(&temp_pool);
}
static int
mail_storage_service_all_iter_deinit(struct mail_storage_service_ctx *ctx)
{
int ret = 0;
if (ctx->auth_list != NULL) {
ret = auth_master_user_list_deinit(&ctx->auth_list);
auth_master_deinit(&ctx->iter_conn);
}
return ret;
}
void mail_storage_service_all_init(struct mail_storage_service_ctx *ctx)
{
enum auth_master_flags flags = 0;
(void)mail_storage_service_all_iter_deinit(ctx);
mail_storage_service_init_settings(ctx, NULL);
/* create a new connection, because the iteration might take a while
and we might want to do USER lookups during it, which don't mix
well in the same connection. */
if (ctx->debug)
flags |= AUTH_MASTER_FLAG_DEBUG;
ctx->iter_conn = auth_master_init(auth_master_get_socket_path(ctx->conn),
flags);
ctx->auth_list = auth_master_user_list_init(ctx->iter_conn, "", NULL);
}
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 mail_storage_service_all_iter_deinit(ctx);
}
void mail_storage_service_deinit(struct mail_storage_service_ctx **_ctx)
{
struct mail_storage_service_ctx *ctx = *_ctx;
*_ctx = NULL;
(void)mail_storage_service_all_iter_deinit(ctx);
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 master_service_settings_parser_get_others(master_service,
user->set_parser);
}
const struct mail_storage_settings *
mail_storage_service_user_get_mail_set(struct mail_storage_service_user *user)
{
return mail_user_set_get_driver_settings(
user->user_info, user->user_set,
MAIL_STORAGE_SET_DRIVER_NAME);
}
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;
}
struct mail_storage_service_ctx *
mail_storage_service_user_get_service_ctx(struct mail_storage_service_user *user)
{
return user->service_ctx;
}
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;
}