mail-storage-service.c revision abf015c9682f0f723db87a7c97bc284ef814818f
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi/* Copyright (c) 2009 Dovecot authors, see the included COPYING file */
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi#include "lib.h"
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi#include "ioloop.h"
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi#include "array.h"
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi#include "hostpid.h"
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi#include "module-dir.h"
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi#include "restrict-access.h"
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi#include "eacces-error.h"
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi#include "str.h"
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi#include "var-expand.h"
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi#include "dict.h"
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi#include "settings-parser.h"
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi#include "auth-master.h"
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi#include "master-service-private.h"
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi#include "master-service-settings.h"
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi#include "mail-user.h"
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi#include "mail-namespace.h"
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi#include "mail-storage.h"
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi#include "mail-storage-service.h"
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi#include <stdlib.h>
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi#include <sys/stat.h>
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi#include <pwd.h>
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi#include <grp.h>
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi#ifdef HAVE_SYS_TIME_H
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi# include <sys/time.h>
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi#endif
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi#ifdef HAVE_SYS_RESOURCE_H
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi# include <sys/resource.h>
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi#endif
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi/* If time moves backwards more than this, kill ourself instead of sleeping. */
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi#define MAX_TIME_BACKWARDS_SLEEP 5
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi#define MAX_NOWARN_FORWARD_SECS 10
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomistruct mail_storage_service_ctx {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi struct master_service *service;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi struct auth_master_connection *conn;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi struct auth_master_user_list_ctx *auth_list;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi const struct setting_parser_info **set_roots;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi enum mail_storage_service_flags flags;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi unsigned int modules_initialized:1;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi unsigned int debug:1;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi};
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomistruct mail_storage_service_user {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi pool_t pool;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi struct mail_storage_service_input input;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi const char *system_groups_user;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi const struct mail_user_settings *user_set;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi const struct setting_parser_info *user_info;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi struct setting_parser_context *set_parser;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi};
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomistatic struct module *modules = NULL;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomistatic void set_keyval(struct setting_parser_context *set_parser,
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi const char *key, const char *value)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi{
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi const char *str;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi str = t_strconcat(key, "=", value, NULL);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (settings_parse_line(set_parser, str) < 0) {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi i_fatal("Invalid userdb input '%s': %s", str,
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi settings_parser_get_error(set_parser));
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi }
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi}
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomistatic bool validate_chroot(const struct mail_user_settings *user_set,
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi const char *dir)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi{
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi const char *const *chroot_dirs;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (*dir == '\0')
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return FALSE;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (*user_set->valid_chroot_dirs == '\0')
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return FALSE;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi chroot_dirs = t_strsplit(user_set->valid_chroot_dirs, ":");
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi while (*chroot_dirs != NULL) {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (**chroot_dirs != '\0' &&
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi strncmp(dir, *chroot_dirs, strlen(*chroot_dirs)) == 0)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return TRUE;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi chroot_dirs++;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi }
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return FALSE;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi}
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomistatic int
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomiuser_reply_handle(struct mail_storage_service_user *user,
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi const struct auth_user_reply *reply,
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi const char **error_r)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi{
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi struct setting_parser_context *set_parser = user->set_parser;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi const char *const *str, *p, *line, *key;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi unsigned int i, count;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi int ret = 0;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (reply->uid != (uid_t)-1) {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (reply->uid == 0) {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi *error_r = "userdb returned 0 as uid";
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return -1;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi }
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi set_keyval(set_parser, "mail_uid", dec2str(reply->uid));
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi }
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (reply->gid != (uid_t)-1)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi set_keyval(set_parser, "mail_gid", dec2str(reply->gid));
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (reply->home != NULL)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi set_keyval(set_parser, "mail_home", reply->home);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (reply->chroot != NULL) {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (!validate_chroot(user->user_set, reply->chroot)) {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi *error_r = t_strdup_printf(
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi "userdb returned invalid chroot directory: %s "
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi "(see valid_chroot_dirs setting)",
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi reply->chroot);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return -1;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi }
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi set_keyval(set_parser, "mail_chroot", reply->chroot);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi }
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi str = array_get(&reply->extra_fields, &count);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi for (i = 0; i < count && ret == 0; i++) {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi line = str[i];
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (strncmp(line, "system_groups_user=", 19) == 0) {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi user->system_groups_user =
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi p_strdup(user->pool, line + 19);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi } else if (strncmp(line, "nice=", 5) == 0) {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi#ifdef HAVE_SETPRIORITY
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi int n = atoi(line + 5);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (n != 0) {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (setpriority(PRIO_PROCESS, 0, n) < 0)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi i_error("setpriority(%d) failed: %m", n);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi }
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi#endif
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi } else T_BEGIN {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (strncmp(line, "mail=", 5) == 0) {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi line = t_strconcat("mail_location=",
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi line + 5, NULL);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi } else if ((p = strchr(str[i], '=')) == NULL)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi line = t_strconcat(str[i], "=yes", NULL);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi else
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi line = str[i];
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi key = t_strcut(line, '=');
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (!settings_parse_is_valid_key(set_parser, key)) {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi /* assume it's a plugin setting */
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi line = t_strconcat("plugin/", line, NULL);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi }
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi ret = settings_parse_line(set_parser, line);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi } T_END;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi }
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (ret < 0) {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi *error_r = t_strdup_printf("Invalid userdb input '%s': %s",
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi str[i], settings_parser_get_error(set_parser));
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi }
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return ret;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi}
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomistatic int
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomiservice_auth_userdb_lookup(struct mail_storage_service_ctx *ctx,
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi const struct mail_storage_service_input *input,
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi pool_t pool, const char **user,
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi const char *const **fields_r,
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi const char **error_r)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi{
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi struct auth_user_info info;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi const char *new_username;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi int ret;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi memset(&info, 0, sizeof(info));
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi info.service = ctx->service->name;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi info.local_ip = input->local_ip;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi info.remote_ip = input->remote_ip;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi ret = auth_master_user_lookup(ctx->conn, *user, &info, pool,
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi &new_username, fields_r);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (ret > 0) {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (strcmp(*user, new_username) != 0) {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (ctx->debug)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi i_debug("changed username to %s", new_username);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi *user = t_strdup(new_username);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi }
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi *user = new_username;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi } else if (ret == 0)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi *error_r = "unknown user";
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi else
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi *error_r = "userdb lookup failed";
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return ret;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi}
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomistatic bool parse_uid(const char *str, uid_t *uid_r)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi{
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi struct passwd *pw;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi char *p;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (*str >= '0' && *str <= '9') {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi *uid_r = (uid_t)strtoul(str, &p, 10);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (*p == '\0')
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return TRUE;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi }
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi pw = getpwnam(str);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (pw == NULL)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return FALSE;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi *uid_r = pw->pw_uid;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return TRUE;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi}
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomistatic bool parse_gid(const char *str, gid_t *gid_r)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi{
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi struct group *gr;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi char *p;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (*str >= '0' && *str <= '9') {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi *gid_r = (gid_t)strtoul(str, &p, 10);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (*p == '\0')
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return TRUE;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi }
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi gr = getgrnam(str);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (gr == NULL)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return FALSE;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi *gid_r = gr->gr_gid;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return TRUE;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi}
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomistatic void
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomiservice_drop_privileges(const struct mail_user_settings *set,
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi const char *system_groups_user,
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi const char *home, const char *chroot,
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi bool disallow_root, bool keep_setuid_root)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi{
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi struct restrict_access_settings rset;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi uid_t current_euid, setuid_uid = 0;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi current_euid = geteuid();
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi restrict_access_init(&rset);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (*set->mail_uid != '\0') {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (!parse_uid(set->mail_uid, &rset.uid))
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi i_fatal("Unknown mail_uid user: %s", set->mail_uid);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (rset.uid < (uid_t)set->first_valid_uid ||
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi (set->last_valid_uid != 0 &&
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi rset.uid > (uid_t)set->last_valid_uid)) {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi i_fatal("Mail access for users with UID %s "
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi "not permitted (see first_valid_uid in config file).",
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi dec2str(rset.uid));
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi }
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi }
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (*set->mail_gid != '\0') {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (!parse_gid(set->mail_gid, &rset.gid))
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi i_fatal("Unknown mail_gid group: %s", set->mail_gid);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (rset.gid < (gid_t)set->first_valid_gid ||
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi (set->last_valid_gid != 0 &&
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi rset.gid > (gid_t)set->last_valid_gid)) {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi i_fatal("Mail access for users with GID %s "
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi "not permitted (see first_valid_gid in config file).",
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi dec2str(rset.gid));
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi }
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi }
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (*set->mail_privileged_group != '\0') {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (!parse_uid(set->mail_privileged_group, &rset.privileged_gid))
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi i_fatal("Unknown mail_gid group: %s", set->mail_gid);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi }
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (*set->mail_access_groups != '\0')
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi rset.extra_groups = set->mail_access_groups;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi rset.first_valid_gid = set->first_valid_gid;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi rset.last_valid_gid = set->last_valid_gid;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi /* we can't chroot if we want to switch between users. there's not
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi much point either (from security point of view) */
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi rset.chroot_dir = *chroot == '\0' || keep_setuid_root ? NULL : chroot;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi rset.system_groups_user = system_groups_user;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (disallow_root &&
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi (rset.uid == 0 || (rset.uid == (uid_t)-1 && current_euid == 0)))
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi i_fatal("Mail access not allowed for root");
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (keep_setuid_root) {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (current_euid != rset.uid) {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (current_euid != 0) {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi /* we're changing the UID,
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi switch back to root first */
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (seteuid(0) < 0)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi i_fatal("seteuid(0) failed: %m");
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi }
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi setuid_uid = rset.uid;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi }
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi rset.uid = (uid_t)-1;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi disallow_root = FALSE;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi }
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi restrict_access(&rset, *home == '\0' ? NULL : home, disallow_root);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (setuid_uid != 0) {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (seteuid(setuid_uid) < 0)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi i_fatal("seteuid(%s) failed: %m", dec2str(setuid_uid));
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi }
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi}
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomistatic int
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomimail_storage_service_init_post(struct mail_storage_service_ctx *ctx,
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi struct mail_storage_service_user *user,
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi const char *home, struct mail_user **mail_user_r,
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi const char **error_r)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi{
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi const struct mail_storage_settings *mail_set;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi struct mail_user *mail_user;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi mail_user = mail_user_alloc(user->input.username, user->user_info,
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi user->user_set);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi mail_user_set_home(mail_user, *home == '\0' ? NULL : home);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi mail_user_set_vars(mail_user, geteuid(), ctx->service->name,
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi &user->input.local_ip, &user->input.remote_ip);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi mail_set = mail_user_set_get_storage_set(mail_user);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (mail_set->mail_debug) {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi i_debug("Effective uid=%s, gid=%s, home=%s",
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi dec2str(geteuid()), dec2str(getegid()), home);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi }
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if ((ctx->flags & MAIL_STORAGE_SERVICE_FLAG_TEMP_PRIV_DROP) != 0 &&
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi (ctx->flags & MAIL_STORAGE_SERVICE_FLAG_ENABLE_CORE_DUMPS) == 0) {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi /* we don't want to write core files to any users' home
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi directories since they could contain information about other
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi users' mails as well. so do no chdiring to home. */
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi } else if (*home != '\0' &&
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi (ctx->flags & MAIL_STORAGE_SERVICE_FLAG_NO_CHDIR) == 0) {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi /* If possible chdir to home directory, so that core file
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi could be written in case we crash. */
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (chdir(home) < 0) {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (errno == EACCES) {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi i_error("%s", eacces_error_get("chdir",
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi t_strconcat(home, "/", NULL)));
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi } if (errno != ENOENT)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi i_error("chdir(%s) failed: %m", home);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi else if (mail_set->mail_debug)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi i_debug("Home dir not found: %s", home);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi }
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi }
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (mail_user_init(mail_user, error_r) < 0) {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi mail_user_unref(&mail_user);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return -1;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi }
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (mail_namespaces_init(mail_user, error_r) < 0) {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi mail_user_unref(&mail_user);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return -1;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi }
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi *mail_user_r = mail_user;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return 0;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi}
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomistatic const struct var_expand_table *
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomiget_var_expand_table(struct master_service *service,
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi struct mail_storage_service_input *input)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi{
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi static struct var_expand_table static_tab[] = {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi { 'u', NULL, "user" },
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi { 'n', NULL, "username" },
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi { 'd', NULL, "domain" },
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi { 's', NULL, "service" },
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi { 'l', NULL, "lip" },
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi { 'r', NULL, "rip" },
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi { 'p', NULL, "pid" },
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi { 'i', NULL, "uid" },
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi { '\0', NULL, NULL }
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi };
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi struct var_expand_table *tab;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi tab = t_malloc(sizeof(static_tab));
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi memcpy(tab, static_tab, sizeof(static_tab));
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi tab[0].value = input->username;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi tab[1].value = t_strcut(input->username, '@');
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi tab[2].value = strchr(input->username, '@');
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (tab[2].value != NULL) tab[2].value++;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi tab[3].value = service->name;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi tab[4].value = net_ip2addr(&input->local_ip);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi tab[5].value = net_ip2addr(&input->remote_ip);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi tab[6].value = my_pid;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi tab[7].value = dec2str(geteuid());
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return tab;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi}
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomistatic const char *
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomiuser_expand_varstr(struct master_service *service,
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi struct mail_storage_service_input *input, const char *str)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi{
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi string_t *ret;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (*str == SETTING_STRVAR_EXPANDED[0])
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return str + 1;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi i_assert(*str == SETTING_STRVAR_UNEXPANDED[0]);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi ret = t_str_new(256);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi var_expand(ret, str + 1, get_var_expand_table(service, input));
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return str_c(ret);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi}
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomistatic void
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomimail_storage_service_init_log(struct master_service *service,
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi struct mail_storage_service_user *user)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi{
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi const struct mail_user_settings *user_set;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi void **sets;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi sets = master_service_settings_get_others(service);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi user_set = sets[0];
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi T_BEGIN {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi string_t *str;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi str = t_str_new(256);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi var_expand(str, user->user_set->mail_log_prefix,
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi get_var_expand_table(service, &user->input));
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi master_service_init_log(service, str_c(str));
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi } T_END;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi}
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomistatic void mail_storage_service_time_moved(time_t old_time, time_t new_time)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi{
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi long diff = new_time - old_time;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (diff > 0) {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (diff > MAX_NOWARN_FORWARD_SECS)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi i_warning("Time jumped forwards %ld seconds", diff);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi }
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi diff = -diff;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (diff > MAX_TIME_BACKWARDS_SLEEP) {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi i_fatal("Time just moved backwards by %ld seconds. "
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi "This might cause a lot of problems, "
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi "so I'll just kill myself now. "
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi "http://wiki.dovecot.org/TimeMovedBackwards", diff);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi } else {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi i_error("Time just moved backwards by %ld seconds. "
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi "I'll sleep now until we're back in present. "
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi "http://wiki.dovecot.org/TimeMovedBackwards", diff);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi /* Sleep extra second to make sure usecs also grows. */
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi diff++;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi while (diff > 0 && sleep(diff) != 0) {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi /* don't use sleep()'s return value, because
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi it could get us to a long loop in case
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi interrupts just keep coming */
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi diff = old_time - time(NULL) + 1;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi }
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi }
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi}
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomistruct mail_storage_service_ctx *
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomimail_storage_service_init(struct master_service *service,
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi const struct setting_parser_info *set_roots[],
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi enum mail_storage_service_flags flags)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi{
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi struct mail_storage_service_ctx *ctx;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi unsigned int count;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi (void)umask(0077);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi io_loop_set_time_moved_callback(current_ioloop,
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi mail_storage_service_time_moved);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi mail_storage_init();
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi mail_storage_register_all();
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi mailbox_list_register_all();
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi ctx = i_new(struct mail_storage_service_ctx, 1);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi ctx->service = service;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi ctx->flags = flags;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi /* @UNSAFE */
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (set_roots == NULL)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi count = 0;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi else
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi for (count = 0; set_roots[count] != NULL; count++) ;
ctx->set_roots = i_new(const struct setting_parser_info *, count + 2);
ctx->set_roots[0] = &mail_user_setting_parser_info;
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_NO_LOG_INIT) == 0) {
master_service_init_log(service,
t_strconcat(service->name, ": ", NULL));
}
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 void
settings_parser_update_children_parent(struct setting_parser_info *parent,
pool_t pool)
{
struct setting_define *new_defs;
struct setting_parser_info *new_info;
unsigned int i, count;
for (count = 0; parent->defines[count].key != NULL; count++) ;
new_defs = p_new(pool, struct setting_define, count + 1);
memcpy(new_defs, parent->defines, sizeof(*new_defs) * count);
parent->defines = new_defs;
for (i = 0; i < count; i++) {
if (new_defs[i].list_info == NULL ||
new_defs[i].list_info->parent == NULL)
continue;
new_info = p_new(pool, struct setting_parser_info, 1);
*new_info = *new_defs[i].list_info;
new_info->parent = parent;
new_defs[i].list_info = new_info;
}
}
static struct setting_parser_info *
dyn_parsers_update_parent(pool_t pool,
struct master_service_settings_input *input)
{
const struct setting_parser_info *old_parent, **new_roots;
struct setting_parser_info *new_parent, *new_info;
struct dynamic_settings_parser *new_dyn_parsers;
unsigned int i, count;
/* settings_parser_info_update() modifies the parent structure.
since we may be using the same structure later, we want it to be
in its original state, so we'll have to copy all structures. */
old_parent = input->dyn_parsers[0].info->parent;
new_parent = p_new(pool, struct setting_parser_info, 1);
*new_parent = *old_parent;
settings_parser_update_children_parent(new_parent, pool);
/* update root */
for (count = 0; input->roots[count] != NULL; count++) ;
new_roots = p_new(pool, const struct setting_parser_info *, count + 1);
for (i = 0; i < count; i++) {
if (input->roots[i] == old_parent)
new_roots[i] = new_parent;
else
new_roots[i] = input->roots[i];
}
input->roots = new_roots;
/* update parent in dyn_parsers */
for (count = 0; input->dyn_parsers[count].name != NULL; count++) ;
new_dyn_parsers = p_new(pool, struct dynamic_settings_parser, count + 1);
for (i = 0; i < count; i++) {
new_dyn_parsers[i] = input->dyn_parsers[i];
new_info = p_new(pool, struct setting_parser_info, 1);
*new_info = *input->dyn_parsers[i].info;
new_info->parent = new_parent;
new_dyn_parsers[i].info = new_info;
}
input->dyn_parsers = new_dyn_parsers;
return new_parent;
}
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 char **error_r)
{
struct master_service_settings_input set_input;
unsigned int i;
memset(&set_input, 0, sizeof(set_input));
set_input.roots = ctx->set_roots;
set_input.dyn_parsers = mail_storage_get_dynamic_parsers();
/* 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 =
(ctx->flags & MAIL_STORAGE_SERVICE_FLAG_USERDB_LOOKUP) == 0;
set_input.dyn_parsers_parent =
dyn_parsers_update_parent(pool, &set_input);
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 (master_service_settings_read(ctx->service, &set_input,
error_r) < 0) {
*error_r = t_strdup_printf("Error reading configuration: %s",
*error_r);
return -1;
}
for (i = 0; ctx->set_roots[i] != NULL; i++) {
if (ctx->set_roots[i] == &mail_user_setting_parser_info) {
*user_info_r = set_input.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)
{
const struct mail_storage_settings *mail_set;
i_assert(ctx->conn == NULL);
mail_set = mail_user_set_get_driver_settings(user_info, user_set,
MAIL_STORAGE_SET_DRIVER_NAME);
ctx->debug = mail_set->mail_debug;
modules = *user_set->mail_plugins == '\0' ? NULL :
module_dir_load(user_set->mail_plugin_dir,
user_set->mail_plugins, TRUE,
master_service_get_version_string(ctx->service));
ctx->conn = auth_master_init(user_set->auth_socket_path,
ctx->debug);
i_assert(mail_user_auth_master_conn == NULL);
mail_user_auth_master_conn = ctx->conn;
}
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)
{
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;
struct auth_user_reply reply;
void **sets;
pool_t user_pool, temp_pool;
int ret = 1;
user_pool = pool_alloconly_create("mail storage service user", 1024*4);
if (mail_storage_service_read_settings(ctx, input, user_pool,
&user_info, error_r) < 0) {
pool_unref(&user_pool);
return -1;
}
sets = settings_parser_get_list(ctx->service->set_parser);
user_set = sets[1];
if (ctx->conn == NULL)
mail_storage_service_first_init(ctx, user_info, user_set);
temp_pool = pool_alloconly_create("userdb lookup", 1024);
if ((ctx->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->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->set_parser =
settings_parser_dup(ctx->service->set_parser, user_pool);
sets = settings_parser_get_list(user->set_parser);
user->user_set = sets[1];
if ((ctx->flags & MAIL_STORAGE_SERVICE_FLAG_USERDB_LOOKUP) == 0) {
const char *home = getenv("HOME");
if (home != NULL)
set_keyval(user->set_parser, "mail_home", home);
}
if (userdb_fields != NULL) {
auth_user_fields_parse(userdb_fields, temp_pool, &reply);
if (user_reply_handle(user, &reply, error_r) < 0)
ret = -1;
}
pool_unref(&temp_pool);
*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 char **error_r)
{
const struct mail_user_settings *user_set = user->user_set;
const char *home, *chroot;
unsigned int len;
bool temp_priv_drop =
(ctx->flags & MAIL_STORAGE_SERVICE_FLAG_TEMP_PRIV_DROP);
/* 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 -1;
}
if ((ctx->flags & MAIL_STORAGE_SERVICE_NO_LOG_INIT) == 0)
mail_storage_service_init_log(ctx->service, user);
if ((ctx->flags & MAIL_STORAGE_SERVICE_FLAG_NO_RESTRICT_ACCESS) == 0) {
service_drop_privileges(user_set, user->system_groups_user,
home, chroot,
(ctx->flags & MAIL_STORAGE_SERVICE_FLAG_DISALLOW_ROOT) != 0,
temp_priv_drop);
if (!temp_priv_drop ||
(ctx->flags & MAIL_STORAGE_SERVICE_FLAG_ENABLE_CORE_DUMPS) != 0)
restrict_access_allow_coredumps(TRUE);
}
if (!ctx->modules_initialized) {
/* privileges dropped for the first time. initialize the
modules now to avoid code running as root. */
module_dir_init(modules);
ctx->modules_initialized = TRUE;
}
/* we couldn't do chrooting, so if chrooting was enabled fix
the home directory */
len = strlen(chroot);
if (len > 2 && strcmp(chroot + len - 2, "/.") == 0 &&
strncmp(home, chroot, len - 2) == 0) {
/* home dir already contains the chroot dir */
if (!temp_priv_drop) {
home += len - 2;
if (*home == '\0')
home = "/";
set_keyval(user->set_parser, "mail_home", home);
chroot = t_strndup(chroot, len - 2);
}
} else if (len > 0 && temp_priv_drop) {
set_keyval(user->set_parser, "mail_home",
t_strconcat(chroot, "/", home, NULL));
}
if (mail_storage_service_init_post(ctx, user, home,
mail_user_r, error_r) < 0)
return -1;
return 0;
}
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;
const char *error;
int ret;
ret = mail_storage_service_lookup(ctx, input, &user, &error);
if (ret <= 0) {
*error_r = t_strdup_printf("User lookup failed: %s", error);
return ret;
}
if (mail_storage_service_next(ctx, user, mail_user_r, &error) < 0) {
mail_storage_service_user_free(&user);
*error_r = t_strdup_printf("User init failed: %s", error);
return -1;
}
*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;
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 char *error;
void **sets;
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, &error) < 0)
i_fatal("%s", error);
sets = settings_parser_get_list(ctx->service->set_parser);
user_set = sets[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);
}
i_free(ctx);
module_dir_unload(&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;
}