Re: [Tails-dev] Please review and test feature/tordate

Delete this message

Reply to this message
Author: anonym
Date:  
To: The Tails public development discussion list
Subject: Re: [Tails-dev] Please review and test feature/tordate
10/05/2011 06:14 PM, intrigeri:
> anonym wrote (05 Oct 2011 10:21:28 GMT) :
>>> I'd like to understand better the "should be safe" assertion.
>>> Other than valid time, what conditions are needed for Tor to consider
>>> a consensus as "verified"?
>
>> From my skim of the Tor sources it seems:
> [...]
>
> => so IMHO the current state of this branch (5a2b5c0) is fit for
> merging into devel. Missing design doc update, though, but I'm
> commited to do it.


I was writing an email about problems with the current state, so I'll
just paste it here:

The approach in 1319c1c is safe for Tor, but it can cause troubles
*outside* of Tor when we use the consensus for setting the time. It
seems handling this edge case gets us into more and more trouble.
Everything below refers to 1319c1c.

Problem 1: is_clock_way_off() isn't really what we want.

Ponder upon this: there can be a window of time less than 6 months since
the Tails release where a majority of the authority certs are expired.
Tor can't verify the consensus so we get an unverified-consensus, but
maybe_set_time_from_tor_consensus doesn't look at that one so it will
fail in an ugly way since cached-consensus doesn't exist.

I guess we instead just want to check if the consensus we got is old,
e.g. something like [ $consensus_valid_until -lt $current_time ].


Problem 2: a malicious authority could still arbitrarily set our time.

If we get an unverified-consensus we set the system time to the release
date, then maybe_set_time_from_tor_consensus will (*definitely*, not
maybe :)) set the time according to cached-consensus -- we just renamed
unverified-consensus to this and somehow that magically allows us to
trust it... bad idea. Sure, Tor will not use the unverified-consensus if
it can't be verified, so that is safe, but we could still end up setting
a time set by by the attacker. Not nice.


Problem 3: what should we do when we get an unverified-consensus but the
time is correct? I suppose this kinda implies something went terribly
wrong. I guess we could just skip setting the time and let Tor run -- it
will be unusable. But the user should be notified, no?


I think the main problem stems from this: currently our only means for
tordate to verify that a consensus is OK is if Tor itself writes a
cached-consensus, and that is awkward. I don't feel focused enough right
now to figure out the right way to do this. See the attached untested
patch for my current line of thinking (do not merge!).

We really should meditate upon this issue so we get it right. We don't
want any more time issues... they are getting on my nerves!

Cheers!
diff --git a/config/chroot_local-includes/etc/NetworkManager/dispatcher.d/20-time.sh b/config/chroot_local-includes/etc/NetworkManager/dispatcher.d/20-time.sh
index a6a486e..7b1b6b4 100755
--- a/config/chroot_local-includes/etc/NetworkManager/dispatcher.d/20-time.sh
+++ b/config/chroot_local-includes/etc/NetworkManager/dispatcher.d/20-time.sh
@@ -53,20 +53,59 @@ tor_is_working() {
     [ -e $TOR_DESCRIPTORS ]
 }


-has_consensus() {
-    grep -qs "^valid-until ${DATE_RE}"'$' ${TOR_CONSENSUS} \
-                          ${TOR_UNVERIFIED_CONSENSUS}
+get_valid_after_from_consensus() {
+    sed -n "/^valid-after \(${DATE_RE}\)"'$/s//\1/p; t q; b n; :q q; :n' $1
+}
+
+get_valid_until_from_consensus() {
+    sed -n "/^valid-until \(${DATE_RE}\)"'$/s//\1/p; t q; b n; :q q; :n' $1
+}
+
+is_old_consensus() {
+    local consensus="$1"
+    local consensus_expiry=$(get_valid_until_from_consensus ${consensus})
+    local consensus_expiry_secs=$(date -ud "${consensus_expiry}" '+%s')
+    locat curdate_secs=$(date '+%s')
+    [ ${consensus_expiry_secs} -lt ${curdate_secs} ]
+}
+
+is_tor_consensus() {
+    local consensus="$1"
+    head -n1 ${consensus} | grep -qs "^network-status-version 3"'$'
+}
+
+has_valid_tor_consensus() {
+    [ -e ${TOR_CONSENSUS} ] && is_tor_consensus ${TOR_CONSENSUS}
 }


 has_only_unverified_consensus() {
-    has_consensus && [ ! -e ${TOR_CONSENSUS} ]
+    ! has_valid_tor_consensus && \
+    is_tor_consensus ${TOR_UNVERIFIED_CONSENSUS}
 }


 wait_for_tor_consensus() {
     log "Waiting for the Tor consensus file to contain a valid time interval"
     while :; do
-        if has_consensus; then
+        if has_valid_tor_consensus; then
             break;
+        elif has_only_unverified_consensus; then
+            # If Tor cannot verify the consensus this is probably
+            # because all authority certificates are "expired" due
+            # to a clock far off into the future. In that case
+            # let's set the clock to the release date.
+            if is_old_consensus ${TOR_UNVERIFIED_CONSENSUS}; then
+                log "It seems the clock is so badly off in the future that Tor couldn't verify the consensus. Setting system time to the release date, restarting Tor and fetching a new consensus."
+                date --set="$(release_date)" > /dev/null
+                service tor stop
+                rm -f "${TOR_UNVERIFIED_CONSENSUS}"
+                service tor start
+            else
+                log "Tor couldn't verify its consensus for unknown reasons so we cannot set the time."
+                # FIXME: what now? notify the user? Or should
+                # we optimistically `return` and hope things
+                # work out any way?
+                    exit 1
+            fi
         fi


         inotifywait -q -t ${INOTIFY_TIMEOUT} -e close_write -e moved_to --format %w%f ${TOR_DIR} || :
@@ -114,8 +153,8 @@ restart_tor() {


 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})
+    vstart=$(get_valid_after_from_consensus ${TOR_CONSENSUS})
+    vend=$(get_valid_until_from_consensus ${TOR_CONSENSUS})
     vmid=$(date -ud "${vstart} -0130" +'%F %T')
     log "Tor: valid-after=${vstart} | valid-until=${vend}"


@@ -144,21 +183,6 @@ release_date() {
     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 more than 6 months after the release date"
-            return 0
-    fi
-    return 1
-}
-


### Main

@@ -167,17 +191,6 @@ if tor_is_working; then
     log "Tor has already opened a circuit"
 else
     wait_for_tor_consensus
-    # If Tor cannot verify the consensus this is probably because all
-    # authority certificates are "expired" due to a clock far off into
-    # the future.seen as invalid. In that case let's set the clock to 
-    # the release date.
-    if is_clock_way_off && has_only_unverified_consensus; then
-        log "It seems the clock is so badly off that Tor couldn't verify the consensus. Setting system time to the release date, restarting Tor and retrying the consensus..."
-        date --set="$(release_date)" > /dev/null
-        service tor stop
-        mv -f "${TOR_UNVERIFIED_CONSENSUS}" "${TOR_CONSENSUS}"
-        service tor start
-    fi
     maybe_set_time_from_tor_consensus
 fi