mail-storage-service.c revision a48ccadf62d342063d0fff00fadc791423cc915f
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen/* Copyright (c) 2009-2015 Dovecot authors, see the included COPYING file */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen#include "lib.h"
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen#include "ioloop.h"
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen#include "array.h"
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen#include "hostpid.h"
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen#include "module-dir.h"
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen#include "restrict-access.h"
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen#include "eacces-error.h"
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen#include "ipwd.h"
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen#include "str.h"
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen#include "var-expand.h"
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen#include "dict.h"
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen#include "settings-parser.h"
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen#include "auth-master.h"
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen#include "master-service-private.h"
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen#include "master-service-settings.h"
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen#include "master-service-settings-cache.h"
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen#include "mail-user.h"
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen#include "mail-namespace.h"
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen#include "mail-storage.h"
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen#include "mail-storage-service.h"
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen#include <stdlib.h>
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen#include <sys/stat.h>
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen#ifdef HAVE_SYS_TIME_H
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen# include <sys/time.h>
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen#endif
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen#ifdef HAVE_SYS_RESOURCE_H
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen# include <sys/resource.h>
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen#endif
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen/* If time moves backwards more than this, kill ourself instead of sleeping. */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen#define MAX_TIME_BACKWARDS_SLEEP 5
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen#define MAX_NOWARN_FORWARD_SECS 10
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen#define ERRSTR_INVALID_USER_SETTINGS \
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen "Invalid user settings. Refer to server log for more information."
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenstruct mail_storage_service_privileges {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen uid_t uid;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen gid_t gid;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen const char *uid_source, *gid_source;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen const char *home;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen const char *chroot;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen};
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenstruct mail_storage_service_ctx {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen pool_t pool;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen struct master_service *service;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen const char *default_log_prefix;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen struct auth_master_connection *conn, *iter_conn;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen struct auth_master_user_list_ctx *auth_list;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen const struct setting_parser_info **set_roots;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen enum mail_storage_service_flags flags;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen const char *set_cache_module, *set_cache_service;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen struct master_service_settings_cache *set_cache;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen pool_t userdb_next_pool;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen const char *const **userdb_next_fieldsp;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen unsigned int debug:1;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen unsigned int log_initialized:1;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen unsigned int config_permission_denied:1;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen};
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenstruct mail_storage_service_user {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen pool_t pool;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen struct mail_storage_service_ctx *service_ctx;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen struct mail_storage_service_input input;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen enum mail_storage_service_flags flags;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen struct ioloop_context *ioloop_ctx;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen const char *log_prefix, *auth_token, *auth_user;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen const char *system_groups_user, *uid_source, *gid_source;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen const struct mail_user_settings *user_set;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen const struct setting_parser_info *user_info;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen struct setting_parser_context *set_parser;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen unsigned int anonymous:1;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen unsigned int admin:1;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen};
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenstruct module *mail_storage_service_modules = NULL;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenstatic bool
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenmail_user_set_get_mail_debug(const struct setting_parser_info *user_info,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen const struct mail_user_settings *user_set)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen{
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen const struct mail_storage_settings *mail_set;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen mail_set = mail_user_set_get_driver_settings(user_info, user_set,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen MAIL_STORAGE_SET_DRIVER_NAME);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return mail_set->mail_debug;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen}
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenstatic void set_keyval(struct mail_storage_service_ctx *ctx,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen struct mail_storage_service_user *user,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen const char *key, const char *value)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen{
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen struct setting_parser_context *set_parser = user->set_parser;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen const char *str;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (master_service_set_has_config_override(ctx->service, key)) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* this setting was already overridden with -o parameter */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (mail_user_set_get_mail_debug(user->user_info,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen user->user_set)) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen i_debug("Ignoring overridden (-o) userdb setting: %s",
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen key);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen str = t_strconcat(key, "=", value, NULL);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (settings_parse_line(set_parser, str) < 0) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen i_fatal("Invalid userdb input '%s': %s", str,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen settings_parser_get_error(set_parser));
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen}
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenstatic int set_line(struct mail_storage_service_ctx *ctx,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen struct mail_storage_service_user *user,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen const char *line)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen{
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen struct setting_parser_context *set_parser = user->set_parser;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen bool mail_debug;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen const char *key, *orig_key, *append_value = NULL;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen unsigned int len;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen int ret;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen mail_debug = mail_user_set_get_mail_debug(user->user_info,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen user->user_set);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (strchr(line, '=') == NULL)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen line = t_strconcat(line, "=yes", NULL);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen orig_key = key = t_strcut(line, '=');
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen len = strlen(key);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (len > 0 && key[len-1] == '+') {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* key+=value */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen append_value = line + len + 1;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen key = t_strndup(key, len-1);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (!settings_parse_is_valid_key(set_parser, key)) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* assume it's a plugin setting */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen key = t_strconcat("plugin/", key, NULL);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen line = t_strconcat("plugin/", line, NULL);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (master_service_set_has_config_override(ctx->service, key)) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* this setting was already overridden with -o parameter */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (mail_debug) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen i_debug("Ignoring overridden (-o) userdb setting: %s",
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen key);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return 1;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (append_value != NULL) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen const void *value;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen enum setting_type type;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen value = settings_parse_get_value(set_parser, key, &type);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (type == SET_STR) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen const char *const *strp = value;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen line = t_strdup_printf("%s=%s%s",
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen key, *strp, append_value);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen } else {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen i_error("Ignoring %s userdb setting. "
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen "'+' can only be used for strings.", orig_key);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen ret = settings_parse_line(set_parser, line);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (mail_debug && ret >= 0) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (strstr(key, "pass") != NULL) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* possibly a password field (e.g. imapc_password).
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen hide the value. */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen line = t_strconcat(key, "=<hidden>", NULL);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen i_debug(ret == 0 ?
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen "Unknown userdb setting: %s" :
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen "Added userdb setting: %s", line);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return ret;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen}
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenstatic bool validate_chroot(const struct mail_user_settings *user_set,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen const char *dir)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen{
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen const char *const *chroot_dirs;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (*dir == '\0')
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return FALSE;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (*user_set->valid_chroot_dirs == '\0')
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return FALSE;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen chroot_dirs = t_strsplit(user_set->valid_chroot_dirs, ":");
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen while (*chroot_dirs != NULL) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (**chroot_dirs != '\0' &&
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen strncmp(dir, *chroot_dirs, strlen(*chroot_dirs)) == 0)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return TRUE;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen chroot_dirs++;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return FALSE;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen}
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenstatic int
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenuser_reply_handle(struct mail_storage_service_ctx *ctx,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen struct mail_storage_service_user *user,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen const struct auth_user_reply *reply,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen const char **error_r)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen{
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen const char *home = reply->home;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen const char *chroot = reply->chroot;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen const char *const *str, *line, *p;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen unsigned int i, count;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen int ret = 0;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (reply->uid != (uid_t)-1) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (reply->uid == 0) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen *error_r = "userdb returned 0 as uid";
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return -1;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen user->uid_source = "userdb lookup";
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen set_keyval(ctx, user, "mail_uid", dec2str(reply->uid));
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (reply->gid != (uid_t)-1) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen user->gid_source = "userdb lookup";
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen set_keyval(ctx, user, "mail_gid", dec2str(reply->gid));
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (home != NULL && chroot == NULL &&
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen *user->user_set->valid_chroot_dirs != '\0' &&
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen (p = strstr(home, "/./")) != NULL) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* wu-ftpd like <chroot>/./<home> - check only if there's even
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen a possibility of using them (non-empty valid_chroot_dirs) */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen chroot = t_strdup_until(home, p);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen home = p + 2;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (home != NULL)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen set_keyval(ctx, user, "mail_home", home);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (chroot != NULL) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (!validate_chroot(user->user_set, chroot)) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen *error_r = t_strdup_printf(
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen "userdb returned invalid chroot directory: %s "
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen "(see valid_chroot_dirs setting)", chroot);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return -1;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen set_keyval(ctx, user, "mail_chroot", chroot);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen user->anonymous = reply->anonymous;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen str = array_get(&reply->extra_fields, &count);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen for (i = 0; i < count; i++) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen line = str[i];
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (strncmp(line, "system_groups_user=", 19) == 0) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen user->system_groups_user =
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen p_strdup(user->pool, line + 19);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen } else if (strncmp(line, "nice=", 5) == 0) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen#ifdef HAVE_SETPRIORITY
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen int n = atoi(line + 5);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (n != 0) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (setpriority(PRIO_PROCESS, 0, n) < 0)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen i_error("setpriority(%d) failed: %m", n);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen#endif
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen } else if (strncmp(line, "auth_token=", 11) == 0) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen user->auth_token = p_strdup(user->pool, line+11);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen } else if (strncmp(line, "auth_user=", 10) == 0) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen user->auth_user = p_strdup(user->pool, line+10);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen } else if (strncmp(line, "admin=", 6) == 0) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen user->admin = line[6] == 'y' || line[6] == 'Y' ||
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen line[6] == '1';
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen } else T_BEGIN {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen ret = set_line(ctx, user, line);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen } T_END;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (ret < 0)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen break;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (ret < 0) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen *error_r = t_strdup_printf("Invalid userdb input '%s': %s",
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen str[i], settings_parser_get_error(user->set_parser));
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return ret;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen}
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenstatic int
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenservice_auth_userdb_lookup(struct mail_storage_service_ctx *ctx,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen const struct mail_storage_service_input *input,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen pool_t pool, const char **user,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen const char *const **fields_r,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen const char **error_r)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen{
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen struct auth_user_info info;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen const char *new_username;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen int ret;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen memset(&info, 0, sizeof(info));
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen info.service = input->service != NULL ? input->service :
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen ctx->service->name;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen info.local_ip = input->local_ip;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen info.remote_ip = input->remote_ip;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen info.local_port = input->local_port;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen info.remote_port = input->remote_port;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen ret = auth_master_user_lookup(ctx->conn, *user, &info, pool,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen &new_username, fields_r);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (ret > 0) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (strcmp(*user, new_username) != 0) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (ctx->debug)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen i_debug("changed username to %s", new_username);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen *user = t_strdup(new_username);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen *user = new_username;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen } else if (ret == 0)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen *error_r = "Unknown user";
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen else if (**fields_r != NULL) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen *error_r = t_strdup(**fields_r);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen ret = -2;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen } else {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen *error_r = MAIL_ERRSTR_CRITICAL_MSG;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return ret;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen}
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenstatic bool parse_uid(const char *str, uid_t *uid_r, const char **error_r)
4c6ddf2491104f917d00e6900e833e80ea02c7b6Timo Sirainen{
4c6ddf2491104f917d00e6900e833e80ea02c7b6Timo Sirainen struct passwd pw;
4c6ddf2491104f917d00e6900e833e80ea02c7b6Timo Sirainen
4c6ddf2491104f917d00e6900e833e80ea02c7b6Timo Sirainen if (str_to_uid(str, uid_r) == 0)
4c6ddf2491104f917d00e6900e833e80ea02c7b6Timo Sirainen return TRUE;
4c6ddf2491104f917d00e6900e833e80ea02c7b6Timo Sirainen
4c6ddf2491104f917d00e6900e833e80ea02c7b6Timo Sirainen switch (i_getpwnam(str, &pw)) {
4c6ddf2491104f917d00e6900e833e80ea02c7b6Timo Sirainen case -1:
4c6ddf2491104f917d00e6900e833e80ea02c7b6Timo Sirainen *error_r = t_strdup_printf("getpwnam(%s) failed: %m", str);
4c6ddf2491104f917d00e6900e833e80ea02c7b6Timo Sirainen return FALSE;
4c6ddf2491104f917d00e6900e833e80ea02c7b6Timo Sirainen case 0:
4c6ddf2491104f917d00e6900e833e80ea02c7b6Timo Sirainen *error_r = t_strconcat("Unknown UNIX UID user: ", str, NULL);
4c6ddf2491104f917d00e6900e833e80ea02c7b6Timo Sirainen return FALSE;
4c6ddf2491104f917d00e6900e833e80ea02c7b6Timo Sirainen default:
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen *uid_r = pw.pw_uid;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return TRUE;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen}
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenstatic bool parse_gid(const char *str, gid_t *gid_r, const char **error_r)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen{
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen struct group gr;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (str_to_gid(str, gid_r) == 0)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return TRUE;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen switch (i_getgrnam(str, &gr)) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen case -1:
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen *error_r = t_strdup_printf("getgrnam(%s) failed: %m", str);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return FALSE;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen case 0:
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen *error_r = t_strconcat("Unknown UNIX GID group: ", str, NULL);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return FALSE;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen default:
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen *gid_r = gr.gr_gid;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return TRUE;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen}
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenstatic const struct var_expand_table *
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenget_var_expand_table(struct master_service *service,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen struct mail_storage_service_user *user,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen const struct mail_storage_service_input *input,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen const struct mail_storage_service_privileges *priv)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen{
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen static struct var_expand_table static_tab[] = {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen { 'u', NULL, "user" },
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen { 'n', NULL, "username" },
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen { 'd', NULL, "domain" },
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen { 's', NULL, "service" },
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen { 'l', NULL, "lip" },
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen { 'r', NULL, "rip" },
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen { 'p', NULL, "pid" },
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen { 'i', NULL, "uid" },
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen { '\0', NULL, "gid" },
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen { '\0', NULL, "session" },
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen { '\0', NULL, "auth_user" },
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen { '\0', NULL, "auth_username" },
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen { '\0', NULL, "auth_domain" },
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen { '\0', NULL, NULL }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen };
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen struct var_expand_table *tab;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen tab = t_malloc(sizeof(static_tab));
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen memcpy(tab, static_tab, sizeof(static_tab));
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen tab[0].value = input->username;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen tab[1].value = t_strcut(input->username, '@');
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen tab[2].value = strchr(input->username, '@');
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (tab[2].value != NULL) tab[2].value++;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen tab[3].value = service->name;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen tab[4].value = net_ip2addr(&input->local_ip);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen tab[5].value = net_ip2addr(&input->remote_ip);
4c6ddf2491104f917d00e6900e833e80ea02c7b6Timo Sirainen tab[6].value = my_pid;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen tab[7].value = priv == NULL ? NULL :
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen dec2str(priv->uid == (uid_t)-1 ? geteuid() : priv->uid);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen tab[8].value = priv == NULL ? NULL :
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen dec2str(priv->gid == (gid_t)-1 ? getegid() : priv->gid);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen tab[9].value = input->session_id;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (user == NULL || user->auth_user == NULL) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen tab[10].value = tab[0].value;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen tab[11].value = tab[1].value;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen tab[12].value = tab[2].value;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen } else {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen tab[10].value = user->auth_user;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen tab[11].value = t_strcut(user->auth_user, '@');
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen tab[12].value = strchr(user->auth_user, '@');
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return tab;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen}
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
d3a7d023b47d2a137f01109e7b38702dca3f11d3Timo Sirainenconst struct var_expand_table *
d3a7d023b47d2a137f01109e7b38702dca3f11d3Timo Sirainenmail_storage_service_get_var_expand_table(struct mail_storage_service_ctx *ctx,
d3a7d023b47d2a137f01109e7b38702dca3f11d3Timo Sirainen struct mail_storage_service_input *input)
d3a7d023b47d2a137f01109e7b38702dca3f11d3Timo Sirainen{
d3a7d023b47d2a137f01109e7b38702dca3f11d3Timo Sirainen struct mail_storage_service_privileges priv;
d3a7d023b47d2a137f01109e7b38702dca3f11d3Timo Sirainen
d3a7d023b47d2a137f01109e7b38702dca3f11d3Timo Sirainen memset(&priv, 0, sizeof(priv));
d3a7d023b47d2a137f01109e7b38702dca3f11d3Timo Sirainen priv.uid = (uid_t)-1;
d3a7d023b47d2a137f01109e7b38702dca3f11d3Timo Sirainen priv.gid = (gid_t)-1;
d3a7d023b47d2a137f01109e7b38702dca3f11d3Timo Sirainen return get_var_expand_table(ctx->service, NULL, input, &priv);
d3a7d023b47d2a137f01109e7b38702dca3f11d3Timo Sirainen}
d3a7d023b47d2a137f01109e7b38702dca3f11d3Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenstatic const char *
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenuser_expand_varstr(struct master_service *service,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen struct mail_storage_service_user *user,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen struct mail_storage_service_privileges *priv,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen const char *str)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen{
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen string_t *ret;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (*str == SETTING_STRVAR_EXPANDED[0])
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return str + 1;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen i_assert(*str == SETTING_STRVAR_UNEXPANDED[0]);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen ret = t_str_new(256);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen var_expand(ret, str + 1,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen get_var_expand_table(service, user, &user->input, priv));
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return str_c(ret);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen}
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenstatic int
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenservice_parse_privileges(struct mail_storage_service_ctx *ctx,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen struct mail_storage_service_user *user,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen struct mail_storage_service_privileges *priv_r,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen const char **error_r)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen{
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen const struct mail_user_settings *set = user->user_set;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen uid_t uid = (uid_t)-1;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen gid_t gid = (gid_t)-1;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen memset(priv_r, 0, sizeof(*priv_r));
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (*set->mail_uid != '\0') {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (!parse_uid(set->mail_uid, &uid, error_r)) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen *error_r = t_strdup_printf("%s (from %s)", *error_r,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen user->uid_source);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return -1;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (uid < (uid_t)set->first_valid_uid ||
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen (set->last_valid_uid != 0 &&
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen uid > (uid_t)set->last_valid_uid)) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen *error_r = t_strdup_printf(
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen "Mail access for users with UID %s not permitted "
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen "(see first_valid_uid in config file, uid from %s).",
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen dec2str(uid), user->uid_source);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen return -1;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen }
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen priv_r->uid = uid;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen priv_r->uid_source = user->uid_source;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (*set->mail_gid != '\0') {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (!parse_gid(set->mail_gid, &gid, error_r)) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen *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, priv_r,
user->user_set->mail_home);
priv_r->chroot = user_expand_varstr(ctx->service, user, priv_r,
user->user_set->mail_chroot);
return 0;
}
static void mail_storage_service_seteuid_root(void)
{
if (seteuid(0) < 0) {
i_fatal("mail-storage-service: "
"Failed to restore temporarily dropped root privileges: "
"seteuid(0) failed: %m");
}
}
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 */
mail_storage_service_seteuid_root();
}
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("mail-storage-service: 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;
/* NOTE: if more user initialization is added, add it also to
mail_user_dup() */
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_user->anonymous = user->anonymous;
mail_user->admin = user->admin;
mail_user->auth_token = p_strdup(mail_user->pool, user->auth_token);
mail_user->auth_user = p_strdup(mail_user->pool, user->auth_user);
mail_user->session_id =
p_strdup(mail_user->pool, user->input.session_id);
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;
}
void mail_storage_service_io_activate_user(struct mail_storage_service_user *user)
{
i_set_failure_prefix("%s", user->log_prefix);
}
void mail_storage_service_io_deactivate_user(struct mail_storage_service_user *user)
{
i_set_failure_prefix("%s", user->service_ctx->default_log_prefix);
}
void mail_storage_service_io_deactivate(struct mail_storage_service_ctx *ctx)
{
i_set_failure_prefix("%s", 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, &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_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 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.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);
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;
}
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);
var_expand(str, user_set->mail_log_prefix,
get_var_expand_table(ctx->service, user, input, priv));
i_set_failure_prefix("%s", 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 */
mail_storage_service_load_modules(ctx, user_info, user_set);
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 = NULL;
user->input.username = p_strdup(user_pool, username);
user->input.session_id = p_strdup(user_pool, input->session_id);
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) {
mail_storage_service_load_modules(ctx, user_info,
user->user_set);
}
*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. */
i_set_failure_prefix("%s(%s%s,%s)",
master_service_get_name(ctx->service), input->username,
input->session_id == NULL ? "" :
t_strdup_printf(",%s", input->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);
}
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) {
io_loop_context_remove_callbacks(user->ioloop_ctx,
mail_storage_service_io_activate_user,
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;
}
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;
}