[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