static int create_inbox(struct mailbox *box) { const char *inbox_path; int fd; inbox_path = mailbox_get_path(box); fd = open(inbox_path, O_RDWR | O_CREAT | O_EXCL, 0660); if (fd == -1 && errno == EACCES) { /* try again with increased privileges */ (void)restrict_access_use_priv_gid(); fd = open(inbox_path, O_RDWR | O_CREAT | O_EXCL, 0660); restrict_access_drop_priv_gid(); } if (fd != -1) { i_close_fd(&fd); return 0; } else if (errno == EACCES) { mail_storage_set_critical(box->storage, "%s", mail_error_create_eacces_msg("open", inbox_path)); return -1; } else if (errno == EEXIST) { mail_storage_set_error(box->storage, MAIL_ERROR_EXISTS, "Mailbox already exists"); return -1; } else { mail_storage_set_critical(box->storage, "open(%s, O_CREAT) failed: %m", inbox_path); return -1; } }
static bool dotlock_callback(unsigned int secs_left, bool stale, void *context) { struct mbox_lock_context *ctx = context; 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. */ lock_types = ctx->lock_type == F_WRLCK || (ctx->lock_type == F_UNLCK && ctx->mbox->mbox_lock_type == F_WRLCK) ? ctx->mbox->storage->write_locks : ctx->mbox->storage->read_locks; for (i = 0; lock_types[i] != (enum mbox_lock_type)-1; i++) { if (lock_types[i] == MBOX_LOCK_DOTLOCK) break; } if (lock_types[i] != (enum mbox_lock_type)-1 && lock_types[i+1] != (enum mbox_lock_type)-1) { i++; if (mbox_lock_list(ctx, ctx->lock_type, 0, i) <= 0) { /* we couldn't get fd lock - it's really locked */ ctx->dotlock_last_stale = TRUE; return FALSE; } mbox_lock_list(ctx, F_UNLCK, 0, i); } } ctx->dotlock_last_stale = stale; index_storage_lock_notify(&ctx->mbox->box, stale ? 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 ATTR_NULL(2) ATTR_NOWARN_UNUSED_RESULT mbox_dotlock_privileged_op(struct mbox_mailbox *mbox, struct dotlock_settings *set, enum mbox_dotlock_op op) { const char *box_path, *dir, *fname; int ret = -1, orig_dir_fd, orig_errno; orig_dir_fd = open(".", O_RDONLY); if (orig_dir_fd == -1) { mailbox_set_critical(&mbox->box, "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 */ box_path = mailbox_get_path(&mbox->box); fname = strrchr(box_path, '/'); if (fname == NULL) { /* already relative */ fname = box_path; } else { dir = t_strdup_until(box_path, fname); if (chdir(dir) < 0) { mailbox_set_critical(&mbox->box, "chdir(%s) failed: %m", dir); i_close_fd(&orig_dir_fd); return -1; } fname++; } if (op == MBOX_DOTLOCK_OP_LOCK) { if (access(fname, R_OK) < 0) { mailbox_set_critical(&mbox->box, "access(%s) failed: %m", box_path); i_close_fd(&orig_dir_fd); return -1; } } if (restrict_access_use_priv_gid() < 0) { i_close_fd(&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; else if (ret < 0 && errno == EACCES) { const char *errmsg = eacces_error_get_creating("file_dotlock_create", fname); mailbox_set_critical(&mbox->box, "%s", errmsg); } else { mbox_set_syscall_error(mbox, "file_dotlock_create()"); } break; case MBOX_DOTLOCK_OP_UNLOCK: /* we're now privileged - avoid doing as much as possible */ ret = file_dotlock_delete(&mbox->mbox_dotlock); if (ret < 0) mbox_set_syscall_error(mbox, "file_dotlock_delete()"); mbox->mbox_used_privileges = FALSE; break; case MBOX_DOTLOCK_OP_TOUCH: ret = file_dotlock_touch(mbox->mbox_dotlock); if (ret < 0) mbox_set_syscall_error(mbox, "file_dotlock_touch()"); break; } orig_errno = errno; restrict_access_drop_priv_gid(); if (fchdir(orig_dir_fd) < 0) { mailbox_set_critical(&mbox->box, "fchdir() failed: %m"); } i_close_fd(&orig_dir_fd); errno = orig_errno; return ret; }