Merge pull request #122926 from primeos/signal-desktop-fix-db-encryption

signal-desktop: Fix the database encryption by preloading SQLCipher
This commit is contained in:
Michael Weiss 2021-05-17 16:06:52 +02:00 committed by GitHub
commit aa2537b554
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 116 additions and 6 deletions

View file

@ -44,12 +44,11 @@ import ./make-test-python.nix ({ pkgs, ...} :
# - https://github.com/NixOS/nixpkgs/issues/108772
# - https://github.com/NixOS/nixpkgs/pull/117555
print(machine.succeed("su - alice -c 'file ~/.config/Signal/sql/db.sqlite'"))
# TODO: The DB should be encrypted and the following should be machine.fail
# instead of machine.succeed but the DB is currently unencrypted and we
# want to notice if this isn't the case anymore as the transition to a
# encrypted DB can cause data loss!:
machine.succeed(
"su - alice -c 'file ~/.config/Signal/sql/db.sqlite' | grep -i sqlite"
"su - alice -c 'file ~/.config/Signal/sql/db.sqlite' | grep 'db.sqlite: data'"
)
machine.fail(
"su - alice -c 'file ~/.config/Signal/sql/db.sqlite' | grep -e SQLite -e database"
)
'';
})

View file

@ -0,0 +1,92 @@
#!@PYTHON@
import json
import os
import re
import shlex
import sqlite3
import subprocess
import sys
DB_PATH = os.path.join(os.environ['HOME'], '.config/Signal/sql/db.sqlite')
DB_COPY = os.path.join(os.environ['HOME'], '.config/Signal/sql/db.tmp')
CONFIG_PATH = os.path.join(os.environ['HOME'], '.config/Signal/config.json')
def zenity_askyesno(title, text):
args = [
'@ZENITY@',
'--question',
'--title',
shlex.quote(title),
'--text',
shlex.quote(text)
]
return subprocess.run(args).returncode == 0
def start_signal():
os.execvp('@SIGNAL-DESKTOP@', ['@SIGNAL-DESKTOP@'] + sys.argv[1:])
def copy_pragma(name):
result = subprocess.run([
'@SQLCIPHER@',
DB_PATH,
f"PRAGMA {name};"
], check=True, capture_output=True).stdout
result = re.search(r'[0-9]+', result.decode()).group(0)
subprocess.run([
'@SQLCIPHER@',
DB_COPY,
f"PRAGMA key = \"x'{key}'\"; PRAGMA {name} = {result};"
], check=True, capture_output=True)
try:
# Test if DB is encrypted:
con = sqlite3.connect(f'file:{DB_PATH}?mode=ro', uri=True)
cursor = con.cursor()
cursor.execute("SELECT name FROM sqlite_master WHERE type='table';")
con.close()
except:
# DB is encrypted, everything ok:
start_signal()
# DB is unencrypted!
answer = zenity_askyesno(
"Error: Signal-Desktop database is not encrypted",
"Should we try to fix this automatically?"
+ "You likely want to backup ~/.config/Signal/ first."
)
if not answer:
answer = zenity_askyesno(
"Launch Signal-Desktop",
"DB is unencrypted, should we still launch Signal-Desktop?"
+ "Warning: This could result in data loss!"
)
if not answer:
print('Aborted')
sys.exit(0)
start_signal()
# Re-encrypt the DB:
with open(CONFIG_PATH) as json_file:
key = json.load(json_file)['key']
result = subprocess.run([
'@SQLCIPHER@',
DB_PATH,
f" ATTACH DATABASE '{DB_COPY}' AS signal_db KEY \"x'{key}'\";"
+ " SELECT sqlcipher_export('signal_db');"
+ " DETACH DATABASE signal_db;"
]).returncode
if result != 0:
print('DB encryption failed')
sys.exit(1)
# Need to copy user_version and schema_version manually:
copy_pragma('user_version')
copy_pragma('schema_version')
os.rename(DB_COPY, DB_PATH)
start_signal()

View file

@ -10,6 +10,9 @@
, hunspellDicts, spellcheckerLanguage ? null # E.g. "de_DE"
# For a full list of available languages:
# $ cat pkgs/development/libraries/hunspell/dictionaries.nix | grep "dictFileName =" | awk '{ print $3 }'
, python3
, gnome
, sqlcipher
}:
let
@ -112,14 +115,20 @@ in stdenv.mkDerivation rec {
# Symlink to bin
mkdir -p $out/bin
ln -s $out/lib/Signal/signal-desktop $out/bin/signal-desktop
ln -s $out/lib/Signal/signal-desktop $out/bin/signal-desktop-unwrapped
runHook postInstall
'';
# Required for $SQLCIPHER_LIB which contains "/build/" inside the path:
noAuditTmpdir = true;
preFixup = ''
export SQLCIPHER_LIB="$out/lib/Signal/resources/app.asar.unpacked/node_modules/better-sqlite3/build/Release/better_sqlite3.node"
test -x "$SQLCIPHER_LIB" # To ensure the location hasn't changed
gappsWrapperArgs+=(
--prefix LD_LIBRARY_PATH : "${lib.makeLibraryPath [ stdenv.cc.cc ] }"
--prefix LD_PRELOAD : "$SQLCIPHER_LIB"
${customLanguageWrapperArgs}
)
@ -131,6 +140,16 @@ in stdenv.mkDerivation rec {
patchelf --add-needed ${libpulseaudio}/lib/libpulse.so $out/lib/Signal/resources/app.asar.unpacked/node_modules/ringrtc/build/linux/libringrtc.node
'';
postFixup = ''
# This hack is temporarily required to avoid data-loss for users:
cp ${./db-reencryption-wrapper.py} $out/bin/signal-desktop
substituteInPlace $out/bin/signal-desktop \
--replace '@PYTHON@' '${python3}/bin/python3' \
--replace '@ZENITY@' '${gnome.zenity}/bin/zenity' \
--replace '@SQLCIPHER@' '${sqlcipher}/bin/sqlcipher' \
--replace '@SIGNAL-DESKTOP@' "$out/bin/signal-desktop-unwrapped"
'';
# Tests if the application launches and waits for "Link your phone to Signal Desktop":
passthru.tests.application-launch = nixosTests.signal-desktop;