git.iko.soy/typeable/nixpkgs
on master help sign in
Merge pull request #122926 from primeos/signal-desktop-fix-db-encryption

signal-desktop: Fix the database encryption by preloading SQLCipher

commitaa2537b5543d587f4d9a80f327712d0ddf4ada50 authorMichael Weiss <dev.primeos@gmail.com> dateMon 17 May 2021, 16:06 (5 years ago) parent631cbc345bd7b3
3 files changed+116−6
nixos/tests/signal-desktop.nix+4−5 pkgs/applications/networking/instant-messengers/signal-desktop/db-reencryption-wrapper.py+92 pkgs/applications/networking/instant-messengers/signal-desktop/default.nix+20−1
nixos/tests/signal-desktop.nix+4−5
@@ -44,12 +44,11 @@ import ./make-test-python.nix ({ pkgs, ...} :
4444 # - https://github.com/NixOS/nixpkgs/issues/108772
4545 # - https://github.com/NixOS/nixpkgs/pull/117555
4646 print(machine.succeed("su - alice -c 'file ~/.config/Signal/sql/db.sqlite'"))
47− # TODO: The DB should be encrypted and the following should be machine.fail
48− # instead of machine.succeed but the DB is currently unencrypted and we
49− # want to notice if this isn't the case anymore as the transition to a
50− # encrypted DB can cause data loss!:
5147 machine.succeed(
52− "su - alice -c 'file ~/.config/Signal/sql/db.sqlite' | grep -i sqlite"
48+ "su - alice -c 'file ~/.config/Signal/sql/db.sqlite' | grep 'db.sqlite: data'"
49+ )
50+ machine.fail(
51+ "su - alice -c 'file ~/.config/Signal/sql/db.sqlite' | grep -e SQLite -e database"
5352 )
5453 '';
5554})
pkgs/applications/networking/instant-messengers/signal-desktop/db-reencryption-wrapper.pynew file+92
@@ -0,0 +1,92 @@
1+#!@PYTHON@
2+
3+import json
4+import os
5+import re
6+import shlex
7+import sqlite3
8+import subprocess
9+import sys
10+
11+
12+DB_PATH = os.path.join(os.environ['HOME'], '.config/Signal/sql/db.sqlite')
13+DB_COPY = os.path.join(os.environ['HOME'], '.config/Signal/sql/db.tmp')
14+CONFIG_PATH = os.path.join(os.environ['HOME'], '.config/Signal/config.json')
15+
16+
17+def zenity_askyesno(title, text):
18+ args = [
19+ '@ZENITY@',
20+ '--question',
21+ '--title',
22+ shlex.quote(title),
23+ '--text',
24+ shlex.quote(text)
25+ ]
26+ return subprocess.run(args).returncode == 0
27+
28+
29+def start_signal():
30+ os.execvp('@SIGNAL-DESKTOP@', ['@SIGNAL-DESKTOP@'] + sys.argv[1:])
31+
32+
33+def copy_pragma(name):
34+ result = subprocess.run([
35+ '@SQLCIPHER@',
36+ DB_PATH,
37+ f"PRAGMA {name};"
38+ ], check=True, capture_output=True).stdout
39+ result = re.search(r'[0-9]+', result.decode()).group(0)
40+ subprocess.run([
41+ '@SQLCIPHER@',
42+ DB_COPY,
43+ f"PRAGMA key = \"x'{key}'\"; PRAGMA {name} = {result};"
44+ ], check=True, capture_output=True)
45+
46+
47+try:
48+ # Test if DB is encrypted:
49+ con = sqlite3.connect(f'file:{DB_PATH}?mode=ro', uri=True)
50+ cursor = con.cursor()
51+ cursor.execute("SELECT name FROM sqlite_master WHERE type='table';")
52+ con.close()
53+except:
54+ # DB is encrypted, everything ok:
55+ start_signal()
56+
57+
58+# DB is unencrypted!
59+answer = zenity_askyesno(
60+ "Error: Signal-Desktop database is not encrypted",
61+ "Should we try to fix this automatically?"
62+ + "You likely want to backup ~/.config/Signal/ first."
63+)
64+if not answer:
65+ answer = zenity_askyesno(
66+ "Launch Signal-Desktop",
67+ "DB is unencrypted, should we still launch Signal-Desktop?"
68+ + "Warning: This could result in data loss!"
69+ )
70+ if not answer:
71+ print('Aborted')
72+ sys.exit(0)
73+ start_signal()
74+
75+# Re-encrypt the DB:
76+with open(CONFIG_PATH) as json_file:
77+ key = json.load(json_file)['key']
78+result = subprocess.run([
79+ '@SQLCIPHER@',
80+ DB_PATH,
81+ f" ATTACH DATABASE '{DB_COPY}' AS signal_db KEY \"x'{key}'\";"
82+ + " SELECT sqlcipher_export('signal_db');"
83+ + " DETACH DATABASE signal_db;"
84+]).returncode
85+if result != 0:
86+ print('DB encryption failed')
87+ sys.exit(1)
88+# Need to copy user_version and schema_version manually:
89+copy_pragma('user_version')
90+copy_pragma('schema_version')
91+os.rename(DB_COPY, DB_PATH)
92+start_signal()
pkgs/applications/networking/instant-messengers/signal-desktop/default.nix+20−1
@@ -10,6 +10,9 @@
1010, hunspellDicts, spellcheckerLanguage ? null # E.g. "de_DE"
1111# For a full list of available languages:
1212# $ cat pkgs/development/libraries/hunspell/dictionaries.nix | grep "dictFileName =" | awk '{ print $3 }'
13+, python3
14+, gnome
15+, sqlcipher
1316}:
1417
1518let
@@ -112,14 +115,20 @@ in stdenv.mkDerivation rec {
112115
113116 # Symlink to bin
114117 mkdir -p $out/bin
115− ln -s $out/lib/Signal/signal-desktop $out/bin/signal-desktop
118+ ln -s $out/lib/Signal/signal-desktop $out/bin/signal-desktop-unwrapped
116119
117120 runHook postInstall
118121 '';
119122
123+ # Required for $SQLCIPHER_LIB which contains "/build/" inside the path:
124+ noAuditTmpdir = true;
125+
120126 preFixup = ''
127+ export SQLCIPHER_LIB="$out/lib/Signal/resources/app.asar.unpacked/node_modules/better-sqlite3/build/Release/better_sqlite3.node"
128+ test -x "$SQLCIPHER_LIB" # To ensure the location hasn't changed
121129 gappsWrapperArgs+=(
122130 --prefix LD_LIBRARY_PATH : "${lib.makeLibraryPath [ stdenv.cc.cc ] }"
131+ --prefix LD_PRELOAD : "$SQLCIPHER_LIB"
123132 ${customLanguageWrapperArgs}
124133 )
125134
@@ -131,6 +140,16 @@ in stdenv.mkDerivation rec {
131140 patchelf --add-needed ${libpulseaudio}/lib/libpulse.so $out/lib/Signal/resources/app.asar.unpacked/node_modules/ringrtc/build/linux/libringrtc.node
132141 '';
133142
143+ postFixup = ''
144+ # This hack is temporarily required to avoid data-loss for users:
145+ cp ${./db-reencryption-wrapper.py} $out/bin/signal-desktop
146+ substituteInPlace $out/bin/signal-desktop \
147+ --replace '@PYTHON@' '${python3}/bin/python3' \
148+ --replace '@ZENITY@' '${gnome.zenity}/bin/zenity' \
149+ --replace '@SQLCIPHER@' '${sqlcipher}/bin/sqlcipher' \
150+ --replace '@SIGNAL-DESKTOP@' "$out/bin/signal-desktop-unwrapped"
151+ '';
152+
134153 # Tests if the application launches and waits for "Link your phone to Signal Desktop":
135154 passthru.tests.application-launch = nixosTests.signal-desktop;
136155