From 5e4003572133c4bd4e31c831d6bf3729425aca29 Mon Sep 17 00:00:00 2001 From: cyBerta Date: Mon, 8 Nov 2021 01:02:11 +0100 Subject: Don't allow fallback tor mechanism for failed geoip service calls. --- .../providersetup/ProviderApiManager.java | 18 ++--- .../bitmaskclient/eip/ProviderApiManagerTest.java | 34 +++++++++ .../BackendMockResponses/BackendMockProvider.java | 3 + ...viceNotReachableTorFallbackBackendResponse.java | 89 ++++++++++++++++++++++ 4 files changed, 135 insertions(+), 9 deletions(-) create mode 100644 app/src/test/java/se/leap/bitmaskclient/testutils/BackendMockResponses/GeoIpServiceNotReachableTorFallbackBackendResponse.java (limited to 'app/src') diff --git a/app/src/production/java/se/leap/bitmaskclient/providersetup/ProviderApiManager.java b/app/src/production/java/se/leap/bitmaskclient/providersetup/ProviderApiManager.java index dfd1bfbf..5416b1f8 100644 --- a/app/src/production/java/se/leap/bitmaskclient/providersetup/ProviderApiManager.java +++ b/app/src/production/java/se/leap/bitmaskclient/providersetup/ProviderApiManager.java @@ -245,7 +245,7 @@ public class ProviderApiManager extends ProviderApiManagerBase { try { URL geoIpUrl = provider.getGeoipUrl().getUrl(); - String geoipJsonString = downloadFromUrlWithProviderCA(geoIpUrl.toString(), provider); + String geoipJsonString = downloadFromUrlWithProviderCA(geoIpUrl.toString(), provider, false); if (DEBUG_MODE) { VpnStatus.logDebug("[API] MENSHEN JSON: " + geoipJsonString); } @@ -292,14 +292,14 @@ public class ProviderApiManager extends ProviderApiManagerBase { } private String downloadWithCommercialCA(String stringUrl, Provider provider) { - return downloadWithCommercialCA(stringUrl, provider, 0); + return downloadWithCommercialCA(stringUrl, provider, true); } /** * Tries to download the contents of the provided url using commercially validated CA certificate from chosen provider. * */ - private String downloadWithCommercialCA(String stringUrl, Provider provider, int tries) { + private String downloadWithCommercialCA(String stringUrl, Provider provider, boolean allowRetry) { String responseString; JSONObject errorJson = new JSONObject(); @@ -326,13 +326,13 @@ public class ProviderApiManager extends ProviderApiManagerBase { } try { - if (tries == 0 && + if (allowRetry && responseString != null && responseString.contains(ERRORS) && TorStatusObservable.getStatus() == OFF && startTorProxy() ) { - return downloadWithCommercialCA(stringUrl, provider, 1); + return downloadWithCommercialCA(stringUrl, provider, false); } } catch (InterruptedException | IllegalStateException | TimeoutException e) { e.printStackTrace(); @@ -353,10 +353,10 @@ public class ProviderApiManager extends ProviderApiManagerBase { } private String downloadFromUrlWithProviderCA(String urlString, Provider provider) { - return downloadFromUrlWithProviderCA(urlString, provider, 0); + return downloadFromUrlWithProviderCA(urlString, provider, true); } - private String downloadFromUrlWithProviderCA(String urlString, Provider provider, int tries) { + private String downloadFromUrlWithProviderCA(String urlString, Provider provider, boolean allowRetry) { String responseString; JSONObject errorJson = new JSONObject(); OkHttpClient okHttpClient = clientGenerator.initSelfSignedCAHttpClient(provider.getCaCert(), getProxyPort(), errorJson); @@ -368,13 +368,13 @@ public class ProviderApiManager extends ProviderApiManagerBase { responseString = sendGetStringToServer(urlString, headerArgs, okHttpClient); try { - if (tries == 0 && + if (allowRetry && responseString != null && responseString.contains(ERRORS) && TorStatusObservable.getStatus() == OFF && startTorProxy() ) { - return downloadFromUrlWithProviderCA(urlString, provider, 1); + return downloadFromUrlWithProviderCA(urlString, provider, false); } } catch (InterruptedException | IllegalStateException | TimeoutException e) { e.printStackTrace(); diff --git a/app/src/test/java/se/leap/bitmaskclient/eip/ProviderApiManagerTest.java b/app/src/test/java/se/leap/bitmaskclient/eip/ProviderApiManagerTest.java index 3411274a..d93d8553 100644 --- a/app/src/test/java/se/leap/bitmaskclient/eip/ProviderApiManagerTest.java +++ b/app/src/test/java/se/leap/bitmaskclient/eip/ProviderApiManagerTest.java @@ -71,6 +71,7 @@ import static se.leap.bitmaskclient.testutils.BackendMockResponses.BackendMockPr import static se.leap.bitmaskclient.testutils.BackendMockResponses.BackendMockProvider.TestBackendErrorCase.ERROR_CASE_UPDATED_CERTIFICATE; import static se.leap.bitmaskclient.testutils.BackendMockResponses.BackendMockProvider.TestBackendErrorCase.ERROR_DNS_RESUOLUTION_TOR_FALLBACK; import static se.leap.bitmaskclient.testutils.BackendMockResponses.BackendMockProvider.TestBackendErrorCase.ERROR_GEOIP_SERVICE_IS_DOWN; +import static se.leap.bitmaskclient.testutils.BackendMockResponses.BackendMockProvider.TestBackendErrorCase.ERROR_GEOIP_SERVICE_IS_DOWN_TOR_FALLBACK; import static se.leap.bitmaskclient.testutils.BackendMockResponses.BackendMockProvider.TestBackendErrorCase.NO_ERROR; import static se.leap.bitmaskclient.testutils.BackendMockResponses.BackendMockProvider.TestBackendErrorCase.NO_ERROR_API_V4; import static se.leap.bitmaskclient.testutils.MockHelper.mockBundle; @@ -515,6 +516,37 @@ public class ProviderApiManagerTest { Provider provider = getConfiguredProvider(); mockFingerprintForCertificate("a5244308a1374709a9afce95e3ae47c1b44bc2398c0a70ccbf8b3a8a97f29494"); mockProviderApiConnector(ERROR_GEOIP_SERVICE_IS_DOWN); + mockPreferences.edit().putBoolean(USE_BRIDGES, false).putBoolean(USE_TOR, false).commit(); + providerApiManager = new ProviderApiManager(mockPreferences, mockResources, mockClientGenerator(), new TestProviderApiServiceCallback()); + + Bundle expectedResult = mockBundle(); + expectedResult.putBoolean(EIP_ACTION_START, true); + expectedResult.putBoolean(BROADCAST_RESULT_KEY, false); + expectedResult.putParcelable(PROVIDER_KEY, provider); + + Intent providerApiCommand = mockIntent(); + + providerApiCommand.setAction(ProviderAPI.DOWNLOAD_GEOIP_JSON); + Bundle extrasBundle = mockBundle(); + extrasBundle.putBoolean(EIP_ACTION_START, true); + providerApiCommand.putExtra(ProviderAPI.RECEIVER_KEY, mockResultReceiver(INCORRECTLY_DOWNLOADED_GEOIP_JSON, expectedResult)); + providerApiCommand.putExtra(PROVIDER_KEY, provider); + providerApiCommand.putExtra(PARAMETERS, extrasBundle); + + providerApiManager.handleIntent(providerApiCommand); + + } + + @Test + public void test_handleIntentGetGeoip_serviceDown_torNotStarted() throws IOException, NoSuchAlgorithmException, CertificateEncodingException, JSONException, TimeoutException, InterruptedException { + if ("insecure".equals(BuildConfig.FLAVOR_implementation)) { + return; + } + + mockTorStatusObservable(null); + Provider provider = getConfiguredProvider(); + mockFingerprintForCertificate("a5244308a1374709a9afce95e3ae47c1b44bc2398c0a70ccbf8b3a8a97f29494"); + mockProviderApiConnector(ERROR_GEOIP_SERVICE_IS_DOWN_TOR_FALLBACK); providerApiManager = new ProviderApiManager(mockPreferences, mockResources, mockClientGenerator(), new TestProviderApiServiceCallback()); Bundle expectedResult = mockBundle(); @@ -532,6 +564,8 @@ public class ProviderApiManagerTest { providerApiCommand.putExtra(PARAMETERS, extrasBundle); providerApiManager.handleIntent(providerApiCommand); + // also assert that Tor was not allowed to start + assertEquals(-1, TorStatusObservable.getProxyPort()); } diff --git a/app/src/test/java/se/leap/bitmaskclient/testutils/BackendMockResponses/BackendMockProvider.java b/app/src/test/java/se/leap/bitmaskclient/testutils/BackendMockResponses/BackendMockProvider.java index 27401807..280aa5a1 100644 --- a/app/src/test/java/se/leap/bitmaskclient/testutils/BackendMockResponses/BackendMockProvider.java +++ b/app/src/test/java/se/leap/bitmaskclient/testutils/BackendMockResponses/BackendMockProvider.java @@ -33,6 +33,7 @@ public class BackendMockProvider { ERROR_CASE_MICONFIGURED_PROVIDER, ERROR_CASE_FETCH_EIP_SERVICE_CERTIFICATE_INVALID, ERROR_GEOIP_SERVICE_IS_DOWN, + ERROR_GEOIP_SERVICE_IS_DOWN_TOR_FALLBACK, ERROR_NO_RESPONSE_BODY, // => NullPointerException ERROR_DNS_RESOLUTION_ERROR, // => UnkownHostException ERROR_SOCKET_TIMEOUT, // => SocketTimeoutException @@ -72,6 +73,8 @@ public class BackendMockProvider { case ERROR_GEOIP_SERVICE_IS_DOWN: new GeoIpServiceIsDownBackendResponse(); break; + case ERROR_GEOIP_SERVICE_IS_DOWN_TOR_FALLBACK: + new GeoIpServiceNotReachableTorFallbackBackendResponse(); case ERROR_DNS_RESUOLUTION_TOR_FALLBACK: new TorFallbackBackendResponse(); break; diff --git a/app/src/test/java/se/leap/bitmaskclient/testutils/BackendMockResponses/GeoIpServiceNotReachableTorFallbackBackendResponse.java b/app/src/test/java/se/leap/bitmaskclient/testutils/BackendMockResponses/GeoIpServiceNotReachableTorFallbackBackendResponse.java new file mode 100644 index 00000000..02aa31fa --- /dev/null +++ b/app/src/test/java/se/leap/bitmaskclient/testutils/BackendMockResponses/GeoIpServiceNotReachableTorFallbackBackendResponse.java @@ -0,0 +1,89 @@ +/** + * Copyright (c) 2018 LEAP Encryption Access Project and contributers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package se.leap.bitmaskclient.testutils.BackendMockResponses; + +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; + +import java.io.IOException; +import java.net.ConnectException; + +import static se.leap.bitmaskclient.testutils.TestSetupHelper.getInputAsString; + +/** + * Created by cyberta on 10.01.18. + */ + +public class GeoIpServiceNotReachableTorFallbackBackendResponse extends BaseBackendResponse { + public GeoIpServiceNotReachableTorFallbackBackendResponse() throws IOException { + super(); + } + int requestAttempt = 0; + + @Override + public Answer getAnswerForRequestStringFromServer() { + return new Answer() { + @Override + public String answer(InvocationOnMock invocation) throws Throwable { + String url = (String) invocation.getArguments()[0]; + + if (url.contains("/provider.json")) { + //download provider json + return getInputAsString(getClass().getClassLoader().getResourceAsStream("riseup.net.json")); + } else if (url.contains("/ca.crt")) { + //download provider ca cert + return getInputAsString(getClass().getClassLoader().getResourceAsStream("riseup.net.pem")); + } else if (url.contains("config/eip-service.json")) { + // download provider service json containing gateways, locations and openvpn settings + return getInputAsString(getClass().getClassLoader().getResourceAsStream("riseup.service.json")); + } else if (url.contains(":9001/json")) { + if (requestAttempt == 0) { + // download geoip json, containing a sorted list of gateways + requestAttempt++; + throw new ConnectException("Failed to connect to api.black.riseup.net/198.252.153.107:9001"); + } else { + // assumtion: 2. connection attempt has been made with proxy on, which is not allowed + // this branch should never be called otherwise you have found a bug + return getInputAsString(getClass().getClassLoader().getResourceAsStream("riseup.geoip.json")); + } + } + return null; + } + }; + } + + @Override + public Answer getAnswerForCanConnect() { + return new Answer() { + @Override + public Boolean answer(InvocationOnMock invocation) throws Throwable { + return true; + } + }; + } + + @Override + public Answer getAnswerForDelete() { + return new Answer() { + @Override + public Boolean answer(InvocationOnMock invocation) throws Throwable { + return true; + } + }; + } + +} -- cgit v1.2.3