static int mail_transaction_log_file_dotlock(struct mail_transaction_log_file *file) { struct dotlock_settings dotlock_set; int ret; if (file->log->dotlock_count > 0) ret = 1; else { mail_transaction_log_get_dotlock_set(file->log, &dotlock_set); ret = file_dotlock_create(&dotlock_set, file->filepath, 0, &file->log->dotlock); } if (ret > 0) { file->log->dotlock_count++; file->locked = TRUE; file->lock_created = time(NULL); return 0; } if (ret < 0) { log_file_set_syscall_error(file, "file_dotlock_create()"); return -1; } mail_index_set_error(file->log->index, "Timeout (%us) while waiting for " "dotlock for transaction log file %s", dotlock_set.timeout, file->filepath); file->log->index->index_lock_timeout = TRUE; return -1; }
static int squat_uidlist_lock(struct squat_uidlist *uidlist) { int ret; for (;;) { i_assert(uidlist->fd != -1); i_assert(uidlist->file_lock == NULL); i_assert(uidlist->dotlock == NULL); if (uidlist->trie->lock_method != FILE_LOCK_METHOD_DOTLOCK) { ret = file_wait_lock(uidlist->fd, uidlist->path, F_WRLCK, uidlist->trie->lock_method, SQUAT_TRIE_LOCK_TIMEOUT, &uidlist->file_lock); } else { ret = file_dotlock_create(&uidlist->trie->dotlock_set, uidlist->path, 0, &uidlist->dotlock); } if (ret == 0) { i_error("squat uidlist %s: Locking timed out", uidlist->path); return 0; } if (ret < 0) return -1; ret = squat_uidlist_is_file_stale(uidlist); if (ret == 0) break; if (uidlist->file_lock != NULL) file_unlock(&uidlist->file_lock); else file_dotlock_delete(&uidlist->dotlock); if (ret < 0) return -1; squat_uidlist_close(uidlist); uidlist->fd = squat_trie_create_fd(uidlist->trie, uidlist->path, 0); if (uidlist->fd == -1) return -1; } return 1; }
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; }