Пример #1
0
static void
mbox_dotlock_log_eacces_error(struct mbox_mailbox *mbox, const char *path)
{
	const char *dir, *errmsg, *name;
	struct stat st;
	struct group group;
	int orig_errno = errno;

	errmsg = eacces_error_get_creating("file_dotlock_create", path);
	dir = strrchr(path, '/');
	dir = dir == NULL ? "." : t_strdup_until(path, dir);
	/* allow privileged locking for
	   a) user's own INBOX,
	   b) another user's shared INBOX, and
	   c) anything called INBOX (in inbox=no namespace) */
	if (!mbox->box.inbox_any && strcmp(mbox->box.name, "INBOX") != 0) {
		mailbox_set_critical(&mbox->box,
			"%s (not INBOX -> no privileged locking)", errmsg);
	} else if (!mbox->mbox_privileged_locking) {
		dir = mailbox_list_get_root_forced(mbox->box.list,
						   MAILBOX_LIST_PATH_TYPE_DIR);
		mailbox_set_critical(&mbox->box,
			"%s (under root dir %s -> no privileged locking)",
			errmsg, dir);
	} else if (stat(dir, &st) == 0 &&
		   (st.st_mode & 02) == 0 && /* not world-writable */
		   (st.st_mode & 020) != 0) { /* group-writable */
		if (i_getgrgid(st.st_gid, &group) <= 0)
			name = dec2str(st.st_gid);
		else
			name = group.gr_name;
		mailbox_set_critical(&mbox->box,
			"%s (set mail_privileged_group=%s)", errmsg, name);
	} else {
		mailbox_set_critical(&mbox->box,
			"%s (nonstandard permissions in %s)", errmsg, dir);
	}
	errno = orig_errno;
}
Пример #2
0
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;
}
Пример #3
0
const char *mail_error_create_eacces_msg(const char *func, const char *path)
{
	return eacces_error_get_creating(func, path);
}
Пример #4
0
static int
sieve_file_storage_save_to(struct sieve_file_storage *fstorage,
	string_t *temp_path, struct istream *input,
	const char *target)
{
	struct sieve_storage *storage = &fstorage->storage;
	struct ostream *output;
	int fd;

	// FIXME: move this to base class
	// FIXME: use io_stream_temp

	fd = safe_mkstemp_hostpid
		(temp_path, fstorage->file_create_mode, (uid_t)-1, (gid_t)-1);
	if ( fd < 0 ) {
		if ( errno == EACCES ) {
			sieve_storage_set_critical(storage,
				"Failed to create temporary file: %s",
				eacces_error_get_creating("open", str_c(temp_path)));
		} else {
			sieve_storage_set_critical(storage,
				"Failed to create temporary file: open(%s) failed: %m",
				str_c(temp_path));
		}
		return -1;
	}

	output = o_stream_create_fd(fd, 0);
	switch ( o_stream_send_istream(output, input) ) {
	case OSTREAM_SEND_ISTREAM_RESULT_FINISHED:
		break;
	case OSTREAM_SEND_ISTREAM_RESULT_WAIT_INPUT:
	case OSTREAM_SEND_ISTREAM_RESULT_WAIT_OUTPUT:
		i_unreached();
	case OSTREAM_SEND_ISTREAM_RESULT_ERROR_INPUT:
		sieve_storage_set_critical(storage,
			"read(%s) failed: %s", i_stream_get_name(input),
			i_stream_get_error(input));
		o_stream_destroy(&output);
		i_unlink(str_c(temp_path));
		return -1;
	case OSTREAM_SEND_ISTREAM_RESULT_ERROR_OUTPUT:
		sieve_storage_set_critical(storage,
			"write(%s) failed: %s", str_c(temp_path),
			o_stream_get_error(output));
		o_stream_destroy(&output);
		i_unlink(str_c(temp_path));
		return -1;
	}
	o_stream_destroy(&output);

	if ( rename(str_c(temp_path), target) < 0 ) {
		if ( ENOQUOTA(errno) ) {
			sieve_storage_set_error(storage,
				SIEVE_ERROR_NO_QUOTA,
				"Not enough disk quota");
		} else if ( errno == EACCES ) {
			sieve_storage_set_critical(storage,
				"%s", eacces_error_get("rename", target));
		} else {
			sieve_storage_set_critical(storage,
				"rename(%s, %s) failed: %m",
				str_c(temp_path), target);
		}
		i_unlink(str_c(temp_path));
	}
	return 0;
}
int sieve_binary_save
(struct sieve_binary *sbin, const char *path, bool update, mode_t save_mode,
	enum sieve_error *error_r)
{
	int result, fd;
	string_t *temp_path;
	struct ostream *stream;

