Package: radicale Version: 2.1.11-6 Severity: normal Dear Maintainer,
Radicale 2.x ignores its umask when creating .ics and .vcf files. This interferes with backups, git diffs (when using the git hook), and anything else that depends on group read permission to access those files. The problem has been acknowledged and fixed in the 3.x development tree: https://github.com/Kozea/Radicale/commit/630d49b However, the fix has not been applied to the 2.x branch. I am attaching a backport. -- System Information: Debian Release: 10.5 APT prefers stable-updates APT policy: (500, 'stable-updates'), (500, 'stable'), (1, 'unstable') Architecture: arm64 (aarch64) Kernel: Linux 5.7.0-2-arm64 (SMP w/6 CPU cores) Locale: LANG=en_US.UTF-8, LC_CTYPE=en_US.UTF-8 (charmap=UTF-8), LANGUAGE=en_US.UTF-8 (charmap=UTF-8) Shell: /bin/sh linked to /usr/bin/dash Init: systemd (via /run/systemd/system) LSM: AppArmor: enabled Versions of packages radicale depends on: ii adduser 3.118 ii init-system-helpers 1.56+nmu1 ii lsb-base 10.2019051400 ii python3 3.7.3-1 ii python3-radicale 2.1.11-6 Versions of packages radicale recommends: ii ssl-cert 1.0.39 Versions of packages radicale suggests: pn apache2 <none> ii apache2-utils 2.4.38-3+deb10u3 pn libapache2-mod-proxy-uwsgi <none> ii python3-bcrypt 3.1.6-1 ii python3-passlib 1.7.1-1 pn uwsgi <none> pn uwsgi-plugin-python3 <none> -- Configuration Files: /etc/radicale/config changed [not included] /etc/radicale/logging changed [not included] -- no debconf information
--- a/radicale/storage.py +++ b/radicale/storage.py @@ -792,26 +792,18 @@ @contextmanager def _atomic_write(self, path, mode="w", newline=None, sync_directory=True): - directory = os.path.dirname(path) - tmp = NamedTemporaryFile( - mode=mode, dir=directory, delete=False, prefix=".Radicale.tmp-", - newline=newline, encoding=None if "b" in mode else self._encoding) - try: - yield tmp - tmp.flush() - try: + parent_dir, name = os.path.split(path) + # Do not use mkstemp because it creates with permissions 0o600 + with TemporaryDirectory( + prefix=".Radicale.tmp-", dir=parent_dir) as tmp_dir: + with open(os.path.join(tmp_dir, name), mode, newline=newline, + encoding=None if "b" in mode else self._encoding) as tmp: + yield tmp + tmp.flush() self._fsync(tmp.fileno()) - except OSError as e: - raise RuntimeError("Fsync'ing file %r failed: %s" % - (path, e)) from e - tmp.close() - os.replace(tmp.name, path) - except BaseException: - tmp.close() - os.remove(tmp.name) - raise + os.replace(os.path.join(tmp_dir, name), path) if sync_directory: - self._sync_directory(directory) + self._sync_directory(parent_dir) @staticmethod def _find_available_file_name(exists_fn, suffix=""):