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:
commit
aa2537b554
@ -44,12 +44,11 @@ import ./make-test-python.nix ({ pkgs, ...} :
|
|||||||
# - https://github.com/NixOS/nixpkgs/issues/108772
|
# - https://github.com/NixOS/nixpkgs/issues/108772
|
||||||
# - https://github.com/NixOS/nixpkgs/pull/117555
|
# - https://github.com/NixOS/nixpkgs/pull/117555
|
||||||
print(machine.succeed("su - alice -c 'file ~/.config/Signal/sql/db.sqlite'"))
|
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(
|
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"
|
||||||
)
|
)
|
||||||
'';
|
'';
|
||||||
})
|
})
|
||||||
|
@ -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()
|
@ -10,6 +10,9 @@
|
|||||||
, hunspellDicts, spellcheckerLanguage ? null # E.g. "de_DE"
|
, hunspellDicts, spellcheckerLanguage ? null # E.g. "de_DE"
|
||||||
# For a full list of available languages:
|
# For a full list of available languages:
|
||||||
# $ cat pkgs/development/libraries/hunspell/dictionaries.nix | grep "dictFileName =" | awk '{ print $3 }'
|
# $ cat pkgs/development/libraries/hunspell/dictionaries.nix | grep "dictFileName =" | awk '{ print $3 }'
|
||||||
|
, python3
|
||||||
|
, gnome
|
||||||
|
, sqlcipher
|
||||||
}:
|
}:
|
||||||
|
|
||||||
let
|
let
|
||||||
@ -112,14 +115,20 @@ in stdenv.mkDerivation rec {
|
|||||||
|
|
||||||
# Symlink to bin
|
# Symlink to bin
|
||||||
mkdir -p $out/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
|
runHook postInstall
|
||||||
'';
|
'';
|
||||||
|
|
||||||
|
# Required for $SQLCIPHER_LIB which contains "/build/" inside the path:
|
||||||
|
noAuditTmpdir = true;
|
||||||
|
|
||||||
preFixup = ''
|
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+=(
|
gappsWrapperArgs+=(
|
||||||
--prefix LD_LIBRARY_PATH : "${lib.makeLibraryPath [ stdenv.cc.cc ] }"
|
--prefix LD_LIBRARY_PATH : "${lib.makeLibraryPath [ stdenv.cc.cc ] }"
|
||||||
|
--prefix LD_PRELOAD : "$SQLCIPHER_LIB"
|
||||||
${customLanguageWrapperArgs}
|
${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
|
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":
|
# Tests if the application launches and waits for "Link your phone to Signal Desktop":
|
||||||
passthru.tests.application-launch = nixosTests.signal-desktop;
|
passthru.tests.application-launch = nixosTests.signal-desktop;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user