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=""):

Reply via email to