The new control port handling in Tor Browser 13 breaks a Tails security feature
Tails has a filtering control port proxy called onion-grater
. It is useful in Tails since it has a system-wide tor
instance shared among many applications and users, and we want to be able to prevent information leaks (and other dangerous actions) over the control port between the tor
clients.
In particular, it has a feature where a client that both uses the control port and socks port and subscribes to STREAM
events only get those that "belong" to itself. This feature was tailored to Tor Browser, so in case it is compromised it will not see other applications' stream usage. This feature also used to intercept getinfo circuit-status
and only list the circuits that are attached to one of its streams. This worked fine for Tor Browser <= 12.5.
With Tor Browser 13 things got complicated for filtering circuit information (filtering stream information is still doable just like before). The new implementation also subscribes to CIRC
events and keeps track of circuits throughout their lives, and expects to have learned about a circuit being built before it sees that circuit referenced by some stream event, otherwise it throws an error and discards that event (which breaks the circuit view in Tor Browser). This makes sense, but onion-grater
ends up with a "chicken and egg" problem: in general, when a circuit is built we don't know what it is going to be used for, we only know that for sure when a stream has been attached to it. So in order to know if a CIRC n BUILT
event (which is what Tor Browser uses to start tracking it) should be forwarded or discarded we need a STREAM
event from the future.
This could be fixed in onion-grater
by saving CIRC n BUILT
events and injecting them before forwarding the STREAM
event that establishes the circuit's ownership, but we already are concerned about onion-grater
's complexity and do not want to do this.
I believe this could be solved in Tor Browser if it didn't immediately throw an error when it receives a STREAM
event that refers to an "unknown" circuit, but first tries a getinfo circuit-status
and accepts the STREAM
event if it learned about the circuit. This would make the implementation almost degenerate to the pre-13 polling, which is fine as far as Tails is concerned. But I say "almost" because there is a subtle issue here: pre-13 we would always poll so we would never have outdated info, but with 13 the info we have about circuits is kept and reused, and only invalidated once the corresponding CIRC n CLOSE
event is received. So we would have to allow (and filter!) CIRC
events too, but it would be pretty wonky:
- Tor Browser subscribes to both
CIRC
andSTREAM
events, filtered byonion-grater
- Let's say circuit n is built, so a
CIRC n BUILT
event is sent to Tor Borwser viaonion-grater
- But
onion-grater
sees that Tor Browser doesn't "own" any stream that uses circuit n, so theCIRC n BUILT
event is dropped, so it is "unknown" to Tor Browser - Later Tor Browser opens a stream that is attached to circuit n
-
onion-grater
forwards theSTREAM
event for the stream using circuit n to Tor Browser - Tor Browser notices that circuit n is "unknown", but it does what I proposed above and polls with
getinfo circuit-status
instead of discarding it -
onion-grater
interceptsgetinfo circuit-status
and given what it learned in step 5 it knows that circuit n is owned by Tor Browser so it can be included in the response - Tor Browser gets the response and now knows about circuit n so it accepts the
STREAM
event - Eventually, after Tor Browser is done with the stream and
tor
decides to close circuit n, aCIRC n CLOSE
is issued,onion-grater
forwards it to Tor Browser, and it stops tracking circuit n
I guess it works, but it feels pretty fragile and ugly. So I am not sure I want you to implement the fix above. What do you think? Perhaps you have some other, saner solution that I have missed?