	if ( error_r != NULL )
		*error_r = SIEVE_ERROR_NONE;

	/* Check whether saving is necessary */
	if ( !update && sbin->path != NULL && strcmp(sbin->path, path) == 0 ) {
		if ( sbin->svinst->debug ) {
			sieve_sys_debug(sbin->svinst, "binary save: not saving binary %s, "
				"because it is already stored", path);
		}
		return 0;
	}

	/* Open it as temp file first, as not to overwrite an existing just yet */
	temp_path = t_str_new(256);
	str_append(temp_path, path);
	str_append_c(temp_path, '.');
	fd = safe_mkstemp_hostpid(temp_path, save_mode, (uid_t)-1, (gid_t)-1);
	if ( fd < 0 ) {
		if ( errno == EACCES ) {
			sieve_sys_error(sbin->svinst,
				"binary save: failed to create temporary file: %s",
				eacces_error_get_creating("open", str_c(temp_path)));
			if ( error_r != NULL )
				*error_r = SIEVE_ERROR_NO_PERMISSION;
		} else {
			sieve_sys_error(sbin->svinst,
				"binary save: failed to create temporary file: open(%s) failed: %m",
				str_c(temp_path));
			if ( error_r != NULL )
				*error_r = SIEVE_ERROR_TEMP_FAILURE;
		}
		return -1;
	}

	/* Save binary */
	result = 1;
	stream = o_stream_create_fd(fd, 0, FALSE);
	if ( !_sieve_binary_save(sbin, stream) ) {
		result = -1;
		if ( error_r != NULL )
			*error_r = SIEVE_ERROR_TEMP_FAILURE;
	}
	o_stream_destroy(&stream);

	/* Close saved binary */
	if ( close(fd) < 0 ) {
		sieve_sys_error(sbin->svinst,
			"binary save: failed to close temporary file: "
			"close(fd=%s) failed: %m", str_c(temp_path));
	}

	/* Replace any original binary atomically */
	if ( result && (rename(str_c(temp_path), path) < 0) ) {
		if ( errno == EACCES ) {
			sieve_sys_error(sbin->svinst, "binary save: failed to save binary: %s",
				eacces_error_get_creating("rename", path));
			if ( error_r != NULL )
				*error_r = SIEVE_ERROR_NO_PERMISSION;
		} else {
			sieve_sys_error(sbin->svinst, "binary save: failed to save binary: "
				"rename(%s, %s) failed: %m", str_c(temp_path), path);
			if ( error_r != NULL )
				*error_r = SIEVE_ERROR_TEMP_FAILURE;
		}
		result = -1;
	}

	if ( result < 0 ) {
		/* Get rid of temp output (if any) */
		if ( unlink(str_c(temp_path)) < 0 && errno != ENOENT ) {
			sieve_sys_error(sbin->svinst,
				"binary save: failed to clean up after error: unlink(%s) failed: %m",
				str_c(temp_path));
		}
	} else {
		if ( sbin->path == NULL ) {
			sbin->path = p_strdup(sbin->pool, path);
		}
	}

	return result;
}