# HG changeset patch # User Timo Sirainen # Date 1204610093 -7200 # Node ID af998ae4254bda0e36af9426dc1c08206745321d # Parent 09cdd4330d732901b6daca51871a87dc94cc7a9e Replaced mail_extra_groups setting with mail_privileged_group and mail_access_groups settings. mail_privileged_group allows temporary access to the group when creating mbox INBOX dotlocks. diff -r 09cdd4330d73 -r af998ae4254b dovecot-example.conf --- a/dovecot-example.conf Tue Mar 04 07:54:41 2008 +0200 +++ b/dovecot-example.conf Tue Mar 04 07:54:53 2008 +0200 @@ -269,12 +269,17 @@ #mail_uid = #mail_gid = -# Grant access to these extra groups for mail processes. Typical use would be -# to give "mail" group write access to /var/mail to be able to create dotlocks. -# WARNING: If your users can create symlinks, this will allow the users to -# read any files that are group-readable by one of these groups! Make sure at -# least all the common mailboxes have 0600 permissions (or a different group). -#mail_extra_groups = +# Group to enable temporarily for privileged operations. Currently this is +# used only for creating mbox dotlock files when creation fails for INBOX. +# Typically this is set to "mail" to give access to /var/mail. +#mail_privileged_group = + +# Grant access to these supplementary groups for mail processes. Typically +# these are used to set up access to shared mailboxes. Note that it may be +# dangerous to set these if users can create symlinks (e.g. if "mail" group is +# set here, ln -s /var/mail ~/mail/var could allow a user to delete others' +# mailboxes, or ln -s /secret/shared/box ~/mail/mybox would allow reading it). +#mail_access_groups = # Allow full filesystem access to clients. There's no access checks other than # what the operating system does for the active UID/GID. It works with both diff -r 09cdd4330d73 -r af998ae4254b src/lib-storage/index/mbox/mbox-lock.c --- a/src/lib-storage/index/mbox/mbox-lock.c Tue Mar 04 07:54:41 2008 +0200 +++ b/src/lib-storage/index/mbox/mbox-lock.c Tue Mar 04 07:54:53 2008 +0200 @@ -1,6 +1,7 @@ /* Copyright (c) 2002-2008 Dovecot authors, see the included COPYING file */ #include "lib.h" +#include "restrict-access.h" #include "nfs-workarounds.h" #include "mail-index-private.h" #include "mbox-storage.h" @@ -38,6 +39,12 @@ MBOX_LOCK_COUNT }; +enum mbox_dotlock_op { + MBOX_DOTLOCK_OP_LOCK, + MBOX_DOTLOCK_OP_UNLOCK, + MBOX_DOTLOCK_OP_TOUCH +}; + struct mbox_lock_context { struct mbox_mailbox *mbox; int lock_status[MBOX_LOCK_COUNT]; @@ -46,6 +53,7 @@ int lock_type; bool dotlock_last_stale; bool fcntl_locked; + bool using_privileges; }; struct mbox_lock_data { @@ -200,6 +208,9 @@ enum mbox_lock_type *lock_types; int i; + if (ctx->using_privileges) + restrict_access_drop_priv_gid(); + if (stale && !ctx->dotlock_last_stale) { /* get next index we wish to try locking. it's the one after dotlocking. */ @@ -231,9 +242,92 @@ MAILBOX_LOCK_NOTIFY_MAILBOX_OVERRIDE : MAILBOX_LOCK_NOTIFY_MAILBOX_ABORT, secs_left); + if (ctx->using_privileges) { + if (restrict_access_use_priv_gid() < 0) { + /* shouldn't get here */ + return FALSE; + } + } return TRUE; } +static int mbox_dotlock_privileged_op(struct mbox_mailbox *mbox, + struct dotlock_settings *set, + enum mbox_dotlock_op op) +{ + const char *dir, *fname; + int ret = -1, orig_dir_fd; + + orig_dir_fd = open(".", O_RDONLY); + if (orig_dir_fd == -1) { + i_error("open(.) failed: %m"); + return -1; + } + + /* allow dotlocks to be created only for files we can read while we're + unprivileged. to make sure there are no race conditions we first + have to chdir to the mbox file's directory and then use relative + paths. unless this is done, users could: + - create *.lock files to any directory writable by the + privileged group + - DoS other users by dotlocking their mailboxes infinitely + */ + fname = strrchr(mbox->path, '/'); + if (fname == NULL) { + /* already relative */ + fname = mbox->path; + } else { + dir = t_strdup_until(mbox->path, fname); + if (chdir(dir) < 0) { + i_error("chdir(%s) failed: %m", dir); + (void)close(orig_dir_fd); + return -1; + } + fname++; + } + if (op == MBOX_DOTLOCK_OP_LOCK) { + if (access(fname, R_OK) < 0) { + i_error("access(%s) failed: %m", mbox->path); + return -1; + } + } + + if (restrict_access_use_priv_gid() < 0) { + (void)close(orig_dir_fd); + return -1; + } + + switch (op) { + case MBOX_DOTLOCK_OP_LOCK: + /* we're now privileged - avoid doing as much as possible */ + ret = file_dotlock_create(set, fname, 0, &mbox->mbox_dotlock); + if (ret > 0) + mbox->mbox_used_privileges = TRUE; + break; + case MBOX_DOTLOCK_OP_UNLOCK: + /* we're now privileged - avoid doing as much as possible */ + ret = file_dotlock_delete(&mbox->mbox_dotlock); + mbox->mbox_used_privileges = FALSE; + break; + case MBOX_DOTLOCK_OP_TOUCH: + if (!file_dotlock_is_locked(mbox->mbox_dotlock)) { + file_dotlock_delete(&mbox->mbox_dotlock); + mbox->mbox_used_privileges = TRUE; + ret = -1; + } else { + ret = file_dotlock_touch(mbox->mbox_dotlock); + } + break; + } + + restrict_access_drop_priv_gid(); + + if (fchdir(orig_dir_fd) < 0) + i_error("fchdir() failed: %m"); + (void)close(orig_dir_fd); + return ret; +} + static int mbox_lock_dotlock_int(struct mbox_lock_context *ctx, int lock_type, bool try) { @@ -245,7 +339,15 @@ if (!mbox->mbox_dotlocked) return 1; - if (file_dotlock_delete(&mbox->mbox_dotlock) <= 0) { + if (!mbox->mbox_used_privileges) + ret = file_dotlock_delete(&mbox->mbox_dotlock); + else { + ctx->using_privileges = TRUE; + ret = mbox_dotlock_privileged_op(mbox, NULL, + MBOX_DOTLOCK_OP_UNLOCK); + ctx->using_privileges = FALSE; + } + if (ret <= 0) { mbox_set_syscall_error(mbox, "file_dotlock_delete()"); ret = -1; } @@ -269,6 +371,13 @@ set.context = ctx; ret = file_dotlock_create(&set, mbox->path, 0, &mbox->mbox_dotlock); + if (ret < 0 && errno == EACCES && restrict_access_have_priv_gid() && + mbox->mbox_privileged_locking) { + /* try again, this time with extra privileges */ + ret = mbox_dotlock_privileged_op(mbox, &set, + MBOX_DOTLOCK_OP_LOCK); + } + if (ret < 0) { if ((ENOSPACE(errno) || errno == EACCES) && try) return 1; @@ -643,3 +752,16 @@ return mbox_unlock_files(&ctx); } + +void mbox_dotlock_touch(struct mbox_mailbox *mbox) +{ + if (mbox->mbox_dotlock == NULL) + return; + + if (!mbox->mbox_used_privileges) + (void)file_dotlock_touch(mbox->mbox_dotlock); + else { + (void)mbox_dotlock_privileged_op(mbox, NULL, + MBOX_DOTLOCK_OP_TOUCH); + } +} diff -r 09cdd4330d73 -r af998ae4254b src/lib-storage/index/mbox/mbox-lock.h --- a/src/lib-storage/index/mbox/mbox-lock.h Tue Mar 04 07:54:41 2008 +0200 +++ b/src/lib-storage/index/mbox/mbox-lock.h Tue Mar 04 07:54:53 2008 +0200 @@ -7,4 +7,6 @@ unsigned int *lock_id_r); int mbox_unlock(struct mbox_mailbox *mbox, unsigned int lock_id); +void mbox_dotlock_touch(struct mbox_mailbox *mbox); + #endif diff -r 09cdd4330d73 -r af998ae4254b src/lib-storage/index/mbox/mbox-storage.c --- a/src/lib-storage/index/mbox/mbox-storage.c Tue Mar 04 07:54:41 2008 +0200 +++ b/src/lib-storage/index/mbox/mbox-storage.c Tue Mar 04 07:54:53 2008 +0200 @@ -396,6 +396,33 @@ return &storage->storage; } +static bool mbox_name_is_dotlock(const char *name) +{ + unsigned int len = strlen(name); + + return len >= 5 && strcmp(name + len - 5, ".lock") == 0; +} + +static bool +mbox_is_valid_existing_name(struct mailbox_list *list, const char *name) +{ + struct mbox_storage *storage = MBOX_LIST_CONTEXT(list); + + return storage->list_module_ctx.super. + is_valid_existing_name(list, name) && + !mbox_name_is_dotlock(name); +} + +static bool +mbox_is_valid_create_name(struct mailbox_list *list, const char *name) +{ + struct mbox_storage *storage = MBOX_LIST_CONTEXT(list); + + return storage->list_module_ctx.super. + is_valid_create_name(list, name) && + !mbox_name_is_dotlock(name); +} + static int mbox_create(struct mail_storage *_storage, const char *data, const char **error_r) { @@ -419,6 +446,8 @@ } _storage->list->v.iter_is_mailbox = mbox_list_iter_is_mailbox; _storage->list->v.delete_mailbox = mbox_list_delete_mailbox; + _storage->list->v.is_valid_existing_name = mbox_is_valid_existing_name; + _storage->list->v.is_valid_create_name = mbox_is_valid_create_name; MODULE_CONTEXT_SET_FULL(_storage->list, mbox_mailbox_list_module, storage, &storage->list_module_ctx); @@ -492,7 +521,7 @@ static void mbox_lock_touch_timeout(struct mbox_mailbox *mbox) { - (void)file_dotlock_touch(mbox->mbox_dotlock); + mbox_dotlock_touch(mbox); } static struct mbox_mailbox * @@ -553,7 +582,7 @@ struct mail_storage *_storage = &storage->storage; struct mbox_mailbox *mbox; struct mail_index *index; - const char *path; + const char *path, *rootdir; path = mailbox_list_get_path(_storage->list, name, MAILBOX_LIST_PATH_TYPE_MAILBOX); @@ -570,6 +599,14 @@ } } + if (strcmp(name, "INBOX") == 0) { + /* if INBOX isn't under the root directory, it's probably in + /var/mail and we want to allow privileged dotlocking */ + rootdir = mailbox_list_get_path(_storage->list, NULL, + MAILBOX_LIST_PATH_TYPE_DIR); + if (strncmp(path, rootdir, strlen(rootdir)) != 0) + mbox->mbox_privileged_locking = TRUE; + } return &mbox->ibox.box; } diff -r 09cdd4330d73 -r af998ae4254b src/lib-storage/index/mbox/mbox-storage.h --- a/src/lib-storage/index/mbox/mbox-storage.h Tue Mar 04 07:54:41 2008 +0200 +++ b/src/lib-storage/index/mbox/mbox-storage.h Tue Mar 04 07:54:53 2008 +0200 @@ -47,6 +47,8 @@ unsigned int mbox_very_dirty_syncs:1; unsigned int mbox_save_md5:1; unsigned int mbox_dotlocked:1; + unsigned int mbox_used_privileges:1; + unsigned int mbox_privileged_locking:1; unsigned int syncing:1; }; diff -r 09cdd4330d73 -r af998ae4254b src/lib/restrict-access.c --- a/src/lib/restrict-access.c Tue Mar 04 07:54:41 2008 +0200 +++ b/src/lib/restrict-access.c Tue Mar 04 07:54:53 2008 +0200 @@ -1,15 +1,22 @@ /* Copyright (c) 2002-2008 Dovecot authors, see the included COPYING file */ +#define _GNU_SOURCE /* setresgid() */ +#include +#include + #include "lib.h" #include "restrict-access.h" #include "env-util.h" #include -#include #include #include -void restrict_access_set_env(const char *user, uid_t uid, gid_t gid, +static gid_t primary_gid = (gid_t)-1, privileged_gid = (gid_t)-1; +static bool using_priv_gid = FALSE; + +void restrict_access_set_env(const char *user, uid_t uid, + gid_t gid, gid_t privileged_gid, const char *chroot_dir, gid_t first_valid_gid, gid_t last_valid_gid, const char *extra_groups) @@ -21,6 +28,10 @@ env_put(t_strdup_printf("RESTRICT_SETUID=%s", dec2str(uid))); env_put(t_strdup_printf("RESTRICT_SETGID=%s", dec2str(gid))); + if (privileged_gid != (gid_t)-1) { + env_put(t_strdup_printf("RESTRICT_SETGID_PRIV=%s", + dec2str(privileged_gid))); + } if (extra_groups != NULL && *extra_groups != '\0') { env_put(t_strconcat("RESTRICT_SETEXTRAGROUPS=", extra_groups, NULL)); @@ -36,6 +47,53 @@ } } +static void restrict_init_groups(gid_t primary_gid, gid_t privileged_gid) +{ + if (privileged_gid == (gid_t)-1) { + if (primary_gid == getgid() && primary_gid == getegid()) { + /* everything is already set */ + return; + } + + if (setgid(primary_gid) != 0) { + i_fatal("setgid(%s) failed with euid=%s, " + "gid=%s, egid=%s: %m", + dec2str(primary_gid), dec2str(geteuid()), + dec2str(getgid()), dec2str(getegid())); + } + return; + } + + if (getegid() != 0 && primary_gid == getgid() && + primary_gid == getegid()) { + /* privileged_gid is hopefully in saved ID. if not, + there's nothing we can do about it. */ + return; + } + +#ifdef HAVE_SETRESGID + if (setresgid(primary_gid, primary_gid, privileged_gid) != 0) { + i_fatal("setresgid(%s,%s,%s) failed with euid=%s: %m", + dec2str(primary_gid), dec2str(primary_gid), + dec2str(privileged_gid), dec2str(geteuid())); + } +#else + /* real: primary_gid + effective: privileged_gid + saved: privileged_gid */ + if (setregid(primary_gid, privileged_gid) != 0) { + i_fatal("setregid(%s,%s) failed with euid=%s: %m", + dec2str(primary_gid), dec2str(privileged_gid), + dec2str(geteuid())); + } + /* effective: privileged_gid -> primary_gid */ + if (setegid(privileged_gid) != 0) { + i_fatal("setegid(%s) failed with euid=%s: %m", + dec2str(privileged_gid), dec2str(geteuid())); + } +#endif +} + static gid_t *get_groups_list(unsigned int *gid_count_r) { gid_t *gid_list; @@ -145,31 +203,33 @@ void restrict_access_by_env(bool disallow_root) { const char *env; - gid_t gid; uid_t uid; bool is_root, have_root_group, preserve_groups = FALSE; + bool allow_root_gid; is_root = geteuid() == 0; - /* set the primary group */ + /* set the primary/privileged group */ env = getenv("RESTRICT_SETGID"); - gid = env == NULL || *env == '\0' ? (gid_t)-1 : + primary_gid = env == NULL || *env == '\0' ? (gid_t)-1 : + (gid_t)strtoul(env, NULL, 10); + env = getenv("RESTRICT_SETGID_PRIV"); + privileged_gid = env == NULL || *env == '\0' ? (gid_t)-1 : (gid_t)strtoul(env, NULL, 10); - have_root_group = gid == 0; - if (gid != (gid_t)-1 && (gid != getgid() || gid != getegid())) { - if (setgid(gid) != 0) { - i_fatal("setgid(%s) failed with euid=%s, egid=%s: %m", - dec2str(gid), dec2str(geteuid()), - dec2str(getegid())); - } + + have_root_group = primary_gid == 0; + if (primary_gid != (gid_t)-1 || privileged_gid != (gid_t)-1) { + if (primary_gid == (gid_t)-1) + primary_gid = getegid(); + restrict_init_groups(primary_gid, privileged_gid); } /* set system user's groups */ env = getenv("RESTRICT_USER"); if (env != NULL && *env != '\0' && is_root) { - if (initgroups(env, gid) < 0) { + if (initgroups(env, primary_gid) < 0) { i_fatal("initgroups(%s, %s) failed: %m", - env, dec2str(gid)); + env, dec2str(primary_gid)); } preserve_groups = TRUE; } @@ -179,7 +239,7 @@ env = getenv("RESTRICT_SETEXTRAGROUPS"); if (is_root) { T_BEGIN { - fix_groups_list(env, gid, preserve_groups, + fix_groups_list(env, primary_gid, preserve_groups, &have_root_group); } T_END; } @@ -228,12 +288,20 @@ } env = getenv("RESTRICT_GID_FIRST"); - if ((!have_root_group || (env != NULL && atoi(env) != 0)) && uid != 0) { + if (env != NULL && atoi(env) != 0) + allow_root_gid = FALSE; + else if (primary_gid == 0 || privileged_gid == 0) + allow_root_gid = TRUE; + else + allow_root_gid = FALSE; + + if (!allow_root_gid && uid != 0) { if (getgid() == 0 || getegid() == 0 || setgid(0) == 0) { - if (gid == 0) + if (primary_gid == 0) i_fatal("GID 0 isn't permitted"); i_fatal("We couldn't drop root group privileges " - "(wanted=%s, gid=%s, egid=%s)", dec2str(gid), + "(wanted=%s, gid=%s, egid=%s)", + dec2str(primary_gid), dec2str(getgid()), dec2str(getegid())); } } @@ -242,8 +310,43 @@ env_put("RESTRICT_USER="); env_put("RESTRICT_CHROOT="); env_put("RESTRICT_SETUID="); - env_put("RESTRICT_SETGID="); + if (privileged_gid == (gid_t)-1) { + /* if we're dropping privileges before executing and + a privileged group is set, the groups must be fixed + after exec */ + env_put("RESTRICT_SETGID="); + env_put("RESTRICT_SETGID_PRIV="); + } env_put("RESTRICT_SETEXTRAGROUPS="); env_put("RESTRICT_GID_FIRST="); env_put("RESTRICT_GID_LAST="); } + +int restrict_access_use_priv_gid(void) +{ + i_assert(!using_priv_gid); + + if (privileged_gid == (gid_t)-1) + return 0; + if (setegid(privileged_gid) < 0) { + i_error("setegid(privileged) failed: %m"); + return -1; + } + using_priv_gid = TRUE; + return 0; +} + +void restrict_access_drop_priv_gid(void) +{ + if (!using_priv_gid) + return; + + if (setegid(primary_gid) < 0) + i_fatal("setegid(primary) failed: %m"); + using_priv_gid = FALSE; +} + +bool restrict_access_have_priv_gid(void) +{ + return privileged_gid != (gid_t)-1; +} diff -r 09cdd4330d73 -r af998ae4254b src/lib/restrict-access.h --- a/src/lib/restrict-access.h Tue Mar 04 07:54:41 2008 +0200 +++ b/src/lib/restrict-access.h Tue Mar 04 07:54:53 2008 +0200 @@ -2,8 +2,10 @@ #define RESTRICT_ACCESS_H /* set environment variables so they can be read with - restrict_access_by_env() */ -void restrict_access_set_env(const char *user, uid_t uid, gid_t gid, + restrict_access_by_env(). If privileged_gid != (gid_t)-1, + the privileged GID can be temporarily enabled/disabled. */ +void restrict_access_set_env(const char *user, uid_t uid, + gid_t gid, gid_t privileged_gid, const char *chroot_dir, gid_t first_valid_gid, gid_t last_valid_gid, const char *extra_groups); @@ -13,4 +15,11 @@ environment settings and we have root uid or gid. */ void restrict_access_by_env(bool disallow_root); +/* If privileged_gid was set, these functions can be used to temporarily + gain access to the group. */ +int restrict_access_use_priv_gid(void); +void restrict_access_drop_priv_gid(void); +/* Returns TRUE if privileged GID exists for this process. */ +bool restrict_access_have_priv_gid(void); + #endif diff -r 09cdd4330d73 -r af998ae4254b src/master/auth-process.c --- a/src/master/auth-process.c Tue Mar 04 07:54:41 2008 +0200 +++ b/src/master/auth-process.c Tue Mar 04 07:54:53 2008 +0200 @@ -421,8 +421,8 @@ int i; /* setup access environment */ - restrict_access_set_env(set->user, set->uid, set->gid, set->chroot, - 0, 0, NULL); + restrict_access_set_env(set->user, set->uid, set->gid, + (gid_t)-1, set->chroot, 0, 0, NULL); /* set other environment */ env_put("DOVECOT_MASTER=1"); diff -r 09cdd4330d73 -r af998ae4254b src/master/login-process.c --- a/src/master/login-process.c Tue Mar 04 07:54:41 2008 +0200 +++ b/src/master/login-process.c Tue Mar 04 07:54:53 2008 +0200 @@ -516,7 +516,7 @@ parameter since we don't want to call initgroups() for login processes. */ restrict_access_set_env(NULL, set->login_uid, - set->server->login_gid, + set->server->login_gid, (gid_t)-1, set->login_chroot ? set->login_dir : NULL, 0, 0, NULL); diff -r 09cdd4330d73 -r af998ae4254b src/master/mail-process.c --- a/src/master/mail-process.c Tue Mar 04 07:54:41 2008 +0200 +++ b/src/master/mail-process.c Tue Mar 04 07:54:53 2008 +0200 @@ -724,9 +724,10 @@ /* setup environment - set the most important environment first (paranoia about filling up environment without noticing) */ - restrict_access_set_env(system_user, uid, gid, chroot_dir, + restrict_access_set_env(system_user, uid, gid, set->mail_priv_gid_t, + chroot_dir, set->first_valid_gid, set->last_valid_gid, - set->mail_extra_groups); + set->mail_access_groups); restrict_process_size(set->mail_process_size, (unsigned int)-1); @@ -834,8 +835,13 @@ any errors above will be logged */ closelog(); - if (set->mail_drop_priv_before_exec) + if (set->mail_drop_priv_before_exec) { restrict_access_by_env(TRUE); + /* privileged GID is now only in saved-GID. if we want to + preserve it accross exec, it needs to be temporarily + in effective gid */ + restrict_access_use_priv_gid(); + } client_process_exec(set->mail_executable, title); i_fatal_status(FATAL_EXEC, "execv(%s) failed: %m", diff -r 09cdd4330d73 -r af998ae4254b src/master/master-settings-defs.c --- a/src/master/master-settings-defs.c Tue Mar 04 07:54:41 2008 +0200 +++ b/src/master/master-settings-defs.c Tue Mar 04 07:54:53 2008 +0200 @@ -64,6 +64,8 @@ DEF_INT(first_valid_gid), DEF_INT(last_valid_gid), DEF_STR(mail_extra_groups), + DEF_STR(mail_access_groups), + DEF_STR(mail_privileged_group), DEF_STR(mail_uid), DEF_STR(mail_gid), diff -r 09cdd4330d73 -r af998ae4254b src/master/master-settings.c --- a/src/master/master-settings.c Tue Mar 04 07:54:41 2008 +0200 +++ b/src/master/master-settings.c Tue Mar 04 07:54:53 2008 +0200 @@ -226,6 +226,8 @@ MEMBER(first_valid_gid) 1, MEMBER(last_valid_gid) 0, MEMBER(mail_extra_groups) "", + MEMBER(mail_access_groups) "", + MEMBER(mail_privileged_group) "", MEMBER(mail_uid) "", MEMBER(mail_gid) "", @@ -701,6 +703,7 @@ set->mail_uid_t = (uid_t)-1; set->mail_gid_t = (gid_t)-1; + set->mail_priv_gid_t = (gid_t)-1; if (*set->mail_uid != '\0') { if (!parse_uid(set->mail_uid, &set->mail_uid_t)) { @@ -714,6 +717,29 @@ return FALSE; } } + if (*set->mail_privileged_group != '\0') { + if (!parse_gid(set->mail_privileged_group, + &set->mail_priv_gid_t)) { + i_error("Non-existing mail_privileged_group: %s", + set->mail_privileged_group); + return FALSE; + } + } + if (*set->mail_extra_groups != '\0') { + if (*set->mail_access_groups != '\0') { + i_error("Can't set both mail_extra_groups " + "and mail_access_groups"); + return FALSE; + } + if (!set->server->warned_mail_extra_groups) { + set->server->warned_mail_extra_groups = TRUE; + i_warning("mail_extra_groups setting was often used " + "insecurely so it is now deprecated, " + "use mail_access_groups or " + "mail_privileged_group instead"); + } + set->mail_access_groups = set->mail_extra_groups; + } if (set->protocol != MAIL_PROTOCOL_ANY && access(t_strcut(set->mail_executable, ' '), X_OK) < 0) { diff -r 09cdd4330d73 -r af998ae4254b src/master/master-settings.h --- a/src/master/master-settings.h Tue Mar 04 07:54:41 2008 +0200 +++ b/src/master/master-settings.h Tue Mar 04 07:54:53 2008 +0200 @@ -76,6 +76,8 @@ unsigned int first_valid_uid, last_valid_uid; unsigned int first_valid_gid, last_valid_gid; const char *mail_extra_groups; + const char *mail_access_groups; + const char *mail_privileged_group; const char *mail_uid; const char *mail_gid; @@ -139,7 +141,7 @@ ARRAY_TYPE(listener) ssl_listens; uid_t login_uid, mail_uid_t; - gid_t mail_gid_t; + gid_t mail_gid_t, mail_priv_gid_t; const char *imap_generated_capability; @@ -254,6 +256,7 @@ ARRAY_DEFINE(dicts, const char *); gid_t login_gid; + unsigned int warned_mail_extra_groups:1; }; extern struct server_settings *settings_root;