Commit 58f005b5 authored by segfault's avatar segfault
Browse files

Merge commit '70091e58' (Tails 3.0~beta3 release) into feature/5688-tails-server

parents e8adad89 70091e58
[Service]
# XXX:Stretch: on Jessie, AppArmorProfile=system_i2p is a no-op, since
# AppArmor support was enabled in Debian's systemd 218-4 (#10925).
ExecStart=
ExecStart=/usr/sbin/aa-exec --profile=system_i2p -- /usr/sbin/wrapper "$I2P_ARGS"
# Type Path Mode UID GID Age Argument
d /run/htpdate 00755 root root - -
# Type Path Mode UID GID Age Argument
d /run/tails-upgrader 00775 root tails-upgrade-frontend - -
d /usr/share/tails-iuk/trusted_gnupg_homedir 00755 root root - -
# Type Path Mode UID GID Age Argument
d /run/tordate 00755 root root - -
#!/bin/sh
set -e
set -u
PERSISTENT_DATA_DIR="${HOME}/Persistent"
OLD_DB="${PERSISTENT_DATA_DIR}/keepassx.kdb"
MIGRATED_DB="${PERSISTENT_DATA_DIR}/New database.kdbx"
NEW_DB="${PERSISTENT_DATA_DIR}/keepassx.kdbx"
# There's a migrated but badly named DB => rename it before opening it:
if [ -e "$MIGRATED_DB" ] && ! [ -e "$NEW_DB" ]; then
mv "$MIGRATED_DB" "$NEW_DB"
/usr/bin/keepassx "$NEW_DB"
# There's an old DB but no new one => import the old DB:
elif mountpoint -q "$PERSISTENT_DATA_DIR" \
&& ! [ -e "$NEW_DB" ] \
&& [ -e "$OLD_DB" ]; then
# Ensure $PERSISTENT_DATA_DIR is pre-selected for saving
# the migrated database
cd "$PERSISTENT_DATA_DIR"
# Trigger the migration from the old KeePassX database to the new format
# used in KeePassX 2.0.x.
/usr/bin/keepassx "$OLD_DB"
# Avoid the user being prompted to open the old DB on next run.
mv "$OLD_DB" "${OLD_DB}.bak"
# Default case:
else
/usr/bin/keepassx "$@"
fi
......@@ -17,4 +17,9 @@ else
FILE="${PAGE}.html"
fi
if [ -n "${2}" ]; then
FILE="${FILE}#${2}"
fi
export TOR_BROWSER_SKIP_OFFLINE_WARNING=yes
exec /usr/local/bin/tor-browser "file://${WIKI_ROOT}/${FILE}"
......@@ -12,7 +12,7 @@ export TEXTDOMAIN
# Import no_abort()
. /usr/local/lib/tails-shell-library/common.sh
TORDATE_DIR=/var/run/tordate
TORDATE_DIR=/run/tordate
TORDATE_DONE_FILE="${TORDATE_DIR}/done"
INOTIFY_TIMEOUT=60
MIN_REAL_MEMFREE=$((300 * 1024))
......
......@@ -19,6 +19,9 @@ PROFILE="${HOME}/.tor-browser/profile.default"
# Import exec_firefox() and configure_best_tor_browser_locale()
. /usr/local/lib/tails-shell-library/tor-browser.sh
# Get LIVE_USERNAME
. /etc/live/config.d/username.conf
# Allow Torbutton access to the control port filter (for new identity).
# Setting a password is required, otherwise Torbutton attempts to
# read the authentication cookie file instead, which fails.
......@@ -32,8 +35,8 @@ export TOR_NO_DISPLAY_NETWORK_SETTINGS='yes'
ask_for_confirmation() {
# Skip dialog if user is already running Tor Browser:
if pgrep -u amnesia -f "${TBB_INSTALL}/firefox" ; then
if [ "${TOR_BROWSER_SKIP_OFFLINE_WARNING:-}" = 'yes' ] || \
pgrep -u "${LIVE_USERNAME}" -f "${TBB_INSTALL}/firefox"; then
return
fi
......
......@@ -10,7 +10,7 @@ unset TOR_CONTROL_PASSWD
unset TOR_FORCE_NET_CONFIG
export TOR_CONFIGURE_ONLY=1
export TOR_CONTROL_PORT=9051
export TOR_CONTROL_COOKIE_AUTH_FILE=/var/run/tor/control.authcookie
export TOR_CONTROL_COOKIE_AUTH_FILE=/run/tor/control.authcookie
export TOR_HIDE_BROWSER_LOGO=1
if echo "$@" | grep -qw -- --force-net-config; then
export TOR_FORCE_NET_CONFIG=1
......
......@@ -6,6 +6,7 @@
import base64
import fcntl
import io
import json
import os
import pwd
......@@ -14,22 +15,22 @@ import signal
import subprocess
import sys
import systemd.daemon
import textwrap
import traceback
REMOTE_SHELL_DEV = '/dev/ttyS0'
def mk_switch_user_fn(uid, gid):
def mk_switch_user_fn(user):
pwd_user = pwd.getpwnam(user)
def switch_user():
os.setgid(gid)
os.setuid(uid)
os.initgroups(user, pwd_user.pw_gid)
os.setgid(pwd_user.pw_gid)
os.setuid(pwd_user.pw_uid)
return switch_user
def run_cmd_as_user(cmd, user):
pwd_user = pwd.getpwnam(user)
switch_user_fn = mk_switch_user_fn(pwd_user.pw_uid,
pwd_user.pw_gid)
def get_user_env(user):
# We try to create an environment identical to what's expected
# inside Tails for the user by logging in (via `su`) as the user,
# setting up the GNOME shell environment, and extracting the
......@@ -41,7 +42,80 @@ def run_cmd_as_user(cmd, user):
wrapped_env_cmd = "su -c '{}' {}".format(env_cmd, user)
pipe = subprocess.Popen(wrapped_env_cmd, stdout=subprocess.PIPE, shell=True)
env_data = pipe.communicate()[0].decode('utf-8')
env = dict((line.split('=', 1) for line in env_data.splitlines()))
return dict((line.split('=', 1) for line in env_data.splitlines()))
# Dogtail does not seem to support the root user interacting with
# other users' applications, and it does not support Python 3 (which
# this script is written in) so let's wrap around an interactive
# Python shell started as a subprocess.
class PythonSession:
def __init__(self, user = None):
interactive_shell_code = '; '.join([
"import sys",
"import code",
"sys.ps1 = ''",
"sys.ps2 = ''",
"code.interact(banner='')",
])
if not user:
user = pwd.getpwuid(os.getuid()).pw_name
env = get_user_env(user)
cwd = env['HOME']
self.process = subprocess.Popen(
["python2", "-u", "-c", interactive_shell_code],
bufsize = 0,
shell=False,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
env=env,
cwd=cwd,
preexec_fn=mk_switch_user_fn(user)
)
init_code = """
import cStringIO
import json
import sys
orig_stdout = sys.stdout
orig_stderr = sys.stderr
""".replace(' ', '').lstrip()
self.process.stdin.write(init_code.encode())
self.process.stdin.flush()
def execute(self, code):
# This wrapping ensures that we can run almost any reasonable
# code and capture what it does.
wrapper = """
fake_stdout = cStringIO.StringIO()
fake_stderr = cStringIO.StringIO()
sys.stdout = fake_stdout
sys.stderr = fake_stderr
exc = None
try:
{code}
except Exception as e:
exc = '%s: %s' % (type(e).__name__, str(e))
# This newline is needed to end the `try` statement.
sys.stdout = orig_stdout
sys.stderr = orig_stderr
out_data = fake_stdout.getvalue()
err_data = fake_stderr.getvalue()
fake_stdout.close()
fake_stderr.close()
print(json.dumps([exc, out_data, err_data]))
""".replace(' ', '').lstrip()
indented_code = textwrap.indent(code, prefix=' '*4)
wrapped_code = wrapper.format(code=indented_code)
self.process.stdin.write(wrapped_code.encode())
self.process.stdin.flush()
return str(self.process.stdout.readline().strip(), 'utf-8')
def run_cmd_as_user(cmd, user):
switch_user_fn = mk_switch_user_fn(user)
env = get_user_env(user)
cwd = env['HOME']
return subprocess.Popen(
cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
......@@ -51,6 +125,7 @@ def run_cmd_as_user(cmd, user):
def main():
port = serial.Serial(port = REMOTE_SHELL_DEV, baudrate = 4000000)
python_sessions = dict()
# Notify systemd that we're ready
systemd.daemon.notify('READY=1')
......@@ -61,10 +136,10 @@ def main():
try:
id, cmd_type, *rest = json.loads(line)
ret = ""
if cmd_type in ['call', 'spawn']:
if cmd_type in ['sh_call', 'sh_spawn']:
user, cmd = rest
p = run_cmd_as_user(cmd, user)
if cmd_type == "spawn":
if cmd_type == "sh_spawn":
returncode, stdout, stderr = 0, "", ""
else:
stdout_b, stderr_b = p.communicate()
......@@ -72,14 +147,22 @@ def main():
stderr = stderr_b.decode('utf-8')
returncode = p.returncode
ret = json.dumps([id, 'success', returncode, stdout, stderr])
elif cmd_type in ['read', 'write', 'append']:
elif cmd_type == 'python_execute':
user, code = rest
if user not in python_sessions:
python_sessions[user] = PythonSession(user)
session = python_sessions[user]
result_str = session.execute(code)
result = json.loads(result_str)
ret = json.dumps([id, 'success'] + result)
elif cmd_type in ['file_read', 'file_write', 'file_append']:
path, *rest = rest
open_mode = cmd_type[0] + 'b'
open_mode = cmd_type[5] + 'b'
with open(path, open_mode) as f:
if cmd_type == 'read':
if cmd_type == 'file_read':
assert(rest == [])
ret = str(base64.b64encode(f.read()), 'utf-8')
elif cmd_type in ['write', 'append']:
elif cmd_type in ['file_write', 'file_append']:
assert(len(rest) == 1)
data = base64.b64decode(rest[0])
ret = f.write(data)
......
......@@ -32,8 +32,8 @@ use POSIX;
### initialization
setlocale(LC_MESSAGES, "");
textdomain("tails");
my $htp_done_file = '/var/run/htpdate/done';
my $htp_success_file = '/var/run/htpdate/success';
my $htp_done_file = '/run/htpdate/done';
my $htp_success_file = '/run/htpdate/success';
my $htp_log_file = '/var/log/htpdate.log';
my $debug;
......
#!/bin/sh
# Import set_simple_config_key().
. /usr/local/lib/tails-shell-library/common.sh
# Import language_code_from_locale().
. /usr/local/lib/tails-shell-library/localization.sh
I2P_DEFAULT_CONFIG="/usr/share/i2p"
I2P_CONFIG="/var/lib/i2p/i2p-config"
I2P_TUNNEL_CONFIG="${I2P_CONFIG}/i2ptunnel.config"
I2P_WRAPPER_LOG="/var/log/i2p/wrapper.log"
i2p_is_enabled() {
grep -qw "i2p" /proc/cmdline
}
i2p_eep_proxy_address() {
# We retrieve the host and port number from the I2P profile. This
# shouldn't be anywhere other than 127.0.0.1:4444 but in case
# someone modifies the hook scripts or the default changes in I2P,
# this check should still work.
local listen_host listen_port
listen_host=$(awk -F= '/^tunnel\.0\.interface/{print $2}' \
"${I2P_TUNNEL_CONFIG}")
listen_port=$(awk -F= '/^tunnel\.0\.listenPort/{print $2}' \
"${I2P_TUNNEL_CONFIG}")
echo ${listen_host}:${listen_port}
}
i2p_reseed_started() {
grep -q 'Reseed start$' "${I2P_WRAPPER_LOG}"
}
i2p_reseed_failed() {
grep -q 'Reseed failed, check network connection$' "${I2P_WRAPPER_LOG}"
}
i2p_reseed_completed() {
grep -q "Reseed complete" "${I2P_WRAPPER_LOG}"
}
i2p_reseed_status() {
if i2p_reseed_completed; then
echo success
elif i2p_reseed_failed; then
echo failure
elif i2p_reseed_started; then
echo running
fi
}
i2p_built_a_tunnel() {
ss -nlp | grep -qwF "$(i2p_eep_proxy_address)"
}
i2p_router_console_address() {
echo 127.0.0.1:7657
}
i2p_router_console_is_ready() {
ss -nlp | grep -qwF "$(i2p_router_console_address)"
}
set_best_i2p_router_console_lang() {
# We will use the detected language even if I2P doesn't support it; it
# will default to English in that case.
local lang="$(language_code_from_locale "${LANG}")"
# We first try to set it in an existing "live" config, even though
# the effect will only appear after a restart.
local config
for config in "${I2P_CONFIG}/router.config" \
"${I2P_DEFAULT_CONFIG}/router.config"; do
if [ -e "${config}" ]; then
set_simple_config_key "${config}" "routerconsole.lang" "${lang}"
return 0
fi
done
return 1
}
......@@ -11,7 +11,7 @@ get_tor_control_port() {
}
tor_control_send() {
COOKIE=/var/run/tor/control.authcookie
COOKIE=/run/tor/control.authcookie
HEXCOOKIE=$(xxd -c 32 -g 0 $COOKIE | cut -d' ' -f2)
/bin/echo -ne "AUTHENTICATE ${HEXCOOKIE}\r\n${1}\r\nQUIT\r\n" | \
nc 127.0.0.1 $(get_tor_control_port) | tr -d "\r"
......
......@@ -75,7 +75,8 @@ mac_spoof_panic() {
fi
module=$(get_module_used_by_nic "${nic}")
nic_name="$(get_name_of_nic ${nic})"
echo "blacklist ${module}" >> /etc/modprobe.d/"${module}"-blacklist.conf
echo "install ${module} /bin/true" >> \
/etc/modprobe.d/"${module}"-blacklist.conf
unload_module_and_rev_deps "${module}" || :
if nic_exists "${nic}"; then
log "Failed to unload module ${module} of NIC ${nic}."
......
......@@ -23,6 +23,7 @@ See https://tails.boum.org/.
#}}}
use Desktop::Notify;
use English '-no_match_vars';
use IPC::System::Simple qw{capturex $EXITVAL};
use Locale::gettext;
use Net::DBus::Reactor;
......@@ -36,10 +37,13 @@ textdomain("tails");
sub action_cb {
my $reactor = shift;
exec(
'/usr/local/bin/tor-browser',
'file:///usr/share/doc/tails/website/doc/advanced_topics/virtualization.en.html#security'
);
unless (fork) {
exec(
'/usr/local/bin/tails-documentation',
'doc/advanced_topics/virtualization',
'security'
);
}
$reactor->shutdown;
}
......@@ -76,7 +80,7 @@ else {
$notify->create(summary => $summary,
body => $body,
actions => { 'moreinfo' => gettext('Learn more'), },
actions => { "moreinfo_$PID" => gettext('Learn more'), },
hints => { 'transient' => 1, },
timeout => 0)->show();
......
......@@ -136,8 +136,8 @@ import yaml
DEFAULT_LISTEN_ADDRESS = 'localhost'
DEFAULT_LISTEN_PORT = 9051
DEFAULT_COOKIE_PATH = '/var/run/tor/control.authcookie'
DEFAULT_CONTROL_SOCKET_PATH = '/var/run/tor/control'
DEFAULT_COOKIE_PATH = '/run/tor/control.authcookie'
DEFAULT_CONTROL_SOCKET_PATH = '/run/tor/control'
class NoRewriteMatch(RuntimeError):
......
#!/bin/sh
set -e
set -u
. gettext.sh
TEXTDOMAIN="tails"
export TEXTDOMAIN
# Import the TBB_EXT variable, and guess_best_tor_browser_locale().
. /usr/local/lib/tails-shell-library/tor-browser.sh
# Import try_cleanup_browser_chroot(), setup_browser_chroot(),
# configure_chroot_dns_servers(), configure_chroot_browser(),
# configure_chroot_browser(), set_chroot_browser_locale()
# set_chroot_torbutton_browser_name(), set_chroot_browser_permissions()
# and run_browser_in_chroot().
. /usr/local/lib/tails-shell-library/chroot-browser.sh
# Import i2p_router_console_is_ready() and i2p_is_enabled().
. /usr/local/lib/tails-shell-library/i2p.sh
error () {
local cli_text="${CMD}: `gettext \"error:\"` ${@}"
local dialog_text="<b><big>`gettext \"Error\"`</big></b>
${@}"
echo "${cli_text}" >&2
sudo -u "${SUDO_USER}" zenity --error --title "" --text "${dialog_text}"
exit 1
}
verify_start () {
# Make sure the user really wants to start the browser in case the router console isn't available
local dialog_msg="<b><big>`gettext \"Do you still want to launch I2P Browser?\"`</big></b>
`gettext \"The I2P router console is not ready.\"`"
local launch="`gettext \"_Launch\"`"
local exit="`gettext \"_Exit\"`"
if ! sudo -u "${SUDO_USER}" \
zenity --question --title "" --text "${dialog_msg}" --default-cancel \
--cancel-label "${exit}" --ok-label "${launch}" ; then
exit 0
fi
}
show_start_notification () {
local title="`gettext \"Starting the I2P Browser...\"`"
local body="`gettext \"This may take a while, so please be patient.\"`"
tails-notify-user "${title}" "${body}" 10000
}
copy_extra_tbb_prefs () {
local chroot="${1}"
local browser_name="${2}"
local browser_user="${3}"
local tbb_prefs="/etc/tor-browser/profile/preferences"
local browser_prefs_dir="${chroot}/home/${browser_user}/.${browser_name}/profile.default/preferences"
mkdir -p "${browser_prefs_dir}"
# Selectively copy the TBB prefs we want
sed '/\(security\|update\|download\|spell\|noscript\|torbrowser\)/!d' "${tbb_prefs}/0000tails.js" > \
"${browser_prefs_dir}/0000tails.js"
sed '/\(capability\|noscript\)/!d' "${tbb_prefs}/extension-overrides.js" > \
"${browser_prefs_dir}/extension-overrides.js"
chown -R "${browser_user}:${browser_user}" "${browser_prefs_dir}"
}
show_shutdown_notification () {
local title="`gettext \"Shutting down the I2P Browser...\"`"
local body="`gettext \"This may take a while, and you may not restart the I2P Browser until it is properly shut down.\"`"
tails-notify-user "${title}" "${body}" 10000
}
# Main script:
# This isn't very useful without I2P...
i2p_is_enabled || exit 0
CMD="$(basename "${0}")"
LOCK="/var/lock/${CMD}"
CONF_DIR="/var/lib/i2p-browser"
COW="${CONF_DIR}/cow"
CHROOT="${CONF_DIR}/chroot"
BROWSER_NAME="i2p-browser"
BROWSER_USER="i2pbrowser"
HOME_PAGE="http://127.0.0.1:7657"
NOSCRIPT_EXT_XPI="${TBB_EXT}/{73a6fe31-595d-460b-a920-fcc0f8843232}.xpi"
TORBUTTON_EXT_DIR="${TBB_EXT}/torbutton@torproject.org"
HUMAN_READABLE_NAME="`gettext \"I2P Browser\"`"
IP4_NAMESERVERS="0.0.0.0"
# Prevent multiple instances of the script.
exec 9>"${LOCK}"
if ! flock -x -n 9; then
error "`gettext \"Another I2P Browser is currently running, or being cleaned up. Please retry in a while.\"`"
fi
if ! i2p_router_console_is_ready; then
verify_start
fi
show_start_notification
echo "* Setting up chroot"
setup_chroot_for_browser "${CHROOT}" "${COW}" "${BROWSER_USER}" || \
error "`gettext \"Failed to setup chroot.\"`"
echo "* Configuring chroot"
configure_chroot_browser "${CHROOT}" "${BROWSER_USER}" "${BROWSER_NAME}" \
"${HUMAN_READABLE_NAME}" "${HOME_PAGE}" "${IP4_NAMESERVERS}" \
"${TBB_EXT}"/langpack-*.xpi "${NOSCRIPT_EXT_XPI}" "${TORBUTTON_EXT_DIR}" && \
copy_extra_tbb_prefs "${CHROOT}" "${BROWSER_NAME}" "${BROWSER_USER}" || \
error "`gettext \"Failed to configure browser.\"`"
echo "* Starting I2P Browser"
run_browser_in_chroot "${CHROOT}" "${BROWSER_NAME}" "${BROWSER_USER}" \
"${SUDO_USER}" || \
error "`gettext \"Failed to run browser.\"`"
echo "* Exiting the I2P Browser"
show_shutdown_notification
exit 0
......@@ -9,7 +9,7 @@ import syslog
PERSISTENCE_DIR = "/live/persistence/TailsData_unlocked"
PACKAGES_LIST_FILE = PERSISTENCE_DIR + "/live-additional-software.conf"
ACTIVATION_FILE = "/var/run/live-additional-software/activated"
ACTIVATION_FILE = "/run/live-additional-software/activated"
def _launch_apt_get(specific_args):
"""Launch apt-get with given args
......
#!/bin/sh
set -e
set -u
# Get LANG
. /etc/default/locale
export LANG
# Initialize gettext support
. gettext.sh