Commit e2caab51 authored by intrigeri's avatar intrigeri
Browse files

Drop kexec-based memory erasure feature (refs: #12354).

It's not reliable enough and provides poor UX. Linux memory poisoning
works well enough to get rid of it.
parent 0f5e1423
......@@ -10,10 +10,9 @@ systemctl enable memlockd.service
# Enable our own systemd unit files
systemctl enable onion-grater.service
systemctl enable tails-autotest-remote-shell.service
systemctl enable tails-reconfigure-kexec.service
systemctl enable tails-reconfigure-memlockd.service
systemctl enable tails-sdmem-on-media-removal.service
systemctl enable tails-set-wireless-devices-state.service
systemctl enable tails-shutdown-on-media-removal.service
systemctl enable tails-tor-has-bootstrapped.target
systemctl enable tails-wait-until-tor-has-bootstrapped.service
systemctl enable tails-tor-has-bootstrapped-flag-file.service
......@@ -46,11 +45,6 @@ systemctl disable tor.service
systemctl disable NetworkManager.service
systemctl disable NetworkManager-wait-online.service
# Don't hide tails-kexec's shutdown messages with an empty splash screen
for suffix in halt kexec poweroff reboot shutdown ; do
systemctl mask "plymouth-${suffix}.service"
done
# systemd-networkd fallbacks to Google's nameservers when no other nameserver
# is provided by the network configuration. In Jessie, this service is disabled
# by default, but it feels safer to make this explicit. Besides, it might be
......
#!/bin/sh
set -e
echo "Adding memory_wipe to the prereqs of all other init-top initramfs scripts"
(
cd /usr/share/initramfs-tools/scripts/init-top
for script in * ; do
[ "$script" != memory_wipe ] || continue
sed --regexp-extended -i \
-e 's/^(PREREQS?)="(.*)"/\1="memory_wipe \2"/' \
"$script"
done
)
# Defaults for kexec initscript
# sourced by kexec-tools.config, /etc/init.d/kexec and /etc/init.d/kexec-load
# Load a kexec kernel (true/false)
LOAD_KEXEC=true
# Kernel and initrd image
# Unused: tails-reconfigure-kexec appends the correct values to this file
# at boot time.
KERNEL_IMAGE=/vmlinux
INITRD=/initrd.img
rebooting() {
systemctl list-jobs systemd-reboot.service | grep -qs systemd-reboot.service
}
if rebooting ; then
APPEND="${APPEND} sdmem=reboot sdmemopts=vllf"
else
APPEND="${APPEND} sdmem=halt sdmemopts=vllf"
fi
+/bin/cat
+/bin/echo
+/bin/sh
+/bin/sleep
+/bin/stty
/etc/default/locale
/etc/init.d/kexec-load
/lib/systemd/system-shutdown/tails-kexec
+/sbin/kexec
+/lib/systemd/systemd-shutdown
+/usr/bin/eject
+/usr/bin/pkill
+/usr/local/sbin/udev-watchdog
#! /bin/sh
# FIXME: this script should be translatable in a better way than the
# ugly case..esac thing. Note that using gettext at this point -i.e.
# after the DVD has been ejected- is probably too brittle. A possible
# solution would be to turn this script into a .in file, with
# placeholders for translatable string. Translatable strings and their
# translations could be managed by ikiwiki+po, and the placeholders
# could be replaced at boot time -depending on the chosen locale- by
# the appropriate strings. Unfortunately po4a does not support shell
# scripts.
PATH=/sbin:/bin
print_text () {
echo "$1" > /dev/console
}
print_empty_line () {
print_text ''
}
### Main
test "x`/bin/cat /sys/kernel/kexec_loaded`y" = "x1y" || exit 0
/bin/stty sane < /dev/console
print_empty_line
print_empty_line
print_text "--------------------------------------------------------------------------------"
# $LANG was set there by the FIXME live-config upstream script
. /etc/default/locale
# Note to translators: any text line must fit on a 80 characters wide screen
case "${LANG}" in
de_DE.UTF-8)
print_text " Sie können nun die Start-DVD oder den Start-USB-Stick entfernen"
print_empty_line
print_text " Der Systemspeicher wird in einigen Sekunden gelöscht..."
print_empty_line
print_text " Die Anzeige könnte anschließend fehlerhaft sein."
print_empty_line
print_text " Falls sich das System in einigen Sekunden nicht selbst ausschaltet,"
print_text " bedeutet dies, dass die Speicherlöschung fehlgeschlagen ist."
;;
es_ES.UTF-8)
print_text " Puede ahora retirar el DVD o el USB de arranque."
print_empty_line
print_text " Se borrará dentro de pocos segundos la memoria RAM del sistema..."
print_empty_line
print_text "Pueden aparecer problemas de visualización en el monitor durante esta operación."
print_empty_line
print_text " If the system does not power off automatically in a few seconds,"
print_text " it may mean the memory wiping has failed."
;;
fr_FR.UTF-8)
print_text " Vous pouvez maintenant retirer le DVD / clé USB de boot."
print_empty_line
print_text " La mémoire vive va être effacée dans quelques secondes..."
print_empty_line
print_text " Il est possible que l'affichage soit corrompu au cours de cette opération."
print_empty_line
print_text " Si l'ordinateur ne s'éteint pas automatiquement après quelques secondes,"
print_text " il est possible que l'effacement de la mémoire ait échoué."
;;
it*)
print_text " Adesso puoi rimuovere il cd o la penna USB."
print_empty_line
print_text " La memoria del computer verra' cancellata tra pochi secondi..."
print_empty_line
print_text " Il display potra' essere corrotto durante questa operazione."
print_empty_line
print_text " Se vostro PC non si spegnera' automaticamente in pochi secondi"
print_text " la cancellazione della memoria potrebbe essere incompleta."
;;
*)
print_text " You can now remove the boot DVD or USB stick."
print_empty_line
print_text " The system memory is going to be wiped in a few seconds..."
print_empty_line
print_text " Display might be corrupted during this operation."
print_empty_line
print_text " If the system does not power off automatically in a few seconds,"
print_text " it may mean the memory wiping has failed."
;;
esac
print_text "--------------------------------------------------------------------------------"
print_empty_line
print_empty_line
/bin/sleep 5
/sbin/kexec -e --reset-vga
[Unit]
Description=Reconfigure kexec depending on running kernel
Documentation=https://tails.boum.org/contribute/design/memory_erasure/
[Service]
Type=oneshot
ExecStart=/usr/local/sbin/tails-reconfigure-kexec
RemainAfterExit=yes
CapabilityBoundingSet=
PrivateDevices=yes
PrivateNetwork=yes
PrivateTmp=yes
ProtectHome=yes
ProtectSystem=yes
[Install]
WantedBy=multi-user.target
[Unit]
Description=Wipe memory on live media removal
Documentation=https://tails.boum.org/contribute/design/memory_erasure/
After=memlockd.service tails-reconfigure-kexec.service tails-reconfigure-memlockd.service
After=memlockd.service tails-reconfigure-memlockd.service
[Service]
Type=simple
......
......@@ -51,9 +51,7 @@ do_stop() {
/usr/bin/eject -m "${BOOT_DEVICE}" || true
fi
/usr/bin/pkill gdm3 || true
/etc/init.d/kexec-load stop || true
/lib/systemd/system-shutdown/tails-kexec || true
/bin/systemctl --force poweroff
}
......
#!/bin/sh
set -e
set -u
PATH="/usr/local/bin:${PATH}"
KEXEC_CONF=/etc/default/kexec
KERNEL_IMAGE=$(tails-get-bootinfo kernel)
INITRD=$(tails-get-bootinfo initrd)
echo "KERNEL_IMAGE=\"${KERNEL_IMAGE}\"" >> "$KEXEC_CONF"
echo "INITRD=\"${INITRD}\"" >> "$KEXEC_CONF"
if grep -qw debug=wipemem /proc/cmdline; then
echo 'APPEND="${APPEND} sdmemdebug=1"' >> "$KEXEC_CONF"
else
echo 'APPEND="${APPEND} quiet"' >> "$KEXEC_CONF"
fi
#!/bin/sh
set -e
# FIXME: what is this used for? dependency-based hooks ordering?
PREREQ=""
prereqs() {
echo "${PREREQ}"
}
case "${1}" in
prereqs)
prereqs
exit 0
;;
esac
. /usr/share/initramfs-tools/hook-functions
copy_exec /sbin/poweroff
copy_exec /sbin/reboot
copy_exec /usr/bin/sdmem
copy_exec /usr/bin/seq
#!/bin/sh
PREREQ=""
# Duplicated in features/step_definitions/erase_memory.rb
KERNEL_MEM_RESERVED_K=$((64*1024))
ADMIN_MEM_RESERVED_K=$((128*1024))
prereqs() {
echo "${PREREQ}"
}
tweak_sysctl() {
echo 3 > /proc/sys/kernel/printk
echo 3 > /proc/sys/vm/drop_caches
echo 0 > /proc/sys/vm/overcommit_memory
echo 0 > /proc/sys/vm/oom_kill_allocating_task
echo 0 > /proc/sys/vm/oom_dump_tasks
if [ "${sdmemdebug}" = 1 ] ; then
# Make sure the kernel doesn't starve, for better reliability when
# running the test suite.
echo "$KERNEL_MEM_RESERVED_K" > /proc/sys/vm/min_free_kbytes
echo "$ADMIN_MEM_RESERVED_K" > /proc/sys/vm/admin_reserve_kbytes
fi
}
case ${1} in
prereqs)
prereqs
exit 0
;;
esac
if [ -n "${sdmem}" ] ; then
tweak_sysctl
if [ -z "${sdmemopts}" ] ; then
sdmemopts="v"
fi
/usr/bin/sdmem "-${sdmemopts}"
echo "Memory has been wiped!"
fi
if [ "${sdmemdebug}" = 1 ] ; then
clear
echo "Going to sleep 10 minutes. Happy dumping!"
sleep 1200
fi
case "${sdmem}" in
halt)
echo "Powering off..."
/sbin/poweroff -fd
;;
reboot)
echo "Restarting..."
/sbin/reboot -fd
;;
*)
;;
esac
......@@ -139,7 +139,6 @@ iptables
isolinux
ferm
keepassx
kexec-tools
keyringer
memlockd
less
......
--- a/etc/init.d/kexec-load 2014-12-01 20:23:12.826065938 +0100
+++ b/etc/init.d/kexec-load 2014-12-01 20:23:31.389572352 +0100
@@ -106,19 +106,6 @@
exit 3
;;
stop)
- # If running systemd, we want kexec reboot only if current
- # command is reboot. If not running systemd, check if
- # runlevel has been changed to 6 which indicates we are
- # rebooting
- if [ -d /run/systemd/system ]; then
- systemctl list-jobs systemd-kexec.service | grep -q systemd-kexec.service
- if [ $? -ne 0 ]; then
- exit 0
- fi
- else if [ -x /sbin/runlevel -a "$(runlevel | awk '{ print $2 }')" != "6" ]; then
- exit 0
- fi
- fi
do_stop
;;
*)
Tails specific: we use kexec on both halt (runlevel 0) on top of reboot
(runlevel 6).
--- chroot.orig/etc/init.d/kexec-load 2011-01-14 12:30:05.089859516 +0100
+++ chroot/etc/init.d/kexec-load 2011-01-14 12:30:29.159667183 +0100
@@ -8,1 +8,1 @@
-# Default-Stop: 6
+# Default-Stop: 0 6
live-boot live-boot/smem boolean true
live-boot live-boot/sdmem boolean true
[[!meta title="Memory erasure"]]
In order to protect against memory recovery such as cold boot attack,
the system RAM is overwritten when Tails is being shutdown or when the
most of the system RAM is overwritten when Tails is being shutdown or when the
boot medium is physically removed.
#### The big picture
The previous implementation of the Tails memory erasure feature
The initial implementation of the Tails memory erasure feature
suffered from flaws that were demonstrated by [[external
audit|security/audits/Blackhat_De-Anonymizing_Live_CDs]]. In short, it
only erased free memory and let data in the aufs read-write branch in
recoverable state.
In order to erase the biggest possible part of the system memory, the
hereby described new implementation, shipped in Tails 0.7, runs in a
Then, in order to erase the biggest possible part of the system memory,
a new implementation, shipped from Tails 0.7 to 2.12, runs in a
fresh environment provided by a newly started Linux kernel. This way,
a given part of the memory either is *used* by the memory erasure
process itself or it is considered as free and thus *erased* by this
process; in any case, it is at least overwritten once.
#### initramfs tweaks
The Linux kernel and initramfs used to erase the memory are the same
as the ones normally used by a Tails system... that actually includes
some bits of code dedicated to this mission.
An initramfs-tools hook includes the necessary files in the initramfs
at build time. A runtime init-premount script either does nothing, or
erases memory before shutting down or rebooting the system; its
behaviour depends on the `sdmem` kernel command line parameter value.
Additionally, the `sdmemopts` kernel command line parameter allows
fine tuning the options passed to the `sdmem` program.
- [[!tails_gitweb config/chroot_local-includes/usr/share/initramfs-tools/hooks/sdmem]]
- [[!tails_gitweb config/chroot_local-includes/usr/share/initramfs-tools/scripts/init-top/memory_wipe]]
- [[!tails_gitweb config/chroot_local-hooks/65-initramfs-scripts]]
These `sdmem` and `sdmemopts` are appended to the fresh kernel command
line parameters, when memory erasure is triggered, by the
`tails-kexec` shutdown script that is itself parameterized by the usual,
slightly customized, kexec-tools configuration file.
- [[!tails_gitweb config/chroot_local-includes/etc/default/kexec]]
- [[!tails_gitweb config/chroot_local-includes/lib/systemd/system-shutdown/tails-kexec]]
#### Actual memory erasure process
The software that performs the actual memory erasure is sdmem, which
is part of the [secure-delete](http://www.thc.org/) package. sdmem is
called using the `-v` (verbose mode) option to give feedback to the
user, as well as the `-llf` options: memory is only overwritten once
with zeros; this is the fastest available mode, and is enough to
protect against every memory forensics attack we know of.
- [[!tails_gitweb config/chroot_local-includes/etc/default/kexec]]
Sadly, this approach suffered from severe usability and reliability
problems (e.g. [[!tails_ticket 12354]], [[!tails_ticket 11786]]).
So it was removed in Tails 3.0, and Tails now relies on the Linux
kernel's freed memory poisoning feature.
#### Triggers
Different kinds of events trigger the memory erasure process. All lead
to run the `tails-kexec` shutdown script.
**First, the memory erasure process is triggered at the end of a normal
shutdown/reboot sequence.** This is implemented by slightly modifying
the System V initscripts shipped by the `kexec-tools` Debian package:
the `kexec-load` initscript, that normally only runs at reboot time,
is enabled to run at shutdown time as well. A custom `tails-kexec`
shutdown script replaces the `kexec` initscript, in order to support the case when
the boot medium is not available anymore at the time this script runs;
it also provides an improved user interface more suitable for Tails
target users needs. Finally, the standard systemd `halt`, `poweroff`,
`reboot`, `kexec` and `shutdown`
actions are taken over by having the `tails-kexec` script, that is run
just before they have a chance to be triggered (thanks to systemd's
`/lib/systemd/system-shutdown/` facility, documented in
`systemd-kexec.service(8)`).
**First, most memory is erased at the end of a normal shutdown/reboot
sequence.** This is implemented by the [[Linux kernel's freed memory
poisoning feature|design/kernel_hardening]], more specifically:
- [[!tails_gitweb config/chroot_local-patches/run_kexec-load_on_halt.diff]]
- [[!tails_gitweb config/chroot_local-hooks/52-update-rc.d]]
- [[!tails_gitweb config/chroot_local-includes/lib/systemd/system-shutdown/tails-kexec]]
* `page_poison`
* passing "P" to `slub_debug`
[[!tails_gitweb features/erase_memory.feature desc="Automated tests"]]
ensure that the most important parts of memory are erased this way.
**Second, the memory erasure process is triggered when the boot medium
is physically removed during runtime (USB boot medium is unplugged or
......@@ -90,9 +50,8 @@ physically removed.
- [[!tails_gitweb config/chroot_local-includes/usr/local/lib/udev-watchdog-wrapper]]
- [[!tails_gitweb config/chroot_local-includes/usr/src/udev-watchdog.c]]
- [[!tails_gitweb config/chroot_local-hooks/52-udev-watchdog]]
- [[!tails_gitweb config/chroot_local-includes/lib/systemd/system/tails-sdmem-on-media-removal.service]]
- [[!tails_gitweb config/chroot_local-includes/lib/systemd/system/tails-shutdown-on-media-removal.service]]
- [[!tails_gitweb config/chroot_local-hooks/52-update-rc.d]]
- [[!tails_gitweb config/chroot_local-patches/run_kexec-load_even_in_emergency_shutdown.diff]]
- [[!tails_gitweb config/chroot_local-includes/lib/systemd/system/gdm.service.d/restart.conf]]
#### Making sure needed files are available
......@@ -105,23 +64,5 @@ to memory erasure time.
- [[!tails_gitweb config/chroot_local-includes/etc/memlockd.cfg]]
- [[!tails_gitweb config/chroot_local-patches/keep_memlockd_on_shutdown.diff]]
- [[!tails_gitweb config/chroot_local-includes/lib/systemd/system/memlockd.service.d/oom.conf]]
- [[!tails_gitweb config/chroot_local-includes/lib/systemd/system/tails-reconfigure-kexec.service]]
- [[!tails_gitweb config/chroot_local-includes/lib/systemd/system/tails-reconfigure-memlockd.service]]
- [[!tails_gitweb config/chroot_local-includes/usr/local/sbin/tails-reconfigure-kexec]]
- [[!tails_gitweb config/chroot_local-includes/usr/local/sbin/tails-reconfigure-memlockd]]
#### User interface
Since this process can take a while the user can leave the computer
and let it finish on its own after removing the boot medium, or simply
turn it off if he or she is not worried about this attack: if Tails
was booted from a DVD it is ejected before the memory wiping is
started, and if it was booted from a USB drive it can be removed as
soon as the memory wiping has been started.
A short but visible message, displayed for a few seconds, explains the
user what is going to happen. To make this possible, we mask the
`plymouth-{halt,kexec,poweroff,reboot,shutdown}` services.
- [[!tails_gitweb config/chroot_local-includes/lib/systemd/system-shutdown/tails-kexec]]
- [[!tails_gitweb config/chroot_local-hooks/52-update-rc.d]]
**FIXME** this process is quite complicated and should be automated using VMs
[[!toc levels=2]]
You are highly welcome to save the result of this test in [[test_results]]
# 0. Prepare the needed tools
Make sure the BIOS is *not* configured to "test" the memory on startup.
Pick one of those:
* [[erase_memory_on_shutdown/qemu_pmemsave]] (pros: no initial setup)
* [[erase_memory_on_shutdown/virtualbox_dumpguestcore]]
(pros: no initial setup)
* [[erase_memory_on_shutdown/pxedump]] (pros: works for bare metal,
possible to `|grep` and avoid writting the huge dump file to disk;
cons: initial setup)
* [[erase_memory_on_shutdown/live_system]] (pros: works for bare metal;
cons: storage requirements, quite more complicated to get right than
the other methods)
# 1. Fill the RAM with a known pattern
* Run `fillram` a few times in parallel (on a 32-bit architecture the
address space of a given process is usually limited at 3 GiB - or
less, depends on the kernel configuration); as root:
for i in $(seq 0 31) ; do fillram & done ; watch -n 0.1 free -m
* The `free` output should allow you to reboot late enough (so that
enough memory is filled) and soon enough (so that the system is
still reactive somehow).
# 2. Test that you can get the pattern after rebooting, if no memory wiping takes place
* Make sure your preferred memory scrapper toolkit is ready.
* Kill fillram processes and reboot with `SysRq + 1` when free memory is under a threshold by running:
while [ $(free -m -o | grep Mem | sed -e 's/ */ /g' | cut -d ' ' -f 4) -ge 256 ] ; do sleep 0.1 ; done ; killall fillram ; echo 1 > /proc/sys/kernel/sysrq ; echo b > /proc/sysrq-trigger
* Dump memory and try to find the known pattern in it, e.g.:
grep -c wipe_didnt_work tails.dump
- you should get some integer larger than zero if the pattern was found in
RAM, which is the expected result;
- you should get `grep: /dev/mem: Cannot allocate memory` otherwise. In that
case, it is **not** useful to process to the next step, there is something
wrong in the way you tested.
# 3. Test that you can*not* get the pattern after rebooting Tails normally
* Redo step 1 (don't forget to add `debug=wipemem` to the kernel
command-line if your memory scrapper toolkit needs it).
* Kill fillram processes and reboot Tails when free memory is under a threshold by running:
while [ $(free -m -o | grep Mem | sed -e 's/ */ /g' | cut -d ' ' -f 4) -ge 256 ] ; do sleep 0.1 ; done ; killall fillram ; reboot
This is especially important on 486 kernels. The threshold might be fine tuned.
* Make sure your preferred memory scrapper toolkit is ready (e.g.
plug your USB scrapper stick).
* When Tails tells you you can unplug the USB stick, unplug the
Tails stick.
* Dump memory and try to find the known pattern in it, e.g.:
grep -c wipe_didnt_work tails.dump
- you should get zero if the pattern was not found in RAM, which is the
optimal (and expected) result;
- you should get an integer larger than zero if the pattern was found in
RAM, which means that smem failed. However, there seems to be certain
legit conditions which can make this happen any way. For instance, the new
kernel loaded with kexec may allocate some buffer in the memory space
that was filled with the pattern, and thus that space will not be wiped
by smem. Hence a "reasonably small" number of occurances is still
acceptible as it currently is unavoidable. For now, let us arbitrarily
choose that up to 500 000 occurences (or around 8 MB since each pattern is
16 bytes) are acceptable.
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment