Commit 58e1b1a3 authored by Tails developers's avatar Tails developers
Browse files

Set time from Tor consensus unless it's already in the valid interval.

This is based on ideas from Liberte Linux' tordate script,
and meant to implement
https://tails.boum.org/todo/remove_the_htp_user_firewall_exception/

This allows greatly simplifying the 50-htp.sh NM hook: no need to do fancy
tricks with /etc/hosts anymore.

Split out and re-order NM hooks:
  First, setup the firewall.
  Then restart Tor.
  Then set the time using Tor consensus, and start HTP (non-blocking) in the background.
  Eventually, restart and cleanup everything that needs to: ttdnsd, pdnsd,
  Vidalia, etc.

Doing so allows us to stop passing a tiny DNS timeout to htpdate / wget anymore:
commit e291af5d, that introduced this "-t 1" option, explains why it was added.
These reasons don't stand anymore: the IPs of the server queried by htpdate are
not in /etc/hosts nowadays.

Non-blocking htpdate has an initscript (/etc/init.d/htpdate, that should not
start on its own); its options were moved to /etc/default/htpdate.

The tails-htp-notify-user script is removed: no need for feedback as this is now
non-blocking and does not prevent actual usage. A bit more KISS does not hurt.
parent 30eb7ced
#!/bin/sh
# We don't start Tor automatically so *this* is the time
# when it is supposed to start.
# Run only when the interface is not "lo":
if [ $1 = "lo" ]; then
exit 0
fi
# Run whenever an interface gets "up", not otherwise:
if [ $2 != "up" ]; then
exit 0
fi
# Workaround https://trac.torproject.org/projects/tor/ticket/2355
if grep -qw bridge /proc/cmdline; then
rm -f /var/lib/tor/*
fi
# A SIGHUP should be enough but there's a bug in Tor. Details:
# * https://trac.torproject.org/projects/tor/ticket/1247
# * https://tails.boum.org/bugs/tor_vs_networkmanager/
service tor restart
#!/bin/sh
# Rationale: Tor needs a somewhat accurate clock to work.
# If the clock is wrong enough to prevent it from opening circuits,
# we set the time to the middle of the valid time interval found
# in the Tor consensus, and we restart it.
# In any case, we use HTP to ask more accurate time information to
# a few authenticated HTTPS servers.
### Init variables
LOG=/var/log/htpdate.log
HTP_DIR=/var/run/htpdate
SUCCESS_FILE=${HTP_DIR}/success
TOR_DIR=/var/lib/tor
TOR_CONSENSUS=${TOR_DIR}/cached-consensus
TOR_DESCRIPTORS=${TOR_DIR}/cached-descriptors
INOTIFY_TIMEOUT=60
DATE_RE='[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9] [0-9][0-9]:[0-9][0-9]:[0-9][0-9]'
### Exit conditions
# Run only when the interface is not "lo":
if [ "$1" = "lo" ]; then
exit 0
fi
# Run whenever an interface gets "up", not otherwise:
if [ "$2" != "up" ]; then
exit 0
fi
# Do not run if we already successed:
if [ -e "$SUCCESS_FILE" ]; then
exit 0
fi
### Create status dir
install -o root -g root -m 0755 -d ${HTP_DIR}
### Create log file
# The htp user needs to write to this file.
# The $LIVE_USERNAME user may need to read this file.
touch "$LOG"
chown htp:nogroup "$LOG"
chmod 644 "$LOG"
### Functions
log() {
echo "$@" >> "${LOG}"
}
tor_is_working() {
[ -e $TOR_DESCRIPTORS ]
}
wait_for_tor_consensus() {
log "Waiting for the Tor consensus file to contain a valid time interval"
while :; do
if grep -qs "^valid-until ${DATE_RE}"'$' ${TOR_CONSENSUS}; then
break;
fi
inotifywait -q -t ${INOTIFY_TIMEOUT} -e close_write -e moved_to --format %w%f ${TOR_DIR} || :
done
}
date_points_are_sane() {
local vstart="$1"
local vend="$2"
vendchk=`date -ud "${vstart} -0300" +'%F %T'`
[ "${vend}" = "${vendchk}" ]
}
time_is_in_valid_tor_range() {
local curdate="$1"
local vstart="$2"
vendcons=`date -ud "${vstart} -0230" +'%F %T'`
order="${vstart}
${curdate}
${vendcons}"
ordersrt=`echo "${order}" | sort`
[ "${order}" = "${ordersrt}" ]
}
maybe_set_time_from_tor_consensus() {
# Get various date points in Tor's format, and do some sanity checks
vstart=`sed -n "/^valid-after \(${DATE_RE}\)"'$/s//\1/p; t q; b n; :q q; :n' ${TOR_CONSENSUS}`
vend=`sed -n "/^valid-until \(${DATE_RE}\)"'$/s//\1/p; t q; b n; :q q; :n' ${TOR_CONSENSUS}`
vmid=`date -ud "${vstart} -0130" +'%F %T'`
log "Tor: valid-after=${vstart} | valid-until=${vend}"
if ! date_points_are_sane "${vstart}" "${vend}"; then
log "Unexpected valid-until: [${vend}] is not [${vstart} + 3h]"
return
fi
curdate=`date -u +'%F %T'`
log "Current time is ${curdate}"
if time_is_in_valid_tor_range "${curdate}" "${vstart}"; then
log "Current time is in valid Tor range"
return
fi
log "Current time is not in valid Tor range, setting to middle of this range: [${vmid}]"
date -us "${vmid}" 1>/dev/null
# Tor is unreliable with picking a circuit after time change
if service tor status >/dev/null; then
log "Restarting Tor service"
service tor restart
fi
}
### Main
# Delegate time setting to other daemons if Tor connections work
if tor_is_working; then
log "Tor has already opened a circuit"
else
wait_for_tor_consensus
maybe_set_time_from_tor_consensus
fi
log "Restarting htpdate"
service htpdate restart
log "htpdate service restarted with return code $?"
#!/bin/sh
# Rationale: Tor needs a somewhat accurate clock to work, and for that
# HTP is currently the only practically usable solution when one wants
# to authenticate the servers providing the time. We then need to get
# the IPs of a bunch of HTTPS servers.
# However, since all DNS lookups are normally made through the Tor
# network, which we are not connected to at this point, we use the
# local DNS servers obtained through DHCP, if possible, or the OpenDNS
# ones otherwise.
# To limit fingerprinting possibilities, we do not want to send HTTP
# requests aimed at an IP-based virtualhost such as https://IP/, but
# rather to the usual hostname (e.g. https://www.eff.org/) as any
# "normal" user would do. Once we have got the HTTPS servers IPs, we
# write these to /etc/hosts so the system resolver knows about them.
# htpdate is then run, and we eventually remove the added entries from
# /etc/hosts.
# Note that all network operations (host, htpdate) are done with the
# htp user, who has an exception in the firewall configuration
# granting it direct access to the needed network ports.
# That's why we tell the htpdate script to drops priviledges and run
# as the htp user all operations but the actual setting of time, which
# has to be done as root.
### Init variables
LOG=/var/log/htpdate.log
DONE_FILE=/var/lib/live/htp-done
SUCCESS_FILE=/var/lib/live/htp-success
VERSION_FILE=/etc/amnesia/version
HTP_POOL="
www.torproject.org
mail.riseup.net
encrypted.google.com
ssl.scroogle.org
"
BEGIN_MAGIC='### BEGIN HTP HOSTS'
END_MAGIC='### END HTP HOSTS'
if [ -n "$DHCP4_DOMAIN_NAME_SERVERS" ]; then
NAME_SERVERS="$DHCP4_DOMAIN_NAME_SERVERS"
else
NAME_SERVERS="208.67.222.222 208.67.220.220"
fi
### Exit conditions
# Run only when the interface is not "lo":
if [ "$1" = "lo" ]; then
exit 0
fi
# Run whenever an interface gets "up", not otherwise:
if [ "$2" != "up" ]; then
exit 0
fi
# Do not run if we already successed:
if [ -e "$SUCCESS_FILE" ]; then
exit 0
fi
### Delete previous state file
rm -f "$DONE_FILE"
### Create log file
# The htp user needs to write to this file.
# The $LIVE_USERNAME user needs to read this file.
touch "$LOG"
chown htp:nogroup "$LOG"
chmod 644 "$LOG"
### Run tails-htp-notify-user (the sooner, the better)
# Get LIVE_USERNAME
. /etc/live/config.d/username
export DISPLAY=':0.0'
export XAUTHORITY="$(echo /var/run/gdm3/auth-for-$LIVE_USERNAME-*/database)"
exec /bin/su -c /usr/local/bin/tails-htp-notify-user "$LIVE_USERNAME" &
### Functions
log() {
echo "$@" >> "${LOG}"
}
quit() {
local exit_code="$1"
shift
local message="$@"
echo "$exit_code" >> "$DONE_FILE"
if [ "$exit_code" -eq 0 ]; then
touch "$SUCCESS_FILE"
fi
log "$message"
exit $exit_code
}
cleanup_etc_hosts() {
log "Cleaning /etc/hosts"
# remove all lines between markers
sed -e "/$BEGIN_MAGIC/,/$END_MAGIC/d" -i /etc/hosts
}
dns_query_cmd() {
local host="$1"
local ns cmd
cmd=""
for ns in $NAME_SERVERS; do
cmd="${cmd:+$cmd || }host '$host' '$ns'"
done
echo "$cmd"
}
add_nameservers_to_etc_hosts() {
trap "cleanup_etc_hosts" EXIT
echo "$BEGIN_MAGIC" >> /etc/hosts
for HTP_HOST in $HTP_POOL; do
# ensure we only get the domain if given a true url
HTP_HOST=${HTP_HOST%%/*}
IP=$(sudo -u htp sh -c "$(dns_query_cmd "$HTP_HOST")" |
awk '/ has address / { print $4 ; quit }')
if [ -z "$IP" ]; then
echo "$END_MAGIC" >> /etc/hosts
quit 17 "Failed to resolve $HTP_HOST"
fi
echo "$IP $HTP_HOST" >> /etc/hosts
done
echo "$END_MAGIC" >> /etc/hosts
}
run_htpdate() {
/usr/local/sbin/htpdate \
-d \
-l "$LOG" \
-a "$HTTP_USER_AGENT" \
-f \
-p \
-u htp \
-t 1 \
$HTP_POOL
}
release_date() {
# outputs something like 20111013
sed -n -e '1s/^.* - \([0-9]\+\)$/\1/p;q' "$VERSION_FILE"
}
is_clock_way_off() {
local release_date_secs="$(date -d "$(release_date)" '+%s')"
local current_date_secs="$(date '+%s')"
if [ "$current_date_secs" -lt "$release_date_secs" ]; then
log "Clock is before the release date"
return 0
fi
if [ "$(($release_date_secs + 259200))" -lt "$current_date_secs" ]; then
log "Clock is approx. 6 months after the release date"
return 0
fi
return 1
}
### Main
HTTP_USER_AGENT="$(/usr/local/bin/getTorbuttonUserAgent)"
if [ -z "$HTTP_USER_AGENT" ]; then
quit 1 "getTorbuttonUserAgent failed."
fi
# Beware: this string is used and parsed in tails-htp-notify-user
log "HTP NetworkManager hook: here we go"
log "Will use these nameservers: $NAME_SERVERS"
add_nameservers_to_etc_hosts
run_htpdate
HTPDATE_RET=$?
# If the clock is already too badly off, htpdate might have fail because
# SSL certificates will not be verifiable. In that case let's set the clock to
# the release date and try again.
if [ "$HTPDATE_RET" -ne 0 ] && is_clock_way_off; then
date --set="$(release_date)" > /dev/null
run_htpdate
HTPDATE_RET=$?
fi
quit $HTPDATE_RET "htpdate exited with return code $HTPDATE_RET"
#! /bin/sh
# Run only when the interface is not "lo":
if [ $1 = "lo" ]; then
exit 0
fi
# Run whenever an interface gets "up", not otherwise:
if [ $2 != "up" ]; then
exit 0
fi
# Restart ttdnsd
service ttdnsd restart
......@@ -10,34 +10,9 @@ if [ $2 != "up" ]; then
exit 0
fi
PIDFILE=/var/run/tor/tor.pid
# Get LIVE_USERNAME
. /etc/live/config.d/username
# Workaround https://trac.torproject.org/projects/tor/ticket/2355
if grep -qw bridge /proc/cmdline; then
rm -f /var/lib/tor/*
fi
# We don't start Tor automatically anymore so *this* is the time when
# it is supposed to start.
# Note: as we disabled the initscript automatic startup, we cannot use
# invoke-rc.d: it would silently ignore our request. That's why we use
# the good old direct initscript invocation rather than any fancy
# frontend.
if [ -r "${PIDFILE}" ]; then
# A SIGHUP should be enough but there's a bug in Tor. Details:
# * https://bugs.torproject.org/flyspray/index.php?do=details&id=1247
# * https://tails.boum.org/bugs/tor_vs_networkmanager/
/etc/init.d/tor restart
else
/etc/init.d/tor start
fi
# Restart ttdnsd
service ttdnsd restart
# Restart Vidalia because it does not automatically reconnect to the new
# Tor instance. Use kill+start as:
# - X-GNOME-AutoRestart does not exist in Lenny's Gnome
......
HTP_POOL="www.torproject.org mail.riseup.net encrypted.google.com ssl.scroogle.org"
HTTP_USER_AGENT="$(/usr/local/bin/getTorbuttonUserAgent)"
#! /bin/sh
### BEGIN INIT INFO
# Provides: htpdate
# Default-Start:
# Default-Stop:
# Required-Start: mountkernfs $local_fs
# Required-Stop:
# Short-Description: Set time using HTP
# Description: Set time using HTP
### END INIT INFO
DESC="Setting time using HTP"
NAME=htpdate
SCRIPTNAME=/etc/init.d/$NAME
HTP_DIR=/var/run/$NAME
PIDFILE=$HTP_DIR/pid
HTP_SUCCESS_FILE=$HTP_DIR/success
LOG=/var/log/$NAME.log
# Load the VERBOSE setting and other rcS variables
. /lib/init/vars.sh
# Define LSB log_* functions.
# Depend on lsb-base (>= 3.2-14) to ensure that this file is present
# and status_of_proc is working.
. /lib/lsb/init-functions
# Create status directory
install -o root -g root -m 0755 -d ${HTP_DIR}
# Source configuration
. /etc/default/$NAME
# Sanity checks
if [ -z "$HTTP_USER_AGENT" ]; then
log "HTTP_USER_AGENT is not set."
exit 2
fi
if [ -z "$HTP_POOL" ]; then
log "HTP_POOL is not set"
exit 3
fi
log() {
echo "$@" >> "${LOG}"
}
do_start() {
start-stop-daemon -S -q -p ${PIDFILE} -bm -x /usr/local/sbin/htpdate -- \
-d \
-l "$LOG" \
-a "$HTTP_USER_AGENT" \
-f \
-p \
-u htp \
-T "$HTP_SUCCESS_FILE" \
$HTP_POOL
}
do_stop() {
start-stop-daemon -K -q -p ${PIDFILE}
}
case "$1" in
start)
[ "$VERBOSE" != no ] && log_daemon_msg "$DESC" "$NAME"
do_start
case "$?" in
0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
esac
;;
stop)
do_stop
;;
restart)
do_stop
do_start
;;
*)
echo "Usage: $SCRIPTNAME (start|stop|restart)" >&2
exit 3
;;
esac
:
#!/usr/bin/perl
use strict;
use warnings;
use 5.10.1;
#man{{{
=head1 NAME
tails-htp-notify-user
=head1 VERSION
Version X.XX
=head1 AUTHOR
Tails dev team <amnesia@boum.org>
See https://tails.boum.org/.
=cut
#}}}
use Data::Dumper;
use Desktop::Notify;
use English '-no_match_vars';
use Locale::gettext;
use POSIX;
### initialization
setlocale(LC_MESSAGES, "");
textdomain("tails-htp-notify-user");
my $htp_done_file = '/var/lib/live/htp-done';
my $htp_success_file = '/var/lib/live/htp-success';
my $htp_log_file = '/var/log/htpdate.log';
my $debug;
### Enabling debug mode until the Squeeze regression is sorted out.
$debug = 1;
### subroutines
sub debug { say STDERR $_[0] unless $debug; }
### main
exit 0 if -e $htp_success_file;
my $notify = Desktop::Notify->new()
or die "Failed creating Desktop::Notify object.";
debug('$notify:' . "\n" . Dumper($notify));
my $summary = gettext("Synchronizing the system's clock");
my $body = gettext("Tor needs an accurate clock to work properly. Please wait...");
my $notification = $notify->create(summary => $summary,
body => $body,
timeout => 0)
or die "Failed to create notification object";
debug('$notification:' . "\n" . Dumper($notification));
$notification->show() or warn "Failed showing notification.";
until (-e $htp_done_file) {
sleep 1;
}
# in case htpdate went fine, close the 'Please wait...' notification
if (-e $htp_success_file) {
$notification->close();
}
# in case htpdate failed, notify the user with the corresponding logs
else {
open(my $htp_log, '<', $htp_log_file)
or die "Can not open file '$htp_log_file': $OS_ERROR";
my $last_log;
while (<$htp_log>) {
if ($_ =~ /^HTP NetworkManager hook: here we go/) {