From 319589d126dd5e5fa20ee146f52268c99559f04c Mon Sep 17 00:00:00 2001 From: cyBerta Date: Wed, 3 Jan 2018 15:59:30 +0100 Subject: 8773 preseeded providers implementation for production flavor --- .../se/leap/bitmaskclient/ConfigurationWizard.java | 35 +++- .../java/se/leap/bitmaskclient/ProviderAPI.java | 187 ++++++++++++++------- 2 files changed, 159 insertions(+), 63 deletions(-) (limited to 'app/src/production/java/se/leap/bitmaskclient') diff --git a/app/src/production/java/se/leap/bitmaskclient/ConfigurationWizard.java b/app/src/production/java/se/leap/bitmaskclient/ConfigurationWizard.java index fc2569b2..363fa66c 100644 --- a/app/src/production/java/se/leap/bitmaskclient/ConfigurationWizard.java +++ b/app/src/production/java/se/leap/bitmaskclient/ConfigurationWizard.java @@ -63,8 +63,16 @@ public class ConfigurationWizard extends BaseConfigurationWizard { mConfigState.setAction(SETTING_UP_PROVIDER); Intent provider_API_command = new Intent(this, ProviderAPI.class); Bundle parameters = new Bundle(); - parameters.putString(Provider.MAIN_URL, selected_provider.mainUrl().toString()); - parameters.putString(Provider.CA_CERT_FINGERPRINT, selected_provider.certificatePin()); + parameters.putString(Provider.MAIN_URL, selected_provider.getMainUrl().toString()); + if (selected_provider.hasCertificatePin()){ + parameters.putString(Provider.CA_CERT_FINGERPRINT, selected_provider.certificatePin()); + } + if (selected_provider.hasCaCert()) { + parameters.putString(Provider.CA_CERT, selected_provider.getCaCert()); + } + if (selected_provider.hasDefinition()) { + parameters.putString(Provider.KEY, selected_provider.getDefinition().toString()); + } provider_API_command.setAction(ProviderAPI.SET_UP_PROVIDER); provider_API_command.putExtra(ProviderAPI.PARAMETERS, parameters); @@ -73,18 +81,39 @@ public class ConfigurationWizard extends BaseConfigurationWizard { startService(provider_API_command); } + @Override public void retrySetUpProvider() { cancelSettingUpProvider(); if (!ProviderAPI.caCertDownloaded()) { addAndSelectNewProvider(ProviderAPI.lastProviderMainUrl()); } else { - Intent provider_API_command = new Intent(this, ProviderAPI.class); + showProgressBar(); + adapter.hideAllBut(adapter.indexOf(selected_provider)); + + Intent provider_API_command = new Intent(this, ProviderAPI.class); provider_API_command.setAction(ProviderAPI.SET_UP_PROVIDER); provider_API_command.putExtra(ProviderAPI.RECEIVER_KEY, providerAPI_result_receiver); + Bundle parameters = new Bundle(); + parameters.putString(Provider.MAIN_URL, selected_provider.getMainUrl().toString()); + provider_API_command.putExtra(ProviderAPI.PARAMETERS, parameters); startService(provider_API_command); } } + @Override + public void updateProviderDetails() { + mConfigState.setAction(SETTING_UP_PROVIDER); + Intent provider_API_command = new Intent(this, ProviderAPI.class); + + provider_API_command.setAction(ProviderAPI.UPDATE_PROVIDER_DETAILS); + provider_API_command.putExtra(ProviderAPI.RECEIVER_KEY, providerAPI_result_receiver); + Bundle parameters = new Bundle(); + parameters.putString(Provider.MAIN_URL, selected_provider.getMainUrl().toString()); + provider_API_command.putExtra(ProviderAPI.PARAMETERS, parameters); + + startService(provider_API_command); + } + } diff --git a/app/src/production/java/se/leap/bitmaskclient/ProviderAPI.java b/app/src/production/java/se/leap/bitmaskclient/ProviderAPI.java index fadb03c3..b27c3dca 100644 --- a/app/src/production/java/se/leap/bitmaskclient/ProviderAPI.java +++ b/app/src/production/java/se/leap/bitmaskclient/ProviderAPI.java @@ -22,20 +22,15 @@ import android.util.Pair; import org.json.JSONException; import org.json.JSONObject; -import org.thoughtcrime.ssl.pinning.util.PinningHelper; import java.io.IOException; import java.net.URL; import java.util.List; -import java.util.Scanner; - -import javax.net.ssl.HttpsURLConnection; -import javax.net.ssl.SSLHandshakeException; import okhttp3.OkHttpClient; import se.leap.bitmaskclient.eip.EIP; -import static se.leap.bitmaskclient.R.string.error_io_exception_user_message; +import static se.leap.bitmaskclient.DownloadFailedDialog.DOWNLOAD_ERRORS.ERROR_CORRUPTED_PROVIDER_JSON; import static se.leap.bitmaskclient.R.string.malformed_url; /** @@ -60,72 +55,139 @@ public class ProviderAPI extends ProviderApiBase { @Override protected Bundle setUpProvider(Bundle task) { int progress = 0; - Bundle current_download = new Bundle(); + Bundle currentDownload = new Bundle(); - if (task != null && task.containsKey(Provider.MAIN_URL)) { - last_provider_main_url = task.containsKey(Provider.MAIN_URL) ? + if (task != null) { + //FIXME: this should be refactored in order to avoid static variables all over here + lastProviderMainUrl = task.containsKey(Provider.MAIN_URL) ? task.getString(Provider.MAIN_URL) : ""; - provider_ca_cert_fingerprint = task.containsKey(Provider.CA_CERT_FINGERPRINT) ? + providerCaCertFingerprint = task.containsKey(Provider.CA_CERT_FINGERPRINT) ? task.getString(Provider.CA_CERT_FINGERPRINT) : ""; + providerCaCert = task.containsKey(Provider.CA_CERT) ? + task.getString(Provider.CA_CERT) : + ""; - CA_CERT_DOWNLOADED = PROVIDER_JSON_DOWNLOADED = EIP_SERVICE_JSON_DOWNLOADED = false; + try { + providerDefinition = task.containsKey(Provider.KEY) ? + new JSONObject(task.getString(Provider.KEY)) : + new JSONObject(); + } catch (JSONException e) { + e.printStackTrace(); + providerDefinition = new JSONObject(); + } + providerApiUrl = getApiUrlWithVersion(providerDefinition); + + checkPersistedProviderUpdates(); + currentDownload = validateProviderDetails(); + + //provider details invalid + if (currentDownload.containsKey(ERRORS)) { + return currentDownload; + } + + //no provider certificate available + if (currentDownload.containsKey(RESULT_KEY) && !currentDownload.getBoolean(RESULT_KEY)) { + resetProviderDetails(); + } + + EIP_SERVICE_JSON_DOWNLOADED = false; go_ahead = true; } if (!PROVIDER_JSON_DOWNLOADED) - current_download = getAndSetProviderJson(last_provider_main_url, provider_ca_cert_fingerprint); - if (PROVIDER_JSON_DOWNLOADED || (current_download.containsKey(RESULT_KEY) && current_download.getBoolean(RESULT_KEY))) { + currentDownload = getAndSetProviderJson(lastProviderMainUrl, providerCaCert, providerDefinition); + if (PROVIDER_JSON_DOWNLOADED || (currentDownload.containsKey(RESULT_KEY) && currentDownload.getBoolean(RESULT_KEY))) { broadcastProgress(progress++); PROVIDER_JSON_DOWNLOADED = true; if (!CA_CERT_DOWNLOADED) - current_download = downloadCACert(); - if (CA_CERT_DOWNLOADED || (current_download.containsKey(RESULT_KEY) && current_download.getBoolean(RESULT_KEY))) { + currentDownload = downloadCACert(); + if (CA_CERT_DOWNLOADED || (currentDownload.containsKey(RESULT_KEY) && currentDownload.getBoolean(RESULT_KEY))) { broadcastProgress(progress++); CA_CERT_DOWNLOADED = true; - current_download = getAndSetEipServiceJson(); - if (current_download.containsKey(RESULT_KEY) && current_download.getBoolean(RESULT_KEY)) { + currentDownload = getAndSetEipServiceJson(); + if (currentDownload.containsKey(RESULT_KEY) && currentDownload.getBoolean(RESULT_KEY)) { broadcastProgress(progress++); EIP_SERVICE_JSON_DOWNLOADED = true; } } } - return current_download; + return currentDownload; } - private Bundle getAndSetProviderJson(String provider_main_url, String provider_ca_cert_fingerprint) { + + private Bundle validateProviderDetails() { + Bundle result = validateCertificateForProvider(providerCaCert, providerDefinition, lastProviderMainUrl); + + //invalid certificate or no certificate + if (result.containsKey(ERRORS) || (result.containsKey(RESULT_KEY) && !result.getBoolean(RESULT_KEY)) ) { + return result; + } + + //valid certificate: skip download, save loaded provider CA cert and provider definition directly + try { + preferences.edit().putString(Provider.KEY, providerDefinition.toString()). + putBoolean(Constants.PROVIDER_ALLOW_ANONYMOUS, providerDefinition.getJSONObject(Provider.SERVICE).getBoolean(Constants.PROVIDER_ALLOW_ANONYMOUS)). + putBoolean(Constants.PROVIDER_ALLOWED_REGISTERED, providerDefinition.getJSONObject(Provider.SERVICE).getBoolean(Constants.PROVIDER_ALLOWED_REGISTERED)). + putString(Provider.CA_CERT, providerCaCert).commit(); + CA_CERT_DOWNLOADED = true; + PROVIDER_JSON_DOWNLOADED = true; + result.putBoolean(RESULT_KEY, true); + } catch (JSONException e) { + e.printStackTrace(); + result.putBoolean(RESULT_KEY, false); + result = setErrorResult(result, getString(R.string.warning_corrupted_provider_details), ERROR_CORRUPTED_PROVIDER_JSON.toString()); + } + + return result; + } + + private void checkPersistedProviderUpdates() { + String providerDomain = getProviderDomain(providerDefinition); + if (hasUpdatedProviderDetails(providerDomain)) { + providerCaCert = getPersistedProviderCA(providerDomain); + providerDefinition = getPersistedProviderDefinition(providerDomain); + providerCaCertFingerprint = getPersistedCaCertFingerprint(providerDomain); + providerApiUrl = getApiUrlWithVersion(providerDefinition); + } + } + + + private Bundle getAndSetProviderJson(String providerMainUrl, String caCert, JSONObject providerDefinition) { Bundle result = new Bundle(); if (go_ahead) { - String provider_dot_json_string; - if(provider_ca_cert_fingerprint.isEmpty()) - provider_dot_json_string = downloadWithCommercialCA(provider_main_url + "/provider.json"); - else - provider_dot_json_string = downloadWithCommercialCA(provider_main_url + "/provider.json", provider_ca_cert_fingerprint); + String providerDotJsonString; + if(providerDefinition.length() == 0 || caCert.isEmpty()) + providerDotJsonString = downloadWithCommercialCA(providerMainUrl + "/provider.json"); + else { + providerDotJsonString = downloadFromApiUrlWithProviderCA("/provider.json", caCert, providerDefinition); + } - if (!isValidJson(provider_dot_json_string)) { + if (!isValidJson(providerDotJsonString)) { result.putString(ERRORS, getString(malformed_url)); result.putBoolean(RESULT_KEY, false); return result; } try { - JSONObject provider_json = new JSONObject(provider_dot_json_string); - provider_api_url = provider_json.getString(Provider.API_URL) + "/" + provider_json.getString(Provider.API_VERSION); - String name = provider_json.getString(Provider.NAME); + JSONObject providerJson = new JSONObject(providerDotJsonString); + String providerDomain = providerJson.getString(Provider.DOMAIN); + providerApiUrl = getApiUrlWithVersion(providerJson); + String name = providerJson.getString(Provider.NAME); //TODO setProviderName(name); - preferences.edit().putString(Provider.KEY, provider_json.toString()).commit(); - preferences.edit().putBoolean(Constants.PROVIDER_ALLOW_ANONYMOUS, provider_json.getJSONObject(Provider.SERVICE).getBoolean(Constants.PROVIDER_ALLOW_ANONYMOUS)).commit(); - preferences.edit().putBoolean(Constants.PROVIDER_ALLOWED_REGISTERED, provider_json.getJSONObject(Provider.SERVICE).getBoolean(Constants.PROVIDER_ALLOWED_REGISTERED)).commit(); - + preferences.edit().putString(Provider.KEY, providerJson.toString()). + putBoolean(Constants.PROVIDER_ALLOW_ANONYMOUS, providerJson.getJSONObject(Provider.SERVICE).getBoolean(Constants.PROVIDER_ALLOW_ANONYMOUS)). + putBoolean(Constants.PROVIDER_ALLOWED_REGISTERED, providerJson.getJSONObject(Provider.SERVICE).getBoolean(Constants.PROVIDER_ALLOWED_REGISTERED)). + putString(Provider.KEY + "." + providerDomain, providerJson.toString()).commit(); result.putBoolean(RESULT_KEY, true); } catch (JSONException e) { //TODO Error message should be contained in that provider_dot_json_string - String reason_to_fail = pickErrorMessage(provider_dot_json_string); + String reason_to_fail = pickErrorMessage(providerDotJsonString); result.putString(ERRORS, reason_to_fail); result.putBoolean(RESULT_KEY, false); } @@ -176,7 +238,7 @@ public class ProviderAPI extends ProviderApiBase { String cert_string = downloadWithProviderCA(new_cert_string_url.toString()); - if (cert_string == null || cert_string.isEmpty() || ConfigHelper.checkErroneousDownload(cert_string)) + if (ConfigHelper.checkErroneousDownload(cert_string)) return false; else return loadCertificate(cert_string); @@ -194,13 +256,16 @@ public class ProviderAPI extends ProviderApiBase { private Bundle downloadCACert() { Bundle result = new Bundle(); try { - JSONObject provider_json = new JSONObject(preferences.getString(Provider.KEY, "")); - String ca_cert_url = provider_json.getString(Provider.CA_CERT_URI); - String cert_string = downloadWithCommercialCA(ca_cert_url); + JSONObject providerJson = new JSONObject(preferences.getString(Provider.KEY, "")); + String caCertUrl = providerJson.getString(Provider.CA_CERT_URI); + String providerDomain = providerJson.getString(Provider.DOMAIN); + + String cert_string = downloadWithCommercialCA(caCertUrl); result.putBoolean(RESULT_KEY, true); if (validCertificate(cert_string) && go_ahead) { preferences.edit().putString(Provider.CA_CERT, cert_string).commit(); + preferences.edit().putString(Provider.CA_CERT + "." + providerDomain, cert_string).commit(); result.putBoolean(RESULT_KEY, true); } else { String reason_to_fail = pickErrorMessage(cert_string); @@ -216,29 +281,6 @@ public class ProviderAPI extends ProviderApiBase { return result; } - //TODO: refactor with ticket #8773 - private String downloadWithCommercialCA(String url_string, String ca_cert_fingerprint) { - String result = ""; - - int seconds_of_timeout = 2; - String[] pins = new String[] {ca_cert_fingerprint}; - try { - URL url = new URL(url_string); - HttpsURLConnection connection = PinningHelper.getPinnedHttpsURLConnection(Dashboard.getContext(), pins, url); - connection.setConnectTimeout(seconds_of_timeout * 1000); - if (!LeapSRPSession.getToken().isEmpty()) - connection.addRequestProperty(LeapSRPSession.AUTHORIZATION_HEADER, "Token token=" + LeapSRPSession.getToken()); - result = new Scanner(connection.getInputStream()).useDelimiter("\\A").next(); - } catch (IOException e) { - if(e instanceof SSLHandshakeException) - result = formatErrorMessage(R.string.error_security_pinnedcertificate); - else - result = formatErrorMessage(error_io_exception_user_message); - } - - return result; - } - /** * Tries to download the contents of the provided url using commercially validated CA certificate from chosen provider. * @@ -273,6 +315,31 @@ public class ProviderAPI extends ProviderApiBase { return responseString; } + + /** + * Tries to download the contents of the provided url using not commercially validated CA certificate from chosen provider. + * + * @return an empty string if it fails, the response body if not. + */ + protected String downloadFromApiUrlWithProviderCA(String path, String caCert, JSONObject providerDefinition) { + String responseString; + JSONObject errorJson = new JSONObject(); + String baseUrl = getApiUrl(providerDefinition); + OkHttpClient okHttpClient = initSelfSignedCAHttpClient(errorJson, caCert); + if (okHttpClient == null) { + return errorJson.toString(); + } + + String urlString = baseUrl + path; + List> headerArgs = getAuthorizationHeader(); + responseString = sendGetStringToServer(urlString, headerArgs, okHttpClient); + + return responseString; + + } + + + /** * Tries to download the contents of the provided url using not commercially validated CA certificate from chosen provider. * -- cgit v1.2.3