mail-storage-service.c revision 6727b100f6afdde039efc755a7cf45c93362f347
2454dfa32c93c20a8522c6ed42fe057baaac9f9aStephan Bosch/* Copyright (c) 2009-2011 Dovecot authors, see the included COPYING file */
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen#include "lib.h"
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen#include "ioloop.h"
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen#include "array.h"
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen#include "hostpid.h"
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen#include "module-dir.h"
2eccb2637d0153bb7f9ad39a70f254cece74342cTimo Sirainen#include "restrict-access.h"
2eccb2637d0153bb7f9ad39a70f254cece74342cTimo Sirainen#include "eacces-error.h"
7877db7b5daad125b6cb3e015574f33871c9a51bTimo Sirainen#include "ipwd.h"
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen#include "str.h"
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen#include "var-expand.h"
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen#include "dict.h"
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen#include "settings-parser.h"
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen#include "auth-master.h"
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen#include "master-service-private.h"
2eccb2637d0153bb7f9ad39a70f254cece74342cTimo Sirainen#include "master-service-settings.h"
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen#include "master-service-settings-cache.h"
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen#include "mail-user.h"
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen#include "mail-namespace.h"
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen#include "mail-storage.h"
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen#include "mail-storage-service.h"
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen#include <stdlib.h>
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen#include <sys/stat.h>
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen#ifdef HAVE_SYS_TIME_H
4e8e7a93628b4ed60aaaa47c6f72c1433f21e81dTimo Sirainen# include <sys/time.h>
a01faca549a403b2eda938cea0b1fb76c3ff44b6Aki Tuomi#endif
a01faca549a403b2eda938cea0b1fb76c3ff44b6Aki Tuomi#ifdef HAVE_SYS_RESOURCE_H
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen# include <sys/resource.h>
a01faca549a403b2eda938cea0b1fb76c3ff44b6Aki Tuomi#endif
a01faca549a403b2eda938cea0b1fb76c3ff44b6Aki Tuomi
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen/* If time moves backwards more than this, kill ourself instead of sleeping. */
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen#define MAX_TIME_BACKWARDS_SLEEP 5
b7324e421e2132cbbf753e6fdbe675bbaecdf929Timo Sirainen#define MAX_NOWARN_FORWARD_SECS 10
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen#define ERRSTR_INVALID_USER_SETTINGS \
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen "Invalid user settings. Refer to server log for more information."
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainenstruct mail_storage_service_ctx {
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen pool_t pool;
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen struct master_service *service;
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen const char *default_log_prefix;
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen struct auth_master_connection *conn;
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen struct auth_master_user_list_ctx *auth_list;
2eccb2637d0153bb7f9ad39a70f254cece74342cTimo Sirainen const struct setting_parser_info **set_roots;
2eccb2637d0153bb7f9ad39a70f254cece74342cTimo Sirainen enum mail_storage_service_flags flags;
2eccb2637d0153bb7f9ad39a70f254cece74342cTimo Sirainen
2eccb2637d0153bb7f9ad39a70f254cece74342cTimo Sirainen const char *set_cache_module, *set_cache_service;
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen struct master_service_settings_cache *set_cache;
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen unsigned int debug:1;
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen unsigned int log_initialized:1;
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen};
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainenstruct mail_storage_service_user {
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen pool_t pool;
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen struct mail_storage_service_ctx *service_ctx;
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen struct mail_storage_service_input input;
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen enum mail_storage_service_flags flags;
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen struct ioloop_context *ioloop_ctx;
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen const char *log_prefix;
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen const char *system_groups_user, *uid_source, *gid_source;
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen const struct mail_user_settings *user_set;
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen const struct setting_parser_info *user_info;
4e8e7a93628b4ed60aaaa47c6f72c1433f21e81dTimo Sirainen struct setting_parser_context *set_parser;
4e8e7a93628b4ed60aaaa47c6f72c1433f21e81dTimo Sirainen};
a01faca549a403b2eda938cea0b1fb76c3ff44b6Aki Tuomi
7877db7b5daad125b6cb3e015574f33871c9a51bTimo Sirainenstruct module *mail_storage_service_modules = NULL;
a01faca549a403b2eda938cea0b1fb76c3ff44b6Aki Tuomi
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainenstatic bool
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainenmail_user_set_get_mail_debug(const struct setting_parser_info *user_info,
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen const struct mail_user_settings *user_set)
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen{
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen const struct mail_storage_settings *mail_set;
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen mail_set = mail_user_set_get_driver_settings(user_info, user_set,
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen MAIL_STORAGE_SET_DRIVER_NAME);
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen return mail_set->mail_debug;
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen}
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainenstatic void set_keyval(struct mail_storage_service_ctx *ctx,
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen struct mail_storage_service_user *user,
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen const char *key, const char *value)
86bdb644d147a73df85abce4325254d694217a5fTimo Sirainen{
a01faca549a403b2eda938cea0b1fb76c3ff44b6Aki Tuomi struct setting_parser_context *set_parser = user->set_parser;
a01faca549a403b2eda938cea0b1fb76c3ff44b6Aki Tuomi const char *str;
a01faca549a403b2eda938cea0b1fb76c3ff44b6Aki Tuomi
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen if (master_service_set_has_config_override(ctx->service, key)) {
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen /* this setting was already overridden with -o parameter */
7877db7b5daad125b6cb3e015574f33871c9a51bTimo Sirainen if (mail_user_set_get_mail_debug(user->user_info,
7877db7b5daad125b6cb3e015574f33871c9a51bTimo Sirainen user->user_set)) {
7877db7b5daad125b6cb3e015574f33871c9a51bTimo Sirainen i_debug("Ignoring overridden (-o) userdb setting: %s",
7877db7b5daad125b6cb3e015574f33871c9a51bTimo Sirainen key);
7877db7b5daad125b6cb3e015574f33871c9a51bTimo Sirainen }
7877db7b5daad125b6cb3e015574f33871c9a51bTimo Sirainen return;
7877db7b5daad125b6cb3e015574f33871c9a51bTimo Sirainen }
7877db7b5daad125b6cb3e015574f33871c9a51bTimo Sirainen
7877db7b5daad125b6cb3e015574f33871c9a51bTimo Sirainen str = t_strconcat(key, "=", value, NULL);
7877db7b5daad125b6cb3e015574f33871c9a51bTimo Sirainen if (settings_parse_line(set_parser, str) < 0) {
7877db7b5daad125b6cb3e015574f33871c9a51bTimo Sirainen i_fatal("Invalid userdb input '%s': %s", str,
7877db7b5daad125b6cb3e015574f33871c9a51bTimo Sirainen settings_parser_get_error(set_parser));
7877db7b5daad125b6cb3e015574f33871c9a51bTimo Sirainen }
7877db7b5daad125b6cb3e015574f33871c9a51bTimo Sirainen}
7877db7b5daad125b6cb3e015574f33871c9a51bTimo Sirainen
7877db7b5daad125b6cb3e015574f33871c9a51bTimo Sirainenstatic int set_line(struct mail_storage_service_ctx *ctx,
7877db7b5daad125b6cb3e015574f33871c9a51bTimo Sirainen struct mail_storage_service_user *user,
7877db7b5daad125b6cb3e015574f33871c9a51bTimo Sirainen const char *line)
7877db7b5daad125b6cb3e015574f33871c9a51bTimo Sirainen{
7877db7b5daad125b6cb3e015574f33871c9a51bTimo Sirainen struct setting_parser_context *set_parser = user->set_parser;
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen bool mail_debug;
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen const char *key, *orig_key, *append_value = NULL;
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen unsigned int len;
7877db7b5daad125b6cb3e015574f33871c9a51bTimo Sirainen int ret;
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen mail_debug = mail_user_set_get_mail_debug(user->user_info,
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen user->user_set);
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen if (strchr(line, '=') == NULL)
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen line = t_strconcat(line, "=yes", NULL);
7877db7b5daad125b6cb3e015574f33871c9a51bTimo Sirainen orig_key = key = t_strcut(line, '=');
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen len = strlen(key);
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen if (len > 0 && key[len-1] == '+') {
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen /* key+=value */
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen append_value = line + len + 1;
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen key = t_strndup(key, len-1);
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen line++;
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen }
7877db7b5daad125b6cb3e015574f33871c9a51bTimo Sirainen
791fb70b3255a11a91ce0c2dc3ae1460d4cf8459Timo Sirainen if (!settings_parse_is_valid_key(set_parser, key)) {
a18e2525cb076066784967d6c8118a01dd38ac6bTimo Sirainen /* assume it's a plugin setting */
a18e2525cb076066784967d6c8118a01dd38ac6bTimo Sirainen key = t_strconcat("plugin/", key, NULL);
2eccb2637d0153bb7f9ad39a70f254cece74342cTimo Sirainen line = t_strconcat("plugin/", line, NULL);
2eccb2637d0153bb7f9ad39a70f254cece74342cTimo Sirainen }
2eccb2637d0153bb7f9ad39a70f254cece74342cTimo Sirainen
a18e2525cb076066784967d6c8118a01dd38ac6bTimo Sirainen if (master_service_set_has_config_override(ctx->service, key)) {
a18e2525cb076066784967d6c8118a01dd38ac6bTimo Sirainen /* this setting was already overridden with -o parameter */
7877db7b5daad125b6cb3e015574f33871c9a51bTimo Sirainen if (mail_debug) {
2eccb2637d0153bb7f9ad39a70f254cece74342cTimo Sirainen i_debug("Ignoring overridden (-o) userdb setting: %s",
7877db7b5daad125b6cb3e015574f33871c9a51bTimo Sirainen key);
7877db7b5daad125b6cb3e015574f33871c9a51bTimo Sirainen }
202b4674243a4a4826c35ed4d089831985c47256Timo Sirainen return 1;
7877db7b5daad125b6cb3e015574f33871c9a51bTimo Sirainen }
7877db7b5daad125b6cb3e015574f33871c9a51bTimo Sirainen
7877db7b5daad125b6cb3e015574f33871c9a51bTimo Sirainen if (append_value != NULL) {
7877db7b5daad125b6cb3e015574f33871c9a51bTimo Sirainen const void *value;
7877db7b5daad125b6cb3e015574f33871c9a51bTimo Sirainen enum setting_type type;
7877db7b5daad125b6cb3e015574f33871c9a51bTimo Sirainen
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen value = settings_parse_get_value(set_parser, key, &type);
95f5b08fa73ddd9a9de40a97aa141e9c74e0645eAki Tuomi if (type == SET_STR) {
95f5b08fa73ddd9a9de40a97aa141e9c74e0645eAki Tuomi const char *const *strp = value;
a01faca549a403b2eda938cea0b1fb76c3ff44b6Aki Tuomi
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen line = t_strdup_printf("%s=%s%s",
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen key, *strp, append_value);
95f5b08fa73ddd9a9de40a97aa141e9c74e0645eAki Tuomi } else {
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen i_error("Ignoring %s userdb setting. "
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen "'+' can only be used for strings.", orig_key);
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen }
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen }
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen ret = settings_parse_line(set_parser, line);
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen if (mail_debug && ret >= 0) {
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen i_debug(ret == 0 ?
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen "Unknown userdb setting: %s" :
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen "Added userdb setting: %s", line);
f6497ac81e6de57870936d538acccb75ce408fc1Timo Sirainen }
f6497ac81e6de57870936d538acccb75ce408fc1Timo Sirainen return ret;
f6497ac81e6de57870936d538acccb75ce408fc1Timo Sirainen}
f6497ac81e6de57870936d538acccb75ce408fc1Timo Sirainen
f6497ac81e6de57870936d538acccb75ce408fc1Timo Sirainenstatic bool validate_chroot(const struct mail_user_settings *user_set,
f6497ac81e6de57870936d538acccb75ce408fc1Timo Sirainen const char *dir)
f6497ac81e6de57870936d538acccb75ce408fc1Timo Sirainen{
f6497ac81e6de57870936d538acccb75ce408fc1Timo Sirainen const char *const *chroot_dirs;
f6497ac81e6de57870936d538acccb75ce408fc1Timo Sirainen
f6497ac81e6de57870936d538acccb75ce408fc1Timo Sirainen if (*dir == '\0')
f6497ac81e6de57870936d538acccb75ce408fc1Timo Sirainen return FALSE;
f6497ac81e6de57870936d538acccb75ce408fc1Timo Sirainen
f6497ac81e6de57870936d538acccb75ce408fc1Timo Sirainen if (*user_set->valid_chroot_dirs == '\0')
f6497ac81e6de57870936d538acccb75ce408fc1Timo Sirainen return FALSE;
f6497ac81e6de57870936d538acccb75ce408fc1Timo Sirainen
f6497ac81e6de57870936d538acccb75ce408fc1Timo Sirainen chroot_dirs = t_strsplit(user_set->valid_chroot_dirs, ":");
f6497ac81e6de57870936d538acccb75ce408fc1Timo Sirainen while (*chroot_dirs != NULL) {
f6497ac81e6de57870936d538acccb75ce408fc1Timo Sirainen if (**chroot_dirs != '\0' &&
f6497ac81e6de57870936d538acccb75ce408fc1Timo Sirainen strncmp(dir, *chroot_dirs, strlen(*chroot_dirs)) == 0)
f6497ac81e6de57870936d538acccb75ce408fc1Timo Sirainen return TRUE;
f6497ac81e6de57870936d538acccb75ce408fc1Timo Sirainen chroot_dirs++;
f6497ac81e6de57870936d538acccb75ce408fc1Timo Sirainen }
f6497ac81e6de57870936d538acccb75ce408fc1Timo Sirainen return FALSE;
f6497ac81e6de57870936d538acccb75ce408fc1Timo Sirainen}
f6497ac81e6de57870936d538acccb75ce408fc1Timo Sirainen
f6497ac81e6de57870936d538acccb75ce408fc1Timo Sirainenstatic int
f6497ac81e6de57870936d538acccb75ce408fc1Timo Sirainenuser_reply_handle(struct mail_storage_service_ctx *ctx,
f6497ac81e6de57870936d538acccb75ce408fc1Timo Sirainen struct mail_storage_service_user *user,
f6ae9ae80a1fcf6c8f45ab759f0074caaa66c9c8Timo Sirainen const struct auth_user_reply *reply,
f6ae9ae80a1fcf6c8f45ab759f0074caaa66c9c8Timo Sirainen const char **error_r)
736b1800b0409ba7443d33ecb8d0fb9f8b091660Timo Sirainen{
736b1800b0409ba7443d33ecb8d0fb9f8b091660Timo Sirainen const char *home = reply->home;
736b1800b0409ba7443d33ecb8d0fb9f8b091660Timo Sirainen const char *chroot = reply->chroot;
736b1800b0409ba7443d33ecb8d0fb9f8b091660Timo Sirainen const char *const *str, *line, *p;
736b1800b0409ba7443d33ecb8d0fb9f8b091660Timo Sirainen unsigned int i, count;
736b1800b0409ba7443d33ecb8d0fb9f8b091660Timo Sirainen int ret = 0;
736b1800b0409ba7443d33ecb8d0fb9f8b091660Timo Sirainen
736b1800b0409ba7443d33ecb8d0fb9f8b091660Timo Sirainen if (reply->uid != (uid_t)-1) {
f6ae9ae80a1fcf6c8f45ab759f0074caaa66c9c8Timo Sirainen if (reply->uid == 0) {
736b1800b0409ba7443d33ecb8d0fb9f8b091660Timo Sirainen *error_r = "userdb returned 0 as uid";
736b1800b0409ba7443d33ecb8d0fb9f8b091660Timo Sirainen return -1;
736b1800b0409ba7443d33ecb8d0fb9f8b091660Timo Sirainen }
736b1800b0409ba7443d33ecb8d0fb9f8b091660Timo Sirainen user->uid_source = "userdb lookup";
736b1800b0409ba7443d33ecb8d0fb9f8b091660Timo Sirainen set_keyval(ctx, user, "mail_uid", dec2str(reply->uid));
736b1800b0409ba7443d33ecb8d0fb9f8b091660Timo Sirainen }
736b1800b0409ba7443d33ecb8d0fb9f8b091660Timo Sirainen if (reply->gid != (uid_t)-1) {
736b1800b0409ba7443d33ecb8d0fb9f8b091660Timo Sirainen user->gid_source = "userdb lookup";
f6ae9ae80a1fcf6c8f45ab759f0074caaa66c9c8Timo Sirainen set_keyval(ctx, user, "mail_gid", dec2str(reply->gid));
f6ae9ae80a1fcf6c8f45ab759f0074caaa66c9c8Timo Sirainen }
b7324e421e2132cbbf753e6fdbe675bbaecdf929Timo Sirainen
a18503d5dc0751a1f9785e48438a219d95c0b9c2Timo Sirainen if (home != NULL && chroot == NULL &&
a18503d5dc0751a1f9785e48438a219d95c0b9c2Timo Sirainen *user->user_set->valid_chroot_dirs != '\0' &&
a18503d5dc0751a1f9785e48438a219d95c0b9c2Timo Sirainen (p = strstr(home, "/./")) != NULL) {
a18503d5dc0751a1f9785e48438a219d95c0b9c2Timo Sirainen /* wu-ftpd like <chroot>/./<home> - check only if there's even
a18503d5dc0751a1f9785e48438a219d95c0b9c2Timo Sirainen a possibility of using them (non-empty valid_chroot_dirs) */
a18503d5dc0751a1f9785e48438a219d95c0b9c2Timo Sirainen chroot = t_strdup_until(home, p);
a18503d5dc0751a1f9785e48438a219d95c0b9c2Timo Sirainen home = p + 2;
a18503d5dc0751a1f9785e48438a219d95c0b9c2Timo Sirainen }
a18503d5dc0751a1f9785e48438a219d95c0b9c2Timo Sirainen
a18503d5dc0751a1f9785e48438a219d95c0b9c2Timo Sirainen if (home != NULL)
a18503d5dc0751a1f9785e48438a219d95c0b9c2Timo Sirainen set_keyval(ctx, user, "mail_home", home);
f6ae9ae80a1fcf6c8f45ab759f0074caaa66c9c8Timo Sirainen
f6ae9ae80a1fcf6c8f45ab759f0074caaa66c9c8Timo Sirainen if (chroot != NULL) {
f6ae9ae80a1fcf6c8f45ab759f0074caaa66c9c8Timo Sirainen if (!validate_chroot(user->user_set, chroot)) {
f6ae9ae80a1fcf6c8f45ab759f0074caaa66c9c8Timo Sirainen *error_r = t_strdup_printf(
f6ae9ae80a1fcf6c8f45ab759f0074caaa66c9c8Timo Sirainen "userdb returned invalid chroot directory: %s "
a18503d5dc0751a1f9785e48438a219d95c0b9c2Timo Sirainen "(see valid_chroot_dirs setting)", chroot);
f6ae9ae80a1fcf6c8f45ab759f0074caaa66c9c8Timo Sirainen return -1;
f6ae9ae80a1fcf6c8f45ab759f0074caaa66c9c8Timo Sirainen }
f6ae9ae80a1fcf6c8f45ab759f0074caaa66c9c8Timo Sirainen set_keyval(ctx, user, "mail_chroot", chroot);
736b1800b0409ba7443d33ecb8d0fb9f8b091660Timo Sirainen }
736b1800b0409ba7443d33ecb8d0fb9f8b091660Timo Sirainen
736b1800b0409ba7443d33ecb8d0fb9f8b091660Timo Sirainen str = array_get(&reply->extra_fields, &count);
736b1800b0409ba7443d33ecb8d0fb9f8b091660Timo Sirainen for (i = 0; i < count && ret >= 0; i++) {
736b1800b0409ba7443d33ecb8d0fb9f8b091660Timo Sirainen line = str[i];
736b1800b0409ba7443d33ecb8d0fb9f8b091660Timo Sirainen if (strncmp(line, "system_groups_user=", 19) == 0) {
736b1800b0409ba7443d33ecb8d0fb9f8b091660Timo Sirainen user->system_groups_user =
736b1800b0409ba7443d33ecb8d0fb9f8b091660Timo Sirainen p_strdup(user->pool, line + 19);
736b1800b0409ba7443d33ecb8d0fb9f8b091660Timo Sirainen } else if (strncmp(line, "nice=", 5) == 0) {
736b1800b0409ba7443d33ecb8d0fb9f8b091660Timo Sirainen#ifdef HAVE_SETPRIORITY
736b1800b0409ba7443d33ecb8d0fb9f8b091660Timo Sirainen int n = atoi(line + 5);
736b1800b0409ba7443d33ecb8d0fb9f8b091660Timo Sirainen
a01faca549a403b2eda938cea0b1fb76c3ff44b6Aki Tuomi if (n != 0) {
a01faca549a403b2eda938cea0b1fb76c3ff44b6Aki Tuomi if (setpriority(PRIO_PROCESS, 0, n) < 0)
a01faca549a403b2eda938cea0b1fb76c3ff44b6Aki Tuomi i_error("setpriority(%d) failed: %m", n);
736b1800b0409ba7443d33ecb8d0fb9f8b091660Timo Sirainen }
736b1800b0409ba7443d33ecb8d0fb9f8b091660Timo Sirainen#endif
736b1800b0409ba7443d33ecb8d0fb9f8b091660Timo Sirainen } else T_BEGIN {
736b1800b0409ba7443d33ecb8d0fb9f8b091660Timo Sirainen ret = set_line(ctx, user, line);
736b1800b0409ba7443d33ecb8d0fb9f8b091660Timo Sirainen } T_END;
f6ae9ae80a1fcf6c8f45ab759f0074caaa66c9c8Timo Sirainen }
736b1800b0409ba7443d33ecb8d0fb9f8b091660Timo Sirainen
736b1800b0409ba7443d33ecb8d0fb9f8b091660Timo Sirainen if (ret < 0) {
736b1800b0409ba7443d33ecb8d0fb9f8b091660Timo Sirainen *error_r = t_strdup_printf("Invalid userdb input '%s': %s",
10972f2a15f5538860fcc1d4adda227d59d2d757Timo Sirainen str[i], settings_parser_get_error(user->set_parser));
10972f2a15f5538860fcc1d4adda227d59d2d757Timo Sirainen }
10972f2a15f5538860fcc1d4adda227d59d2d757Timo Sirainen return ret;
10972f2a15f5538860fcc1d4adda227d59d2d757Timo Sirainen}
10972f2a15f5538860fcc1d4adda227d59d2d757Timo Sirainen
10972f2a15f5538860fcc1d4adda227d59d2d757Timo Sirainenstatic int
10972f2a15f5538860fcc1d4adda227d59d2d757Timo Sirainenservice_auth_userdb_lookup(struct mail_storage_service_ctx *ctx,
10972f2a15f5538860fcc1d4adda227d59d2d757Timo Sirainen const struct mail_storage_service_input *input,
10972f2a15f5538860fcc1d4adda227d59d2d757Timo Sirainen pool_t pool, const char **user,
10972f2a15f5538860fcc1d4adda227d59d2d757Timo Sirainen const char *const **fields_r,
bb869cc24b24a8df84a43154c628785d6aee784cTimo Sirainen const char **error_r)
bb869cc24b24a8df84a43154c628785d6aee784cTimo Sirainen{
bb869cc24b24a8df84a43154c628785d6aee784cTimo Sirainen struct auth_user_info info;
bb869cc24b24a8df84a43154c628785d6aee784cTimo Sirainen const char *new_username;
bb869cc24b24a8df84a43154c628785d6aee784cTimo Sirainen int ret;
bb869cc24b24a8df84a43154c628785d6aee784cTimo Sirainen
bb869cc24b24a8df84a43154c628785d6aee784cTimo Sirainen memset(&info, 0, sizeof(info));
bb869cc24b24a8df84a43154c628785d6aee784cTimo Sirainen info.service = ctx->service->name;
bb869cc24b24a8df84a43154c628785d6aee784cTimo Sirainen info.local_ip = input->local_ip;
bb869cc24b24a8df84a43154c628785d6aee784cTimo Sirainen info.remote_ip = input->remote_ip;
bb869cc24b24a8df84a43154c628785d6aee784cTimo Sirainen
bb869cc24b24a8df84a43154c628785d6aee784cTimo Sirainen ret = auth_master_user_lookup(ctx->conn, *user, &info, pool,
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen &new_username, fields_r);
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen if (ret > 0) {
4e8e7a93628b4ed60aaaa47c6f72c1433f21e81dTimo Sirainen if (strcmp(*user, new_username) != 0) {
7877db7b5daad125b6cb3e015574f33871c9a51bTimo Sirainen if (ctx->debug)
f6497ac81e6de57870936d538acccb75ce408fc1Timo Sirainen i_debug("changed username to %s", new_username);
f6ae9ae80a1fcf6c8f45ab759f0074caaa66c9c8Timo Sirainen *user = t_strdup(new_username);
736b1800b0409ba7443d33ecb8d0fb9f8b091660Timo Sirainen }
10972f2a15f5538860fcc1d4adda227d59d2d757Timo Sirainen *user = new_username;
bb869cc24b24a8df84a43154c628785d6aee784cTimo Sirainen } else if (ret == 0)
ae9691f7ef36d5272d72c90fa51393dfea5dd126Timo Sirainen *error_r = "Unknown user";
else if (**fields_r != NULL) {
*error_r = t_strdup(**fields_r);
ret = -2;
} else {
*error_r = MAIL_ERRSTR_CRITICAL_MSG;
}
return ret;
}
static bool parse_uid(const char *str, uid_t *uid_r, const char **error_r)
{
struct passwd pw;
if (str_to_uid(str, uid_r) == 0)
return TRUE;
switch (i_getpwnam(str, &pw)) {
case -1:
*error_r = t_strdup_printf("getpwnam(%s) failed: %m", str);
return FALSE;
case 0:
*error_r = t_strconcat("Unknown UNIX UID user: ", str, NULL);
return FALSE;
default:
*uid_r = pw.pw_uid;
return TRUE;
}
}
static bool parse_gid(const char *str, gid_t *gid_r, const char **error_r)
{
struct group gr;
if (str_to_gid(str, gid_r) == 0)
return TRUE;
switch (i_getgrnam(str, &gr)) {
case -1:
*error_r = t_strdup_printf("getgrnam(%s) failed: %m", str);
return FALSE;
case 0:
*error_r = t_strconcat("Unknown UNIX GID group: ", str, NULL);
return FALSE;
default:
*gid_r = gr.gr_gid;
return TRUE;
}
}
static int
service_drop_privileges(struct mail_storage_service_user *user,
const struct mail_user_settings *set,
const char *home, const char *chroot,
bool disallow_root, bool keep_setuid_root,
bool setenv_only, const char **error_r)
{
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 (*set->mail_uid != '\0') {
if (!parse_uid(set->mail_uid, &rset.uid, &error)) {
*error_r = t_strdup_printf("%s (from %s)", error,
user->uid_source);
return -1;
}
if (rset.uid < (uid_t)set->first_valid_uid ||
(set->last_valid_uid != 0 &&
rset.uid > (uid_t)set->last_valid_uid)) {
*error_r = t_strdup_printf(
"Mail access for users with UID %s not permitted "
"(see first_valid_uid in config file, uid from %s).",
dec2str(rset.uid), user->uid_source);
return -1;
}
rset.uid_source = user->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 (*set->mail_gid != '\0') {
if (!parse_gid(set->mail_gid, &rset.gid, &error)) {
*error_r = t_strdup_printf("%s (from %s)", error,
user->gid_source);
return -1;
}
if (rset.gid < (gid_t)set->first_valid_gid ||
(set->last_valid_gid != 0 &&
rset.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(rset.gid), user->gid_source);
return -1;
}
rset.gid_source = user->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 = *chroot == '\0' ? NULL : 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, 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, *home == '\0' ? NULL : 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,
const char *home, struct mail_user **mail_user_r,
const char **error_r)
{
const struct mail_storage_settings *mail_set;
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, geteuid(), ctx->service->name,
&user->input.local_ip, &user->input.remote_ip);
mail_set = mail_user_set_get_storage_set(mail_user);
if (mail_set->mail_debug) {
i_debug("Effective uid=%s, gid=%s, home=%s",
dec2str(geteuid()), dec2str(getegid()), home);
}
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 const struct var_expand_table *
get_var_expand_table(struct master_service *service,
struct mail_storage_service_input *input)
{
static struct var_expand_table static_tab[] = {
{ 'u', NULL, "user" },
{ 'n', NULL, "username" },
{ 'd', NULL, "domain" },
{ 's', NULL, "service" },
{ 'l', NULL, "lip" },
{ 'r', NULL, "rip" },
{ 'p', NULL, "pid" },
{ 'i', NULL, "uid" },
{ '\0', NULL, NULL }
};
struct var_expand_table *tab;
tab = t_malloc(sizeof(static_tab));
memcpy(tab, static_tab, sizeof(static_tab));
tab[0].value = input->username;
tab[1].value = t_strcut(input->username, '@');
tab[2].value = strchr(input->username, '@');
if (tab[2].value != NULL) tab[2].value++;
tab[3].value = service->name;
tab[4].value = net_ip2addr(&input->local_ip);
tab[5].value = net_ip2addr(&input->remote_ip);
tab[6].value = my_pid;
tab[7].value = dec2str(geteuid());
return tab;
}
static const char *
user_expand_varstr(struct master_service *service,
struct mail_storage_service_input *input, const char *str)
{
string_t *ret;
if (*str == SETTING_STRVAR_EXPANDED[0])
return str + 1;
i_assert(*str == SETTING_STRVAR_UNEXPANDED[0]);
ret = t_str_new(256);
var_expand(ret, str + 1, get_var_expand_table(service, input));
return str_c(ret);
}
static void mail_storage_service_io_activate(void *context)
{
struct mail_storage_service_user *user = context;
i_set_failure_prefix(user->log_prefix);
}
static void mail_storage_service_io_deactivate(void *context)
{
struct mail_storage_service_user *user = context;
i_set_failure_prefix(user->service_ctx->default_log_prefix);
}
static void
mail_storage_service_init_log(struct mail_storage_service_ctx *ctx,
struct mail_storage_service_user *user)
{
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));
user->log_prefix = p_strdup(user->pool, str_c(str));
} T_END;
master_service_init_log(ctx->service, user->log_prefix);
user->ioloop_ctx = io_loop_context_new(current_ioloop);
io_loop_context_add_callbacks(user->ioloop_ctx,
mail_storage_service_io_activate,
mail_storage_service_io_deactivate,
user);
}
static void mail_storage_service_time_moved(time_t old_time, time_t new_time)
{
long diff = new_time - old_time;
if (diff > 0) {
if (diff > MAX_NOWARN_FORWARD_SECS)
i_warning("Time jumped forwards %ld seconds", diff);
return;
}
diff = -diff;
if (diff > MAX_TIME_BACKWARDS_SLEEP) {
i_fatal("Time just moved backwards by %ld seconds. "
"This might cause a lot of problems, "
"so I'll just kill myself now. "
"http://wiki2.dovecot.org/TimeMovedBackwards", diff);
} else {
i_error("Time just moved backwards by %ld seconds. "
"I'll sleep now until we're back in present. "
"http://wiki2.dovecot.org/TimeMovedBackwards", diff);
/* Sleep extra second to make sure usecs also grows. */
diff++;
while (diff > 0 && sleep(diff) != 0) {
/* don't use sleep()'s return value, because
it could get us to a long loop in case
interrupts just keep coming */
diff = old_time - time(NULL) + 1;
}
}
}
struct mail_storage_service_ctx *
mail_storage_service_init(struct master_service *service,
const struct setting_parser_info *set_roots[],
enum mail_storage_service_flags flags)
{
struct mail_storage_service_ctx *ctx;
pool_t pool;
unsigned int count;
(void)umask(0077);
io_loop_set_time_moved_callback(current_ioloop,
mail_storage_service_time_moved);
mail_storage_init();
mail_storage_register_all();
mailbox_list_register_all();
pool = pool_alloconly_create("mail storage service", 2048);
ctx = p_new(pool, struct mail_storage_service_ctx, 1);
ctx->pool = pool;
ctx->service = service;
ctx->flags = flags;
/* @UNSAFE */
if (set_roots == NULL)
count = 0;
else
for (count = 0; set_roots[count] != NULL; count++) ;
ctx->set_roots =
p_new(pool, const struct setting_parser_info *, count + 2);
ctx->set_roots[0] = &mail_user_setting_parser_info;
if (set_roots != NULL) {
memcpy(ctx->set_roots + 1, set_roots,
sizeof(*ctx->set_roots) * count);
}
/* do all the global initialization. delay initializing plugins until
we drop privileges the first time. */
if ((flags & MAIL_STORAGE_SERVICE_FLAG_NO_LOG_INIT) == 0) {
/* note: we may not have read any settings yet, so this logging
may still be going to wrong location */
ctx->default_log_prefix =
p_strconcat(pool, service->name, ": ", NULL);
master_service_init_log(service, ctx->default_log_prefix);
}
dict_drivers_register_builtin();
return ctx;
}
struct auth_master_connection *
mail_storage_service_get_auth_conn(struct mail_storage_service_ctx *ctx)
{
i_assert(ctx->conn != NULL);
return ctx->conn;
}
static enum mail_storage_service_flags
mail_storage_service_input_get_flags(struct mail_storage_service_ctx *ctx,
const struct mail_storage_service_input *input)
{
enum mail_storage_service_flags flags;
flags = (ctx->flags & ~input->flags_override_remove) |
input->flags_override_add;
if (input->no_userdb_lookup) {
/* FIXME: for API backwards compatibility only */
flags &= ~MAIL_STORAGE_SERVICE_FLAG_USERDB_LOOKUP;
}
return flags;
}
int mail_storage_service_read_settings(struct mail_storage_service_ctx *ctx,
const struct mail_storage_service_input *input,
pool_t pool,
const struct setting_parser_info **user_info_r,
const struct setting_parser_context **parser_r,
const char **error_r)
{
struct master_service_settings_input set_input;
const struct setting_parser_info *const *roots;
struct master_service_settings_output set_output;
const struct dynamic_settings_parser *dyn_parsers;
enum mail_storage_service_flags flags;
unsigned int i;
flags = input == NULL ? ctx->flags :
mail_storage_service_input_get_flags(ctx, input);
memset(&set_input, 0, sizeof(set_input));
set_input.roots = ctx->set_roots;
set_input.preserve_user = TRUE;
/* settings reader may exec doveconf, which is going to clear
environment, and if we're not doing a userdb lookup we want to
use $HOME */
set_input.preserve_home =
(flags & MAIL_STORAGE_SERVICE_FLAG_USERDB_LOOKUP) == 0;
set_input.use_sysexits =
(flags & MAIL_STORAGE_SERVICE_FLAG_USE_SYSEXITS) != 0;
if (input != NULL) {
set_input.module = input->module;
set_input.service = input->service;
set_input.username = input->username;
set_input.local_ip = input->local_ip;
set_input.remote_ip = input->remote_ip;
}
if (ctx->set_cache == NULL) {
ctx->set_cache_module = p_strdup(ctx->pool, set_input.module);
ctx->set_cache_service = p_strdup(ctx->pool, set_input.service);
ctx->set_cache = master_service_settings_cache_init(
ctx->service, set_input.module, set_input.service);
} else {
/* already looked up settings at least once.
we really shouldn't be execing anymore. */
set_input.never_exec = TRUE;
}
dyn_parsers = mail_storage_get_dynamic_parsers(pool);
if (null_strcmp(set_input.module, ctx->set_cache_module) == 0 &&
null_strcmp(set_input.service, ctx->set_cache_service) == 0) {
if (master_service_settings_cache_read(ctx->set_cache,
&set_input, dyn_parsers,
parser_r, error_r) < 0) {
*error_r = t_strdup_printf(
"Error reading configuration: %s", *error_r);
return -1;
}
} else {
settings_parser_dyn_update(pool, &set_input.roots, dyn_parsers);
if (master_service_settings_read(ctx->service, &set_input,
&set_output, error_r) < 0) {
*error_r = t_strdup_printf(
"Error reading configuration: %s", *error_r);
return -1;
}
*parser_r = ctx->service->set_parser;
}
roots = settings_parser_get_roots(*parser_r);
for (i = 0; roots[i] != NULL; i++) {
if (strcmp(roots[i]->module_name,
mail_user_setting_parser_info.module_name) == 0) {
*user_info_r = roots[i];
return 0;
}
}
i_unreached();
return -1;
}
static void
mail_storage_service_first_init(struct mail_storage_service_ctx *ctx,
const struct setting_parser_info *user_info,
const struct mail_user_settings *user_set)
{
enum auth_master_flags flags = 0;
i_assert(ctx->conn == NULL);
ctx->debug = mail_user_set_get_mail_debug(user_info, user_set);
if (ctx->debug)
flags |= AUTH_MASTER_FLAG_DEBUG;
if ((ctx->flags & MAIL_STORAGE_SERVICE_FLAG_NO_IDLE_TIMEOUT) != 0)
flags |= AUTH_MASTER_FLAG_NO_IDLE_TIMEOUT;
ctx->conn = auth_master_init(user_set->auth_socket_path, flags);
i_assert(mail_user_auth_master_conn == NULL);
mail_user_auth_master_conn = ctx->conn;
}
static void
mail_storage_service_load_modules(struct mail_storage_service_ctx *ctx,
const struct setting_parser_info *user_info,
const struct mail_user_settings *user_set)
{
struct module_dir_load_settings mod_set;
if (*user_set->mail_plugins == '\0')
return;
if ((ctx->flags & MAIL_STORAGE_SERVICE_FLAG_NO_PLUGINS) != 0)
return;
memset(&mod_set, 0, sizeof(mod_set));
mod_set.version = master_service_get_version_string(ctx->service);
mod_set.binary_name = master_service_get_name(ctx->service);
mod_set.setting_name = "mail_plugins";
mod_set.require_init_funcs = TRUE;
mod_set.debug = mail_user_set_get_mail_debug(user_info, user_set);
mail_storage_service_modules =
module_dir_load_missing(mail_storage_service_modules,
user_set->mail_plugin_dir,
user_set->mail_plugins, &mod_set);
}
int mail_storage_service_lookup(struct mail_storage_service_ctx *ctx,
const struct mail_storage_service_input *input,
struct mail_storage_service_user **user_r,
const char **error_r)
{
enum mail_storage_service_flags flags;
struct mail_storage_service_user *user;
const char *username = input->username;
const struct setting_parser_info *user_info;
const struct mail_user_settings *user_set;
const char *const *userdb_fields, *error;
struct auth_user_reply reply;
const struct setting_parser_context *set_parser;
pool_t user_pool, temp_pool;
int ret = 1;
user_pool = pool_alloconly_create("mail storage service user", 1024*5);
if (mail_storage_service_read_settings(ctx, input, user_pool,
&user_info, &set_parser,
&error) < 0) {
i_error("user %s: %s", username, error);
pool_unref(&user_pool);
*error_r = MAIL_ERRSTR_CRITICAL_MSG;
return -1;
}
flags = mail_storage_service_input_get_flags(ctx, input);
if ((flags & MAIL_STORAGE_SERVICE_FLAG_NO_LOG_INIT) == 0 &&
!ctx->log_initialized) {
/* initialize logging again, in case we only read the
settings for the first above */
ctx->log_initialized = TRUE;
master_service_init_log(ctx->service,
t_strconcat(ctx->service->name, ": ", NULL));
}
user_set = settings_parser_get_list(set_parser)[1];
if (ctx->conn == NULL)
mail_storage_service_first_init(ctx, user_info, user_set);
/* load global plugins */
mail_storage_service_load_modules(ctx, user_info, user_set);
temp_pool = pool_alloconly_create("userdb lookup", 2048);
if ((flags & MAIL_STORAGE_SERVICE_FLAG_USERDB_LOOKUP) != 0) {
ret = service_auth_userdb_lookup(ctx, input, temp_pool,
&username, &userdb_fields,
error_r);
if (ret <= 0) {
pool_unref(&temp_pool);
pool_unref(&user_pool);
return ret;
}
} else {
userdb_fields = input->userdb_fields;
}
user = p_new(user_pool, struct mail_storage_service_user, 1);
memset(user_r, 0, sizeof(user_r));
user->service_ctx = ctx;
user->pool = user_pool;
user->input = *input;
user->input.userdb_fields = NULL;
user->input.username = p_strdup(user_pool, username);
user->user_info = user_info;
user->flags = flags;
user->set_parser = settings_parser_dup(set_parser, user_pool);
if (!settings_parser_check(user->set_parser, user_pool, &error))
i_panic("settings_parser_check() failed: %s", error);
user->user_set = settings_parser_get_list(user->set_parser)[1];
user->gid_source = "mail_gid setting";
user->uid_source = "mail_uid setting";
if ((flags & MAIL_STORAGE_SERVICE_FLAG_DEBUG) != 0)
(void)settings_parse_line(user->set_parser, "mail_debug=yes");
if ((flags & MAIL_STORAGE_SERVICE_FLAG_USERDB_LOOKUP) == 0) {
const char *home = getenv("HOME");
if (home != NULL)
set_keyval(ctx, user, "mail_home", home);
}
if (userdb_fields != NULL) {
auth_user_fields_parse(userdb_fields, temp_pool, &reply);
if (user_reply_handle(ctx, user, &reply, &error) < 0) {
i_error("user %s: Invalid settings in userdb: %s",
username, error);
*error_r = ERRSTR_INVALID_USER_SETTINGS;
ret = -2;
}
}
pool_unref(&temp_pool);
/* load per-user plugins */
if (ret > 0) {
mail_storage_service_load_modules(ctx, user_info,
user->user_set);
}
*user_r = user;
return ret;
}
int mail_storage_service_next(struct mail_storage_service_ctx *ctx,
struct mail_storage_service_user *user,
struct mail_user **mail_user_r)
{
const struct mail_user_settings *user_set = user->user_set;
const char *home, *chroot, *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;
/* variable strings are expanded in mail_user_init(),
but we need the home and chroot sooner so do them separately here. */
home = user_expand_varstr(ctx->service, &user->input,
user_set->mail_home);
chroot = user_expand_varstr(ctx->service, &user->input,
user_set->mail_chroot);
if (*home != '/' && *home != '\0') {
i_error("user %s: "
"Relative home directory paths not supported: %s",
user->input.username, 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(chroot);
if (len > 2 && strcmp(chroot + len - 2, "/.") == 0 &&
strncmp(home, chroot, len - 2) == 0) {
/* mail_chroot = /chroot/. means that the home dir already
contains the chroot dir. remove it from home. */
if (use_chroot) {
home += len - 2;
if (*home == '\0')
home = "/";
chroot = t_strndup(chroot, len - 2);
set_keyval(ctx, user, "mail_home", home);
set_keyval(ctx, user, "mail_chroot", chroot);
}
} else if (len > 0 && !use_chroot) {
/* we're not going to chroot. fix home directory so we can
access it. */
if (*home == '\0' || strcmp(home, "/") == 0)
home = chroot;
else
home = t_strconcat(chroot, home, NULL);
chroot = "";
set_keyval(ctx, user, "mail_home", home);
}
if ((user->flags & MAIL_STORAGE_SERVICE_FLAG_NO_LOG_INIT) == 0)
mail_storage_service_init_log(ctx, user);
if ((user->flags & MAIL_STORAGE_SERVICE_FLAG_NO_RESTRICT_ACCESS) == 0) {
if (service_drop_privileges(user, user_set, home, chroot,
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, home,
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)
{
const struct mail_user_settings *user_set = user->user_set;
const char *home, *chroot, *error;
home = user_expand_varstr(ctx->service, &user->input,
user_set->mail_home);
chroot = user_expand_varstr(ctx->service, &user->input,
user_set->mail_chroot);
if (service_drop_privileges(user, user_set, home, chroot,
FALSE, FALSE, TRUE,
&error) < 0)
i_fatal("%s", 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, user);
io_loop_context_unref(&user->ioloop_ctx);
}
settings_parser_deinit(&user->set_parser);
pool_unref(&user->pool);
}
void mail_storage_service_init_settings(struct mail_storage_service_ctx *ctx,
const struct mail_storage_service_input *input)
{
const struct setting_parser_info *user_info;
const struct mail_user_settings *user_set;
const struct setting_parser_context *set_parser;
const char *error;
pool_t temp_pool;
if (ctx->conn != NULL)
return;
temp_pool = pool_alloconly_create("service all settings", 4096);
if (mail_storage_service_read_settings(ctx, input, temp_pool,
&user_info, &set_parser,
&error) < 0)
i_fatal("%s", error);
user_set = settings_parser_get_list(set_parser)[1];
mail_storage_service_first_init(ctx, user_info, user_set);
pool_unref(&temp_pool);
}
unsigned int
mail_storage_service_all_init(struct mail_storage_service_ctx *ctx)
{
if (ctx->auth_list != NULL)
(void)auth_master_user_list_deinit(&ctx->auth_list);
mail_storage_service_init_settings(ctx, NULL);
ctx->auth_list = auth_master_user_list_init(ctx->conn);
return auth_master_user_list_count(ctx->auth_list);
}
int mail_storage_service_all_next(struct mail_storage_service_ctx *ctx,
const char **username_r)
{
i_assert((ctx->flags & MAIL_STORAGE_SERVICE_FLAG_USERDB_LOOKUP) != 0);
*username_r = auth_master_user_list_next(ctx->auth_list);
if (*username_r != NULL)
return 1;
return auth_master_user_list_deinit(&ctx->auth_list);
}
void mail_storage_service_deinit(struct mail_storage_service_ctx **_ctx)
{
struct mail_storage_service_ctx *ctx = *_ctx;
*_ctx = NULL;
if (ctx->auth_list != NULL)
(void)auth_master_user_list_deinit(&ctx->auth_list);
if (ctx->conn != NULL) {
if (mail_user_auth_master_conn == ctx->conn)
mail_user_auth_master_conn = NULL;
auth_master_deinit(&ctx->conn);
}
if (ctx->set_cache != NULL)
master_service_settings_cache_deinit(&ctx->set_cache);
pool_unref(&ctx->pool);
module_dir_unload(&mail_storage_service_modules);
mail_storage_deinit();
dict_drivers_unregister_builtin();
}
void **mail_storage_service_user_get_set(struct mail_storage_service_user *user)
{
return settings_parser_get_list(user->set_parser) + 1;
}
const struct mail_storage_service_input *
mail_storage_service_user_get_input(struct mail_storage_service_user *user)
{
return &user->input;
}
struct setting_parser_context *
mail_storage_service_user_get_settings_parser(struct mail_storage_service_user *user)
{
return user->set_parser;
}
void *mail_storage_service_get_settings(struct master_service *service)
{
void **sets, *set;
T_BEGIN {
sets = master_service_settings_get_others(service);
set = sets[1];
} T_END;
return set;
}