Commit 8e778208 authored by bertagaz's avatar bertagaz
Browse files

Merge branch 'feature/11753-port-to-python' into devel

 Conflicts:
	refresh-translations

Fix-committed: #11753
parents d9e17ed8 7a8c0304
......@@ -215,3 +215,7 @@ patch \
/usr/share/debootstrap/scripts/jessie \
data/debootstrap/scripts/jessie.patch
sed -i "s,%%topdir%%,$(pwd)," /usr/share/debootstrap/scripts/tails-build-jessie
# Make the python library available in Tails
install -d -m 2777 config/chroot_local-includes/tmp/
cp -r submodules/pythonlib config/chroot_local-includes/tmp/
#!/bin/sh
set -e
set -u
echo "Installing the tailslib python library"
(
cd /tmp/pythonlib
python3 setup.py clean
python3 setup.py install
)
rm -rf /tmp/pythonlib
#!/bin/sh
#! /usr/bin/env python3
"""
Tails electrum wrapper
set -e
set -u
Test with "python3 electrum.py doctest".
The tests will start the tor-browser so you probably
want to use a tester that handles user interaction or
run the tests from the command line and answer prompts as needed.
. gettext.sh
TEXTDOMAIN="tails"
export TEXTDOMAIN
goodcrypto.com converted from bash to python and added basic tests.
CONF_DIR="${HOME}"/.electrum
>>> # run script
>>> sh.Command(sys.argv[0])()
<BLANKLINE>
"""
electrum_config_is_persistent() {
[ "$(findmnt --noheadings --output SOURCE --target "${CONF_DIR}")" = "/dev/mapper/TailsData_unlocked[/electrum]" ]
}
import os
import sys
from gettext import gettext
verify_start () {
local dialog_msg="<b><big>`gettext \"Persistence is disabled for Electrum\"`</big></b>
import sh
`gettext \"When you reboot Tails, all of Electrum's data will be lost, including your Bitcoin wallet. It is strongly recommended to only run Electrum when its persistence feature is activated.\"`
os.environ['TEXTDOMAIN'] = 'tails'
`gettext \"Do you want to start Electrum anyway?\"`
"
local launch="`gettext \"_Launch\"`"
local exit="`gettext \"_Exit\"`"
zenity --question --title "" --text "${dialog_msg}" --default-cancel \
--ok-label "${launch}" --cancel-label "${exit}"
}
HOME_DIR = os.environ['HOME']
CONF_DIR = os.path.join(HOME_DIR, '.electrum')
if ! electrum_config_is_persistent; then
verify_start || exit 0
fi
exec /usr/bin/electrum "${@}"
def main(*args):
if not electrum_config_is_persistent():
if not verify_start():
return
os.execv('/usr/bin/electrum', ['/usr/bin/electrum'] + list(args))
def electrum_config_is_persistent():
"""Return True iff electrum config is persistent.
>>> electrum_config_is_persistent()
False
"""
filesystem = sh.findmnt('--noheadings',
'--output', 'SOURCE',
'--target', CONF_DIR).stdout.decode().strip()
return filesystem in sh.glob('/dev/mapper/TailsData_unlocked[/electrum]')
def verify_start():
"""Ask user whether to start Electrum.
>>> verify_start() in (True, False)
True
"""
disabled_text = gettext('Persistence is disabled for Electrum')
warning_text = gettext(
"When you reboot Tails, all of Electrum's data will be lost, including your Bitcoin wallet. It is strongly recommended to only run Electrum when its persistence feature is activated.")
question_text = gettext('Do you want to start Electrum anyway?')
dialog_msg = ('<b><big>{}</big></b>\n\n{}\n\n{}\n'.
format(disabled_text, warning_text, question_text))
launch_text = gettext('_Launch')
exit_text = gettext('_Exit')
# results 0 == True; 1 == False; 5 == Timeout
results = sh.zenity('--question', '--title', '', '--default-cancel',
'--ok-label', launch_text,
'--cancel-label', exit_text,
'--text', dialog_msg,
_ok_code=[0,1,5])
start = results.exit_code == 0
return start
if __name__ == '__main__':
if len(sys.argv) > 1 and sys.argv[1] == 'doctest':
import doctest
doctest.testmod()
else:
main(*sys.argv[1:])
#!/bin/sh
LIVE_IMAGE_MOUNTPOINT=/lib/live/mount/medium
for arg in $(cat /proc/cmdline) ; do
case "$arg" in
BOOT_IMAGE=*)
kernel="${arg#BOOT_IMAGE=}"
;;
initrd=*)
initrd="${arg#initrd=}"
;;
esac
done
# Sanity checks
[ -n "$kernel" ] || exit 4
[ -n "$initrd" ] || exit 5
case "$1" in
kernel)
echo "${LIVE_IMAGE_MOUNTPOINT}${kernel}"
;;
initrd)
echo "${LIVE_IMAGE_MOUNTPOINT}${initrd}"
;;
*)
echo "Usage: $0 kernel|initrd" >&2
exit 3
esac
exit 0
#! /usr/bin/env python3
"""
Get Tails boot info.
Test with "python3 tails-get-bootinfo.py doctest".
goodcrypto.com converted from bash to python and added basic tests.
>>> import sh
>>> sh.Command(sys.argv[0])('kernel')
/lib/live/mount/medium/live/vmlinuz
>>> sh.Command(sys.argv[0])('initrd')
/lib/live/mount/medium/live/initrd.img
>>> sh.Command(sys.argv[0])(_ok_code=(1))
Usage: tails-get-bootinfo kernel|initrd
<BLANKLINE>
"""
import sys
LIVE_IMAGE_MOUNTPOINT = '/lib/live/mount/medium'
def main(*args):
kernel = None
initrd = None
with open('/proc/cmdline') as f:
kernel_params = f.read().split()
for param in kernel_params:
if param.startswith('BOOT_IMAGE='):
kernel = param[len('BOOT_IMAGE='):]
elif param.startswith('initrd='):
initrd = param[len('initrd='):]
if not kernel or not initrd:
print('Failed to parse /proc/cmdline', file=sys.stderr)
sys.exit(1)
if 'kernel' in args:
print(LIVE_IMAGE_MOUNTPOINT + kernel, end="")
elif 'initrd' in args:
print(LIVE_IMAGE_MOUNTPOINT + initrd, end="")
else:
print('Usage: tails-get-bootinfo kernel|initrd', file=sys.stderr)
sys.exit(1)
if __name__ == "__main__":
if len(sys.argv) > 1 and sys.argv[1] == 'doctest':
import doctest
doctest.testmod()
elif len(sys.argv) == 2:
main(*sys.argv[1:])
else:
print('Usage: tails-get-bootinfo kernel|initrd', file=sys.stderr)
sys.exit(1)
#!/bin/sh
#! /usr/bin/env python3
"""
Tails upgrade frontend wrapper.
set -e
set -u
Test with "python3 tails-upgrade-frontend-wrapper.py doctest".
The tests will start the upgrade process which could pop up a dialog box
so you probably want to use a tester that handles user interaction or
run the tests from the command line and answer prompts as needed.
CMD=$(basename ${0})
goodcrypto.com converted from bash to python and added basic tests.
. gettext.sh
TEXTDOMAIN="tails"
export TEXTDOMAIN
>>> # run this script (without waiting 30 seconds)
>>> sh.Command(sys.argv[0])("--no-wait")
<BLANKLINE>
"""
# Import no_abort()
. /usr/local/lib/tails-shell-library/common.sh
import os
import sys
import time
from gettext import gettext
TORDATE_DIR=/run/tordate
TORDATE_DONE_FILE="${TORDATE_DIR}/done"
INOTIFY_TIMEOUT=60
MIN_REAL_MEMFREE=$((300 * 1024))
RUN_AS_USER=tails-upgrade-frontend
import sh
import psutil
### Functions
os.environ['PATH'] = '/usr/local/bin:/usr/bin:/bin'
os.environ['TEXTDOMAIN'] = 'tails'
error () {
local cli_text="${CMD}: `gettext \"error:\"` ${@}"
local dialog_text="<b><big>`gettext \"Error\"`</big></b>
CMD = os.path.basename(sys.argv[0])
TORDATE_DIR = '/run/tordate'
TORDATE_DONE_FILE = '{}/done'.format(TORDATE_DIR)
INOTIFY_TIMEOUT = 60
MIN_AVAILABLE_MEMORY = (300 * 1024 * 1024) # In Bytes
RUN_AS_USER = 'tails-upgrade-frontend'
${@}"
echo "${cli_text}" >&2
zenity --error --title "" --text "${dialog_text}"
exit 1
}
check_free_memory () {
local min_real_memfree memfree buffers cached real_memfree
local errormsg
min_real_memfree="$1"
memfree=$(awk '/^MemFree:/{print $2}' /proc/meminfo)
buffers=$(awk '/^Buffers:/{print $2}' /proc/meminfo)
cached=$(awk '/^Cached:/{print $2}' /proc/meminfo)
tmpfs=$(df --type=tmpfs --local --output=used --total | tail -n1)
real_memfree=$(($memfree + $buffers + $cached - $tmpfs))
errormsg="`gettext \"<b>Not enough memory available to check for upgrades.</b>
ERROR_MESSAGE = gettext('''\"<b>Not enough memory available to check for upgrades.</b>
Make sure this system satisfies the requirements for running Tails.
See file:///usr/share/doc/tails/website/doc/about/requirements.en.html
......@@ -50,26 +40,67 @@ See file:///usr/share/doc/tails/website/doc/about/requirements.en.html
Try to restart Tails to check for upgrades again.
Or do a manual upgrade.
See https://tails.boum.org/doc/first_steps/upgrade#manual\"`"
if [ "$real_memfree" -lt "$MIN_REAL_MEMFREE" ] ; then
echo "Only $real_memfree MemFree + Buffers + Cached - usage of tmpfs, while $MIN_REAL_MEMFREE is needed." >&2
error "$errormsg"
fi
}
### Main
sleep 30
check_free_memory "$MIN_REAL_MEMFREE"
# Go to a place where everyone, especially Archive::Tar::Wrapper called by
# tails-install-iuk, can chdir back after it has chdir'd elsewhere to do
# its job.
cd /
xhost +SI:localuser:"$RUN_AS_USER"
no_abort sudo -u "$RUN_AS_USER" /usr/bin/tails-upgrade-frontend "${@}"
xhost -SI:localuser:"$RUN_AS_USER"
exit ${_NO_ABORT_RET}
See https://tails.boum.org/doc/first_steps/upgrade#manual\"''')
def main(*args):
if "--no-wait" not in args:
time.sleep(30)
else:
args = (arg for arg in args if arg != "--no-wait")
check_free_memory(MIN_AVAILABLE_MEMORY)
# Go to a place where everyone, especially Archive::Tar::Wrapper called by
# tails-install-iuk, can chdir back after it has chdir'd elsewhere to do
# its job.
os.chdir('/')
os.execv(
"/bin/sh",
(
"/bin/sh", "-c",
"xhost +SI:localuser:{user};"
"sudo -u {user} /usr/bin/tails-upgrade-frontend {args};"
"xhost -SI:localuser:{user}".format(user=RUN_AS_USER, args=" ".join(args))
)
)
def error(msg):
"""Show error and exit."""
cli_text = '{}: {} {}'.format(CMD, gettext('error:'), msg)
dialog_text = '''<b><big>{}</big></b>\n\n{}'''.format(gettext('Error'), msg)
print(cli_text, file=sys.stderr)
sh.zenity('--error', '--title', "", '--text', dialog_text, _ok_code=[0,1,5])
sys.exit(1)
def check_free_memory(min_available_memory):
"""Check for enough free memory.
# 1 KiB should be available when running the doctest
>>> check_free_memory(1024)
# 1 TiB should not be available, an error prompt should be displayed
>>> try:
... check_free_memory(1024*1024*1024*1024)
... fail()
... except SystemExit:
... pass
"""
available_memory = psutil.virtual_memory().available
if available_memory < min_available_memory:
print('Only {} Bytes memory available, while {} is needed'.format(
available_memory, min_available_memory), file=sys.stderr)
error(ERROR_MESSAGE)
if __name__ == '__main__':
if len(sys.argv) > 1 and sys.argv[1] == 'doctest':
import doctest
doctest.testmod()
else:
main(*sys.argv[1:])
#!/bin/sh
#! /usr/bin/env python3
set -e
import logging
import time
import sys
# Import try_for()
. /usr/local/lib/tails-shell-library/common.sh
import sh
# Import tor_bootstrap_progress()
. /usr/local/lib/tails-shell-library/tor.sh
from tailslib.tor import tor_has_bootstrapped
from tailslib.exceptions import TorFailedToBoostrapError
# Import log()
. /usr/local/lib/tails-shell-library/log.sh
_LOG_TAG="$(basename $0)"
systemctl restart tor@default.service
logger = logging.getLogger(__name__)
if try_for 270 '[ "$(tor_bootstrap_progress)" -eq 100 ]'; then
log "Tor has successfully bootstrapped."
else
log "Tor failed to bootstrap"
exit 1
fi
TIMEOUT = 270
def main():
restart_tor()
def restart_tor():
""" Restart the Tor systemd service
>>> restart_tor()
"""
sh.systemctl('restart', 'tor@default.service')
for i in range(TIMEOUT):
if tor_has_bootstrapped():
logger.info("Tor has successfully bootstrapped")
return
time.sleep(1)
raise TorFailedToBoostrapError("Tor failed to bootstrap within %s seconds" % TIMEOUT)
if __name__ == "__main__":
if len(sys.argv) > 1 and sys.argv[1] == 'doctest':
import doctest
doctest.testmod()
else:
main()
#!/bin/sh
# *** WARNING about debug_file and debug_directory *********************
#
# Great attention must be given to the ownership situation of these
# files and their parent directories in order to avoid a symlink-based
# attack that could read the contents of any file and make it
# accessible to the user running this script (typicall the live
# user). Therefore, when adding a new file, give as the first argument
# 'root' only if the complete path to it (including the file itself)
# is owned by root and already exists before the system is connected to
# the network (that is, before GDM's PostLogin script is run).
# If not, the following rules must be followed strictly:
#
# * only one non-root user is involved in the ownership situation (the
# file, its dir and the parent dirs). From now on let's assume it is
# the case and call it $USER.
#
# * if any non-root group has write access, it must not have any
# members.
#
# If any of these rules does not apply, the file cannot be added here
# safely and something is probably quite wrong and should be
# investigated carefully.
debug_command() {
echo
echo "===== output of command $@ ====="
"$@"
}
debug_file() {
local user="${1}"
shift
file="${1}"
[ ! -e "${file}" ] && return
echo
echo "===== content of ${file} ====="
sudo -u "${user}" -- cat "${file}"
}
debug_directory() {
local user="${1}"
shift
dir="${1}"
[ ! -d "${dir}" ] && return
echo
echo "===== listing of ${dir} ====="
sudo -u "${user}" -- /bin/ls -lA "${dir}"
for file in "${dir}"/* ; do
debug_file "${user}" "${file}"
done
}
debug_file root "/proc/cmdline"
# General hardware and filesystems information
debug_command /usr/sbin/dmidecode -s system-manufacturer
debug_command /usr/sbin/dmidecode -s system-product-name
debug_command /usr/sbin/dmidecode -s system-version
debug_command "/usr/bin/lspci"
debug_command /bin/df --human-readable --print-type
debug_command /bin/mount --show-labels
debug_command "/bin/lsmod"
debug_file root "/proc/asound/cards"
debug_file root "/proc/asound/devices"
debug_file root "/proc/asound/modules"
# Miscellaneous configuration and log files
debug_file root "/etc/X11/xorg.conf"
debug_file Debian-gdm "/var/log/gdm3/tails-greeter.errors"
debug_file root "/var/log/live/boot.log"
debug_file root "/var/log/live/config.log"
debug_file root "/var/lib/live/config/tails.physical_security"
# Persistence
debug_file root "/var/lib/gdm3/tails.persistence"
debug_file root "/live/persistence/TailsData_unlocked/persistence.conf"
debug_file root "/live/persistence/TailsData_unlocked/live-additional-software.conf"
debug_directory root "/live/persistence/TailsData_unlocked/apt-sources.list.d"
debug_file root "/var/log/live-persist"
# The Journal
debug_command /bin/journalctl --catalog --no-pager
#! /usr/bin/env python3
"""
Debug Tails.
Test with "python3 tails-debugging-info.py doctest" as root.
goodcrypto.com converted from bash to python and added basic tests.
*** WARNING about debug_file and debug_directory *********************
Great attention must be given to the ownership situation of these
files and their parent directories in order to avoid a symlink-based
attack that could read the contents of any file and make it
accessible to the user running this script (typically the live
user). Therefore, when adding a new file, give as the first argument
'root' only if the complete path to it (including the file itself)
is owned by root and already exists before the system is connected to
the network (that is, before GDM's PostLogin script is run).
If not, the following rules must be followed strictly:
* only one non-root user is involved in the ownership situation (the
file, its dir and the parent dirs). From now on let's assume it is
the case and call it $USER.
* if any non-root group has write access, it must not have any
members.
If any of these rules does not apply, the file cannot be added here
safely and something is probably quite wrong and should be
investigated carefully.
>>> # run script
>>> import sh
>>> this_command = sh.Command(sys.argv[0])
>>> this_command()
<BLANKLINE>
...
"""
import os
import sys
from pwd import getpwuid
import subprocess
# AppArmor Ux rules don't sanitize PATH, which can lead to an
# exploited application (that's allowed to run this script unconfined)
# having this script run arbitrary code, violating that application's
# confinement. Let's prevent that by setting PATH to a list of
# directories where only root can write.
os.environ['PATH'] = '/usr/local/bin:/usr/bin:/bin'
def main():
"""Print debug information.
>>> main()
<BLANKLINE>
...
"""
debug_file('root', '/proc/cmdline')
# General hardware and filesystems information
debug_command('/usr/sbin/dmidecode', '-s', 'system-manufacturer')
debug_command('/usr/sbin/dmidecode', '-s', 'system-product-name')
debug_command('/usr/sbin/dmidecode', '-s', 'system-version')