#!/bin/sh
set -e
CMD=$(basename ${0})
LOCK=/var/lock/${CMD}
. gettext.sh
TEXTDOMAIN="tails"
export TEXTDOMAIN
ROFS=/lib/live/mount/rootfs/filesystem.squashfs
CONF_DIR=/var/lib/unsafe-browser
COW=${CONF_DIR}/cow
CHROOT=${CONF_DIR}/chroot
CLEARNET_USER=clearnet
# Import tor_is_working()
. /usr/local/lib/tails-shell-library/tor.sh
# Import the TBB_INSTALL, TBB_EXT and TBB_PROFILE variables
. /usr/local/lib/tails-shell-library/tor-browser.sh
WARNING_PAGE='/usr/share/doc/tails/website/misc/unsafe_browser_warning'
LANG_CODE="$(echo ${LANG} | head -c 2)"
if [ -r "${WARNING_PAGE}.${LANG_CODE}.html" ]; then
START_PAGE="${WARNING_PAGE}.${LANG_CODE}.html"
else
START_PAGE="${WARNING_PAGE}.en.html"
fi
if [ -e /var/lib/gdm3/tails.camouflage ]; then
CAMOUFLAGE=yes
fi
cleanup () {
# Break down the chroot and kill all of its processes
local counter=0
local ret=0
while [ "${counter}" -le 10 ] && \
pgrep -u ${CLEARNET_USER} 1>/dev/null 2>&1; do
pkill -u ${CLEARNET_USER} 1>/dev/null 2>&1
ret=${?}
sleep 1
counter=$((${counter}+1))
done
[ ${ret} -eq 0 ] || pkill -9 -u ${CLEARNET_USER} 1>/dev/null 2>&1
for mnt in ${CHROOT}/dev ${CHROOT}/proc ${CHROOT} ${COW}; do
counter=0
while [ "${counter}" -le 10 ] && mountpoint -q ${mnt} 2>/dev/null; do
umount ${mnt} 2>/dev/null
sleep 1
counter=$((${counter}+1))
done
done
rmdir ${COW} ${CHROOT} 2>/dev/null
}
error () {
local cli_text="${CMD}: `gettext \"error:\"` ${@}"
local dialog_text="`gettext \"Error\"`
${@}"
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
local dialog_msg="`gettext \"Do you really want to launch the Unsafe Browser?\"`
`gettext \"Network activity within the Unsafe Browser is not anonymous. Only use the Unsafe Browser if necessary, for example if you have to login or register to activate your Internet connection.\"`"
local launch="`gettext \"_Launch\"`"
local exit="`gettext \"_Exit\"`"
# Since zenity can't set the default button to cancel, we switch the
# labels and interpret the return value as its negation.
if sudo -u ${SUDO_USER} zenity --question --title "" --ok-label "${exit}" \
--cancel-label "${launch}" --text "${dialog_msg}"; then
exit 0
fi
}
show_start_notification () {
local title="`gettext \"Starting the Unsafe Browser...\"`"
local body="`gettext \"This may take a while, so please be patient.\"`"
tails-notify-user "${title}" "${body}" 10000
}
setup_chroot () {
# Setup a chroot on an aufs "fork" of the filesystem.
# FIXME: When LXC matures to the point where it becomes a viable option
# for creating isolated jails, the chroot can be used as its rootfs.
echo "* Setting up chroot"
trap cleanup INT
trap cleanup EXIT
mkdir -p ${COW} ${CHROOT} && \
mount -t tmpfs tmpfs ${COW} && \
mount -t aufs -o noatime,noxino,dirs=${COW}=rw:${ROFS}=rr+wh aufs ${CHROOT} && \
mount -t proc proc ${CHROOT}/proc && \
mount --bind /dev ${CHROOT}/dev || \
error "`gettext \"Failed to setup chroot.\"`"
# Workaround for todo/buggy_aufs_vs_unsafe-browser
chmod -t ${COW}
}
set_chroot_browser_name () {
NAME="${1}"
LONG=$(echo ${LANG} | grep -o "^[a-zA-Z_]*")
SHORT=${LONG%%_*}
EXT_DIR=${CHROOT}/"${TBB_EXT}"
BRANDING=branding/brand.dtd
if [ -e "${EXT_DIR}/langpack-${LONG}@firefox.mozilla.org.xpi" ]; then
PACK="${EXT_DIR}/langpack-${LONG}@firefox.mozilla.org.xpi"
TOP=browser/chrome
REST=${LONG}/locale
elif [ -e "${EXT_DIR}/langpack-${SHORT}@firefox.mozilla.org.xpi" ]; then
PACK="${EXT_DIR}/langpack-${SHORT}@firefox.mozilla.org.xpi"
TOP=browser/chrome
REST=${SHORT}/locale
else
PACK="${CHROOT}/"${TBB_EXT}"/Browser/browser/omni.ja"
TOP=chrome
REST=en-US/locale
fi
TMP=$(mktemp -d)
# Non-zero exit code due to non-standard ZIP archive.
# The following steps will fail soon if the extraction failed anyway.
unzip -d "${TMP}" "${PACK}" || true
sed -i "s/<"'!'"ENTITY\s\+brand\(Full\|Short\)Name.*$/<"'!'"ENTITY brand\1Name \"${NAME}\">/" "${TMP}/${TOP}/${REST}/${BRANDING}"
rm "${PACK}"
(cd $TMP ; 7z a -tzip "${PACK}" .)
chmod a+r "${PACK}"
rm -Rf "${TMP}"
}
configure_chroot () {
echo "* Configuring chroot"
# Set the chroot's DNS servers to those obtained through DHCP
rm -f ${CHROOT}/etc/resolv.conf
for NS in ${IP4_NAMESERVERS}; do
echo "nameserver ${NS}" >> ${CHROOT}/etc/resolv.conf
done
chmod a+r ${CHROOT}/etc/resolv.conf
# Remove all Iceweasel addons: some adds proxying, which we don't
# want; some may change the fingerprint compared to a standard
# Iceweasel install. Note: We cannot use apt-get since we don't ship its
# lists (#6531). Too bad, APT supports globbing, while dkpg does not.
dpkg -l 'xul-ext-*' | /bin/grep '^ii' | awk '{print $2}' | \
xargs chroot ${CHROOT} dpkg --remove
# Create a fresh Iceweasel profile for the clearnet user
CLEARNET_PROFILE="${CHROOT}"/home/clearnet/.tor-browser/profile.default
CLEARNET_EXT="${CLEARNET_PROFILE}"/extensions
mkdir -p "${CLEARNET_EXT}"
cp -Pr "${TBB_PROFILE}"/extensions/langpack-*.xpi \
"${TBB_PROFILE}"/extensions/branding@amnesia.boum.org \
"${CLEARNET_EXT}"
CLEARNET_PREFS="${CLEARNET_PROFILE}"/preferences/prefs.js
mkdir -p "$(dirname "${CLEARNET_PREFS}")"
# Localization
cat /var/lib/tails-user-session/browser-locale.js >> "${CLEARNET_PREFS}"
# Disable proxying in the chroot
echo 'pref("network.proxy.type", 0);' >> "${CLEARNET_PREFS}"
echo 'pref("network.proxy.socks_remote_dns", false);' >> "${CLEARNET_PREFS}"
# Set the name (e.g. window title) of the browser
set_chroot_browser_name "`gettext \"Unsafe Browser\"`"
# Set start page to something that explains what's going on
echo 'user_pref("browser.startup.homepage", "'${START_PAGE}'");' >> \
"${CLEARNET_PREFS}"
# Remove all bookmarks
rm -f ${CHROOT}/"${TBB_PROFILE}"/bookmarks.html
rm -f ${CLEARNET_PROFILE}/bookmarks.html
rm -f ${CLEARNET_PROFILE}/places.sqlite
chown -R clearnet:clearnet ${CHROOT}/home/clearnet/.tor-browser
# Set a scary theme (except if we're using Windows
# camouflage). Note that the tails-activate-win8-theme script that
# we may run below requires that the browser profile is writable
# by the user running the script (i.e. clearnet).
if [ -z "${CAMOUFLAGE}" ]; then
cat >> "${CLEARNET_PREFS}" </dev/null
chroot ${CHROOT} sudo -u ${CLEARNET_USER} "${TBB_INSTALL}"/Browser/firefox -DISPLAY=:0.0 -profile /home/clearnet/.tor-browser/profile.default
sudo -u ${SUDO_USER} xhost -SI:localuser:${CLEARNET_USER} 2>/dev/null
}
show_shutdown_notification () {
local title="`gettext \"Shutting down the Unsafe Browser...\"`"
local body="`gettext \"This may take a while, and you may not restart the Unsafe Browser until it is properly shut down.\"`"
tails-notify-user "${title}" "${body}" 10000
}
maybe_restart_tor () {
# Restart Tor if it's not working (a captive portal may have prevented
# Tor from bootstrapping, and a restart is the fastest way to get
# wheels turning)
if ! tor_is_working; then
echo "* Restarting Tor"
restart-tor
if ! service tor status >/dev/null; then
error "`gettext \"Failed to restart Tor.\"`"
fi
fi
}
# Prevent multiple instances of the script.
exec 9>${LOCK}
if ! flock -x -n 9; then
error "`gettext \"Another Unsafe Browser is currently running, or being cleaned up. Please retry in a while.\"`"
fi
# Get the DNS servers that was obtained from NetworkManager, if any...
NM_ENV=/var/lib/NetworkManager/env
if [ -r "${NM_ENV}" ]; then
. ${NM_ENV}
fi
# ... otherwise fail.
# FIXME: Or would it make sense to fallback to Google's DNS or OpenDNS?
# Some stupid captive portals may allow DNS to any host, but chances are
# that only the portal's DNS would forward to the login page.
if [ -z "${IP4_NAMESERVERS}" ]; then
error "`gettext \"No DNS server was obtained through DHCP or manually configured in NetworkManager.\"`"
fi
verify_start
show_start_notification
setup_chroot
configure_chroot
run_browser_in_chroot
show_shutdown_notification
maybe_restart_tor
exit 0