mail-storage-service.c revision 073353fe5b7ddbc44fd2b099e023b84254041083
02c335c23bf5fa225a467c19f2c063fb0dc7b8c3Timo Sirainen/* Copyright (c) 2009-2017 Dovecot authors, see the included COPYING file */
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
08d6658a4e2ec8104cd1307f6baa75fdb07a24f8Mark Washenberger#include "lib.h"
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen#include "ioloop.h"
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen#include "array.h"
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen#include "base64.h"
08d6658a4e2ec8104cd1307f6baa75fdb07a24f8Mark Washenberger#include "hostpid.h"
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen#include "module-dir.h"
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen#include "restrict-access.h"
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen#include "eacces-error.h"
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen#include "ipwd.h"
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen#include "str.h"
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen#include "var-expand.h"
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen#include "dict.h"
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen#include "settings-parser.h"
25d624dd86700c82cd28427f3d3bebe7c8f7f459Timo Sirainen#include "auth-master.h"
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen#include "master-service-private.h"
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen#include "master-service-settings.h"
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen#include "master-service-settings-cache.h"
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen#include "mail-user.h"
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen#include "mail-namespace.h"
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen#include "mail-storage.h"
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen#include "mail-storage-service.h"
714e2da5096fb52b8845d3c79f9bb26225a606c9Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen#include <sys/stat.h>
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen#ifdef HAVE_SYS_TIME_H
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen# include <sys/time.h>
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen#endif
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen#ifdef HAVE_SYS_RESOURCE_H
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen# include <sys/resource.h>
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen#endif
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen/* If time moves backwards more than this, kill ourself instead of sleeping. */
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen#define MAX_TIME_BACKWARDS_SLEEP 5
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen#define MAX_NOWARN_FORWARD_SECS 10
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
04870054863757edf048c81dcce3c5e7dec453cdTimo Sirainen#define ERRSTR_INVALID_USER_SETTINGS \
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen "Invalid user settings. Refer to server log for more information."
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenstruct mail_storage_service_privileges {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen uid_t uid;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen gid_t gid;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen const char *uid_source, *gid_source;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
bd63b5b860658b01b1f46f26d406e1e4a9dc019aTimo Sirainen const char *home;
15a07b47846c47a81d69a14d649564e222d6f742Timo Sirainen const char *chroot;
cd2cd224d3216a243d55c71c298a5b7684de0ac4Timo Sirainen};
c1faff067b29fb48426cb84260adba563e93189aTimo Sirainen
6882df5fbca4a09cdaa95f54d70bb31b5920528cTimo Sirainenstruct mail_storage_service_ctx {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen pool_t pool;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen struct master_service *service;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen const char *default_log_prefix;
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen struct auth_master_connection *conn, *iter_conn;
eacce2276278ce6a8176a9a100807dba50bbfb36Timo Sirainen struct auth_master_user_list_ctx *auth_list;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen const struct setting_parser_info **set_roots;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen enum mail_storage_service_flags flags;
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen const char *set_cache_module, *set_cache_service;
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen struct master_service_settings_cache *set_cache;
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen pool_t userdb_next_pool;
c0a87e5f3316a57e6f915882fa1951d0fbb74a61Timo Sirainen const char *const **userdb_next_fieldsp;
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen bool debug:1;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen bool log_initialized:1;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen bool config_permission_denied:1;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen};
4145cbac82bfc0c8bfeceeca0ef841700117930cTimo Sirainen
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainenstruct mail_storage_service_user {
eacce2276278ce6a8176a9a100807dba50bbfb36Timo Sirainen pool_t pool;
eacce2276278ce6a8176a9a100807dba50bbfb36Timo Sirainen struct mail_storage_service_ctx *service_ctx;
eacce2276278ce6a8176a9a100807dba50bbfb36Timo Sirainen struct mail_storage_service_input input;
eacce2276278ce6a8176a9a100807dba50bbfb36Timo Sirainen enum mail_storage_service_flags flags;
eacce2276278ce6a8176a9a100807dba50bbfb36Timo Sirainen
eacce2276278ce6a8176a9a100807dba50bbfb36Timo Sirainen struct ioloop_context *ioloop_ctx;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen const char *log_prefix, *auth_token, *auth_user;
eacce2276278ce6a8176a9a100807dba50bbfb36Timo Sirainen
eacce2276278ce6a8176a9a100807dba50bbfb36Timo Sirainen const char *system_groups_user, *uid_source, *gid_source;
eacce2276278ce6a8176a9a100807dba50bbfb36Timo Sirainen const struct mail_user_settings *user_set;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen const struct setting_parser_info *user_info;
e1203014de25c8c3d3975a9f4b4a04616df4bba2Timo Sirainen struct setting_parser_context *set_parser;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen unsigned int session_id_counter;
e1203014de25c8c3d3975a9f4b4a04616df4bba2Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen bool anonymous:1;
e10d8b1291090c26b9ef499637e6e632485ca5beTimo Sirainen bool admin:1;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen};
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenstruct module *mail_storage_service_modules = NULL;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenstatic int
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenmail_storage_service_var_expand(struct mail_storage_service_ctx *ctx,
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen string_t *str, const char *format,
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen struct mail_storage_service_user *user,
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen const struct mail_storage_service_input *input,
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen const struct mail_storage_service_privileges *priv,
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen const char **error_r);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenstatic bool
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenmail_user_set_get_mail_debug(const struct setting_parser_info *user_info,
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen const struct mail_user_settings *user_set)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen{
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen const struct mail_storage_settings *mail_set;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen mail_set = mail_user_set_get_driver_settings(user_info, user_set,
411f318ed3a25fa66c1b932e10df43841e2725c9Timo Sirainen MAIL_STORAGE_SET_DRIVER_NAME);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return mail_set->mail_debug;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen}
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenstatic void set_keyval(struct mail_storage_service_ctx *ctx,
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen struct mail_storage_service_user *user,
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen const char *key, const char *value)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen{
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen struct setting_parser_context *set_parser = user->set_parser;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (master_service_set_has_config_override(ctx->service, key)) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen /* this setting was already overridden with -o parameter */
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (mail_user_set_get_mail_debug(user->user_info,
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen user->user_set)) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen i_debug("Ignoring overridden (-o) userdb setting: %s",
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen key);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return;
411f318ed3a25fa66c1b932e10df43841e2725c9Timo Sirainen }
411f318ed3a25fa66c1b932e10df43841e2725c9Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (settings_parse_keyvalue(set_parser, key, value) < 0) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen i_fatal("Invalid userdb input %s=%s: %s", key, value,
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen settings_parser_get_error(set_parser));
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen}
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenstatic int set_line(struct mail_storage_service_ctx *ctx,
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen struct mail_storage_service_user *user,
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen const char *line)
3a9eb305fd4aad5502cb7e64625874385ab5bc19Timo Sirainen{
714e2da5096fb52b8845d3c79f9bb26225a606c9Timo Sirainen struct setting_parser_context *set_parser = user->set_parser;
714e2da5096fb52b8845d3c79f9bb26225a606c9Timo Sirainen bool mail_debug;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen const char *key, *orig_key, *append_value = NULL;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen size_t len;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen int ret;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen mail_debug = mail_user_set_get_mail_debug(user->user_info,
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen user->user_set);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (strchr(line, '=') == NULL)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen line = t_strconcat(line, "=yes", NULL);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen orig_key = key = t_strcut(line, '=');
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen len = strlen(key);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (len > 0 && key[len-1] == '+') {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen /* key+=value */
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen append_value = line + len + 1;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen key = t_strndup(key, len-1);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (!settings_parse_is_valid_key(set_parser, key)) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen /* assume it's a plugin setting */
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen key = t_strconcat("plugin/", key, NULL);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen line = t_strconcat("plugin/", line, NULL);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (master_service_set_has_config_override(ctx->service, key)) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen /* this setting was already overridden with -o parameter */
44f93baa7b8dca7d00bf187cd3db1c15eed384d2Timo Sirainen if (mail_debug) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen i_debug("Ignoring overridden (-o) userdb setting: %s",
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen key);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return 1;
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen }
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen if (append_value != NULL) {
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen const void *value;
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen enum setting_type type;
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen value = settings_parse_get_value(set_parser, key, &type);
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen if (value != NULL && type == SET_STR) {
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen const char *const *strp = value;
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen line = t_strdup_printf("%s=%s%s",
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen key, *strp, append_value);
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen } else {
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen i_error("Ignoring %s userdb setting. "
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen "'+' can only be used for strings.", orig_key);
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen }
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen }
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen ret = settings_parse_line(set_parser, line);
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen if (mail_debug && ret >= 0) {
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen if (strstr(key, "pass") != NULL) {
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen /* possibly a password field (e.g. imapc_password).
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen hide the value. */
c7f6992db44e9cd33b3b0d754833a1503ee9a53fAki Tuomi line = t_strconcat(key, "=<hidden>", NULL);
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen }
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen i_debug(ret == 0 ?
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen "Unknown userdb setting: %s" :
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen "Added userdb setting: %s", line);
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen }
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen return ret;
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen}
c7f6992db44e9cd33b3b0d754833a1503ee9a53fAki Tuomi
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainenstatic bool validate_chroot(const struct mail_user_settings *user_set,
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen const char *dir)
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen{
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen const char *const *chroot_dirs;
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen
45c872f65e4f327ef166c6e2b71bb43e188ac562Timo Sirainen if (*dir == '\0')
45c872f65e4f327ef166c6e2b71bb43e188ac562Timo Sirainen return FALSE;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (*user_set->valid_chroot_dirs == '\0')
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return FALSE;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen chroot_dirs = t_strsplit(user_set->valid_chroot_dirs, ":");
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen while (*chroot_dirs != NULL) {
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen if (**chroot_dirs != '\0' &&
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen strncmp(dir, *chroot_dirs, strlen(*chroot_dirs)) == 0)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return TRUE;
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen chroot_dirs++;
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen }
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen return FALSE;
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen}
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenstatic int
c7f6992db44e9cd33b3b0d754833a1503ee9a53fAki Tuomiuser_reply_handle(struct mail_storage_service_ctx *ctx,
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen struct mail_storage_service_user *user,
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen const struct auth_user_reply *reply,
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen const char **error_r)
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen{
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen const char *home = reply->home;
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen const char *chroot = reply->chroot;
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen const char *const *str, *line, *p;
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen unsigned int i, count;
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen int ret = 0;
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen
45c872f65e4f327ef166c6e2b71bb43e188ac562Timo Sirainen if (reply->uid != (uid_t)-1) {
45c872f65e4f327ef166c6e2b71bb43e188ac562Timo Sirainen if (reply->uid == 0) {
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen *error_r = "userdb returned 0 as uid";
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen return -1;
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen }
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen user->uid_source = "userdb lookup";
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen set_keyval(ctx, user, "mail_uid", dec2str(reply->uid));
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen }
34015eb0b74735f2fac07c12697bde20a94735e6Timo Sirainen if (reply->gid != (uid_t)-1) {
34015eb0b74735f2fac07c12697bde20a94735e6Timo Sirainen user->gid_source = "userdb lookup";
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen set_keyval(ctx, user, "mail_gid", dec2str(reply->gid));
34015eb0b74735f2fac07c12697bde20a94735e6Timo Sirainen }
34015eb0b74735f2fac07c12697bde20a94735e6Timo Sirainen
34015eb0b74735f2fac07c12697bde20a94735e6Timo Sirainen if (home != NULL && chroot == NULL &&
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen *user->user_set->valid_chroot_dirs != '\0' &&
34015eb0b74735f2fac07c12697bde20a94735e6Timo Sirainen (p = strstr(home, "/./")) != NULL) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen /* wu-ftpd like <chroot>/./<home> - check only if there's even
c7f6992db44e9cd33b3b0d754833a1503ee9a53fAki Tuomi a possibility of using them (non-empty valid_chroot_dirs) */
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen chroot = t_strdup_until(home, p);
45c872f65e4f327ef166c6e2b71bb43e188ac562Timo Sirainen home = p + 2;
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen }
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen if (home != NULL)
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen set_keyval(ctx, user, "mail_home", home);
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen if (chroot != NULL) {
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen if (!validate_chroot(user->user_set, chroot)) {
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen *error_r = t_strdup_printf(
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen "userdb returned invalid chroot directory: %s "
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen "(see valid_chroot_dirs setting)", chroot);
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen return -1;
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen }
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen set_keyval(ctx, user, "mail_chroot", chroot);
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen user->anonymous = reply->anonymous;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen str = array_get(&reply->extra_fields, &count);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen for (i = 0; i < count; i++) {
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen line = str[i];
45c872f65e4f327ef166c6e2b71bb43e188ac562Timo Sirainen if (strncmp(line, "system_groups_user=", 19) == 0) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen user->system_groups_user =
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen p_strdup(user->pool, line + 19);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen } else if (strncmp(line, "nice=", 5) == 0) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen#ifdef HAVE_SETPRIORITY
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen int n;
04a7b696e5255aa956277a0f7cabee736c69ec96Timo Sirainen if (str_to_int(line + 5, &n) < 0) {
04a7b696e5255aa956277a0f7cabee736c69ec96Timo Sirainen i_error("userdb returned invalid nice value %s",
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen line + 5);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen } else if (n != 0) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (setpriority(PRIO_PROCESS, 0, n) < 0)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen i_error("setpriority(%d) failed: %m", n);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen#endif
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen } else if (strncmp(line, "auth_token=", 11) == 0) {
44f93baa7b8dca7d00bf187cd3db1c15eed384d2Timo Sirainen user->auth_token = p_strdup(user->pool, line+11);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen } else if (strncmp(line, "auth_user=", 10) == 0) {
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen user->auth_user = p_strdup(user->pool, line+10);
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen } else if (strncmp(line, "admin=", 6) == 0) {
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen user->admin = line[6] == 'y' || line[6] == 'Y' ||
45c872f65e4f327ef166c6e2b71bb43e188ac562Timo Sirainen line[6] == '1';
3e0bae44b65f5c46989fcef3d1e07203f496327eTimo Sirainen } else T_BEGIN {
75771bd32a86b7360d28f1254ffba99f63d763d9Aki Tuomi ret = set_line(ctx, user, line);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen } T_END;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (ret < 0)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen break;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
e10d8b1291090c26b9ef499637e6e632485ca5beTimo Sirainen if (ret < 0) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen *error_r = t_strdup_printf("Invalid userdb input '%s': %s",
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen str[i], settings_parser_get_error(user->set_parser));
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return ret;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen}
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenstatic int
ec5fec7eab19e134a2607b7e224b3e14a1771ee0Timo Sirainenservice_auth_userdb_lookup(struct mail_storage_service_ctx *ctx,
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen const struct mail_storage_service_input *input,
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen pool_t pool, const char **user,
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen const char *const **fields_r,
04a7b696e5255aa956277a0f7cabee736c69ec96Timo Sirainen const char **error_r)
04a7b696e5255aa956277a0f7cabee736c69ec96Timo Sirainen{
ec5fec7eab19e134a2607b7e224b3e14a1771ee0Timo Sirainen struct auth_user_info info;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen const char *new_username;
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen int ret;
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen i_zero(&info);
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen info.service = input->service != NULL ? input->service :
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen ctx->service->name;
c0a87e5f3316a57e6f915882fa1951d0fbb74a61Timo Sirainen info.local_ip = input->local_ip;
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen info.remote_ip = input->remote_ip;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen info.local_port = input->local_port;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen info.remote_port = input->remote_port;
c0a87e5f3316a57e6f915882fa1951d0fbb74a61Timo Sirainen info.debug = input->debug;
4145cbac82bfc0c8bfeceeca0ef841700117930cTimo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen ret = auth_master_user_lookup(ctx->conn, *user, &info, pool,
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen &new_username, fields_r);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (ret > 0) {
e10d8b1291090c26b9ef499637e6e632485ca5beTimo Sirainen if (strcmp(*user, new_username) != 0) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (ctx->debug)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen i_debug("changed username to %s", new_username);
04870054863757edf048c81dcce3c5e7dec453cdTimo Sirainen *user = t_strdup(new_username);
04870054863757edf048c81dcce3c5e7dec453cdTimo Sirainen }
04870054863757edf048c81dcce3c5e7dec453cdTimo Sirainen *user = new_username;
04870054863757edf048c81dcce3c5e7dec453cdTimo Sirainen } else if (ret == 0)
e1203014de25c8c3d3975a9f4b4a04616df4bba2Timo Sirainen *error_r = "Unknown user";
e1203014de25c8c3d3975a9f4b4a04616df4bba2Timo Sirainen else if (**fields_r != NULL) {
e593e507ee5ea3869271a631874c5c4b5c7a294dTimo Sirainen *error_r = t_strdup(**fields_r);
e10d8b1291090c26b9ef499637e6e632485ca5beTimo Sirainen ret = -2;
e1203014de25c8c3d3975a9f4b4a04616df4bba2Timo Sirainen } else {
e1203014de25c8c3d3975a9f4b4a04616df4bba2Timo Sirainen *error_r = MAIL_ERRSTR_CRITICAL_MSG;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return ret;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen}
44f93baa7b8dca7d00bf187cd3db1c15eed384d2Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenstatic bool parse_uid(const char *str, uid_t *uid_r, const char **error_r)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen{
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen struct passwd pw;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (str_to_uid(str, uid_r) == 0)
e10d8b1291090c26b9ef499637e6e632485ca5beTimo Sirainen return TRUE;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen switch (i_getpwnam(str, &pw)) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen case -1:
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen *error_r = t_strdup_printf("getpwnam(%s) failed: %m", str);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return FALSE;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen case 0:
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen *error_r = t_strconcat("Unknown UNIX UID user: ", str, NULL);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return FALSE;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen default:
04a7b696e5255aa956277a0f7cabee736c69ec96Timo Sirainen *uid_r = pw.pw_uid;
04a7b696e5255aa956277a0f7cabee736c69ec96Timo Sirainen return TRUE;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen}
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenstatic bool parse_gid(const char *str, gid_t *gid_r, const char **error_r)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen{
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen struct group gr;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
44f93baa7b8dca7d00bf187cd3db1c15eed384d2Timo Sirainen if (str_to_gid(str, gid_r) == 0)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return TRUE;
44f93baa7b8dca7d00bf187cd3db1c15eed384d2Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen switch (i_getgrnam(str, &gr)) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen case -1:
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen *error_r = t_strdup_printf("getgrnam(%s) failed: %m", str);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return FALSE;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen case 0:
e10d8b1291090c26b9ef499637e6e632485ca5beTimo Sirainen *error_r = t_strconcat("Unknown UNIX GID group: ", str, NULL);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return FALSE;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen default:
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen *gid_r = gr.gr_gid;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return TRUE;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen}
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
e5b723864630e40c9028808ef417dd3d6fbf495bTimo Sirainenstatic const struct var_expand_table *
e5b723864630e40c9028808ef417dd3d6fbf495bTimo Sirainenget_var_expand_table(struct master_service *service,
e5b723864630e40c9028808ef417dd3d6fbf495bTimo Sirainen struct mail_storage_service_user *user,
e5b723864630e40c9028808ef417dd3d6fbf495bTimo Sirainen const struct mail_storage_service_input *input,
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen const struct mail_storage_service_privileges *priv)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen{
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen const char *username = t_strcut(input->username, '@');
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen const char *domain = i_strchr_to_next(input->username, '@');
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen const char *uid = priv == NULL ? NULL :
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen dec2str(priv->uid == (uid_t)-1 ? geteuid() : priv->uid);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen const char *gid = priv == NULL ? NULL :
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen dec2str(priv->gid == (gid_t)-1 ? getegid() : priv->gid);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen const char *auth_user, *auth_username, *auth_domain;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (user == NULL || user->auth_user == NULL) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen auth_user = input->username;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen auth_username = username;
e5b723864630e40c9028808ef417dd3d6fbf495bTimo Sirainen auth_domain = domain;
e5b723864630e40c9028808ef417dd3d6fbf495bTimo Sirainen } else {
e5b723864630e40c9028808ef417dd3d6fbf495bTimo Sirainen auth_user = user->auth_user;
e5b723864630e40c9028808ef417dd3d6fbf495bTimo Sirainen auth_username = t_strcut(user->auth_user, '@');
e5b723864630e40c9028808ef417dd3d6fbf495bTimo Sirainen auth_domain = i_strchr_to_next(user->auth_user, '@');
e5b723864630e40c9028808ef417dd3d6fbf495bTimo Sirainen }
e5b723864630e40c9028808ef417dd3d6fbf495bTimo Sirainen
e5b723864630e40c9028808ef417dd3d6fbf495bTimo Sirainen const struct var_expand_table stack_tab[] = {
e5b723864630e40c9028808ef417dd3d6fbf495bTimo Sirainen { 'u', input->username, "user" },
e5b723864630e40c9028808ef417dd3d6fbf495bTimo Sirainen { 'n', username, "username" },
e5b723864630e40c9028808ef417dd3d6fbf495bTimo Sirainen { 'd', domain, "domain" },
e5b723864630e40c9028808ef417dd3d6fbf495bTimo Sirainen { 's', service->name, "service" },
e5b723864630e40c9028808ef417dd3d6fbf495bTimo Sirainen { 'l', net_ip2addr(&input->local_ip), "lip" },
e5b723864630e40c9028808ef417dd3d6fbf495bTimo Sirainen { 'r', net_ip2addr(&input->remote_ip), "rip" },
e5b723864630e40c9028808ef417dd3d6fbf495bTimo Sirainen { 'p', my_pid, "pid" },
e5b723864630e40c9028808ef417dd3d6fbf495bTimo Sirainen { 'i', uid, "uid" },
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen { '\0', gid, "gid" },
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen { '\0', input->session_id, "session" },
31a574fda352ef4f71dbff9c30e15e4744e132c0Timo Sirainen { '\0', auth_user, "auth_user" },
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen { '\0', auth_username, "auth_username" },
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen { '\0', auth_domain, "auth_domain" },
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen { '\0', NULL, NULL }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen };
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen struct var_expand_table *tab;
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen tab = t_malloc_no0(sizeof(stack_tab));
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen memcpy(tab, stack_tab, sizeof(stack_tab));
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen return tab;
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen}
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainenconst struct var_expand_table *
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenmail_storage_service_get_var_expand_table(struct mail_storage_service_ctx *ctx,
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen struct mail_storage_service_input *input)
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen{
6882df5fbca4a09cdaa95f54d70bb31b5920528cTimo Sirainen struct mail_storage_service_privileges priv;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen i_zero(&priv);
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen priv.uid = (uid_t)-1;
714e2da5096fb52b8845d3c79f9bb26225a606c9Timo Sirainen priv.gid = (gid_t)-1;
714e2da5096fb52b8845d3c79f9bb26225a606c9Timo Sirainen return get_var_expand_table(ctx->service, NULL, input, &priv);
714e2da5096fb52b8845d3c79f9bb26225a606c9Timo Sirainen}
714e2da5096fb52b8845d3c79f9bb26225a606c9Timo Sirainen
714e2da5096fb52b8845d3c79f9bb26225a606c9Timo Sirainenstatic bool
714e2da5096fb52b8845d3c79f9bb26225a606c9Timo Sirainenuser_expand_varstr(struct mail_storage_service_ctx *ctx,
714e2da5096fb52b8845d3c79f9bb26225a606c9Timo Sirainen struct mail_storage_service_user *user,
6882df5fbca4a09cdaa95f54d70bb31b5920528cTimo Sirainen struct mail_storage_service_privileges *priv,
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen const char *str, const char **value_r, const char **error_r)
6882df5fbca4a09cdaa95f54d70bb31b5920528cTimo Sirainen{
6882df5fbca4a09cdaa95f54d70bb31b5920528cTimo Sirainen string_t *value;
6882df5fbca4a09cdaa95f54d70bb31b5920528cTimo Sirainen int ret;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
6882df5fbca4a09cdaa95f54d70bb31b5920528cTimo Sirainen if (*str == SETTING_STRVAR_EXPANDED[0]) {
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen *value_r = str + 1;
6882df5fbca4a09cdaa95f54d70bb31b5920528cTimo Sirainen return TRUE;
6882df5fbca4a09cdaa95f54d70bb31b5920528cTimo Sirainen }
6882df5fbca4a09cdaa95f54d70bb31b5920528cTimo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen i_assert(*str == SETTING_STRVAR_UNEXPANDED[0]);
6882df5fbca4a09cdaa95f54d70bb31b5920528cTimo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen value = t_str_new(256);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen ret = mail_storage_service_var_expand(ctx, value, str + 1, user,
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen &user->input, priv, error_r);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen *value_r = str_c(value);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return ret > 0;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen}
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenstatic int
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenservice_parse_privileges(struct mail_storage_service_ctx *ctx,
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen struct mail_storage_service_user *user,
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen struct mail_storage_service_privileges *priv_r,
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen const char **error_r)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen{
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen const struct mail_user_settings *set = user->user_set;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen uid_t uid = (uid_t)-1;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen gid_t gid = (gid_t)-1;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen const char *error;
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen i_zero(priv_r);
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen if (*set->mail_uid != '\0') {
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen if (!parse_uid(set->mail_uid, &uid, error_r)) {
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen *error_r = t_strdup_printf("%s (from %s)", *error_r,
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen user->uid_source);
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen return -1;
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen }
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen if (uid < (uid_t)set->first_valid_uid ||
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen (set->last_valid_uid != 0 &&
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen uid > (uid_t)set->last_valid_uid)) {
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen *error_r = t_strdup_printf(
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen "Mail access for users with UID %s not permitted "
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen "(see first_valid_uid in config file, uid from %s).",
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen dec2str(uid), user->uid_source);
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen return -1;
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen }
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen }
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen priv_r->uid = uid;
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen priv_r->uid_source = user->uid_source;
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen if (*set->mail_gid != '\0') {
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen if (!parse_gid(set->mail_gid, &gid, error_r)) {
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen *error_r = t_strdup_printf("%s (from %s)", *error_r,
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen user->gid_source);
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen return -1;
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen }
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen if (gid < (gid_t)set->first_valid_gid ||
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen (set->last_valid_gid != 0 &&
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen gid > (gid_t)set->last_valid_gid)) {
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen *error_r = t_strdup_printf(
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen "Mail access for users with GID %s not permitted "
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen "(see first_valid_gid in config file, gid from %s).",
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen dec2str(gid), user->gid_source);
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen return -1;
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen }
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen }
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen priv_r->gid = gid;
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen priv_r->gid_source = user->gid_source;
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen /* variable strings are expanded in mail_user_init(),
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen but we need the home and chroot sooner so do them separately here. */
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen if (!user_expand_varstr(ctx, user, priv_r, user->user_set->mail_home,
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen &priv_r->home, &error)) {
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen *error_r = t_strdup_printf(
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen "Failed to expand mail_home '%s': %s",
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen user->user_set->mail_home, error);
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen return -1;
31a574fda352ef4f71dbff9c30e15e4744e132c0Timo Sirainen }
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen if (!user_expand_varstr(ctx, user, priv_r, user->user_set->mail_chroot,
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen &priv_r->chroot, &error)) {
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen *error_r = t_strdup_printf(
eb4d4f557fa75aa2a47639e9deb75a21f44eb42aTimo Sirainen "Failed to expand mail_chroot '%s': %s",
eb4d4f557fa75aa2a47639e9deb75a21f44eb42aTimo Sirainen user->user_set->mail_chroot, error);
eb4d4f557fa75aa2a47639e9deb75a21f44eb42aTimo Sirainen return -1;
eb4d4f557fa75aa2a47639e9deb75a21f44eb42aTimo Sirainen }
eb4d4f557fa75aa2a47639e9deb75a21f44eb42aTimo Sirainen return 0;
eb4d4f557fa75aa2a47639e9deb75a21f44eb42aTimo Sirainen}
eb4d4f557fa75aa2a47639e9deb75a21f44eb42aTimo Sirainen
eb4d4f557fa75aa2a47639e9deb75a21f44eb42aTimo Sirainenstatic void mail_storage_service_seteuid_root(void)
eb4d4f557fa75aa2a47639e9deb75a21f44eb42aTimo Sirainen{
eb4d4f557fa75aa2a47639e9deb75a21f44eb42aTimo Sirainen if (seteuid(0) < 0) {
eb4d4f557fa75aa2a47639e9deb75a21f44eb42aTimo Sirainen i_fatal("mail-storage-service: "
eb4d4f557fa75aa2a47639e9deb75a21f44eb42aTimo Sirainen "Failed to restore temporarily dropped root privileges: "
eb4d4f557fa75aa2a47639e9deb75a21f44eb42aTimo Sirainen "seteuid(0) failed: %m");
eb4d4f557fa75aa2a47639e9deb75a21f44eb42aTimo Sirainen }
eb4d4f557fa75aa2a47639e9deb75a21f44eb42aTimo Sirainen}
eb4d4f557fa75aa2a47639e9deb75a21f44eb42aTimo Sirainen
eb4d4f557fa75aa2a47639e9deb75a21f44eb42aTimo Sirainenstatic int
eb4d4f557fa75aa2a47639e9deb75a21f44eb42aTimo Sirainenservice_drop_privileges(struct mail_storage_service_user *user,
eb4d4f557fa75aa2a47639e9deb75a21f44eb42aTimo Sirainen struct mail_storage_service_privileges *priv,
eb4d4f557fa75aa2a47639e9deb75a21f44eb42aTimo Sirainen bool disallow_root, bool keep_setuid_root,
eb4d4f557fa75aa2a47639e9deb75a21f44eb42aTimo Sirainen bool setenv_only, const char **error_r)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen{
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen const struct mail_user_settings *set = user->user_set;
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen struct restrict_access_settings rset;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen uid_t current_euid, setuid_uid = 0;
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen const char *cur_chroot, *error;
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen current_euid = geteuid();
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen restrict_access_init(&rset);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen restrict_access_get_env(&rset);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (priv->uid != (uid_t)-1) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen rset.uid = priv->uid;
04a7b696e5255aa956277a0f7cabee736c69ec96Timo Sirainen rset.uid_source = priv->uid_source;
04a7b696e5255aa956277a0f7cabee736c69ec96Timo Sirainen } else if (rset.uid == (uid_t)-1 &&
04a7b696e5255aa956277a0f7cabee736c69ec96Timo Sirainen disallow_root && current_euid == 0) {
04a7b696e5255aa956277a0f7cabee736c69ec96Timo Sirainen *error_r = "User is missing UID (see mail_uid setting)";
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return -1;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (priv->gid != (gid_t)-1) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen rset.gid = priv->gid;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen rset.gid_source = priv->gid_source;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen } else if (rset.gid == (gid_t)-1 && disallow_root &&
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen set->first_valid_gid > 0 && getegid() == 0) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen *error_r = "User is missing GID (see mail_gid setting)";
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return -1;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (*set->mail_privileged_group != '\0') {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (!parse_gid(set->mail_privileged_group, &rset.privileged_gid,
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen &error)) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen *error_r = t_strdup_printf(
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen "%s (in mail_privileged_group setting)", error);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return -1;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (*set->mail_access_groups != '\0') {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen rset.extra_groups = t_strconcat(set->mail_access_groups, ",",
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen rset.extra_groups, NULL);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen rset.first_valid_gid = set->first_valid_gid;
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen rset.last_valid_gid = set->last_valid_gid;
c3d40f3092af25cad9e807a85eaad4d92aab107bTimo Sirainen rset.chroot_dir = *priv->chroot == '\0' ? NULL : priv->chroot;
c3d40f3092af25cad9e807a85eaad4d92aab107bTimo Sirainen rset.system_groups_user = user->system_groups_user;
c3d40f3092af25cad9e807a85eaad4d92aab107bTimo Sirainen
c3d40f3092af25cad9e807a85eaad4d92aab107bTimo Sirainen cur_chroot = restrict_access_get_current_chroot();
c3d40f3092af25cad9e807a85eaad4d92aab107bTimo Sirainen if (cur_chroot != NULL) {
c3d40f3092af25cad9e807a85eaad4d92aab107bTimo Sirainen /* we're already chrooted. make sure the chroots are equal. */
c3d40f3092af25cad9e807a85eaad4d92aab107bTimo Sirainen if (rset.chroot_dir == NULL) {
c3d40f3092af25cad9e807a85eaad4d92aab107bTimo Sirainen *error_r = "Process is already chrooted, "
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen "can't un-chroot for this user";
c3d40f3092af25cad9e807a85eaad4d92aab107bTimo Sirainen return -1;
c3d40f3092af25cad9e807a85eaad4d92aab107bTimo Sirainen }
c3d40f3092af25cad9e807a85eaad4d92aab107bTimo Sirainen if (strcmp(rset.chroot_dir, cur_chroot) != 0) {
c3d40f3092af25cad9e807a85eaad4d92aab107bTimo Sirainen *error_r = t_strdup_printf(
c3d40f3092af25cad9e807a85eaad4d92aab107bTimo Sirainen "Process is already chrooted to %s, "
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen "can't chroot to %s", cur_chroot, priv->chroot);
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen return -1;
3e0bae44b65f5c46989fcef3d1e07203f496327eTimo Sirainen }
3e0bae44b65f5c46989fcef3d1e07203f496327eTimo Sirainen /* chrooting to same directory where we're already chrooted */
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen rset.chroot_dir = NULL;
e10d8b1291090c26b9ef499637e6e632485ca5beTimo Sirainen }
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen if (disallow_root &&
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen (rset.uid == 0 || (rset.uid == (uid_t)-1 && current_euid == 0))) {
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen *error_r = "Mail access not allowed for root";
411f318ed3a25fa66c1b932e10df43841e2725c9Timo Sirainen return -1;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (keep_setuid_root) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (current_euid != rset.uid && rset.uid != (uid_t)-1) {
3e0bae44b65f5c46989fcef3d1e07203f496327eTimo Sirainen if (current_euid != 0) {
3e0bae44b65f5c46989fcef3d1e07203f496327eTimo Sirainen /* we're changing the UID,
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen switch back to root first */
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen mail_storage_service_seteuid_root();
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen }
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen setuid_uid = rset.uid;
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen }
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen rset.uid = (uid_t)-1;
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen disallow_root = FALSE;
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (!setenv_only) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen restrict_access(&rset, *priv->home == '\0' ? NULL : priv->home,
eb4d4f557fa75aa2a47639e9deb75a21f44eb42aTimo Sirainen disallow_root);
eb4d4f557fa75aa2a47639e9deb75a21f44eb42aTimo Sirainen } else {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen restrict_access_set_env(&rset);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
e10d8b1291090c26b9ef499637e6e632485ca5beTimo Sirainen if (setuid_uid != 0 && !setenv_only) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (seteuid(setuid_uid) < 0)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen i_fatal("mail-storage-service: seteuid(%s) failed: %m",
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen dec2str(setuid_uid));
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return 0;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen}
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
714e2da5096fb52b8845d3c79f9bb26225a606c9Timo Sirainenstatic int
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenmail_storage_service_init_post(struct mail_storage_service_ctx *ctx,
04a7b696e5255aa956277a0f7cabee736c69ec96Timo Sirainen struct mail_storage_service_user *user,
04a7b696e5255aa956277a0f7cabee736c69ec96Timo Sirainen struct mail_storage_service_privileges *priv,
04a7b696e5255aa956277a0f7cabee736c69ec96Timo Sirainen struct mail_user **mail_user_r,
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen const char **error_r)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen{
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen const struct mail_storage_settings *mail_set;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen const char *home = priv->home;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen struct mail_user *mail_user;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen /* NOTE: if more user initialization is added, add it also to
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen mail_user_dup() */
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen mail_user = mail_user_alloc_nodup_set(user->input.username,
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen user->user_info, user->user_set);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen mail_user->_service_user = user;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen mail_user_set_home(mail_user, *home == '\0' ? NULL : home);
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen mail_user_set_vars(mail_user, ctx->service->name,
6882df5fbca4a09cdaa95f54d70bb31b5920528cTimo Sirainen &user->input.local_ip, &user->input.remote_ip);
714e2da5096fb52b8845d3c79f9bb26225a606c9Timo Sirainen mail_user->uid = priv->uid == (uid_t)-1 ? geteuid() : priv->uid;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen mail_user->gid = priv->gid == (gid_t)-1 ? getegid() : priv->gid;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen mail_user->anonymous = user->anonymous;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen mail_user->admin = user->admin;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen mail_user->auth_token = p_strdup(mail_user->pool, user->auth_token);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen mail_user->auth_user = p_strdup(mail_user->pool, user->auth_user);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (user->input.session_create_time != 0) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen mail_user->session_create_time =
eb4d4f557fa75aa2a47639e9deb75a21f44eb42aTimo Sirainen user->input.session_create_time;
eb4d4f557fa75aa2a47639e9deb75a21f44eb42aTimo Sirainen mail_user->session_restored = TRUE;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (user->session_id_counter++ == 0) {
e10d8b1291090c26b9ef499637e6e632485ca5beTimo Sirainen mail_user->session_id =
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen p_strdup(mail_user->pool, user->input.session_id);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen } else {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen mail_user->session_id =
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen p_strdup_printf(mail_user->pool, "%s:%u",
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen user->input.session_id,
cd2cd224d3216a243d55c71c298a5b7684de0ac4Timo Sirainen user->session_id_counter);
cd2cd224d3216a243d55c71c298a5b7684de0ac4Timo Sirainen }
6882df5fbca4a09cdaa95f54d70bb31b5920528cTimo Sirainen mail_user->userdb_fields = user->input.userdb_fields == NULL ? NULL :
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen p_strarray_dup(mail_user->pool, user->input.userdb_fields);
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen mail_user->autoexpunge_enabled =
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen (user->flags & MAIL_STORAGE_SERVICE_FLAG_AUTOEXPUNGE) != 0;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
cd2cd224d3216a243d55c71c298a5b7684de0ac4Timo Sirainen mail_set = mail_user_set_get_storage_set(mail_user);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (mail_set->mail_debug) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen string_t *str = t_str_new(64);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen str_printfa(str, "Effective uid=%s, gid=%s, home=%s",
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen dec2str(geteuid()), dec2str(getegid()), home);
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen if (*priv->chroot != '\0')
cd2cd224d3216a243d55c71c298a5b7684de0ac4Timo Sirainen str_printfa(str, ", chroot=%s", priv->chroot);
ac0fed903142d28ae3a1d5d00d2097fdf161b138Timo Sirainen i_debug("%s", str_c(str));
ac0fed903142d28ae3a1d5d00d2097fdf161b138Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if ((user->flags & MAIL_STORAGE_SERVICE_FLAG_TEMP_PRIV_DROP) != 0 &&
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen (user->flags & MAIL_STORAGE_SERVICE_FLAG_ENABLE_CORE_DUMPS) == 0) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen /* we don't want to write core files to any users' home
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen directories since they could contain information about other
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen users' mails as well. so do no chdiring to home. */
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen } else if ((user->flags & MAIL_STORAGE_SERVICE_FLAG_NO_CHDIR) == 0) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen /* If possible chdir to home directory, so that core file
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen could be written in case we crash.
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen
b66d803de86bfb411165b3465b0d9ef64ecfe2a1Timo Sirainen fallback to chdir()ing to root directory. this is needed
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen because the current directory may not be accessible after
23fdad6c7e2581921f511e24cd9371c9eaebcef9Timo Sirainen dropping privileges, and for example unlink_directory()
23fdad6c7e2581921f511e24cd9371c9eaebcef9Timo Sirainen requires ability to open the current directory. */
8552b0cad8ffe9ccb8270577ba28b8010c89af11Timo Sirainen if (home[0] == '\0') {
if (chdir("/") < 0)
i_error("chdir(/) failed: %m");
} else 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 (chdir("/") < 0)
i_error("chdir(/) failed: %m");
}
}
if (mail_user_init(mail_user, error_r) < 0) {
mail_user_unref(&mail_user);
return -1;
}
if ((user->flags & MAIL_STORAGE_SERVICE_FLAG_NO_NAMESPACES) == 0) {
if (mail_namespaces_init(mail_user, error_r) < 0) {
mail_user_unref(&mail_user);
return -1;
}
}
*mail_user_r = mail_user;
return 0;
}
void mail_storage_service_io_activate_user(struct mail_storage_service_user *user)
{
i_set_failure_prefix("%s", user->log_prefix);
}
void mail_storage_service_io_deactivate_user(struct mail_storage_service_user *user)
{
i_set_failure_prefix("%s", user->service_ctx->default_log_prefix);
}
void mail_storage_service_io_deactivate(struct mail_storage_service_ctx *ctx)
{
i_set_failure_prefix("%s", ctx->default_log_prefix);
}
static const char *field_get_default(const char *data)
{
const char *p;
p = strchr(data, ':');
if (p == NULL)
return "";
else {
/* default value given */
return p+1;
}
}
const char *mail_storage_service_fields_var_expand(const char *data,
const char *const *fields)
{
const char *field_name = t_strcut(data, ':');
unsigned int i;
size_t field_name_len;
if (fields == NULL)
return field_get_default(data);
field_name_len = strlen(field_name);
for (i = 0; fields[i] != NULL; i++) {
if (strncmp(fields[i], field_name, field_name_len) == 0 &&
fields[i][field_name_len] == '=')
return fields[i] + field_name_len+1;
}
return field_get_default(data);
}
static int
mail_storage_service_input_var_userdb(const char *data, void *context,
const char **value_r,
const char **error_r ATTR_UNUSED)
{
struct mail_storage_service_user *user = context;
*value_r = mail_storage_service_fields_var_expand(data,
user == NULL ? NULL : user->input.userdb_fields);
return 1;
}
static int
mail_storage_service_var_expand(struct mail_storage_service_ctx *ctx,
string_t *str, const char *format,
struct mail_storage_service_user *user,
const struct mail_storage_service_input *input,
const struct mail_storage_service_privileges *priv,
const char **error_r)
{
static const struct var_expand_func_table func_table[] = {
{ "userdb", mail_storage_service_input_var_userdb },
{ NULL, NULL }
};
return var_expand_with_funcs(str, format,
get_var_expand_table(ctx->service, user, input, priv),
func_table, user, error_r);
}
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)
{
const char *error;
ctx->log_initialized = TRUE;
T_BEGIN {
string_t *str;
str = t_str_new(256);
(void)mail_storage_service_var_expand(ctx, str,
user->user_set->mail_log_prefix,
user, &user->input, priv, &error);
user->log_prefix = p_strdup(user->pool, str_c(str));
} T_END;
master_service_init_log(ctx->service, user->log_prefix);
if (master_service_get_client_limit(master_service) == 1)
i_set_failure_send_prefix(user->log_prefix);
io_loop_context_add_callbacks(user->ioloop_ctx,
mail_storage_service_io_activate_user,
mail_storage_service_io_deactivate_user,
user);
}
static void mail_storage_service_time_moved(time_t old_time, time_t new_time)
{
long diff = new_time - old_time;
if (diff > 0) {
if (diff > MAX_NOWARN_FORWARD_SECS)
i_warning("Time jumped forwards %ld seconds", diff);
return;
}
diff = -diff;
if (diff > MAX_TIME_BACKWARDS_SLEEP) {
i_fatal("Time just moved backwards by %ld seconds. "
"This might cause a lot of problems, "
"so I'll just kill myself now. "
"http://wiki2.dovecot.org/TimeMovedBackwards", diff);
} else {
i_error("Time just moved backwards by %ld seconds. "
"I'll sleep now until we're back in present. "
"http://wiki2.dovecot.org/TimeMovedBackwards", diff);
/* Sleep extra second to make sure usecs also grows. */
diff++;
while (diff > 0 && sleep(diff) != 0) {
/* don't use sleep()'s return value, because
it could get us to a long loop in case
interrupts just keep coming */
diff = old_time - time(NULL) + 1;
}
}
}
struct mail_storage_service_ctx *
mail_storage_service_init(struct master_service *service,
const struct setting_parser_info *set_roots[],
enum mail_storage_service_flags flags)
{
struct mail_storage_service_ctx *ctx;
const char *version;
pool_t pool;
unsigned int count;
version = master_service_get_version_string(service);
if (version != NULL && strcmp(version, PACKAGE_VERSION) != 0) {
i_fatal("Version mismatch: libdovecot-storage.so is '%s', "
"while the running Dovecot binary is '%s'",
PACKAGE_VERSION, version);
}
if ((flags & MAIL_STORAGE_SERVICE_FLAG_TEMP_PRIV_DROP) != 0 &&
getuid() != 0) {
/* service { user } isn't root. the permission drop can't be
temporary. */
flags &= ~MAIL_STORAGE_SERVICE_FLAG_TEMP_PRIV_DROP;
}
(void)umask(0077);
io_loop_set_time_moved_callback(current_ioloop,
mail_storage_service_time_moved);
mail_storage_init();
pool = pool_alloconly_create("mail storage service", 2048);
ctx = p_new(pool, struct mail_storage_service_ctx, 1);
ctx->pool = pool;
ctx->service = service;
ctx->flags = flags;
/* @UNSAFE */
if (set_roots == NULL)
count = 0;
else
for (count = 0; set_roots[count] != NULL; count++) ;
ctx->set_roots =
p_new(pool, const struct setting_parser_info *, count + 2);
ctx->set_roots[0] = &mail_user_setting_parser_info;
if (set_roots != NULL) {
memcpy(ctx->set_roots + 1, set_roots,
sizeof(*ctx->set_roots) * count);
}
/* do all the global initialization. delay initializing plugins until
we drop privileges the first time. */
if ((flags & MAIL_STORAGE_SERVICE_FLAG_NO_LOG_INIT) == 0) {
/* note: we may not have read any settings yet, so this logging
may still be going to wrong location */
ctx->default_log_prefix =
p_strconcat(pool, service->name, ": ", NULL);
master_service_init_log(service, ctx->default_log_prefix);
}
dict_drivers_register_builtin();
return ctx;
}
struct auth_master_connection *
mail_storage_service_get_auth_conn(struct mail_storage_service_ctx *ctx)
{
i_assert(ctx->conn != NULL);
return ctx->conn;
}
static enum mail_storage_service_flags
mail_storage_service_input_get_flags(struct mail_storage_service_ctx *ctx,
const struct mail_storage_service_input *input)
{
enum mail_storage_service_flags flags;
flags = (ctx->flags & ~input->flags_override_remove) |
input->flags_override_add;
if (input->no_userdb_lookup) {
/* FIXME: for API backwards compatibility only */
flags &= ~MAIL_STORAGE_SERVICE_FLAG_USERDB_LOOKUP;
}
return flags;
}
int mail_storage_service_read_settings(struct mail_storage_service_ctx *ctx,
const struct mail_storage_service_input *input,
pool_t pool,
const struct setting_parser_info **user_info_r,
const struct setting_parser_context **parser_r,
const char **error_r)
{
struct master_service_settings_input set_input;
const struct setting_parser_info *const *roots;
struct master_service_settings_output set_output;
const struct dynamic_settings_parser *dyn_parsers;
enum mail_storage_service_flags flags;
unsigned int i;
ctx->config_permission_denied = FALSE;
flags = input == NULL ? ctx->flags :
mail_storage_service_input_get_flags(ctx, input);
i_zero(&set_input);
set_input.roots = ctx->set_roots;
set_input.preserve_user = TRUE;
/* settings reader may exec doveconf, which is going to clear
environment, and if we're not doing a userdb lookup we want to
use $HOME */
set_input.preserve_home =
(flags & MAIL_STORAGE_SERVICE_FLAG_USERDB_LOOKUP) == 0;
set_input.use_sysexits =
(flags & MAIL_STORAGE_SERVICE_FLAG_USE_SYSEXITS) != 0;
if (input != NULL) {
set_input.module = input->module;
set_input.service = input->service;
set_input.username = input->username;
set_input.local_ip = input->local_ip;
set_input.remote_ip = input->remote_ip;
}
if (input == NULL) {
/* global settings read - don't create a cache for thi */
} else if (ctx->set_cache == NULL) {
ctx->set_cache_module = p_strdup(ctx->pool, set_input.module);
ctx->set_cache_service = p_strdup(ctx->pool, set_input.service);
ctx->set_cache = master_service_settings_cache_init(
ctx->service, set_input.module, set_input.service);
} else {
/* already looked up settings at least once.
we really shouldn't be execing anymore. */
set_input.never_exec = TRUE;
}
dyn_parsers = mail_storage_get_dynamic_parsers(pool);
if (null_strcmp(set_input.module, ctx->set_cache_module) == 0 &&
null_strcmp(set_input.service, ctx->set_cache_service) == 0 &&
ctx->set_cache != NULL) {
if (master_service_settings_cache_read(ctx->set_cache,
&set_input, dyn_parsers,
parser_r, error_r) < 0) {
*error_r = t_strdup_printf(
"Error reading configuration: %s", *error_r);
return -1;
}
} else {
settings_parser_dyn_update(pool, &set_input.roots, dyn_parsers);
if (master_service_settings_read(ctx->service, &set_input,
&set_output, error_r) < 0) {
*error_r = t_strdup_printf(
"Error reading configuration: %s", *error_r);
ctx->config_permission_denied =
set_output.permission_denied;
return -1;
}
*parser_r = ctx->service->set_parser;
}
roots = settings_parser_get_roots(*parser_r);
for (i = 0; roots[i] != NULL; i++) {
if (strcmp(roots[i]->module_name,
mail_user_setting_parser_info.module_name) == 0) {
*user_info_r = roots[i];
return 0;
}
}
i_unreached();
return -1;
}
void mail_storage_service_set_auth_conn(struct mail_storage_service_ctx *ctx,
struct auth_master_connection *conn)
{
i_assert(ctx->conn == NULL);
i_assert(mail_user_auth_master_conn == NULL);
ctx->conn = conn;
mail_user_auth_master_conn = conn;
}
static void
mail_storage_service_first_init(struct mail_storage_service_ctx *ctx,
const struct setting_parser_info *user_info,
const struct mail_user_settings *user_set)
{
enum auth_master_flags flags = 0;
ctx->debug = mail_user_set_get_mail_debug(user_info, user_set);
if (ctx->debug)
flags |= AUTH_MASTER_FLAG_DEBUG;
if ((ctx->flags & MAIL_STORAGE_SERVICE_FLAG_NO_IDLE_TIMEOUT) != 0)
flags |= AUTH_MASTER_FLAG_NO_IDLE_TIMEOUT;
mail_storage_service_set_auth_conn(ctx,
auth_master_init(user_set->auth_socket_path, flags));
}
static int
mail_storage_service_load_modules(struct mail_storage_service_ctx *ctx,
const struct setting_parser_info *user_info,
const struct mail_user_settings *user_set,
const char **error_r)
{
struct module_dir_load_settings mod_set;
if (*user_set->mail_plugins == '\0')
return 0;
if ((ctx->flags & MAIL_STORAGE_SERVICE_FLAG_NO_PLUGINS) != 0)
return 0;
i_zero(&mod_set);
mod_set.abi_version = DOVECOT_ABI_VERSION;
mod_set.binary_name = master_service_get_name(ctx->service);
mod_set.setting_name = "mail_plugins";
mod_set.require_init_funcs = TRUE;
mod_set.debug = mail_user_set_get_mail_debug(user_info, user_set);
return module_dir_try_load_missing(&mail_storage_service_modules,
user_set->mail_plugin_dir,
user_set->mail_plugins,
&mod_set, error_r);
}
static int extra_field_key_cmp_p(const char *const *s1, const char *const *s2)
{
const char *p1 = *s1, *p2 = *s2;
for (; *p1 == *p2; p1++, p2++) {
if (*p1 == '\0')
return 0;
}
if (*p1 == '=')
return -1;
if (*p2 == '=')
return 1;
return *p1 - *p2;
}
static void
mail_storage_service_set_log_prefix(struct mail_storage_service_ctx *ctx,
const struct mail_user_settings *user_set,
struct mail_storage_service_user *user,
const struct mail_storage_service_input *input,
const struct mail_storage_service_privileges *priv)
{
string_t *str;
const char *error;
str = t_str_new(256);
(void)mail_storage_service_var_expand(ctx, str, user_set->mail_log_prefix,
user, input, priv, &error);
i_set_failure_prefix("%s", str_c(str));
}
static const char *
mail_storage_service_generate_session_id(pool_t pool, const char *prefix)
{
guid_128_t guid;
size_t prefix_len = prefix == NULL ? 0 : strlen(prefix);
string_t *str = str_new(pool, MAX_BASE64_ENCODED_SIZE(prefix_len + 1 + sizeof(guid)));
if (prefix != NULL)
str_printfa(str, "%s:", prefix);
guid_128_generate(guid);
base64_encode(guid, sizeof(guid), str);
/* remove the trailing "==" */
i_assert(str_data(str)[str_len(str)-2] == '=');
str_truncate(str, str_len(str)-2);
return str_c(str);
}
static int
mail_storage_service_lookup_real(struct mail_storage_service_ctx *ctx,
const struct mail_storage_service_input *input,
bool update_log_prefix,
struct mail_storage_service_user **user_r,
const char **error_r)
{
enum mail_storage_service_flags flags;
struct mail_storage_service_user *user;
const char *username = input->username;
const struct setting_parser_info *user_info;
const struct mail_user_settings *user_set;
const char *const *userdb_fields, *error;
struct auth_user_reply reply;
const struct setting_parser_context *set_parser;
void **sets;
pool_t user_pool, temp_pool;
int ret = 1;
user_pool = pool_alloconly_create(MEMPOOL_GROWING"mail storage service user", 1024*6);
flags = mail_storage_service_input_get_flags(ctx, input);
if ((flags & MAIL_STORAGE_SERVICE_FLAG_TEMP_PRIV_DROP) != 0 &&
geteuid() != 0) {
/* we dropped privileges only temporarily. switch back to root
before reading settings, so we'll definitely have enough
permissions to connect to the config socket. */
mail_storage_service_seteuid_root();
}
if (mail_storage_service_read_settings(ctx, input, user_pool,
&user_info, &set_parser,
error_r) < 0) {
if (ctx->config_permission_denied) {
/* just restart and maybe next time we will open the
config socket before dropping privileges */
i_fatal("%s", *error_r);
}
pool_unref(&user_pool);
return -1;
}
if ((flags & MAIL_STORAGE_SERVICE_FLAG_NO_LOG_INIT) == 0 &&
!ctx->log_initialized) {
/* initialize logging again, in case we only read the
settings for the first above */
ctx->log_initialized = TRUE;
master_service_init_log(ctx->service,
t_strconcat(ctx->service->name, ": ", NULL));
update_log_prefix = TRUE;
}
sets = master_service_settings_parser_get_others(master_service,
set_parser);
user_set = sets[0];
if (update_log_prefix)
mail_storage_service_set_log_prefix(ctx, user_set, NULL, input, NULL);
if (ctx->conn == NULL)
mail_storage_service_first_init(ctx, user_info, user_set);
/* load global plugins */
if (mail_storage_service_load_modules(ctx, user_info, user_set, error_r) < 0) {
pool_unref(&user_pool);
return -1;
}
if (ctx->userdb_next_pool == NULL)
temp_pool = pool_alloconly_create("userdb lookup", 2048);
else {
temp_pool = ctx->userdb_next_pool;
ctx->userdb_next_pool = NULL;
pool_ref(temp_pool);
}
if ((flags & MAIL_STORAGE_SERVICE_FLAG_USERDB_LOOKUP) != 0) {
ret = service_auth_userdb_lookup(ctx, input, temp_pool,
&username, &userdb_fields, error_r);
if (ret <= 0) {
pool_unref(&temp_pool);
pool_unref(&user_pool);
return ret;
}
if (ctx->userdb_next_fieldsp != NULL)
*ctx->userdb_next_fieldsp = userdb_fields;
} else {
userdb_fields = input->userdb_fields;
}
user = p_new(user_pool, struct mail_storage_service_user, 1);
user->service_ctx = ctx;
user->pool = user_pool;
user->input = *input;
user->input.userdb_fields = userdb_fields == NULL ? NULL :
p_strarray_dup(user_pool, userdb_fields);
user->input.username = p_strdup(user_pool, username);
user->input.session_id = p_strdup(user_pool, input->session_id);
if (user->input.session_id == NULL) {
user->input.session_id =
mail_storage_service_generate_session_id(user_pool,
input->session_id_prefix);
}
user->input.session_create_time = input->session_create_time;
user->user_info = user_info;
user->flags = flags;
user->set_parser = settings_parser_dup(set_parser, user_pool);
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) {
*error_r = t_strdup_printf(
"Invalid settings in userdb: %s", error);
ret = -2;
}
}
if (ret > 0 && !settings_parser_check(user->set_parser, user_pool, &error)) {
*error_r = t_strdup_printf(
"Invalid settings (probably caused by userdb): %s", error);
ret = -2;
}
pool_unref(&temp_pool);
/* load per-user plugins */
if (ret > 0) {
if (mail_storage_service_load_modules(ctx, user_info,
user->user_set,
error_r) < 0) {
ret = -2;
}
}
*user_r = user;
return ret;
}
int mail_storage_service_lookup(struct mail_storage_service_ctx *ctx,
const struct mail_storage_service_input *input,
struct mail_storage_service_user **user_r,
const char **error_r)
{
char *old_log_prefix = i_strdup(i_get_failure_prefix());
bool update_log_prefix;
int ret;
if (io_loop_get_current_context(current_ioloop) == NULL) {
/* no user yet. log prefix should be just "imap:" or something
equally unhelpful. we don't know the proper log format yet,
but initialize it to something better until we know it. */
const char *session_id =
input->session_id != NULL ? input->session_id :
(input->session_id_prefix != NULL ?
input->session_id_prefix : NULL);
i_set_failure_prefix("%s(%s%s,%s)",
master_service_get_name(ctx->service), input->username,
session_id == NULL ? "" : t_strdup_printf(",%s", session_id),
input->remote_ip.family == 0 ? "" :
t_strdup_printf(",%s", net_ip2addr(&input->remote_ip)));
update_log_prefix = TRUE;
} else {
/* we might be here because we're doing a user lookup for a
shared user. the log prefix is likely already usable, so
just append our own without replacing the whole thing. */
i_set_failure_prefix("%suser-lookup(%s)",
old_log_prefix, input->username);
update_log_prefix = FALSE;
}
ret = mail_storage_service_lookup_real(ctx, input, update_log_prefix,
user_r, error_r);
i_set_failure_prefix("%s", old_log_prefix);
i_free(old_log_prefix);
return ret;
}
void mail_storage_service_save_userdb_fields(struct mail_storage_service_ctx *ctx,
pool_t pool, const char *const **userdb_fields_r)
{
i_assert(pool != NULL);
i_assert(userdb_fields_r != NULL);
ctx->userdb_next_pool = pool;
ctx->userdb_next_fieldsp = userdb_fields_r;
*userdb_fields_r = NULL;
}
static int
mail_storage_service_next_real(struct mail_storage_service_ctx *ctx,
struct mail_storage_service_user *user,
struct mail_user **mail_user_r,
const char **error_r)
{
struct mail_storage_service_privileges priv;
const char *error;
size_t 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_r) < 0)
return -2;
if (*priv.home != '/' && *priv.home != '\0') {
*error_r = t_strdup_printf(
"Relative home directory paths not supported: %s",
priv.home);
return -2;
}
/* we can't chroot if we want to switch between users. there's
not much point either (from security point of view). but if we're
already chrooted, we'll just have to continue and hope that the
current chroot is the same as the wanted chroot */
use_chroot = !temp_priv_drop ||
restrict_access_get_current_chroot() != NULL;
len = strlen(priv.chroot);
if (len > 2 && strcmp(priv.chroot + len - 2, "/.") == 0 &&
strncmp(priv.home, priv.chroot, len - 2) == 0) {
/* mail_chroot = /chroot/. means that the home dir already
contains the chroot dir. remove it from home. */
if (use_chroot) {
priv.home += len - 2;
if (*priv.home == '\0')
priv.home = "/";
priv.chroot = t_strndup(priv.chroot, len - 2);
set_keyval(ctx, user, "mail_home", priv.home);
set_keyval(ctx, user, "mail_chroot", priv.chroot);
}
} else if (len > 0 && !use_chroot) {
/* we're not going to chroot. fix home directory so we can
access it. */
if (*priv.home == '\0' || strcmp(priv.home, "/") == 0)
priv.home = priv.chroot;
else
priv.home = t_strconcat(priv.chroot, priv.home, NULL);
priv.chroot = "";
set_keyval(ctx, user, "mail_home", priv.home);
}
/* create ioloop context regardless of logging. it's also used by
stats plugin. */
user->ioloop_ctx = io_loop_context_new(current_ioloop);
if ((user->flags & MAIL_STORAGE_SERVICE_FLAG_NO_LOG_INIT) == 0)
mail_storage_service_init_log(ctx, user, &priv);
if ((user->flags & MAIL_STORAGE_SERVICE_FLAG_NO_RESTRICT_ACCESS) == 0) {
if (service_drop_privileges(user, &priv,
disallow_root, temp_priv_drop,
FALSE, &error) < 0) {
*error_r = t_strdup_printf(
"Couldn't drop privileges: %s", error);
return -1;
}
if (!temp_priv_drop ||
(user->flags & MAIL_STORAGE_SERVICE_FLAG_ENABLE_CORE_DUMPS) != 0)
restrict_access_allow_coredumps(TRUE);
}
/* privileges are dropped. initialize plugins that haven't been
initialized yet. */
module_dir_init(mail_storage_service_modules);
if (mail_storage_service_init_post(ctx, user, &priv,
mail_user_r, error_r) < 0)
return -2;
return 0;
}
int mail_storage_service_next(struct mail_storage_service_ctx *ctx,
struct mail_storage_service_user *user,
struct mail_user **mail_user_r,
const char **error_r)
{
char *old_log_prefix = i_strdup(i_get_failure_prefix());
int ret;
mail_storage_service_set_log_prefix(ctx, user->user_set, user,
&user->input, NULL);
i_set_failure_prefix("%s", old_log_prefix);
ret = mail_storage_service_next_real(ctx, user, mail_user_r, error_r);
if ((user->flags & MAIL_STORAGE_SERVICE_FLAG_NO_LOG_INIT) != 0)
i_set_failure_prefix("%s", old_log_prefix);
i_free(old_log_prefix);
return ret;
}
void mail_storage_service_restrict_setenv(struct mail_storage_service_ctx *ctx,
struct mail_storage_service_user *user)
{
struct mail_storage_service_privileges priv;
const char *error;
if (service_parse_privileges(ctx, user, &priv, &error) < 0)
i_fatal("user %s: %s", user->input.username, error);
if (service_drop_privileges(user, &priv,
FALSE, FALSE, TRUE, &error) < 0)
i_fatal("user %s: %s", user->input.username, error);
}
int mail_storage_service_lookup_next(struct mail_storage_service_ctx *ctx,
const struct mail_storage_service_input *input,
struct mail_storage_service_user **user_r,
struct mail_user **mail_user_r,
const char **error_r)
{
struct mail_storage_service_user *user;
int ret;
ret = mail_storage_service_lookup(ctx, input, &user, error_r);
if (ret <= 0)
return ret;
ret = mail_storage_service_next(ctx, user, mail_user_r, error_r);
if (ret < 0) {
mail_storage_service_user_free(&user);
return ret;
}
*user_r = user;
return 1;
}
void mail_storage_service_user_free(struct mail_storage_service_user **_user)
{
struct mail_storage_service_user *user = *_user;
*_user = NULL;
if (user->ioloop_ctx != NULL) {
if ((user->flags & MAIL_STORAGE_SERVICE_FLAG_NO_LOG_INIT) == 0) {
io_loop_context_remove_callbacks(user->ioloop_ctx,
mail_storage_service_io_activate_user,
mail_storage_service_io_deactivate_user, user);
if (io_loop_get_current_context(current_ioloop) == user->ioloop_ctx)
mail_storage_service_io_deactivate_user(user);
}
io_loop_context_unref(&user->ioloop_ctx);
}
settings_parser_deinit(&user->set_parser);
pool_unref(&user->pool);
}
void mail_storage_service_init_settings(struct mail_storage_service_ctx *ctx,
const struct mail_storage_service_input *input)
{
const struct setting_parser_info *user_info;
const struct mail_user_settings *user_set;
const struct setting_parser_context *set_parser;
const char *error;
pool_t temp_pool;
void **sets;
if (ctx->conn != NULL)
return;
temp_pool = pool_alloconly_create("service all settings", 4096);
if (mail_storage_service_read_settings(ctx, input, temp_pool,
&user_info, &set_parser,
&error) < 0)
i_fatal("%s", error);
sets = master_service_settings_parser_get_others(master_service,
set_parser);
user_set = sets[0];
mail_storage_service_first_init(ctx, user_info, user_set);
pool_unref(&temp_pool);
}
static int
mail_storage_service_all_iter_deinit(struct mail_storage_service_ctx *ctx)
{
int ret = 0;
if (ctx->auth_list != NULL) {
ret = auth_master_user_list_deinit(&ctx->auth_list);
auth_master_deinit(&ctx->iter_conn);
}
return ret;
}
void mail_storage_service_all_init_mask(struct mail_storage_service_ctx *ctx,
const char *user_mask_hint)
{
enum auth_master_flags flags = 0;
(void)mail_storage_service_all_iter_deinit(ctx);
mail_storage_service_init_settings(ctx, NULL);
/* create a new connection, because the iteration might take a while
and we might want to do USER lookups during it, which don't mix
well in the same connection. */
if (ctx->debug)
flags |= AUTH_MASTER_FLAG_DEBUG;
ctx->iter_conn = auth_master_init(auth_master_get_socket_path(ctx->conn),
flags);
ctx->auth_list = auth_master_user_list_init(ctx->iter_conn,
user_mask_hint, NULL);
}
int mail_storage_service_all_next(struct mail_storage_service_ctx *ctx,
const char **username_r)
{
i_assert((ctx->flags & MAIL_STORAGE_SERVICE_FLAG_USERDB_LOOKUP) != 0);
*username_r = auth_master_user_list_next(ctx->auth_list);
if (*username_r != NULL)
return 1;
return mail_storage_service_all_iter_deinit(ctx);
}
void mail_storage_service_deinit(struct mail_storage_service_ctx **_ctx)
{
struct mail_storage_service_ctx *ctx = *_ctx;
*_ctx = NULL;
(void)mail_storage_service_all_iter_deinit(ctx);
if (ctx->conn != NULL) {
if (mail_user_auth_master_conn == ctx->conn)
mail_user_auth_master_conn = NULL;
auth_master_deinit(&ctx->conn);
}
if (ctx->set_cache != NULL)
master_service_settings_cache_deinit(&ctx->set_cache);
pool_unref(&ctx->pool);
module_dir_unload(&mail_storage_service_modules);
mail_storage_deinit();
dict_drivers_unregister_builtin();
}
void **mail_storage_service_user_get_set(struct mail_storage_service_user *user)
{
return master_service_settings_parser_get_others(master_service,
user->set_parser);
}
const struct mail_storage_settings *
mail_storage_service_user_get_mail_set(struct mail_storage_service_user *user)
{
return mail_user_set_get_driver_settings(
user->user_info, user->user_set,
MAIL_STORAGE_SET_DRIVER_NAME);
}
const struct mail_storage_service_input *
mail_storage_service_user_get_input(struct mail_storage_service_user *user)
{
return &user->input;
}
struct setting_parser_context *
mail_storage_service_user_get_settings_parser(struct mail_storage_service_user *user)
{
return user->set_parser;
}
struct mail_storage_service_ctx *
mail_storage_service_user_get_service_ctx(struct mail_storage_service_user *user)
{
return user->service_ctx;
}
pool_t mail_storage_service_user_get_pool(struct mail_storage_service_user *user)
{
return user->pool;
}
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;
}