mail-storage-service.c revision 984e5c91288139f8a2582be705ee7ef0d157a3f6
2454dfa32c93c20a8522c6ed42fe057baaac9f9aStephan Bosch/* Copyright (c) 2009-2013 Dovecot authors, see the included COPYING file */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "lib.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "ioloop.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "array.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "hostpid.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "module-dir.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "restrict-access.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "eacces-error.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "ipwd.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "str.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "var-expand.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "dict.h"
2b9dbb270ad82e58d5f3581436e6f143176d5819Timo Sirainen#include "settings-parser.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "auth-master.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "master-service-private.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "master-service-settings.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "master-service-settings-cache.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "mail-user.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "mail-namespace.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "mail-storage.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "mail-storage-service.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include <stdlib.h>
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include <sys/stat.h>
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
cc0a651962a3e54d5a62231ac5847ae7f9f7de7fTimo Sirainen#ifdef HAVE_SYS_TIME_H
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen# include <sys/time.h>
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#endif
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#ifdef HAVE_SYS_RESOURCE_H
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen# include <sys/resource.h>
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#endif
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen/* If time moves backwards more than this, kill ourself instead of sleeping. */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#define MAX_TIME_BACKWARDS_SLEEP 5
0001f76bf725c5cf403bade8556f142dd43144eeTimo Sirainen#define MAX_NOWARN_FORWARD_SECS 10
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
c18ff860dc22960fd37c272d929f889c7939a2c8Timo Sirainen#define ERRSTR_INVALID_USER_SETTINGS \
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen "Invalid user settings. Refer to server log for more information."
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
08a8b3de61139ba02371afc8240ac85be0e8b17cTimo Sirainenstruct mail_storage_service_privileges {
c18ff860dc22960fd37c272d929f889c7939a2c8Timo Sirainen uid_t uid;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen gid_t gid;
4ee00532a265bdfb38539d811fcd12d51210ac35Timo Sirainen const char *uid_source, *gid_source;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen const char *home;
b67974c4b89ab6950c2694cce8dfb1b6561cc084Josef 'Jeff' Sipek const char *chroot;
b67974c4b89ab6950c2694cce8dfb1b6561cc084Josef 'Jeff' Sipek};
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainenstruct mail_storage_service_ctx {
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen pool_t pool;
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen struct master_service *service;
b67974c4b89ab6950c2694cce8dfb1b6561cc084Josef 'Jeff' Sipek const char *default_log_prefix;
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen struct auth_master_connection *conn;
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen struct auth_master_user_list_ctx *auth_list;
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen const struct setting_parser_info **set_roots;
03010dbaa74ec70f062994dfe3cd39bedc99a28bTimo Sirainen enum mail_storage_service_flags flags;
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen const char *set_cache_module, *set_cache_service;
c18ff860dc22960fd37c272d929f889c7939a2c8Timo Sirainen struct master_service_settings_cache *set_cache;
714c6a150480112eb1a5f309d0cc39b60613a719Timo Sirainen
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen pool_t userdb_next_pool;
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen const char *const **userdb_next_fieldsp;
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen unsigned int debug:1;
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen unsigned int log_initialized:1;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen};
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstruct mail_storage_service_user {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen pool_t pool;
b67974c4b89ab6950c2694cce8dfb1b6561cc084Josef 'Jeff' Sipek struct mail_storage_service_ctx *service_ctx;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct mail_storage_service_input input;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen enum mail_storage_service_flags flags;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct ioloop_context *ioloop_ctx;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen const char *log_prefix, *auth_token;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen const char *system_groups_user, *uid_source, *gid_source;
9a02317c852face76737763fa6ec43b444688de5Timo Sirainen const struct mail_user_settings *user_set;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen const struct setting_parser_info *user_info;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct setting_parser_context *set_parser;
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen unsigned int anonymous:1;
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen};
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainenstruct module *mail_storage_service_modules = NULL;
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen
cc0a651962a3e54d5a62231ac5847ae7f9f7de7fTimo Sirainenstatic bool
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenmail_user_set_get_mail_debug(const struct setting_parser_info *user_info,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen const struct mail_user_settings *user_set)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen const struct mail_storage_settings *mail_set;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen mail_set = mail_user_set_get_driver_settings(user_info, user_set,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen MAIL_STORAGE_SET_DRIVER_NAME);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return mail_set->mail_debug;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstatic void set_keyval(struct mail_storage_service_ctx *ctx,
b67974c4b89ab6950c2694cce8dfb1b6561cc084Josef 'Jeff' Sipek struct mail_storage_service_user *user,
b67974c4b89ab6950c2694cce8dfb1b6561cc084Josef 'Jeff' Sipek const char *key, const char *value)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct setting_parser_context *set_parser = user->set_parser;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen const char *str;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (master_service_set_has_config_override(ctx->service, key)) {
0001f76bf725c5cf403bade8556f142dd43144eeTimo Sirainen /* this setting was already overridden with -o parameter */
e2eac5bb5637c2d4aaf453389750740931822b92Timo Sirainen if (mail_user_set_get_mail_debug(user->user_info,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen user->user_set)) {
e2bdacc34dde56aa664059ab56e8b77e82bd1805Timo Sirainen i_debug("Ignoring overridden (-o) userdb setting: %s",
e2bdacc34dde56aa664059ab56e8b77e82bd1805Timo Sirainen key);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen str = t_strconcat(key, "=", value, NULL);
cd83124e5d070a016c590bb0b1096d7828c7b6adTimo Sirainen if (settings_parse_line(set_parser, str) < 0) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_fatal("Invalid userdb input '%s': %s", str,
08a8b3de61139ba02371afc8240ac85be0e8b17cTimo Sirainen settings_parser_get_error(set_parser));
08a8b3de61139ba02371afc8240ac85be0e8b17cTimo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstatic int set_line(struct mail_storage_service_ctx *ctx,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct mail_storage_service_user *user,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen const char *line)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct setting_parser_context *set_parser = user->set_parser;
b67974c4b89ab6950c2694cce8dfb1b6561cc084Josef 'Jeff' Sipek bool mail_debug;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen const char *key, *orig_key, *append_value = NULL;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen unsigned int len;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen int ret;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
5e702db5540b2303e25554dee21bbf35a4813381Timo Sirainen mail_debug = mail_user_set_get_mail_debug(user->user_info,
5e702db5540b2303e25554dee21bbf35a4813381Timo Sirainen user->user_set);
5e702db5540b2303e25554dee21bbf35a4813381Timo Sirainen if (strchr(line, '=') == NULL)
5e702db5540b2303e25554dee21bbf35a4813381Timo Sirainen line = t_strconcat(line, "=yes", NULL);
5e702db5540b2303e25554dee21bbf35a4813381Timo Sirainen orig_key = key = t_strcut(line, '=');
46908da82cd45dd90ca7e438c8453bc9669868ecTimo Sirainen
46908da82cd45dd90ca7e438c8453bc9669868ecTimo Sirainen len = strlen(key);
5e702db5540b2303e25554dee21bbf35a4813381Timo Sirainen if (len > 0 && key[len-1] == '+') {
c18ff860dc22960fd37c272d929f889c7939a2c8Timo Sirainen /* key+=value */
c18ff860dc22960fd37c272d929f889c7939a2c8Timo Sirainen append_value = line + len + 1;
c18ff860dc22960fd37c272d929f889c7939a2c8Timo Sirainen key = t_strndup(key, len-1);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (!settings_parse_is_valid_key(set_parser, key)) {
e6440616c02bb1404dc35debf45d9741260c7831Timo Sirainen /* assume it's a plugin setting */
e6440616c02bb1404dc35debf45d9741260c7831Timo Sirainen key = t_strconcat("plugin/", key, NULL);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen line = t_strconcat("plugin/", line, NULL);
0001f76bf725c5cf403bade8556f142dd43144eeTimo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (master_service_set_has_config_override(ctx->service, key)) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* this setting was already overridden with -o parameter */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (mail_debug) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_debug("Ignoring overridden (-o) userdb setting: %s",
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen key);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return 1;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (append_value != NULL) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen const void *value;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen enum setting_type type;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen value = settings_parse_get_value(set_parser, key, &type);
de62ce819d59a529530da4b57be1b8d6dad13d6bTimo Sirainen if (type == SET_STR) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen const char *const *strp = value;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen line = t_strdup_printf("%s=%s%s",
e6440616c02bb1404dc35debf45d9741260c7831Timo Sirainen key, *strp, append_value);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen } else {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_error("Ignoring %s userdb setting. "
e6440616c02bb1404dc35debf45d9741260c7831Timo Sirainen "'+' can only be used for strings.", orig_key);
1f19649986397419d014febd1337c6eb7b530f26Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen ret = settings_parse_line(set_parser, line);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (mail_debug && ret >= 0) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (strstr(key, "pass") != NULL) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* possibly a password field (e.g. imapc_password).
e6440616c02bb1404dc35debf45d9741260c7831Timo Sirainen hide the value. */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen line = t_strconcat(key, "=<hidden>", NULL);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_debug(ret == 0 ?
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen "Unknown userdb setting: %s" :
cc0a651962a3e54d5a62231ac5847ae7f9f7de7fTimo Sirainen "Added userdb setting: %s", line);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return ret;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstatic bool validate_chroot(const struct mail_user_settings *user_set,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen const char *dir)
06eb8c1371aa06478d8840b1373cab7c2752d5edTimo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen const char *const *chroot_dirs;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
e6440616c02bb1404dc35debf45d9741260c7831Timo Sirainen if (*dir == '\0')
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return FALSE;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
e6440616c02bb1404dc35debf45d9741260c7831Timo Sirainen if (*user_set->valid_chroot_dirs == '\0')
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return FALSE;
06eb8c1371aa06478d8840b1373cab7c2752d5edTimo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen chroot_dirs = t_strsplit(user_set->valid_chroot_dirs, ":");
06eb8c1371aa06478d8840b1373cab7c2752d5edTimo Sirainen while (*chroot_dirs != NULL) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (**chroot_dirs != '\0' &&
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen strncmp(dir, *chroot_dirs, strlen(*chroot_dirs)) == 0)
c18ff860dc22960fd37c272d929f889c7939a2c8Timo Sirainen return TRUE;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen chroot_dirs++;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
06eb8c1371aa06478d8840b1373cab7c2752d5edTimo Sirainen return FALSE;
06eb8c1371aa06478d8840b1373cab7c2752d5edTimo Sirainen}
06eb8c1371aa06478d8840b1373cab7c2752d5edTimo Sirainen
06eb8c1371aa06478d8840b1373cab7c2752d5edTimo Sirainenstatic int
06eb8c1371aa06478d8840b1373cab7c2752d5edTimo Sirainenuser_reply_handle(struct mail_storage_service_ctx *ctx,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct mail_storage_service_user *user,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen const struct auth_user_reply *reply,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen const char **error_r)
f776b9a125c59a96de6807e9558942cf7b7ab079Timo Sirainen{
836e57b1e7817d008f8ae05cd4b506f420fed80dTimo Sirainen const char *home = reply->home;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen const char *chroot = reply->chroot;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen const char *const *str, *line, *p;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen unsigned int i, count;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen int ret = 0;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (reply->uid != (uid_t)-1) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (reply->uid == 0) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen *error_r = "userdb returned 0 as uid";
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return -1;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen user->uid_source = "userdb lookup";
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen set_keyval(ctx, user, "mail_uid", dec2str(reply->uid));
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (reply->gid != (uid_t)-1) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen user->gid_source = "userdb lookup";
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen set_keyval(ctx, user, "mail_gid", dec2str(reply->gid));
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
804fa3f03bd9170272168a5ad214053bbe3160c7Josef 'Jeff' Sipek
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (home != NULL && chroot == NULL &&
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen *user->user_set->valid_chroot_dirs != '\0' &&
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen (p = strstr(home, "/./")) != NULL) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* wu-ftpd like <chroot>/./<home> - check only if there's even
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen a possibility of using them (non-empty valid_chroot_dirs) */
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen chroot = t_strdup_until(home, p);
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen home = p + 2;
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen }
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen if (home != NULL)
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen set_keyval(ctx, user, "mail_home", home);
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen if (chroot != NULL) {
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen if (!validate_chroot(user->user_set, chroot)) {
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen *error_r = t_strdup_printf(
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen "userdb returned invalid chroot directory: %s "
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen "(see valid_chroot_dirs setting)", chroot);
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen return -1;
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen }
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen set_keyval(ctx, user, "mail_chroot", chroot);
efe78d3ba24fc866af1c79b9223dc0809ba26cadStephan Bosch }
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen user->anonymous = reply->anonymous;
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen str = array_get(&reply->extra_fields, &count);
03010dbaa74ec70f062994dfe3cd39bedc99a28bTimo Sirainen for (i = 0; i < count; i++) {
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen line = str[i];
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen if (strncmp(line, "system_groups_user=", 19) == 0) {
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen user->system_groups_user =
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen p_strdup(user->pool, line + 19);
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen } else if (strncmp(line, "nice=", 5) == 0) {
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen#ifdef HAVE_SETPRIORITY
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen int n = atoi(line + 5);
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen if (n != 0) {
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen if (setpriority(PRIO_PROCESS, 0, n) < 0)
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen i_error("setpriority(%d) failed: %m", n);
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen }
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen#endif
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen } else if (strncmp(line, "auth_token=", 11) == 0) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen user->auth_token = p_strdup(user->pool, line+11);
b67974c4b89ab6950c2694cce8dfb1b6561cc084Josef 'Jeff' Sipek } else T_BEGIN {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen ret = set_line(ctx, user, line);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen } T_END;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (ret < 0)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen break;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
ddedc8b77c5bccc8d224ab5f8716d9874f5d8512Timo Sirainen if (ret < 0) {
ddedc8b77c5bccc8d224ab5f8716d9874f5d8512Timo Sirainen *error_r = t_strdup_printf("Invalid userdb input '%s': %s",
ddedc8b77c5bccc8d224ab5f8716d9874f5d8512Timo Sirainen str[i], settings_parser_get_error(user->set_parser));
ddedc8b77c5bccc8d224ab5f8716d9874f5d8512Timo Sirainen }
ddedc8b77c5bccc8d224ab5f8716d9874f5d8512Timo Sirainen return ret;
ddedc8b77c5bccc8d224ab5f8716d9874f5d8512Timo Sirainen}
f1612f8421207632e1dc9addd6c23e7f7098a54cTimo Sirainen
62958c5eefcd7dd84717b487ca36ec3a86949eb9Timo Sirainenstatic int
56aa9083e1742d0083885aaf0c5b8581577731aeTimo Sirainenservice_auth_userdb_lookup(struct mail_storage_service_ctx *ctx,
f1612f8421207632e1dc9addd6c23e7f7098a54cTimo Sirainen const struct mail_storage_service_input *input,
56aa9083e1742d0083885aaf0c5b8581577731aeTimo Sirainen pool_t pool, const char **user,
f71c2d4e6b802bf8e622bcd5df29286262d05d5aTimo Sirainen const char *const **fields_r,
f71c2d4e6b802bf8e622bcd5df29286262d05d5aTimo Sirainen const char **error_r)
f71c2d4e6b802bf8e622bcd5df29286262d05d5aTimo Sirainen{
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen struct auth_user_info info;
08a8b3de61139ba02371afc8240ac85be0e8b17cTimo Sirainen const char *new_username;
f71c2d4e6b802bf8e622bcd5df29286262d05d5aTimo Sirainen int ret;
f71c2d4e6b802bf8e622bcd5df29286262d05d5aTimo Sirainen
08a8b3de61139ba02371afc8240ac85be0e8b17cTimo Sirainen memset(&info, 0, sizeof(info));
08a8b3de61139ba02371afc8240ac85be0e8b17cTimo Sirainen info.service = input->service != NULL ? input->service :
08a8b3de61139ba02371afc8240ac85be0e8b17cTimo Sirainen ctx->service->name;
08a8b3de61139ba02371afc8240ac85be0e8b17cTimo Sirainen info.local_ip = input->local_ip;
08a8b3de61139ba02371afc8240ac85be0e8b17cTimo Sirainen info.remote_ip = input->remote_ip;
3c40cf45bb10548bf434aea0fca32b99acc99a35Timo Sirainen info.local_port = input->local_port;
3c40cf45bb10548bf434aea0fca32b99acc99a35Timo Sirainen info.remote_port = input->remote_port;
3c40cf45bb10548bf434aea0fca32b99acc99a35Timo Sirainen
3c40cf45bb10548bf434aea0fca32b99acc99a35Timo Sirainen ret = auth_master_user_lookup(ctx->conn, *user, &info, pool,
3c40cf45bb10548bf434aea0fca32b99acc99a35Timo Sirainen &new_username, fields_r);
3c40cf45bb10548bf434aea0fca32b99acc99a35Timo Sirainen if (ret > 0) {
3c40cf45bb10548bf434aea0fca32b99acc99a35Timo Sirainen if (strcmp(*user, new_username) != 0) {
3c40cf45bb10548bf434aea0fca32b99acc99a35Timo Sirainen if (ctx->debug)
3c40cf45bb10548bf434aea0fca32b99acc99a35Timo Sirainen i_debug("changed username to %s", new_username);
3c40cf45bb10548bf434aea0fca32b99acc99a35Timo Sirainen *user = t_strdup(new_username);
9865d9e7c5713e41db939222ed9c0225a11fb99eTimo Sirainen }
9865d9e7c5713e41db939222ed9c0225a11fb99eTimo Sirainen *user = new_username;
9865d9e7c5713e41db939222ed9c0225a11fb99eTimo Sirainen } else if (ret == 0)
9865d9e7c5713e41db939222ed9c0225a11fb99eTimo Sirainen *error_r = "Unknown user";
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen else if (**fields_r != NULL) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen *error_r = t_strdup(**fields_r);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen ret = -2;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen } else {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen *error_r = MAIL_ERRSTR_CRITICAL_MSG;
2b9dbb270ad82e58d5f3581436e6f143176d5819Timo Sirainen }
3aae8844765b1d74d847e8e37daa135ac7035e6bTimo Sirainen return ret;
2b9dbb270ad82e58d5f3581436e6f143176d5819Timo Sirainen}
3aae8844765b1d74d847e8e37daa135ac7035e6bTimo Sirainen
2b9dbb270ad82e58d5f3581436e6f143176d5819Timo Sirainenstatic bool parse_uid(const char *str, uid_t *uid_r, const char **error_r)
2b9dbb270ad82e58d5f3581436e6f143176d5819Timo Sirainen{
3aae8844765b1d74d847e8e37daa135ac7035e6bTimo Sirainen struct passwd pw;
3aae8844765b1d74d847e8e37daa135ac7035e6bTimo Sirainen
3aae8844765b1d74d847e8e37daa135ac7035e6bTimo Sirainen if (str_to_uid(str, uid_r) == 0)
3aae8844765b1d74d847e8e37daa135ac7035e6bTimo Sirainen return TRUE;
3aae8844765b1d74d847e8e37daa135ac7035e6bTimo Sirainen
2b9dbb270ad82e58d5f3581436e6f143176d5819Timo Sirainen switch (i_getpwnam(str, &pw)) {
3aae8844765b1d74d847e8e37daa135ac7035e6bTimo Sirainen case -1:
2b9dbb270ad82e58d5f3581436e6f143176d5819Timo Sirainen *error_r = t_strdup_printf("getpwnam(%s) failed: %m", str);
2b9dbb270ad82e58d5f3581436e6f143176d5819Timo Sirainen return FALSE;
2b9dbb270ad82e58d5f3581436e6f143176d5819Timo Sirainen case 0:
2b9dbb270ad82e58d5f3581436e6f143176d5819Timo Sirainen *error_r = t_strconcat("Unknown UNIX UID user: ", str, NULL);
fb5efc6ed69da679d9da31ef62daa7024de18212Timo Sirainen return FALSE;
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen default:
5833a4972491fdb7b78eac2280f31dc4b9fa2bb7Timo Sirainen *uid_r = pw.pw_uid;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return TRUE;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
08a8b3de61139ba02371afc8240ac85be0e8b17cTimo Sirainen
c18ff860dc22960fd37c272d929f889c7939a2c8Timo Sirainenstatic bool parse_gid(const char *str, gid_t *gid_r, const char **error_r)
c18ff860dc22960fd37c272d929f889c7939a2c8Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct group gr;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (str_to_gid(str, gid_r) == 0)
62958c5eefcd7dd84717b487ca36ec3a86949eb9Timo Sirainen return TRUE;
62958c5eefcd7dd84717b487ca36ec3a86949eb9Timo Sirainen
62958c5eefcd7dd84717b487ca36ec3a86949eb9Timo Sirainen switch (i_getgrnam(str, &gr)) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen case -1:
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen *error_r = t_strdup_printf("getgrnam(%s) failed: %m", str);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return FALSE;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen case 0:
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen *error_r = t_strconcat("Unknown UNIX GID group: ", str, NULL);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return FALSE;
9f19a50d5966643c4d1c5ca06868ac2ad31bc4d5Timo Sirainen default:
9f19a50d5966643c4d1c5ca06868ac2ad31bc4d5Timo Sirainen *gid_r = gr.gr_gid;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return TRUE;
b67974c4b89ab6950c2694cce8dfb1b6561cc084Josef 'Jeff' Sipek }
b780aa272b742a43579cdb523cc79cc8d4521306Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstatic const struct var_expand_table *
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenget_var_expand_table(struct master_service *service,
9f19a50d5966643c4d1c5ca06868ac2ad31bc4d5Timo Sirainen struct mail_storage_service_input *input,
9f19a50d5966643c4d1c5ca06868ac2ad31bc4d5Timo Sirainen struct mail_storage_service_privileges *priv)
9f19a50d5966643c4d1c5ca06868ac2ad31bc4d5Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen static struct var_expand_table static_tab[] = {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen { 'u', NULL, "user" },
08a8b3de61139ba02371afc8240ac85be0e8b17cTimo Sirainen { 'n', NULL, "username" },
08a8b3de61139ba02371afc8240ac85be0e8b17cTimo Sirainen { 'd', NULL, "domain" },
62958c5eefcd7dd84717b487ca36ec3a86949eb9Timo Sirainen { 's', NULL, "service" },
08a8b3de61139ba02371afc8240ac85be0e8b17cTimo Sirainen { 'l', NULL, "lip" },
08a8b3de61139ba02371afc8240ac85be0e8b17cTimo Sirainen { 'r', NULL, "rip" },
08a8b3de61139ba02371afc8240ac85be0e8b17cTimo Sirainen { 'p', NULL, "pid" },
08a8b3de61139ba02371afc8240ac85be0e8b17cTimo Sirainen { 'i', NULL, "uid" },
08a8b3de61139ba02371afc8240ac85be0e8b17cTimo Sirainen { '\0', NULL, "gid" },
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen { '\0', NULL, "session" },
c18ff860dc22960fd37c272d929f889c7939a2c8Timo Sirainen { '\0', NULL, NULL }
08a8b3de61139ba02371afc8240ac85be0e8b17cTimo Sirainen };
08a8b3de61139ba02371afc8240ac85be0e8b17cTimo Sirainen struct var_expand_table *tab;
08a8b3de61139ba02371afc8240ac85be0e8b17cTimo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen tab = t_malloc(sizeof(static_tab));
b780aa272b742a43579cdb523cc79cc8d4521306Timo Sirainen memcpy(tab, static_tab, sizeof(static_tab));
ca98892a6b8a30ffc1fe26fcf02c7d59e3204e7eTimo Sirainen
ca98892a6b8a30ffc1fe26fcf02c7d59e3204e7eTimo Sirainen tab[0].value = input->username;
ca98892a6b8a30ffc1fe26fcf02c7d59e3204e7eTimo Sirainen tab[1].value = t_strcut(input->username, '@');
b780aa272b742a43579cdb523cc79cc8d4521306Timo Sirainen tab[2].value = strchr(input->username, '@');
ca98892a6b8a30ffc1fe26fcf02c7d59e3204e7eTimo Sirainen if (tab[2].value != NULL) tab[2].value++;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen tab[3].value = service->name;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen tab[4].value = net_ip2addr(&input->local_ip);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen tab[5].value = net_ip2addr(&input->remote_ip);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen tab[6].value = my_pid;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen tab[7].value = dec2str(priv->uid == (uid_t)-1 ? geteuid() : priv->uid);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen tab[8].value = dec2str(priv->gid == (gid_t)-1 ? getegid() : priv->gid);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen tab[9].value = input->session_id;
b67974c4b89ab6950c2694cce8dfb1b6561cc084Josef 'Jeff' Sipek return tab;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenconst struct var_expand_table *
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenmail_storage_service_get_var_expand_table(struct mail_storage_service_ctx *ctx,
c18ff860dc22960fd37c272d929f889c7939a2c8Timo Sirainen struct mail_storage_service_input *input)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
c18ff860dc22960fd37c272d929f889c7939a2c8Timo Sirainen struct mail_storage_service_privileges priv;
08a8b3de61139ba02371afc8240ac85be0e8b17cTimo Sirainen
08a8b3de61139ba02371afc8240ac85be0e8b17cTimo Sirainen memset(&priv, 0, sizeof(priv));
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen priv.uid = (uid_t)-1;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen priv.gid = (gid_t)-1;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return get_var_expand_table(ctx->service, input, &priv);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstatic const char *
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenuser_expand_varstr(struct master_service *service,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct mail_storage_service_input *input,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct mail_storage_service_privileges *priv,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen const char *str)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen string_t *ret;
b67974c4b89ab6950c2694cce8dfb1b6561cc084Josef 'Jeff' Sipek
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen if (*str == SETTING_STRVAR_EXPANDED[0])
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return str + 1;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
553cf4f8a8850efffdc714ec1d1ae45a5fc7905dTimo Sirainen i_assert(*str == SETTING_STRVAR_UNEXPANDED[0]);
553cf4f8a8850efffdc714ec1d1ae45a5fc7905dTimo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen ret = t_str_new(256);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen var_expand(ret, str + 1, get_var_expand_table(service, input, priv));
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return str_c(ret);
7f735cb86b2d8abd8f230089065eacfc24e9e5d6Timo Sirainen}
553cf4f8a8850efffdc714ec1d1ae45a5fc7905dTimo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstatic int
b67974c4b89ab6950c2694cce8dfb1b6561cc084Josef 'Jeff' Sipekservice_parse_privileges(struct mail_storage_service_ctx *ctx,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct mail_storage_service_user *user,
efe78d3ba24fc866af1c79b9223dc0809ba26cadStephan Bosch struct mail_storage_service_privileges *priv_r,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen const char **error_r)
767ff4367960efd5fa868f3b56f850fd4c205c8bTimo Sirainen{
08df28a63b3efb0f0ee30c3e7ef44c0a1e7bb459Timo Sirainen const struct mail_user_settings *set = user->user_set;
08df28a63b3efb0f0ee30c3e7ef44c0a1e7bb459Timo Sirainen uid_t uid = (uid_t)-1;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen gid_t gid = (gid_t)-1;
08df28a63b3efb0f0ee30c3e7ef44c0a1e7bb459Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen memset(priv_r, 0, sizeof(*priv_r));
553cf4f8a8850efffdc714ec1d1ae45a5fc7905dTimo Sirainen if (*set->mail_uid != '\0') {
03010dbaa74ec70f062994dfe3cd39bedc99a28bTimo Sirainen if (!parse_uid(set->mail_uid, &uid, error_r)) {
553cf4f8a8850efffdc714ec1d1ae45a5fc7905dTimo Sirainen *error_r = t_strdup_printf("%s (from %s)", *error_r,
553cf4f8a8850efffdc714ec1d1ae45a5fc7905dTimo Sirainen user->uid_source);
553cf4f8a8850efffdc714ec1d1ae45a5fc7905dTimo Sirainen return -1;
553cf4f8a8850efffdc714ec1d1ae45a5fc7905dTimo Sirainen }
61279c3c77aa4a6f8d1de82b468ab01b14418318Timo Sirainen if (uid < (uid_t)set->first_valid_uid ||
61279c3c77aa4a6f8d1de82b468ab01b14418318Timo Sirainen (set->last_valid_uid != 0 &&
553cf4f8a8850efffdc714ec1d1ae45a5fc7905dTimo Sirainen uid > (uid_t)set->last_valid_uid)) {
553cf4f8a8850efffdc714ec1d1ae45a5fc7905dTimo Sirainen *error_r = t_strdup_printf(
553cf4f8a8850efffdc714ec1d1ae45a5fc7905dTimo Sirainen "Mail access for users with UID %s not permitted "
553cf4f8a8850efffdc714ec1d1ae45a5fc7905dTimo Sirainen "(see first_valid_uid in config file, uid from %s).",
553cf4f8a8850efffdc714ec1d1ae45a5fc7905dTimo Sirainen dec2str(uid), user->uid_source);
553cf4f8a8850efffdc714ec1d1ae45a5fc7905dTimo Sirainen return -1;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen priv_r->uid = uid;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen priv_r->uid_source = user->uid_source;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (*set->mail_gid != '\0') {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (!parse_gid(set->mail_gid, &gid, error_r)) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen *error_r = t_strdup_printf("%s (from %s)", *error_r,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen user->gid_source);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return -1;
553cf4f8a8850efffdc714ec1d1ae45a5fc7905dTimo Sirainen }
553cf4f8a8850efffdc714ec1d1ae45a5fc7905dTimo Sirainen if (gid < (gid_t)set->first_valid_gid ||
1554bed8d2b4e4286c10f7d6bcf716b246bd5bafTimo Sirainen (set->last_valid_gid != 0 &&
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen gid > (gid_t)set->last_valid_gid)) {
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen *error_r = t_strdup_printf(
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen "Mail access for users with GID %s not permitted "
e237ebeb97f42950eef3efd0d3db85590160d5fbTimo Sirainen "(see first_valid_gid in config file, gid from %s).",
a825281071af96cc148e49c64ac36d8c5cf26f71Timo Sirainen dec2str(gid), user->gid_source);
08df28a63b3efb0f0ee30c3e7ef44c0a1e7bb459Timo Sirainen return -1;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
priv_r->gid = gid;
priv_r->gid_source = user->gid_source;
/* variable strings are expanded in mail_user_init(),
but we need the home and chroot sooner so do them separately here. */
priv_r->home = user_expand_varstr(ctx->service, &user->input, priv_r,
user->user_set->mail_home);
priv_r->chroot = user_expand_varstr(ctx->service, &user->input, priv_r,
user->user_set->mail_chroot);
return 0;
}
static int
service_drop_privileges(struct mail_storage_service_user *user,
struct mail_storage_service_privileges *priv,
bool disallow_root, bool keep_setuid_root,
bool setenv_only, const char **error_r)
{
const struct mail_user_settings *set = user->user_set;
struct restrict_access_settings rset;
uid_t current_euid, setuid_uid = 0;
const char *cur_chroot, *error;
current_euid = geteuid();
restrict_access_init(&rset);
restrict_access_get_env(&rset);
if (priv->uid != (uid_t)-1) {
rset.uid = priv->uid;
rset.uid_source = priv->uid_source;
} else if (rset.uid == (uid_t)-1 &&
disallow_root && current_euid == 0) {
*error_r = "User is missing UID (see mail_uid setting)";
return -1;
}
if (priv->gid != (gid_t)-1) {
rset.gid = priv->gid;
rset.gid_source = priv->gid_source;
} else if (rset.gid == (gid_t)-1 && disallow_root &&
set->first_valid_gid > 0 && getegid() == 0) {
*error_r = "User is missing GID (see mail_gid setting)";
return -1;
}
if (*set->mail_privileged_group != '\0') {
if (!parse_gid(set->mail_privileged_group, &rset.privileged_gid,
&error)) {
*error_r = t_strdup_printf(
"%s (in mail_privileged_group setting)", error);
return -1;
}
}
if (*set->mail_access_groups != '\0') {
rset.extra_groups = t_strconcat(set->mail_access_groups, ",",
rset.extra_groups, NULL);
}
rset.first_valid_gid = set->first_valid_gid;
rset.last_valid_gid = set->last_valid_gid;
rset.chroot_dir = *priv->chroot == '\0' ? NULL : priv->chroot;
rset.system_groups_user = user->system_groups_user;
cur_chroot = restrict_access_get_current_chroot();
if (cur_chroot != NULL) {
/* we're already chrooted. make sure the chroots are equal. */
if (rset.chroot_dir == NULL) {
*error_r = "Process is already chrooted, "
"can't un-chroot for this user";
return -1;
}
if (strcmp(rset.chroot_dir, cur_chroot) != 0) {
*error_r = t_strdup_printf(
"Process is already chrooted to %s, "
"can't chroot to %s", cur_chroot, priv->chroot);
return -1;
}
/* chrooting to same directory where we're already chrooted */
rset.chroot_dir = NULL;
}
if (disallow_root &&
(rset.uid == 0 || (rset.uid == (uid_t)-1 && current_euid == 0))) {
*error_r = "Mail access not allowed for root";
return -1;
}
if (keep_setuid_root) {
if (current_euid != rset.uid) {
if (current_euid != 0) {
/* we're changing the UID,
switch back to root first */
if (seteuid(0) < 0)
i_fatal("seteuid(0) failed: %m");
}
setuid_uid = rset.uid;
}
rset.uid = (uid_t)-1;
disallow_root = FALSE;
}
if (!setenv_only) {
restrict_access(&rset, *priv->home == '\0' ? NULL : priv->home,
disallow_root);
} else {
restrict_access_set_env(&rset);
}
if (setuid_uid != 0 && !setenv_only) {
if (seteuid(setuid_uid) < 0)
i_fatal("seteuid(%s) failed: %m", dec2str(setuid_uid));
}
return 0;
}
static int
mail_storage_service_init_post(struct mail_storage_service_ctx *ctx,
struct mail_storage_service_user *user,
struct mail_storage_service_privileges *priv,
struct mail_user **mail_user_r,
const char **error_r)
{
const struct mail_storage_settings *mail_set;
const char *home = priv->home;
struct mail_user *mail_user;
mail_user = mail_user_alloc(user->input.username, user->user_info,
user->user_set);
mail_user_set_home(mail_user, *home == '\0' ? NULL : home);
mail_user_set_vars(mail_user, ctx->service->name,
&user->input.local_ip, &user->input.remote_ip);
mail_user->uid = priv->uid == (uid_t)-1 ? geteuid() : priv->uid;
mail_user->gid = priv->gid == (gid_t)-1 ? getegid() : priv->gid;
mail_user->anonymous = user->anonymous;
mail_user->auth_token = p_strdup(mail_user->pool, user->auth_token);
mail_set = mail_user_set_get_storage_set(mail_user);
if (mail_set->mail_debug) {
string_t *str = t_str_new(64);
str_printfa(str, "Effective uid=%s, gid=%s, home=%s",
dec2str(geteuid()), dec2str(getegid()), home);
if (*priv->chroot != '\0')
str_printfa(str, ", chroot=%s", priv->chroot);
i_debug("%s", str_c(str));
}
if ((user->flags & MAIL_STORAGE_SERVICE_FLAG_TEMP_PRIV_DROP) != 0 &&
(user->flags & MAIL_STORAGE_SERVICE_FLAG_ENABLE_CORE_DUMPS) == 0) {
/* we don't want to write core files to any users' home
directories since they could contain information about other
users' mails as well. so do no chdiring to home. */
} else if (*home != '\0' &&
(user->flags & MAIL_STORAGE_SERVICE_FLAG_NO_CHDIR) == 0) {
/* If possible chdir to home directory, so that core file
could be written in case we crash. */
if (chdir(home) < 0) {
if (errno == EACCES) {
i_error("%s", eacces_error_get("chdir",
t_strconcat(home, "/", NULL)));
} if (errno != ENOENT)
i_error("chdir(%s) failed: %m", home);
else if (mail_set->mail_debug)
i_debug("Home dir not found: %s", home);
}
}
if (mail_user_init(mail_user, error_r) < 0) {
mail_user_unref(&mail_user);
return -1;
}
if ((user->flags & MAIL_STORAGE_SERVICE_FLAG_NO_NAMESPACES) == 0) {
if (mail_namespaces_init(mail_user, error_r) < 0) {
mail_user_unref(&mail_user);
return -1;
}
}
*mail_user_r = mail_user;
return 0;
}
static void mail_storage_service_io_activate(void *context)
{
struct mail_storage_service_user *user = context;
i_set_failure_prefix("%s", user->log_prefix);
}
static void mail_storage_service_io_deactivate(void *context)
{
struct mail_storage_service_user *user = context;
i_set_failure_prefix("%s", user->service_ctx->default_log_prefix);
}
static void
mail_storage_service_init_log(struct mail_storage_service_ctx *ctx,
struct mail_storage_service_user *user,
struct mail_storage_service_privileges *priv)
{
ctx->log_initialized = TRUE;
T_BEGIN {
string_t *str;
str = t_str_new(256);
var_expand(str, user->user_set->mail_log_prefix,
get_var_expand_table(ctx->service, &user->input, priv));
user->log_prefix = p_strdup(user->pool, str_c(str));
} T_END;
master_service_init_log(ctx->service, user->log_prefix);
if (master_service_get_client_limit(master_service) == 1)
i_set_failure_send_prefix(user->log_prefix);
user->ioloop_ctx = io_loop_context_new(current_ioloop);
io_loop_context_add_callbacks(user->ioloop_ctx,
mail_storage_service_io_activate,
mail_storage_service_io_deactivate,
user);
}
static void mail_storage_service_time_moved(time_t old_time, time_t new_time)
{
long diff = new_time - old_time;
if (diff > 0) {
if (diff > MAX_NOWARN_FORWARD_SECS)
i_warning("Time jumped forwards %ld seconds", diff);
return;
}
diff = -diff;
if (diff > MAX_TIME_BACKWARDS_SLEEP) {
i_fatal("Time just moved backwards by %ld seconds. "
"This might cause a lot of problems, "
"so I'll just kill myself now. "
"http://wiki2.dovecot.org/TimeMovedBackwards", diff);
} else {
i_error("Time just moved backwards by %ld seconds. "
"I'll sleep now until we're back in present. "
"http://wiki2.dovecot.org/TimeMovedBackwards", diff);
/* Sleep extra second to make sure usecs also grows. */
diff++;
while (diff > 0 && sleep(diff) != 0) {
/* don't use sleep()'s return value, because
it could get us to a long loop in case
interrupts just keep coming */
diff = old_time - time(NULL) + 1;
}
}
}
struct mail_storage_service_ctx *
mail_storage_service_init(struct master_service *service,
const struct setting_parser_info *set_roots[],
enum mail_storage_service_flags flags)
{
struct mail_storage_service_ctx *ctx;
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);
}
(void)umask(0077);
io_loop_set_time_moved_callback(current_ioloop,
mail_storage_service_time_moved);
mail_storage_init();
mail_storage_register_all();
mailbox_list_register_all();
pool = pool_alloconly_create("mail storage service", 2048);
ctx = p_new(pool, struct mail_storage_service_ctx, 1);
ctx->pool = pool;
ctx->service = service;
ctx->flags = flags;
/* @UNSAFE */
if (set_roots == NULL)
count = 0;
else
for (count = 0; set_roots[count] != NULL; count++) ;
ctx->set_roots =
p_new(pool, const struct setting_parser_info *, count + 2);
ctx->set_roots[0] = &mail_user_setting_parser_info;
if (set_roots != NULL) {
memcpy(ctx->set_roots + 1, set_roots,
sizeof(*ctx->set_roots) * count);
}
/* do all the global initialization. delay initializing plugins until
we drop privileges the first time. */
if ((flags & MAIL_STORAGE_SERVICE_FLAG_NO_LOG_INIT) == 0) {
/* note: we may not have read any settings yet, so this logging
may still be going to wrong location */
ctx->default_log_prefix =
p_strconcat(pool, service->name, ": ", NULL);
master_service_init_log(service, ctx->default_log_prefix);
}
dict_drivers_register_builtin();
return ctx;
}
struct auth_master_connection *
mail_storage_service_get_auth_conn(struct mail_storage_service_ctx *ctx)
{
i_assert(ctx->conn != NULL);
return ctx->conn;
}
static enum mail_storage_service_flags
mail_storage_service_input_get_flags(struct mail_storage_service_ctx *ctx,
const struct mail_storage_service_input *input)
{
enum mail_storage_service_flags flags;
flags = (ctx->flags & ~input->flags_override_remove) |
input->flags_override_add;
if (input->no_userdb_lookup) {
/* FIXME: for API backwards compatibility only */
flags &= ~MAIL_STORAGE_SERVICE_FLAG_USERDB_LOOKUP;
}
return flags;
}
int mail_storage_service_read_settings(struct mail_storage_service_ctx *ctx,
const struct mail_storage_service_input *input,
pool_t pool,
const struct setting_parser_info **user_info_r,
const struct setting_parser_context **parser_r,
const char **error_r)
{
struct master_service_settings_input set_input;
const struct setting_parser_info *const *roots;
struct master_service_settings_output set_output;
const struct dynamic_settings_parser *dyn_parsers;
enum mail_storage_service_flags flags;
unsigned int i;
flags = input == NULL ? ctx->flags :
mail_storage_service_input_get_flags(ctx, input);
memset(&set_input, 0, sizeof(set_input));
set_input.roots = ctx->set_roots;
set_input.preserve_user = TRUE;
/* settings reader may exec doveconf, which is going to clear
environment, and if we're not doing a userdb lookup we want to
use $HOME */
set_input.preserve_home =
(flags & MAIL_STORAGE_SERVICE_FLAG_USERDB_LOOKUP) == 0;
set_input.use_sysexits =
(flags & MAIL_STORAGE_SERVICE_FLAG_USE_SYSEXITS) != 0;
if (input != NULL) {
set_input.module = input->module;
set_input.service = input->service;
set_input.username = input->username;
set_input.local_ip = input->local_ip;
set_input.remote_ip = input->remote_ip;
}
if (ctx->set_cache == NULL) {
ctx->set_cache_module = p_strdup(ctx->pool, set_input.module);
ctx->set_cache_service = p_strdup(ctx->pool, set_input.service);
ctx->set_cache = master_service_settings_cache_init(
ctx->service, set_input.module, set_input.service);
} else {
/* already looked up settings at least once.
we really shouldn't be execing anymore. */
set_input.never_exec = TRUE;
}
dyn_parsers = mail_storage_get_dynamic_parsers(pool);
if (null_strcmp(set_input.module, ctx->set_cache_module) == 0 &&
null_strcmp(set_input.service, ctx->set_cache_service) == 0) {
if (master_service_settings_cache_read(ctx->set_cache,
&set_input, dyn_parsers,
parser_r, error_r) < 0) {
*error_r = t_strdup_printf(
"Error reading configuration: %s", *error_r);
return -1;
}
} else {
settings_parser_dyn_update(pool, &set_input.roots, dyn_parsers);
if (master_service_settings_read(ctx->service, &set_input,
&set_output, error_r) < 0) {
*error_r = t_strdup_printf(
"Error reading configuration: %s", *error_r);
return -1;
}
*parser_r = ctx->service->set_parser;
}
roots = settings_parser_get_roots(*parser_r);
for (i = 0; roots[i] != NULL; i++) {
if (strcmp(roots[i]->module_name,
mail_user_setting_parser_info.module_name) == 0) {
*user_info_r = roots[i];
return 0;
}
}
i_unreached();
return -1;
}
static void
mail_storage_service_first_init(struct mail_storage_service_ctx *ctx,
const struct setting_parser_info *user_info,
const struct mail_user_settings *user_set)
{
enum auth_master_flags flags = 0;
i_assert(ctx->conn == NULL);
ctx->debug = mail_user_set_get_mail_debug(user_info, user_set);
if (ctx->debug)
flags |= AUTH_MASTER_FLAG_DEBUG;
if ((ctx->flags & MAIL_STORAGE_SERVICE_FLAG_NO_IDLE_TIMEOUT) != 0)
flags |= AUTH_MASTER_FLAG_NO_IDLE_TIMEOUT;
ctx->conn = auth_master_init(user_set->auth_socket_path, flags);
i_assert(mail_user_auth_master_conn == NULL);
mail_user_auth_master_conn = ctx->conn;
}
static void
mail_storage_service_load_modules(struct mail_storage_service_ctx *ctx,
const struct setting_parser_info *user_info,
const struct mail_user_settings *user_set)
{
struct module_dir_load_settings mod_set;
if (*user_set->mail_plugins == '\0')
return;
if ((ctx->flags & MAIL_STORAGE_SERVICE_FLAG_NO_PLUGINS) != 0)
return;
memset(&mod_set, 0, sizeof(mod_set));
mod_set.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;
}
int mail_storage_service_lookup(struct mail_storage_service_ctx *ctx,
const struct mail_storage_service_input *input,
struct mail_storage_service_user **user_r,
const char **error_r)
{
enum mail_storage_service_flags flags;
struct mail_storage_service_user *user;
const char *username = input->username;
const struct setting_parser_info *user_info;
const struct mail_user_settings *user_set;
const char *const *userdb_fields, *error;
struct auth_user_reply reply;
const struct setting_parser_context *set_parser;
void **sets;
pool_t user_pool, temp_pool;
int ret = 1;
user_pool = pool_alloconly_create("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. */
if (seteuid(0) < 0)
i_fatal("seteuid(0) failed: %m");
}
if (mail_storage_service_read_settings(ctx, input, user_pool,
&user_info, &set_parser,
&error) < 0) {
i_error("user %s: %s", username, error);
pool_unref(&user_pool);
*error_r = MAIL_ERRSTR_CRITICAL_MSG;
return -1;
}
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));
}
sets = master_service_settings_parser_get_others(master_service,
set_parser);
user_set = sets[0];
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->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("user %s: Invalid settings in userdb: %s",
username, error);
*error_r = ERRSTR_INVALID_USER_SETTINGS;
ret = -2;
}
}
pool_unref(&temp_pool);
/* load per-user plugins */
if (ret > 0) {
mail_storage_service_load_modules(ctx, user_info,
user->user_set);
}
*user_r = user;
return ret;
}
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;
}
int mail_storage_service_next(struct mail_storage_service_ctx *ctx,
struct mail_storage_service_user *user,
struct mail_user **mail_user_r)
{
struct mail_storage_service_privileges priv;
const char *error;
unsigned int len;
bool disallow_root =
(user->flags & MAIL_STORAGE_SERVICE_FLAG_DISALLOW_ROOT) != 0;
bool temp_priv_drop =
(user->flags & MAIL_STORAGE_SERVICE_FLAG_TEMP_PRIV_DROP) != 0;
bool use_chroot;
if (service_parse_privileges(ctx, user, &priv, &error) < 0) {
i_error("user %s: %s", user->input.username, error);
return -2;
}
if (*priv.home != '/' && *priv.home != '\0') {
i_error("user %s: "
"Relative home directory paths not supported: %s",
user->input.username, priv.home);
return -2;
}
/* we can't chroot if we want to switch between users. there's
not much point either (from security point of view). but if we're
already chrooted, we'll just have to continue and hope that the
current chroot is the same as the wanted chroot */
use_chroot = !temp_priv_drop ||
restrict_access_get_current_chroot() != NULL;
len = strlen(priv.chroot);
if (len > 2 && strcmp(priv.chroot + len - 2, "/.") == 0 &&
strncmp(priv.home, priv.chroot, len - 2) == 0) {
/* mail_chroot = /chroot/. means that the home dir already
contains the chroot dir. remove it from home. */
if (use_chroot) {
priv.home += len - 2;
if (*priv.home == '\0')
priv.home = "/";
priv.chroot = t_strndup(priv.chroot, len - 2);
set_keyval(ctx, user, "mail_home", priv.home);
set_keyval(ctx, user, "mail_chroot", priv.chroot);
}
} else if (len > 0 && !use_chroot) {
/* we're not going to chroot. fix home directory so we can
access it. */
if (*priv.home == '\0' || strcmp(priv.home, "/") == 0)
priv.home = priv.chroot;
else
priv.home = t_strconcat(priv.chroot, priv.home, NULL);
priv.chroot = "";
set_keyval(ctx, user, "mail_home", priv.home);
}
if ((user->flags & MAIL_STORAGE_SERVICE_FLAG_NO_LOG_INIT) == 0)
mail_storage_service_init_log(ctx, user, &priv);
if ((user->flags & MAIL_STORAGE_SERVICE_FLAG_NO_RESTRICT_ACCESS) == 0) {
if (service_drop_privileges(user, &priv,
disallow_root, temp_priv_drop,
FALSE, &error) < 0) {
i_error("user %s: Couldn't drop privileges: %s",
user->input.username, error);
return -1;
}
if (!temp_priv_drop ||
(user->flags & MAIL_STORAGE_SERVICE_FLAG_ENABLE_CORE_DUMPS) != 0)
restrict_access_allow_coredumps(TRUE);
}
/* privileges are dropped. initialize plugins that haven't been
initialized yet. */
module_dir_init(mail_storage_service_modules);
if (mail_storage_service_init_post(ctx, user, &priv,
mail_user_r, &error) < 0) {
i_error("user %s: Initialization failed: %s",
user->input.username, error);
return -2;
}
return 0;
}
void mail_storage_service_restrict_setenv(struct mail_storage_service_ctx *ctx,
struct mail_storage_service_user *user)
{
struct mail_storage_service_privileges priv;
const char *error;
if (service_parse_privileges(ctx, user, &priv, &error) < 0)
i_fatal("user %s: %s", user->input.username, error);
if (service_drop_privileges(user, &priv,
FALSE, FALSE, TRUE, &error) < 0)
i_fatal("user %s: %s", user->input.username, error);
}
int mail_storage_service_lookup_next(struct mail_storage_service_ctx *ctx,
const struct mail_storage_service_input *input,
struct mail_storage_service_user **user_r,
struct mail_user **mail_user_r,
const char **error_r)
{
struct mail_storage_service_user *user;
int ret;
ret = mail_storage_service_lookup(ctx, input, &user, error_r);
if (ret <= 0)
return ret;
ret = mail_storage_service_next(ctx, user, mail_user_r);
if (ret < 0) {
mail_storage_service_user_free(&user);
*error_r = ret == -2 ? ERRSTR_INVALID_USER_SETTINGS :
MAIL_ERRSTR_CRITICAL_MSG;
return ret;
}
*user_r = user;
return 1;
}
void mail_storage_service_user_free(struct mail_storage_service_user **_user)
{
struct mail_storage_service_user *user = *_user;
*_user = NULL;
if (user->ioloop_ctx != NULL) {
io_loop_context_remove_callbacks(user->ioloop_ctx,
mail_storage_service_io_activate,
mail_storage_service_io_deactivate, user);
io_loop_context_unref(&user->ioloop_ctx);
}
settings_parser_deinit(&user->set_parser);
pool_unref(&user->pool);
}
void mail_storage_service_init_settings(struct mail_storage_service_ctx *ctx,
const struct mail_storage_service_input *input)
{
const struct setting_parser_info *user_info;
const struct mail_user_settings *user_set;
const struct setting_parser_context *set_parser;
const char *error;
pool_t temp_pool;
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);
}
void mail_storage_service_all_init(struct mail_storage_service_ctx *ctx)
{
if (ctx->auth_list != NULL)
(void)auth_master_user_list_deinit(&ctx->auth_list);
mail_storage_service_init_settings(ctx, NULL);
ctx->auth_list = auth_master_user_list_init(ctx->conn, "", NULL);
}
int mail_storage_service_all_next(struct mail_storage_service_ctx *ctx,
const char **username_r)
{
i_assert((ctx->flags & MAIL_STORAGE_SERVICE_FLAG_USERDB_LOOKUP) != 0);
*username_r = auth_master_user_list_next(ctx->auth_list);
if (*username_r != NULL)
return 1;
return auth_master_user_list_deinit(&ctx->auth_list);
}
void mail_storage_service_deinit(struct mail_storage_service_ctx **_ctx)
{
struct mail_storage_service_ctx *ctx = *_ctx;
*_ctx = NULL;
if (ctx->auth_list != NULL)
(void)auth_master_user_list_deinit(&ctx->auth_list);
if (ctx->conn != NULL) {
if (mail_user_auth_master_conn == ctx->conn)
mail_user_auth_master_conn = NULL;
auth_master_deinit(&ctx->conn);
}
if (ctx->set_cache != NULL)
master_service_settings_cache_deinit(&ctx->set_cache);
pool_unref(&ctx->pool);
module_dir_unload(&mail_storage_service_modules);
mail_storage_deinit();
dict_drivers_unregister_builtin();
}
void **mail_storage_service_user_get_set(struct mail_storage_service_user *user)
{
return 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;
}