From c2bdea1c91060ebae40093e6cee5146aec539fa2 Mon Sep 17 00:00:00 2001 From: theScrabi Date: Thu, 21 Jun 2018 09:44:07 +0200 Subject: [PATCH] make https connection work with new library --- .../android/lib/common/OwnCloudClient.java | 63 ++++++++++--------- .../android/lib/common/http/HttpClient.java | 39 ++++++++++-- .../lib/common/network/NetworkUtils.java | 2 +- .../operations/RemoteOperationResult.java | 29 ++++++--- .../status/GetRemoteStatusOperation.java | 14 ++++- 5 files changed, 99 insertions(+), 48 deletions(-) diff --git a/src/com/owncloud/android/lib/common/OwnCloudClient.java b/src/com/owncloud/android/lib/common/OwnCloudClient.java index dd01ddb4..642586c8 100644 --- a/src/com/owncloud/android/lib/common/OwnCloudClient.java +++ b/src/com/owncloud/android/lib/common/OwnCloudClient.java @@ -1,23 +1,23 @@ /* ownCloud Android Library is available under MIT license * Copyright (C) 2018 ownCloud GmbH. * Copyright (C) 2012 Bartek Przybylski - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * @@ -35,6 +35,7 @@ import com.owncloud.android.lib.common.authentication.OwnCloudCredentialsFactory import com.owncloud.android.lib.common.authentication.OwnCloudCredentialsFactory.OwnCloudAnonymousCredentials; import com.owncloud.android.lib.common.http.methods.HttpBaseMethod; import com.owncloud.android.lib.common.network.RedirectionPath; +import com.owncloud.android.lib.common.operations.RemoteOperationResult; import com.owncloud.android.lib.common.utils.Log_OC; import com.owncloud.android.lib.resources.status.OwnCloudVersion; @@ -247,7 +248,7 @@ public class OwnCloudClient extends HttpClient { preventCrashDueToInvalidPort(method); Log_OC.d(TAG + " #" + mInstanceNumber, "REQUEST " + - method.getName() + " " + method.getPath()); + method.getName() + " " + method.getPath()); //logCookiesAtRequest(method.getRequestHeaders(), "before"); //logCookiesAtState("before"); @@ -292,6 +293,8 @@ public class OwnCloudClient extends HttpClient { } while (repeatWithFreshCredentials); return status; + + } private void checkFirstRedirection(HttpMethod method) { @@ -338,10 +341,10 @@ public class OwnCloudClient extends HttpClient { RedirectionPath result = new RedirectionPath(status, MAX_REDIRECTIONS_COUNT); while (redirectionsCount < MAX_REDIRECTIONS_COUNT && - (status == HttpStatus.SC_MOVED_PERMANENTLY || - status == HttpStatus.SC_MOVED_TEMPORARILY || - status == HttpStatus.SC_TEMPORARY_REDIRECT) - ) { + (status == HttpStatus.SC_MOVED_PERMANENTLY || + status == HttpStatus.SC_MOVED_TEMPORARILY || + status == HttpStatus.SC_TEMPORARY_REDIRECT) + ) { Header location = method.getResponseHeader("Location"); if (location == null) { @@ -351,7 +354,7 @@ public class OwnCloudClient extends HttpClient { String locationStr = location.getValue(); Log_OC.d(TAG + " #" + mInstanceNumber, - "Location to redirect: " + locationStr); + "Location to redirect: " + locationStr); result.addLocation(locationStr); @@ -401,7 +404,7 @@ public class OwnCloudClient extends HttpClient { } catch (IOException io) { Log_OC.e(TAG, "Unexpected exception while exhausting not interesting HTTP response;" + - " will be IGNORED", io); + " will be IGNORED", io); } } } @@ -456,8 +459,8 @@ public class OwnCloudClient extends HttpClient { for (int i = 0; i < headers.length; i++) { if (headers[i].getName().toLowerCase().equals("cookie")) { Log_OC.d(TAG + " #" + mInstanceNumber, - "Cookies at request (" + when + ") (" + counter++ + "): " + - headers[i].getValue()); + "Cookies at request (" + when + ") (" + counter++ + "): " + + headers[i].getValue()); } } if (counter == 0) { @@ -473,10 +476,10 @@ public class OwnCloudClient extends HttpClient { Log_OC.d(TAG + " #" + mInstanceNumber, "Cookies at STATE (before)"); for (int i = 0; i < cookies.length; i++) { Log_OC.d(TAG + " #" + mInstanceNumber, " (" + i + "):" + - "\n name: " + cookies[i].getName() + - "\n value: " + cookies[i].getValue() + - "\n domain: " + cookies[i].getDomain() + - "\n path: " + cookies[i].getPath() + "\n name: " + cookies[i].getName() + + "\n value: " + cookies[i].getValue() + + "\n domain: " + cookies[i].getDomain() + + "\n path: " + cookies[i].getPath() ); } } @@ -487,7 +490,7 @@ public class OwnCloudClient extends HttpClient { for (int i = 0; i < headers.length; i++) { if (headers[i].getName().toLowerCase().equals("set-cookie")) { Log_OC.d(TAG + " #" + mInstanceNumber, - "Set-Cookie (" + counter++ + "): " + headers[i].getValue()); + "Set-Cookie (" + counter++ + "): " + headers[i].getValue()); } } if (counter == 0) { @@ -523,7 +526,7 @@ public class OwnCloudClient extends HttpClient { Log_OC.d(TAG, " path: " + cookie.getPath()); Log_OC.d(TAG, " version: " + cookie.getVersion()); Log_OC.d(TAG, " expiryDate: " + - (cookie.getExpiryDate() != null ? cookie.getExpiryDate().toString() : "--")); + (cookie.getExpiryDate() != null ? cookie.getExpiryDate().toString() : "--")); Log_OC.d(TAG, " comment: " + cookie.getComment()); Log_OC.d(TAG, " secure: " + cookie.getSecure()); } @@ -584,7 +587,7 @@ public class OwnCloudClient extends HttpClient { if (invalidated) { if (getCredentials().authTokenCanBeRefreshed() && - repeatCounter < MAX_REPEAT_COUNT_WITH_FRESH_CREDENTIALS) { + repeatCounter < MAX_REPEAT_COUNT_WITH_FRESH_CREDENTIALS) { try { mAccount.loadCredentials(mContext); @@ -594,9 +597,9 @@ public class OwnCloudClient extends HttpClient { } catch (AccountsException | IOException e) { Log_OC.e( - TAG, - "Error while trying to refresh auth token for " + mAccount.getSavedAccount().name, - e + TAG, + "Error while trying to refresh auth token for " + mAccount.getSavedAccount().name, + e ); } } @@ -627,7 +630,7 @@ public class OwnCloudClient extends HttpClient { boolean should = (httpStatusCode == HttpStatus.SC_UNAUTHORIZED || isIdPRedirection()); // invalid credentials should &= (mCredentials != null && // real credentials - !(mCredentials instanceof OwnCloudCredentialsFactory.OwnCloudAnonymousCredentials)); + !(mCredentials instanceof OwnCloudCredentialsFactory.OwnCloudAnonymousCredentials)); // test if have all the needed to effectively invalidate ... should &= (mAccount != null && mAccount.getSavedAccount() != null && mContext != null); @@ -646,8 +649,8 @@ public class OwnCloudClient extends HttpClient { private boolean invalidateAccountCredentials() { AccountManager am = AccountManager.get(mContext); am.invalidateAuthToken( - mAccount.getSavedAccount().type, - mCredentials.getAuthToken() + mAccount.getSavedAccount().type, + mCredentials.getAuthToken() ); am.clearPassword(mAccount.getSavedAccount()); // being strict, only needed for Basic Auth credentials return true; diff --git a/src/com/owncloud/android/lib/common/http/HttpClient.java b/src/com/owncloud/android/lib/common/http/HttpClient.java index 3dfc8f7b..76af65e9 100644 --- a/src/com/owncloud/android/lib/common/http/HttpClient.java +++ b/src/com/owncloud/android/lib/common/http/HttpClient.java @@ -24,12 +24,25 @@ package com.owncloud.android.lib.common.http; +import android.content.Context; + import com.owncloud.android.lib.common.OwnCloudClientManagerFactory; import com.owncloud.android.lib.common.http.interceptors.HttpInterceptor; import com.owncloud.android.lib.common.http.interceptors.UserAgentInterceptor; +import com.owncloud.android.lib.common.network.AdvancedX509TrustManager; +import com.owncloud.android.lib.common.network.NetworkUtils; +import com.owncloud.android.lib.common.utils.Log_OC; + +import org.apache.http.conn.ssl.BrowserCompatHostnameVerifier; import java.util.Arrays; +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSession; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; + import okhttp3.OkHttpClient; import okhttp3.Protocol; @@ -38,17 +51,33 @@ import okhttp3.Protocol; * @author David González Verdugo */ public class HttpClient { + private static final String TAG = HttpClient.class.toString(); private static OkHttpClient sOkHttpClient; private static HttpInterceptor sOkHttpInterceptor; + private static Context sContext; + + public static void setContext(Context context) { + sContext = context; + } public static OkHttpClient getOkHttpClient() { if (sOkHttpClient == null) { - sOkHttpClient = new OkHttpClient.Builder() - .addInterceptor(getOkHttpInterceptor()) - .protocols(Arrays.asList(Protocol.HTTP_1_1)) - .followRedirects(false) - .build(); + try { + final X509TrustManager trustManager = new AdvancedX509TrustManager( + NetworkUtils.getKnownServersStore(sContext)); + final SSLContext sslContext = SSLContext.getInstance("TLS"); + sslContext.init(null, new TrustManager[] {trustManager}, null); + sOkHttpClient = new OkHttpClient.Builder() + .addInterceptor(getOkHttpInterceptor()) + .protocols(Arrays.asList(Protocol.HTTP_1_1)) + .followRedirects(false) + .sslSocketFactory(sslContext.getSocketFactory(), trustManager) + .hostnameVerifier(new BrowserCompatHostnameVerifier()) + .build(); + } catch (Exception e) { + Log_OC.e(TAG, "Could not setup SSL system.", e); + } } return sOkHttpClient; } diff --git a/src/com/owncloud/android/lib/common/network/NetworkUtils.java b/src/com/owncloud/android/lib/common/network/NetworkUtils.java index a6e422b5..b5fa0cde 100644 --- a/src/com/owncloud/android/lib/common/network/NetworkUtils.java +++ b/src/com/owncloud/android/lib/common/network/NetworkUtils.java @@ -147,7 +147,7 @@ public class NetworkUtils { * @throws CertificateException When an exception occurred while loading the certificates from the local * trust store. */ - private static KeyStore getKnownServersStore(Context context) + public static KeyStore getKnownServersStore(Context context) throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException { if (mKnownServersStore == null) { //mKnownServersStore = KeyStore.getInstance("BKS"); diff --git a/src/com/owncloud/android/lib/common/operations/RemoteOperationResult.java b/src/com/owncloud/android/lib/common/operations/RemoteOperationResult.java index 6669fa8b..4b1d1347 100644 --- a/src/com/owncloud/android/lib/common/operations/RemoteOperationResult.java +++ b/src/com/owncloud/android/lib/common/operations/RemoteOperationResult.java @@ -53,10 +53,15 @@ import java.util.List; import java.util.Map; import javax.net.ssl.SSLException; +import javax.net.ssl.SSLPeerUnverifiedException; import okhttp3.Headers; + + + + /** * The result of a remote operation required to an ownCloud server. * @@ -191,17 +196,21 @@ public class RemoteOperationResult implements Serializable { mCode = ResultCode.ACCOUNT_EXCEPTION; } else if (e instanceof SSLException || e instanceof RuntimeException) { - CertificateCombinedException se = getCertificateCombinedException(e); - if (se != null) { - mException = se; - if (se.isRecoverable()) { - mCode = ResultCode.SSL_RECOVERABLE_PEER_UNVERIFIED; - } - } else if (e instanceof RuntimeException) { - mCode = ResultCode.HOST_NOT_AVAILABLE; - + if(e instanceof SSLPeerUnverifiedException) { + mCode = ResultCode.SSL_RECOVERABLE_PEER_UNVERIFIED; } else { - mCode = ResultCode.SSL_ERROR; + CertificateCombinedException se = getCertificateCombinedException(e); + if (se != null) { + mException = se; + if (se.isRecoverable()) { + mCode = ResultCode.SSL_RECOVERABLE_PEER_UNVERIFIED; + } + } else if (e instanceof RuntimeException) { + mCode = ResultCode.HOST_NOT_AVAILABLE; + + } else { + mCode = ResultCode.SSL_ERROR; + } } } else if (e instanceof FileNotFoundException) { diff --git a/src/com/owncloud/android/lib/resources/status/GetRemoteStatusOperation.java b/src/com/owncloud/android/lib/resources/status/GetRemoteStatusOperation.java index abb76398..6a4d5a2f 100644 --- a/src/com/owncloud/android/lib/resources/status/GetRemoteStatusOperation.java +++ b/src/com/owncloud/android/lib/resources/status/GetRemoteStatusOperation.java @@ -40,11 +40,15 @@ import com.owncloud.android.lib.common.utils.Log_OC; import org.json.JSONException; import org.json.JSONObject; +import java.security.cert.CertPathValidatorException; import java.sql.Time; import java.util.ArrayList; import java.util.concurrent.TimeUnit; +import javax.net.ssl.SSLPeerUnverifiedException; + import static com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode.OK; +import static com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode.SSL_RECOVERABLE_PEER_UNVERIFIED; /** * Checks if the server is valid and if the server supports the Share API @@ -87,9 +91,15 @@ public class GetRemoteStatusOperation extends RemoteOperation { getMethod.setReadTimeout(TRY_CONNECTION_TIMEOUT, TimeUnit.SECONDS); getMethod.setConnectionTimeout(TRY_CONNECTION_TIMEOUT, TimeUnit.SECONDS); - int status = client.executeHttpMethod(getMethod); + int status; + try { + status = client.executeHttpMethod(getMethod); + mLatestResult = new RemoteOperationResult(OK); + } catch (SSLPeerUnverifiedException certEx) { + mLatestResult = new RemoteOperationResult(certEx); + return false; + } - mLatestResult = new RemoteOperationResult(OK); boolean isRedirectToNonSecureConnection = false;