[tor-bugs] #21863 [Applications/Tor Browser]: Ensure proxy safety on Android
Tor Bug Tracker & Wiki
blackhole at torproject.org
Fri Jun 22 18:05:11 UTC 2018
#21863: Ensure proxy safety on Android
-------------------------------------------------+-------------------------
Reporter: gk | Owner: sysrqb
Type: defect | Status:
| accepted
Priority: Very High | Milestone:
Component: Applications/Tor Browser | Version:
Severity: Normal | Resolution:
Keywords: tbb-mobile, tbb-7.0-must, tbb- | Actual Points:
proxy-bypass, TorBrowserTeam201806 |
Parent ID: #5709 | Points:
Reviewer: | Sponsor:
| Sponsor4
-------------------------------------------------+-------------------------
Comment (by sysrqb):
In addition, I went down the rabbit hole: "What does Android *do* when you
ask it to establish a connection using a proxy". The result of this long
and windy path is "it seems safe", but only when the Java/Dalvik/ART VM
uses the default AOSP configuration.
I'll attempt succinctly explaining proxy safety on Android here, but we
should seriously consider using Necko for all networking calls in the
future (which means exposing necko via jni). I believe GeckoView is
already considering this - but how hard could it be? :)
As an example, let's look at [[https://dxr.mozilla.org/mozilla-
esr60/source/mobile/android/base/java/org/mozilla/gecko/updater/UpdateService.java#388|Fennec's
Updater download]] logic. Fennec uses the wrapper
`org.mozilla.gecko.util.ProxySelector.openConnectionWithProxy(java.net.URI)`
most places. This provides [[https://dxr.mozilla.org/mozilla-
esr60/source/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/ProxySelector.java#31|a
central location]] where Fennec decides if a connection should be
establishing via a proxy or if the connection should be direct. When
`openConnectionWithProxy()` is called, Fennec asks the Dalvik/Art system
for the currently configured proxies. From the list of returned proxies,
Fennec chooses the first one. (Unfortunately, it seems like Fennec doesn't
configure a proxy, it must be configured somewhere else).
`openConnectionWithProxy()` calls
`java.net.URL::openConnection(java.net.Proxy)`. This is the entrance into
the Android SDK.
[[https://developer.android.com/reference/java/net/URL.html#openConnection(java.net.Proxy)|openConnection()]]
establishes the
[[https://android.googlesource.com/platform/libcore/+/master/ojluni/src/main/java/java/net/URL.java#1038|initial
connection]] by passing the request onto another library. The protocol
[[https://android.googlesource.com/platform/libcore/+/master/ojluni/src/main/java/java/net/URL.java#226|handler]]
is dynamically found when the URL object is
[[https://android.googlesource.com/platform/libcore/+/master/ojluni/src/main/java/java/net/URL.java#604|instantiated]]
(and statically cached after that). It retrieves a list of protocol
handlers from the
[[https://android.googlesource.com/platform/libcore/+/master/ojluni/src/main/java/java/net/URL.java#1190|system
properties]]. This list may be
[[https://developer.android.com/reference/java/lang/System.html#getProperties()|empty]].
If the list is empty, or the system doesn't have a handler for the
specific protocol, then it checks a set of
[[https://android.googlesource.com/platform/libcore/+/master/ojluni/src/main/java/java/net/URL.java#1225|default
handlers]]. For the sake of simplicity, let's assume we're requesting a
connection using the `http` protocol. That protocol defaults to using
[[https://android.googlesource.com/platform/libcore/+/master/ojluni/src/main/java/java/net/URL.java#1286|com.android.okhttp.HttpHandler]].
At this point, we go into the `okhttp` library with a call into its
[[https://android.googlesource.com/platform/libcore/+/master/ojluni/src/main/java/java/net/URL.java#1055|URLStreamHandler:openConnection()]]
method. This brings us
[[https://android.googlesource.com/platform/external/okhttp/+/master/android/main/java/com/squareup/okhttp/HttpHandler.java#68|here]].
The `HttpHandler` instantiates a helper `OkHttpClient` client which is
responsible for the connection. The proxy is
[[https://android.googlesource.com/platform/external/okhttp/+/master/android/main/java/com/squareup/okhttp/HttpHandler.java#93|explicitly]]
[[https://android.googlesource.com/platform/external/okhttp/+/master/android/main/java/com/squareup/okhttp/HttpHandler.java#68|set]],
as well. From here, the newly instantiated connection is returned down the
stack. The requested connect is established when the caller requests the
[[https://android.googlesource.com/platform/external/okhttp/+/master
/okhttp-
urlconnection/src/main/java/com/squareup/okhttp/internal/huc/HttpURLConnectionImpl.java#239|InputStream]]
or the
[[https://android.googlesource.com/platform/external/okhttp/+/master
/okhttp-
urlconnection/src/main/java/com/squareup/okhttp/internal/huc/HttpURLConnectionImpl.java#257|OutputStream]].
At this time, a HttpEngine object is
[[https://android.googlesource.com/platform/external/okhttp/+/master
/okhttp-
urlconnection/src/main/java/com/squareup/okhttp/internal/huc/HttpURLConnectionImpl.java#385|instantiated]]
where a new StreamAllocation is
[[https://android.googlesource.com/platform/external/okhttp/+/master/okhttp/src/main/java/com/squareup/okhttp/internal/http/HttpEngine.java#180|created]].
The `StreamAllocation` is important because this is the next location
where the Proxy information is handed-off from one layer to the next, and
it is
[[https://android.googlesource.com/platform/external/okhttp/+/master/okhttp/src/main/java/com/squareup/okhttp/internal/http/HttpEngine.java#972|
bundled]] into an
[[https://android.googlesource.com/platform/external/okhttp/+/master/okhttp/src/main/java/com/squareup/okhttp/Address.java|Address]].
After creating the `HttpHandler`,
[[https://android.googlesource.com/platform/external/okhttp/+/master
/okhttp-
urlconnection/src/main/java/com/squareup/okhttp/internal/huc/HttpURLConnectionImpl.java#450|execute()]]
is called, next calling
[[https://android.googlesource.com/platform/external/okhttp/+/master/okhttp/src/main/java/com/squareup/okhttp/internal/http/HttpEngine.java#187|sendRequest()]]
where
[[https://android.googlesource.com/platform/external/okhttp/+/master/okhttp/src/main/java/com/squareup/okhttp/internal/http/HttpEngine.java#279|connect()]]
[[https://android.googlesource.com/platform/external/okhttp/+/master/okhttp/src/main/java/com/squareup/okhttp/internal/http/HttpEngine.java#224|
is called]]. Then the previously-created StreamAllocation finds the
"route" it will use for its
[[https://android.googlesource.com/platform/external/okhttp/+/master/okhttp/src/main/java/com/squareup/okhttp/internal/http/StreamAllocation.java#95|new
"stream"]] (socket connection). The
[[https://android.googlesource.com/platform/external/okhttp/+/master/okhttp/src/main/java/com/squareup/okhttp/internal/http/StreamAllocation.java#175|route]]
is another abstraction where the Address and Proxy are
[[https://android.googlesource.com/platform/external/okhttp/+/master/okhttp/src/main/java/com/squareup/okhttp/Route.java|bundled]]
together. Next,
[[https://android.googlesource.com/platform/external/okhttp/+/master/okhttp/src/main/java/com/squareup/okhttp/internal/http/StreamAllocation.java#184|connect()]]
is called on the connection where the first socket is
[[https://android.googlesource.com/platform/external/okhttp/+/master/okhttp/src/main/java/com/squareup/okhttp/internal/io/RealConnection.java#109|created]]
via
[[https://android.googlesource.com/platform/libcore/+/master/ojluni/src/main/java/java/net/Socket.java#89|Socket]].
`Socket` then
[[https://android.googlesource.com/platform/libcore/+/master/ojluni/src/main/java/java/net/Socket.java#145
|hands-off]] to `SocksSocketImpl`. After the SocksSocket s created,
`OkHttp` calls a platform-specific
[[https://android.googlesource.com/platform/external/okhttp/+/master/okhttp/src/main/java/com/squareup/okhttp/internal/io/RealConnection.java#141|connectSocket()]]
method. On Android, this effectively calls
[[https://android.googlesource.com/platform/external/okhttp/+/master/okhttp/src/main/java/com/squareup/okhttp/internal/Platform.java#230|connect()]]
directly on the socket.
[[https://android.googlesource.com/platform/libcore/+/master/ojluni/src/main/java/java/net/Socket.java#621|Socket]]
calls
[[https://android.googlesource.com/platform/libcore/+/master/ojluni/src/main/java/java/net/SocksSocketImpl.java#327|SocksSocketImpl]]
where the connection is
[[https://android.googlesource.com/platform/libcore/+/master/ojluni/src/main/java/java/net/SocksSocketImpl.java#78|actually
established]] with the proxy server. At this point, the SOCKS handshake
continues
[[https://android.googlesource.com/platform/libcore/+/master/ojluni/src/main/java/java/net/SocksSocketImpl.java#460|synchronously]].
If `connect()` does not throw an exception and it returns, then the
proxied connection was successfully established.
My main concern with this is the lack of control we have over this logic.
I didn't audit the code from 5 years ago, and this could change at any
time. Currently, it seems like Android respects a request by an app to use
a proxy for a connection, but that could break at any time.
--
Ticket URL: <https://trac.torproject.org/projects/tor/ticket/21863#comment:24>
Tor Bug Tracker & Wiki <https://trac.torproject.org/>
The Tor Project: anonymity online
More information about the tor-bugs
mailing list