static int deliver_create_dir(struct mail_user *user, const char *dir)
{
struct mail_namespace *ns;
+ const char *origin;
mode_t mode;
gid_t gid;
if (ns == NULL)
ns = user->namespaces;
- mailbox_list_get_dir_permissions(ns->list, NULL, &mode, &gid);
- if (mkdir_parents_chown(dir, mode, (uid_t)-1, gid) == 0) {
+ mailbox_list_get_dir_permissions(ns->list, NULL, &mode, &gid, &origin);
+ if (mkdir_parents_chgrp(dir, mode, gid, origin) == 0) {
return 0;
} else if (errno == EACCES) {
i_error("%s", eacces_error_get_creating("mkdir_parents_chown",
enum mail_index_sync_type fsync_mask;
mode_t mode;
gid_t gid;
+ char *gid_origin;
pool_t extension_pool;
ARRAY_DEFINE(extensions, struct mail_index_registered_ext);
str = t_str_new(256);
str_append(str, strmap->path);
- fd = safe_mkstemp_hostpid(str, view->view->index->mode,
- (uid_t)-1, view->view->index->gid);
+ fd = safe_mkstemp_hostpid_group(str, view->view->index->mode,
+ view->view->index->gid,
+ view->view->index->gid_origin);
temp_path = str_c(str);
if (fd == -1) {
array_free(&index->keywords);
array_free(&index->module_contexts);
+ i_free(index->gid_origin);
i_free(index->error);
i_free(index->dir);
i_free(index->prefix);
}
void mail_index_set_permissions(struct mail_index *index,
- mode_t mode, gid_t gid)
+ mode_t mode, gid_t gid, const char *gid_origin)
{
index->mode = mode & 0666;
index->gid = gid;
+
+ i_free(index->gid_origin);
+ index->gid_origin = i_strdup(gid_origin);
}
uint32_t mail_index_ext_register(struct mail_index *index, const char *name,
really matter. ignore silently. */
return;
}
- mail_index_file_set_syscall_error(index, path, "fchown()");
+ if (errno != EACCES)
+ mail_index_file_set_syscall_error(index, path, "fchown()");
+ else {
+ mail_index_set_error(index, "%s",
+ eperm_error_get_chgrp("fchown", path, index->gid,
+ index->gid_origin));
+ }
/* continue, but change permissions so that only the common
subset of group and world is used. this makes sure no one
void mail_index_set_fsync_types(struct mail_index *index,
enum mail_index_sync_type fsync_mask);
void mail_index_set_permissions(struct mail_index *index,
- mode_t mode, gid_t gid);
+ mode_t mode, gid_t gid, const char *gid_origin);
/* Open index. Returns 1 if ok, 0 if index doesn't exist and CREATE flags
wasn't given, -1 if error. */
static int create_cydir(struct mail_storage *storage, struct mailbox_list *list,
const char *path)
{
+ const char *origin;
mode_t mode;
gid_t gid;
- mailbox_list_get_dir_permissions(list, NULL, &mode, &gid);
- if (mkdir_parents_chown(path, mode, (uid_t)-1, gid) < 0 &&
+ mailbox_list_get_dir_permissions(list, NULL, &mode, &gid, &origin);
+ if (mkdir_parents_chgrp(path, mode, gid, origin) < 0 &&
errno != EEXIST) {
if (!mail_storage_set_error_from_errno(storage)) {
mail_storage_set_critical(storage,
#include "file-lock.h"
#include "mkdir-parents.h"
#include "fdatasync-path.h"
+#include "eacces-error.h"
#include "str.h"
#include "dbox-storage.h"
#include "dbox-file.h"
if (fd == -1) {
mail_storage_set_critical(&storage->storage,
"open(%s, O_CREAT) failed: %m", path);
- } else if (storage->create_gid != (gid_t)-1) {
- if (fchown(fd, (uid_t)-1, storage->create_gid) < 0) {
+ } else if (storage->create_gid == (gid_t)-1) {
+ /* no group change */
+ } else if (fchown(fd, (uid_t)-1, storage->create_gid) < 0) {
+ if (errno == EPERM) {
+ mail_storage_set_critical(&storage->storage, "%s",
+ eperm_error_get_chgrp("fchown", path,
+ storage->create_gid,
+ storage->create_gid_origin));
+ } else {
mail_storage_set_critical(&storage->storage,
"fchown(%s, -1, %ld) failed: %m",
path, (long)storage->create_gid);
- /* continue anyway */
}
+ /* continue anyway */
}
return fd;
}
static int dbox_map_mkdir_storage(struct dbox_storage *storage)
{
- if (mkdir_parents_chown(storage->storage_dir, storage->create_mode,
- (uid_t)-1, storage->create_gid) < 0 &&
+ if (mkdir_parents_chgrp(storage->storage_dir, storage->create_mode,
+ storage->create_gid,
+ storage->create_gid_origin) < 0 &&
errno != EEXIST) {
mail_storage_set_critical(&storage->storage,
"mkdir(%s) failed: %m", storage->storage_dir);
const char **error_r)
{
struct dbox_storage *storage = (struct dbox_storage *)_storage;
- const char *dir;
+ const char *dir, *origin;
storage->set = mail_storage_get_driver_settings(_storage);
i_assert(storage->set->dbox_max_open_files >= 2);
storage->map = dbox_map_init(storage);
mailbox_list_get_dir_permissions(ns->list, NULL, &storage->create_mode,
- &storage->create_gid);
+ &storage->create_gid, &origin);
+ storage->create_gid_origin = p_strdup(_storage->pool, origin);
return 0;
}
const struct mailbox_update *update)
{
struct dbox_mailbox *mbox = (struct dbox_mailbox *)box;
+ const char *origin;
mode_t mode;
gid_t gid;
int ret;
- mailbox_list_get_dir_permissions(box->list, NULL, &mode, &gid);
- if (mkdir_parents_chown(box->path, mode, (uid_t)-1, gid) == 0) {
+ mailbox_list_get_dir_permissions(box->list, NULL, &mode, &gid, &origin);
+ if (mkdir_parents_chgrp(box->path, mode, gid, origin) == 0) {
/* create indexes immediately with the dbox header */
if (index_storage_mailbox_open(box) < 0)
return -1;
dbox_mailbox_create(struct mailbox *box, const struct mailbox_update *update,
bool directory)
{
- const char *path, *alt_path;
+ const char *path, *alt_path, *origin;
struct stat st;
path = mailbox_list_get_path(box->list, box->name,
mode_t mode;
gid_t gid;
- mailbox_list_get_dir_permissions(box->list, NULL, &mode, &gid);
- if (mkdir_parents_chown(path, mode, (uid_t)-1, gid) == 0)
+ mailbox_list_get_dir_permissions(box->list, NULL, &mode,
+ &gid, &origin);
+ if (mkdir_parents_chgrp(path, mode, gid, origin) == 0)
return 0;
else if (errno == EEXIST) {
mail_storage_set_error(box->storage, MAIL_ERROR_EXISTS,
if (ret < 0 && errno == ENOENT) {
/* either source mailbox doesn't exist or trash directory
doesn't exist. try creating the trash and retrying. */
+ const char *origin;
mode_t mode;
gid_t gid;
- mailbox_list_get_dir_permissions(list, NULL, &mode, &gid);
- if (mkdir_parents_chown(trash_dir, mode, (uid_t)-1, gid) < 0 &&
+ mailbox_list_get_dir_permissions(list, NULL, &mode,
+ &gid, &origin);
+ if (mkdir_parents_chgrp(trash_dir, mode, gid, origin) < 0 &&
errno != EEXIST) {
mailbox_list_set_critical(list,
"mkdir(%s) failed: %m", trash_dir);
/* mode/gid to use for new dbox storage files */
mode_t create_mode;
gid_t create_gid;
+ const char *create_gid_origin;
ARRAY_DEFINE(open_files, struct dbox_file *);
static int create_missing_index_dir(struct mailbox *box)
{
const char *root_dir, *index_dir, *p, *parent_dir;
+ const char *origin, *parent_origin;
mode_t mode, parent_mode;
gid_t gid, parent_gid;
int n = 0;
if (strcmp(index_dir, root_dir) == 0 || *index_dir == '\0')
return 0;
- mailbox_list_get_dir_permissions(box->list, box->name, &mode, &gid);
- while (mkdir_chown(index_dir, mode, (uid_t)-1, gid) < 0) {
+ mailbox_list_get_dir_permissions(box->list, box->name, &mode,
+ &gid, &origin);
+ while (mkdir_chgrp(index_dir, mode, gid, origin) < 0) {
if (errno == EEXIST)
break;
}
/* create the parent directory first */
mailbox_list_get_dir_permissions(box->list, NULL,
- &parent_mode, &parent_gid);
+ &parent_mode, &parent_gid,
+ &parent_origin);
parent_dir = t_strdup_until(index_dir, p);
- if (mkdir_parents_chown(parent_dir, parent_mode,
- (uid_t)-1, parent_gid) < 0 &&
+ if (mkdir_parents_chgrp(parent_dir, parent_mode,
+ parent_gid, parent_origin) < 0 &&
errno != EEXIST) {
mail_storage_set_critical(box->storage,
"mkdir(%s) failed: %m", parent_dir);
struct mailbox *box = &ibox->box;
const char *path;
gid_t dir_gid;
+ const char *origin, *dir_origin;
if (name != NULL)
box->name = p_strdup(box->pool, name);
if (box->file_create_mode == 0) {
mailbox_list_get_permissions(box->list, name,
&box->file_create_mode,
- &box->file_create_gid);
+ &box->file_create_gid, &origin);
+ box->file_create_gid_origin = p_strdup(box->pool, origin);
mailbox_list_get_dir_permissions(box->list, name,
&box->dir_create_mode,
- &dir_gid);
+ &dir_gid, &dir_origin);
mail_index_set_permissions(ibox->index,
box->file_create_mode,
- box->file_create_gid);
+ box->file_create_gid, origin);
}
}
#include "hash.h"
#include "str.h"
#include "istream.h"
+#include "eacces-error.h"
#include "file-dotlock.h"
#include "write-full.h"
#include "nfs-workarounds.h"
const char *path, int fd)
{
struct maildir_mailbox *mbox = mk->mbox;
+ struct mailbox *box = &mbox->ibox.box;
const char *const *keywords;
unsigned int i, count;
string_t *str;
return -1;
}
- if (st.st_gid != mbox->ibox.box.file_create_gid &&
- mbox->ibox.box.file_create_gid != (gid_t)-1) {
- if (fchown(fd, (uid_t)-1, mbox->ibox.box.file_create_gid) < 0) {
- mail_storage_set_critical(mk->storage,
- "fchown(%s) failed: %m", path);
+ if (st.st_gid != box->file_create_gid &&
+ box->file_create_gid != (gid_t)-1) {
+ if (fchown(fd, (uid_t)-1, box->file_create_gid) < 0) {
+ if (errno == EPERM) {
+ mail_storage_set_critical(mk->storage, "%s",
+ eperm_error_get_chgrp("fchown", path,
+ box->file_create_gid,
+ box->file_create_gid_origin));
+ } else {
+ mail_storage_set_critical(mk->storage,
+ "fchown(%s) failed: %m", path);
+ }
}
}
#include "istream-crlf.h"
#include "ostream.h"
#include "fdatasync-path.h"
+#include "eacces-error.h"
#include "str.h"
#include "index-mail.h"
#include "maildir-storage.h"
}
} else if (box->file_create_gid != (gid_t)-1) {
if (fchown(fd, (uid_t)-1, box->file_create_gid) < 0) {
- mail_storage_set_critical(box->storage,
- "fchown(%s) failed: %m", str_c(path));
+ if (errno == EPERM) {
+ mail_storage_set_critical(box->storage, "%s",
+ eperm_error_get_chgrp("fchown",
+ str_c(path),
+ box->file_create_gid,
+ box->file_create_gid_origin));
+ } else {
+ mail_storage_set_critical(box->storage,
+ "fchown(%s) failed: %m", str_c(path));
+ }
}
}
#include "hostpid.h"
#include "str.h"
#include "mkdir-parents.h"
+#include "eacces-error.h"
#include "unlink-directory.h"
#include "unlink-old-files.h"
#include "mailbox-uidvalidity.h"
return TRUE;
}
-static int mkdir_verify(struct mail_storage *storage, struct mail_namespace *ns,
- const char *dir, mode_t mode, gid_t gid, bool verify)
+static int
+mkdir_verify(struct mail_storage *storage, struct mail_namespace *ns,
+ const char *dir, mode_t mode, gid_t gid, const char *gid_origin,
+ bool verify)
{
struct stat st;
}
}
- if (mkdir_parents_chown(dir, mode, (uid_t)-1, gid) == 0)
+ if (mkdir_parents_chgrp(dir, mode, gid, gid_origin) == 0)
return 0;
if (errno == EEXIST) {
/* create or fix maildir, ignore if it already exists */
static int
create_maildir(struct mail_storage *storage, struct mail_namespace *ns,
- const char *dir, mode_t mode, gid_t gid, bool verify)
+ const char *dir, mode_t mode, gid_t gid, const char *gid_origin,
+ bool verify)
{
const char *path;
unsigned int i;
for (i = 0; i < N_ELEMENTS(maildir_subdirs); i++) {
path = t_strconcat(dir, "/", maildir_subdirs[i], NULL);
- if (mkdir_verify(storage, ns, path, mode, gid, verify) < 0)
+ if (mkdir_verify(storage, ns, path, mode, gid,
+ gid_origin, verify) < 0)
return -1;
}
return 0;
{
struct maildir_mailbox *mbox = (struct maildir_mailbox *)box;
struct stat st;
+ const char *shared_path;
/* for shared mailboxes get the create mode from the
permissions of dovecot-shared file. */
- if (stat(t_strconcat(box->path, "/dovecot-shared", NULL), &st) == 0) {
+ shared_path = t_strconcat(box->path, "/dovecot-shared", NULL);
+ if (stat(shared_path, &st) == 0) {
if ((st.st_mode & S_ISGID) != 0 ||
(st.st_mode & 0060) == 0) {
/* Ignore GID */
st.st_gid = (gid_t)-1;
}
mail_index_set_permissions(mbox->ibox.index,
- st.st_mode & 0666, st.st_gid);
+ st.st_mode & 0666, st.st_gid,
+ shared_path);
box->file_create_mode = st.st_mode & 0666;
box->dir_create_mode = get_dir_mode(st.st_mode & 0666);
box->file_create_gid = st.st_gid;
+ mbox->ibox.box.file_create_gid_origin =
+ p_strdup(box->pool, shared_path);
box->private_flags_mask = MAIL_SEEN;
}
static int maildir_mailbox_open(struct mailbox *box)
{
struct stat st;
+ const char *gid_origin;
mode_t mode;
gid_t gid;
int ret;
if (inbox || stat(box->path, &st) == 0) {
/* yes, we'll need to create the missing dirs */
mailbox_list_get_dir_permissions(box->list, box->name,
- &mode, &gid);
+ &mode, &gid, &gid_origin);
if (create_maildir(box->storage, box->list->ns, box->path,
- mode, gid, TRUE) < 0)
+ mode, gid, gid_origin, TRUE) < 0)
return -1;
return maildir_mailbox_open_existing(box);
static int
maildir_create_shared(struct mail_storage *storage, struct mail_namespace *ns,
- const char *dir, mode_t mode, gid_t gid)
+ const char *dir, mode_t mode, gid_t gid,
+ const char *gid_origin)
{
const char *path;
mode_t old_mask;
if ((mode & 0060) != 0) mode |= 0010;
if ((mode & 0006) != 0) mode |= 0001;
- if (create_maildir(storage, ns, dir, mode, gid, FALSE) < 0)
+ if (create_maildir(storage, ns, dir, mode, gid, gid_origin, FALSE) < 0)
return -1;
old_mask = umask(0777 ^ mode);
}
if (fchown(fd, (uid_t)-1, gid) < 0) {
- mail_storage_set_critical(storage, "fchown(%s) failed: %m",
- path);
+ if (errno == EPERM) {
+ mail_storage_set_critical(storage, "%s",
+ eperm_error_get_chgrp("fchown", path,
+ gid, gid_origin));
+ } else {
+ mail_storage_set_critical(storage,
+ "fchown(%s) failed: %m", path);
+ }
}
(void)close(fd);
return 0;
bool directory)
{
struct stat st;
- const char *path, *root_dir, *shared_path;
+ const char *path, *root_dir, *shared_path, *gid_origin;
mode_t old_mask;
int fd;
its permissions and gid, and copy the dovecot-shared inside it. */
shared_path = t_strconcat(root_dir, "/dovecot-shared", NULL);
if (stat(shared_path, &st) == 0) {
+ gid_origin = shared_path;
if (maildir_create_shared(box->storage, box->list->ns, path,
- st.st_mode & 0666, st.st_gid) < 0)
+ st.st_mode & 0666, st.st_gid,
+ gid_origin) < 0)
return -1;
} else {
- mailbox_list_get_dir_permissions(box->list, NULL,
- &st.st_mode, &st.st_gid);
+ mailbox_list_get_dir_permissions(box->list, NULL, &st.st_mode,
+ &st.st_gid, &gid_origin);
if (create_maildir(box->storage, box->list->ns, path,
- st.st_mode, st.st_gid, FALSE) < 0)
+ st.st_mode, st.st_gid, gid_origin,
+ FALSE) < 0)
return -1;
}
umask(old_mask);
if (fd != -1) {
/* if dovecot-shared exists, use the same group */
- if (st.st_gid != (gid_t)-1 &&
- fchown(fd, (uid_t)-1, st.st_gid) < 0) {
+ if (st.st_gid == (gid_t)-1) {
+ /* doesn't exist */
+ } else if (fchown(fd, (uid_t)-1, st.st_gid) == 0) {
+ /* ok */
+ } else if (errno == EPERM) {
+ mail_storage_set_critical(box->storage, "%s",
+ eperm_error_get_chgrp("fchown", path,
+ st.st_gid, gid_origin));
+ } else {
mail_storage_set_critical(box->storage,
"fchown(%s) failed: %m", path);
}
return -1;
}
- if (box->file_create_gid != (gid_t)-1) {
- if (fchown(fd, (uid_t)-1, box->file_create_gid) < 0) {
+ if (box->file_create_gid != (gid_t)-1 &&
+ fchown(fd, (uid_t)-1, box->file_create_gid) < 0) {
+ if (errno == EPERM) {
+ mail_storage_set_critical(box->storage, "%s",
+ eperm_error_get_chgrp("fchown", temp_path,
+ box->file_create_gid,
+ box->file_create_gid_origin));
+ } else {
mail_storage_set_critical(box->storage,
"fchown(%s) failed: %m", temp_path);
}
static int maildir_create_path(struct mailbox *box, const char *path,
bool is_mail_dir)
{
- const char *p, *parent;
+ const char *p, *parent, *origin;
mode_t parent_mode;
gid_t parent_gid;
- if (mkdir_chown(path, box->dir_create_mode,
- (uid_t)-1, box->file_create_gid) == 0)
+ if (mkdir_chgrp(path, box->dir_create_mode, box->file_create_gid,
+ box->file_create_gid_origin) == 0)
return 0;
switch (errno) {
}
/* create index/control root directory */
parent = t_strdup_until(path, p);
- mailbox_list_get_dir_permissions(box->list, NULL,
- &parent_mode, &parent_gid);
- if (mkdir_parents_chown(parent, parent_mode, (uid_t)-1,
- parent_gid) == 0 || errno == EEXIST) {
+ mailbox_list_get_dir_permissions(box->list, NULL, &parent_mode,
+ &parent_gid, &origin);
+ if (mkdir_parents_chgrp(parent, parent_mode, parent_gid,
+ origin) == 0 ||
+ errno == EEXIST) {
/* should work now, try again */
return maildir_create_path(box, path, TRUE);
}
bool directory)
{
struct mail_storage *storage = box->storage;
- const char *path, *p;
+ const char *path, *p, *origin;
struct stat st;
mode_t mode;
gid_t gid;
p = directory ? path + strlen(path) : strrchr(path, '/');
if (p != NULL) {
p = t_strdup_until(path, p);
- mailbox_list_get_dir_permissions(box->list, NULL, &mode, &gid);
- if (mkdir_parents_chown(p, mode, (uid_t)-1, gid) < 0 &&
+ mailbox_list_get_dir_permissions(box->list, NULL, &mode, &gid,
+ &origin);
+ if (mkdir_parents_chgrp(p, mode, gid, origin) < 0 &&
errno != EEXIST) {
if (!mail_storage_set_error_from_errno(storage)) {
mail_storage_set_critical(storage,
const char *newname, bool rename_children)
{
struct mail_storage *oldstorage;
- const char *oldpath, *newpath, *p;
+ const char *oldpath, *newpath, *p, *origin;
enum mailbox_list_path_type path_type;
struct stat st;
mode_t mode;
/* create the hierarchy */
p = strrchr(newpath, '/');
if (p != NULL) {
- mailbox_list_get_dir_permissions(newlist, NULL, &mode, &gid);
+ mailbox_list_get_dir_permissions(newlist, NULL, &mode,
+ &gid, &origin);
p = t_strdup_until(newpath, p);
- if (mkdir_parents_chown(p, mode, (uid_t)-1, gid) < 0 &&
+ if (mkdir_parents_chgrp(p, mode, gid, origin) < 0 &&
errno != EEXIST) {
if (mailbox_list_set_error_from_errno(oldlist))
return -1;
{
struct dotlock_settings dotlock_set;
struct dotlock *dotlock;
- const char *line, *p, *dir;
+ const char *line, *p, *dir, *origin;
struct istream *input;
struct ostream *output;
int fd_in, fd_out;
dotlock_set.timeout = SUBSCRIPTION_FILE_LOCK_TIMEOUT;
dotlock_set.stale_timeout = SUBSCRIPTION_FILE_CHANGE_TIMEOUT;
- mailbox_list_get_permissions(list, NULL, &mode, &gid);
- mailbox_list_get_dir_permissions(list, NULL, &dir_mode, &gid);
- fd_out = file_dotlock_open_mode(&dotlock_set, path, 0,
- mode, (uid_t)-1, gid, &dotlock);
+ mailbox_list_get_permissions(list, NULL, &mode, &gid, &origin);
+ mailbox_list_get_dir_permissions(list, NULL, &dir_mode, &gid, &origin);
+ fd_out = file_dotlock_open_group(&dotlock_set, path, 0,
+ mode, gid, origin, &dotlock);
if (fd_out == -1 && errno == ENOENT) {
/* directory hasn't been created yet. */
p = strrchr(path, '/');
dir = p == NULL ? NULL : t_strdup_until(path, p);
if (dir != NULL &&
- mkdir_parents_chown(dir, dir_mode, (uid_t)-1, gid) < 0 &&
+ mkdir_parents_chgrp(dir, dir_mode, gid, origin) < 0 &&
errno != EEXIST) {
subswrite_set_syscall_error(list, "mkdir()", dir);
return -1;
}
- fd_out = file_dotlock_open_mode(&dotlock_set, path, 0,
- mode, (uid_t)-1, gid, &dotlock);
+ fd_out = file_dotlock_open_group(&dotlock_set, path, 0,
+ mode, gid, origin, &dotlock);
}
if (fd_out == -1) {
if (errno == EAGAIN) {
/* mode and GID to use for newly created files/dirs */
mode_t file_create_mode, dir_create_mode;
gid_t file_create_gid;
+ /* origin (e.g. path) where the file_create_gid was got from */
+ const char *file_create_gid_origin;
/* Mailbox notification settings: */
unsigned int notify_min_interval;
mail_storage_create_root(struct mailbox_list *list,
enum mail_storage_flags flags, const char **error_r)
{
- const char *root_dir;
+ const char *root_dir, *origin;
struct stat st;
mode_t mode;
gid_t gid;
}
/* we need to create the root directory. */
- mailbox_list_get_dir_permissions(list, NULL, &mode, &gid);
- if (mkdir_parents_chown(root_dir, mode, (uid_t)-1, gid) < 0 &&
+ mailbox_list_get_dir_permissions(list, NULL, &mode, &gid, &origin);
+ if (mkdir_parents_chgrp(root_dir, mode, gid, origin) < 0 &&
errno != EEXIST) {
*error_r = mail_error_create_eacces_msg("mkdir", root_dir);
return -1;
/* -1 if not set yet. use mailbox_list_get_permissions() to set them */
mode_t file_create_mode, dir_create_mode;
gid_t file_create_gid;
+ /* origin (e.g. path) where the file_create_gid was got from */
+ const char *file_create_gid_origin;
char *error_string;
enum mail_error error;
static void
mailbox_list_get_permissions_full(struct mailbox_list *list, const char *name,
mode_t *file_mode_r, mode_t *dir_mode_r,
- gid_t *gid_r)
+ gid_t *gid_r, const char **gid_origin_r)
{
const char *path;
struct stat st;
/* return defaults */
mailbox_list_get_permissions_full(list, NULL,
file_mode_r,
- dir_mode_r, gid_r);
+ dir_mode_r, gid_r,
+ gid_origin_r);
return;
}
/* return safe defaults */
*file_mode_r = 0600;
*dir_mode_r = 0700;
*gid_r = (gid_t)-1;
+ *gid_origin_r = "defaults";
} else {
*file_mode_r = st.st_mode & 0666;
*dir_mode_r = st.st_mode & 0777;
+ *gid_origin_r = path;
if (!S_ISDIR(st.st_mode)) {
/* we're getting permissions from a file.
list->file_create_mode = *file_mode_r;
list->dir_create_mode = *dir_mode_r;
list->file_create_gid = *gid_r;
+ list->file_create_gid_origin =
+ p_strdup(list->pool, *gid_origin_r);
}
if (list->mail_set->mail_debug && name == NULL) {
}
}
-void mailbox_list_get_permissions(struct mailbox_list *list, const char *name,
- mode_t *mode_r, gid_t *gid_r)
+void mailbox_list_get_permissions(struct mailbox_list *list,
+ const char *name,
+ mode_t *mode_r, gid_t *gid_r,
+ const char **gid_origin_r)
{
mode_t dir_mode;
if (list->file_create_mode != (mode_t)-1 && name == NULL) {
*mode_r = list->file_create_mode;
*gid_r = list->file_create_gid;
+ *gid_origin_r = list->file_create_gid_origin;
return;
}
- mailbox_list_get_permissions_full(list, name, mode_r, &dir_mode, gid_r);
+ mailbox_list_get_permissions_full(list, name, mode_r, &dir_mode, gid_r,
+ gid_origin_r);
}
void mailbox_list_get_dir_permissions(struct mailbox_list *list,
const char *name,
- mode_t *mode_r, gid_t *gid_r)
+ mode_t *mode_r, gid_t *gid_r,
+ const char **gid_origin_r)
{
mode_t file_mode;
if (list->dir_create_mode != (mode_t)-1 && name == NULL) {
*mode_r = list->dir_create_mode;
*gid_r = list->file_create_gid;
+ *gid_origin_r = list->file_create_gid_origin;
return;
}
mailbox_list_get_permissions_full(list, name, &file_mode,
- mode_r, gid_r);
+ mode_r, gid_r, gid_origin_r);
}
bool mailbox_list_is_valid_pattern(struct mailbox_list *list,
/* Returns the mode and GID that should be used when creating new files to
the specified mailbox, or to mailbox list root if name is NULL. (gid_t)-1 is
returned if it's not necessary to change the default gid. */
-void mailbox_list_get_permissions(struct mailbox_list *list, const char *name,
- mode_t *mode_r, gid_t *gid_r);
+void mailbox_list_get_permissions(struct mailbox_list *list,
+ const char *name,
+ mode_t *mode_r, gid_t *gid_r,
+ const char **gid_origin_r);
/* Like mailbox_list_get_permissions(), but add execute-bits for mode
if either read or write bit is set (e.g. 0640 -> 0750). */
void mailbox_list_get_dir_permissions(struct mailbox_list *list,
const char *name,
- mode_t *mode_r, gid_t *gid_r);
+ mode_t *mode_r, gid_t *gid_r,
+ const char **gid_origin_r);
/* Returns TRUE if the name doesn't contain any invalid characters.
The create name check can be more strict. */
const struct group *group;
string_t *errmsg;
struct stat st, dir_st;
- int ret = -1;
+ int orig_errno, ret = -1;
+ orig_errno = errno;
errmsg = t_str_new(256);
str_printfa(errmsg, "%s(%s) failed: Permission denied (euid=%s",
func, path, dec2str(geteuid()));
str_printfa(errmsg, " UNIX perms seem ok, ACL problem?");
}
str_append_c(errmsg, ')');
+ errno = orig_errno;
return str_c(errmsg);
}
{
return eacces_error_get_full(func, path, TRUE);
}
+
+const char *eperm_error_get_chgrp(const char *func, const char *path,
+ gid_t gid, const char *gid_origin)
+{
+ string_t *errmsg;
+ const struct group *group;
+ int orig_errno = errno;
+
+ errmsg = t_str_new(256);
+
+ str_printfa(errmsg, "%s(%s, -1, %s", func, path, dec2str(gid));
+ group = getgrgid(gid);
+ if (group != NULL)
+ str_printfa(errmsg, "(%s)", group->gr_name);
+
+ str_printfa(errmsg, ") failed: Operation not permitted (egid=%s",
+ dec2str(getegid()));
+ group = getgrgid(getegid());
+ if (group != NULL)
+ str_printfa(errmsg, "(%s)", group->gr_name);
+ if (gid_origin != NULL)
+ str_printfa(errmsg, ", group based on %s", gid_origin);
+ str_append_c(errmsg, ')');
+ errno = orig_errno;
+ return str_c(errmsg);
+}
/* Return a user-friendly error message for EACCES failures. */
const char *eacces_error_get(const char *func, const char *path);
const char *eacces_error_get_creating(const char *func, const char *path);
+/* Return a user-friendly error message for fchown() or chown() EPERM
+ failures when only the group is being changed. gid_origin specifies why
+ exactly this group is being used. */
+const char *eperm_error_get_chgrp(const char *func, const char *path,
+ gid_t gid, const char *gid_origin);
#endif
#include "hex-binary.h"
#include "hostpid.h"
#include "randgen.h"
+#include "eacces-error.h"
#include "write-full.h"
#include "safe-mkstemp.h"
#include "nfs-workarounds.h"
return dotlock->fd;
}
-int file_dotlock_open_mode(const struct dotlock_settings *set, const char *path,
- enum dotlock_create_flags flags,
- mode_t mode, uid_t uid, gid_t gid,
- struct dotlock **dotlock_r)
+static int
+file_dotlock_open_mode_full(const struct dotlock_settings *set, const char *path,
+ enum dotlock_create_flags flags,
+ mode_t mode, uid_t uid, gid_t gid,
+ const char *gid_origin, struct dotlock **dotlock_r)
{
struct dotlock *dotlock;
mode_t old_mask;
if (fd != -1) {
if (fchown(fd, uid, gid) < 0) {
- i_error("fchown(%s, %ld, %ld) failed: %m",
- file_dotlock_get_lock_path(dotlock),
- (long)uid, (long)gid);
+ if (errno == EPERM && uid == (uid_t)-1) {
+ i_error("%s", eperm_error_get_chgrp("fchown",
+ file_dotlock_get_lock_path(dotlock),
+ gid, gid_origin));
+ } else {
+ i_error("fchown(%s, %ld, %ld) failed: %m",
+ file_dotlock_get_lock_path(dotlock),
+ (long)uid, (long)gid);
+ }
file_dotlock_delete(&dotlock);
return -1;
}
return fd;
}
+int file_dotlock_open_mode(const struct dotlock_settings *set, const char *path,
+ enum dotlock_create_flags flags,
+ mode_t mode, uid_t uid, gid_t gid,
+ struct dotlock **dotlock_r)
+{
+ return file_dotlock_open_mode_full(set, path, flags, mode, uid, gid,
+ NULL, dotlock_r);
+}
+
+int file_dotlock_open_group(const struct dotlock_settings *set, const char *path,
+ enum dotlock_create_flags flags,
+ mode_t mode, gid_t gid, const char *gid_origin,
+ struct dotlock **dotlock_r)
+{
+ return file_dotlock_open_mode_full(set, path, flags, mode, (uid_t)-1,
+ gid, gid_origin, dotlock_r);
+}
+
int file_dotlock_replace(struct dotlock **dotlock_p,
enum dotlock_replace_flags flags)
{
enum dotlock_create_flags flags,
mode_t mode, uid_t uid, gid_t gid,
struct dotlock **dotlock_r);
+int file_dotlock_open_group(const struct dotlock_settings *set, const char *path,
+ enum dotlock_create_flags flags,
+ mode_t mode, gid_t gid, const char *gid_origin,
+ struct dotlock **dotlock_r);
/* Replaces the file dotlock protects with the dotlock file itself. */
int file_dotlock_replace(struct dotlock **dotlock,
enum dotlock_replace_flags flags);
/* Copyright (c) 2003-2009 Dovecot authors, see the included COPYING file */
#include "lib.h"
+#include "str.h"
+#include "eacces-error.h"
#include "mkdir-parents.h"
#include <sys/stat.h>
#include <unistd.h>
+#include <pwd.h>
+#include <grp.h>
-int mkdir_chown(const char *path, mode_t mode, uid_t uid, gid_t gid)
+static int
+mkdir_chown_full(const char *path, mode_t mode, uid_t uid,
+ gid_t gid, const char *gid_origin)
{
+ string_t *str;
mode_t old_mask;
- int ret;
+ int ret, orig_errno;
old_mask = umask(0);
ret = mkdir(path, mode);
return -1;
}
if (chown(path, uid, gid) < 0) {
- i_error("chown(%s, %ld, %ld) failed: %m", path,
- uid == (uid_t)-1 ? -1L : (long)uid,
- gid == (gid_t)-1 ? -1L : (long)gid);
+ if (errno == EPERM && uid == (uid_t)-1) {
+ i_error("%s", eperm_error_get_chgrp("chown", path, gid,
+ gid_origin));
+ return -1;
+ }
+ orig_errno = errno;
+
+ str = t_str_new(256);
+ str_printfa(str, "chown(%s, %ld", path,
+ uid == (uid_t)-1 ? -1L : (long)uid);
+ if (uid != (uid_t)-1) {
+ struct passwd *pw = getpwuid(uid);
+
+ if (pw != NULL)
+ str_printfa(str, "(%s)", pw->pw_name);
+
+ }
+ str_printfa(str, ", %ld",
+ gid == (gid_t)-1 ? -1L : (long)gid);
+ if (gid != (gid_t)-1) {
+ struct group *gr = getgrgid(uid);
+
+ if (gr != NULL)
+ str_printfa(str, "(%s)", gr->gr_name);
+ }
+ errno = orig_errno;
+ i_error("%s) failed: %m", str_c(str));
return -1;
}
return 0;
}
-int mkdir_parents_chown(const char *path, mode_t mode, uid_t uid, gid_t gid)
+int mkdir_chown(const char *path, mode_t mode, uid_t uid, gid_t gid)
+{
+ return mkdir_chown_full(path, mode, uid, gid, NULL);
+}
+
+int mkdir_chgrp(const char *path, mode_t mode,
+ gid_t gid, const char *gid_origin)
+{
+ return mkdir_chown_full(path, mode, (uid_t)-1, gid, gid_origin);
+}
+
+static int
+mkdir_parents_chown_full(const char *path, mode_t mode, uid_t uid, gid_t gid,
+ const char *gid_origin)
{
const char *p;
int ret;
- if (mkdir_chown(path, mode, uid, gid) < 0) {
+ if (mkdir_chown_full(path, mode, uid, gid, gid_origin) < 0) {
if (errno != ENOENT)
return -1;
return -1; /* shouldn't happen */
T_BEGIN {
- ret = mkdir_parents_chown(t_strdup_until(path, p),
- mode, uid, gid);
+ ret = mkdir_parents_chown_full(t_strdup_until(path, p),
+ mode, uid,
+ gid, gid_origin);
} T_END;
if (ret < 0)
return -1;
/* should work now */
- if (mkdir_chown(path, mode, uid, gid) < 0)
+ if (mkdir_chown_full(path, mode, uid, gid, gid_origin) < 0)
return -1;
}
return 0;
}
+int mkdir_parents_chown(const char *path, mode_t mode, uid_t uid, gid_t gid)
+{
+ return mkdir_parents_chown_full(path, mode, uid, gid, NULL);
+}
+
+int mkdir_parents_chgrp(const char *path, mode_t mode,
+ gid_t gid, const char *gid_origin)
+{
+ return mkdir_parents_chown_full(path, mode, (uid_t)-1, gid, gid_origin);
+}
+
int mkdir_parents(const char *path, mode_t mode)
{
return mkdir_parents_chown(path, mode, (uid_t)-1, (gid_t)-1);
int mkdir_parents(const char *path, mode_t mode);
/* Like mkdir_parents(), but use the given uid/gid for newly created
- directories. */
+ directories. (uid_t)-1 or (gid_t)-1 can be used to indicate that it
+ doesn't need to be changed. */
int mkdir_parents_chown(const char *path, mode_t mode, uid_t uid, gid_t gid);
+/* Like mkdir_parents_chown(), but change only group. If chown() fails with
+ EACCES, use gid_origin in the error message. */
+int mkdir_parents_chgrp(const char *path, mode_t mode,
+ gid_t gid, const char *gid_origin);
/* Like mkdir_parents_chown(), but don't actually create any parents. */
int mkdir_chown(const char *path, mode_t mode, uid_t uid, gid_t gid);
+int mkdir_chgrp(const char *path, mode_t mode,
+ gid_t gid, const char *gid_origin);
#endif
#include "hex-binary.h"
#include "randgen.h"
#include "hostpid.h"
+#include "eacces-error.h"
#include "safe-mkstemp.h"
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
-int safe_mkstemp(string_t *prefix, mode_t mode, uid_t uid, gid_t gid)
+static int
+safe_mkstemp_full(string_t *prefix, mode_t mode, uid_t uid, gid_t gid,
+ const char *gid_origin)
{
size_t prefix_len;
struct stat st;
return -1;
}
}
- if (uid != (uid_t)-1 || gid != (gid_t)-1) {
- if (fchown(fd, uid, gid) < 0) {
+ if (uid == (uid_t)-1 && gid == (gid_t)-1)
+ return fd;
+
+ if (fchown(fd, uid, gid) < 0) {
+ if (errno == EPERM) {
+ i_error("%s", eperm_error_get_chgrp("fchown",
+ str_c(prefix), gid, gid_origin));
+ } else {
i_error("fchown(%s, %ld, %ld) failed: %m",
str_c(prefix),
uid == (uid_t)-1 ? -1L : (long)uid,
gid == (gid_t)-1 ? -1L : (long)gid);
- (void)close(fd);
- (void)unlink(str_c(prefix));
- return -1;
}
+ (void)close(fd);
+ (void)unlink(str_c(prefix));
+ return -1;
}
return fd;
}
+int safe_mkstemp(string_t *prefix, mode_t mode, uid_t uid, gid_t gid)
+{
+ return safe_mkstemp_full(prefix, mode, uid, gid, NULL);
+}
+
+int safe_mkstemp_group(string_t *prefix, mode_t mode,
+ gid_t gid, const char *gid_origin)
+{
+ return safe_mkstemp_full(prefix, mode, (uid_t)-1, gid, gid_origin);
+}
+
int safe_mkstemp_hostpid(string_t *prefix, mode_t mode, uid_t uid, gid_t gid)
{
str_printfa(prefix, "%s.%s.", my_hostname, my_pid);
return safe_mkstemp(prefix, mode, uid, gid);
}
+
+int safe_mkstemp_hostpid_group(string_t *prefix, mode_t mode,
+ gid_t gid, const char *gid_origin)
+{
+ str_printfa(prefix, "%s.%s.", my_hostname, my_pid);
+ return safe_mkstemp_group(prefix, mode, gid, gid_origin);
+}
created filename. uid and gid can be (uid_t)-1 and (gid_t)-1 to use the
defaults. */
int safe_mkstemp(string_t *prefix, mode_t mode, uid_t uid, gid_t gid);
+int safe_mkstemp_group(string_t *prefix, mode_t mode,
+ gid_t gid, const char *gid_origin);
/* Append host and PID to the prefix. */
int safe_mkstemp_hostpid(string_t *prefix, mode_t mode, uid_t uid, gid_t gid);
+int safe_mkstemp_hostpid_group(string_t *prefix, mode_t mode,
+ gid_t gid, const char *gid_origin);
#endif
struct mail_namespace *ns;
struct mailbox_list_iterate_context *iter;
const struct mailbox_info *info;
- const char *rootdir, *acllist_path;
+ const char *rootdir, *acllist_path, *origin;
struct ostream *output;
struct stat st;
string_t *path;
/* Build it into a temporary file and rename() over. There's no need
to use locking, because even if multiple processes are rebuilding
the file at the same time the result should be the same. */
- mailbox_list_get_permissions(list, NULL, &mode, &gid);
- fd = safe_mkstemp(path, mode, (uid_t)-1, gid);
+ mailbox_list_get_permissions(list, NULL, &mode, &gid, &origin);
+ fd = safe_mkstemp_group(path, mode, gid, origin);
if (fd == -1) {
if (errno == EACCES) {
/* Ignore silently if we can't create it */
struct dotlock **dotlock_r)
{
struct acl_object *_aclobj = &aclobj->aclobj;
+ const char *gid_origin;
mode_t mode;
gid_t gid;
int fd;
/* first lock the ACL file */
mailbox_list_get_permissions(_aclobj->backend->list, _aclobj->name,
- &mode, &gid);
- fd = file_dotlock_open_mode(&dotlock_set, aclobj->local_path, 0,
- mode, (uid_t)-1, gid, dotlock_r);
+ &mode, &gid, &gid_origin);
+ fd = file_dotlock_open_group(&dotlock_set, aclobj->local_path, 0,
+ mode, gid, gid_origin, dotlock_r);
if (fd == -1) {
- i_error("file_dotlock_open_mode(%s) failed: %m",
- aclobj->local_path);
+ i_error("file_dotlock_open(%s) failed: %m", aclobj->local_path);
return -1;
}
mailbox_move(struct mailbox_list *src_list, const char *src_name,
struct mailbox_list *dest_list, const char **_dest_name)
{
- const char *dir, *dest_name = *_dest_name;
+ const char *dir, *origin, *dest_name = *_dest_name;
enum mail_error error;
mode_t mode;
gid_t gid;
/* make sure the destination root directory exists */
- mailbox_list_get_dir_permissions(dest_list, NULL, &mode, &gid);
+ mailbox_list_get_dir_permissions(dest_list, NULL, &mode, &gid, &origin);
dir = mailbox_list_get_path(dest_list, NULL, MAILBOX_LIST_PATH_TYPE_DIR);
- if (mkdir_parents_chown(dir, mode, (uid_t)-1, gid) < 0 &&
+ if (mkdir_parents_chgrp(dir, mode, gid, origin) < 0 &&
errno != EEXIST) {
mailbox_list_set_critical(src_list,
- "mkdir_parents_chown(%s) failed: %m", dir);
+ "mkdir_parents(%s) failed: %m", dir);
return -1;
}
struct mail_namespace *const *namespaces;
unsigned int i, count;
struct dotlock *dotlock;
- const char *p, *dir;
+ const char *p, *dir, *gid_origin, *dir_gid_origin;
string_t *str;
mode_t mode, dir_mode;
gid_t gid, dir_gid;
/* figure out what permissions we should use for maildirsize.
use the inbox namespace's permissions if possible. */
- mode = 0600; dir_mode = 0700;
+ mode = 0600; dir_mode = 0700; gid_origin = "default";
gid = dir_gid = (gid_t)-1;
namespaces = array_get(&root->root.quota->namespaces, &count);
i_assert(count > 0);
for (i = 0; i < count; i++) {
if ((namespaces[i]->flags & NAMESPACE_FLAG_INBOX) != 0) {
mailbox_list_get_permissions(namespaces[i]->list,
- NULL, &mode, &gid);
+ NULL, &mode, &gid,
+ &gid_origin);
mailbox_list_get_dir_permissions(namespaces[i]->list,
NULL,
- &dir_mode, &dir_gid);
+ &dir_mode, &dir_gid,
+ &dir_gid_origin);
break;
}
}
dotlock_settings.use_excl_lock = set->dotlock_use_excl;
dotlock_settings.nfs_flush = set->mail_nfs_storage;
- fd = file_dotlock_open_mode(&dotlock_settings, path,
- DOTLOCK_CREATE_FLAG_NONBLOCK,
- mode, (uid_t)-1, gid, &dotlock);
+ fd = file_dotlock_open_group(&dotlock_settings, path,
+ DOTLOCK_CREATE_FLAG_NONBLOCK,
+ mode, gid, gid_origin, &dotlock);
if (fd == -1 && errno == ENOENT) {
/* the control directory doesn't exist yet? create it */
p = strrchr(path, '/');
dir = t_strdup_until(path, p);
- if (mkdir_parents(dir, dir_mode) < 0 && errno != EEXIST) {
+ if (mkdir_parents_chgrp(dir, dir_mode, dir_gid,
+ dir_gid_origin) < 0 &&
+ errno != EEXIST) {
i_error("mkdir_parents(%s) failed: %m", dir);
return -1;
}
- if (dir_gid != (gid_t)-1) {
- if (chown(dir, (uid_t)-1, dir_gid) < 0) {
- i_error("chown(%s,-1,%ld) failed: %m",
- dir, (long)dir_gid);
- }
- }
- fd = file_dotlock_open_mode(&dotlock_settings, path,
- DOTLOCK_CREATE_FLAG_NONBLOCK,
- mode, (uid_t)-1, gid, &dotlock);
+ fd = file_dotlock_open_group(&dotlock_settings, path,
+ DOTLOCK_CREATE_FLAG_NONBLOCK,
+ mode, gid, gid_origin, &dotlock);
}
if (fd == -1) {
if (errno == EAGAIN) {