Use calling app's Network Security Config for HTTPS downloads
Chad Brubaker [Mon, 20 Jun 2016 19:41:47 +0000 (12:41 -0700)]
Bug:29505888
Change-Id: Ifc33fd75e44d1dbc5a4ce5caa8e1ff938b94623e

src/com/android/providers/downloads/DownloadThread.java
src/com/android/providers/downloads/RealSystemFacade.java
src/com/android/providers/downloads/SystemFacade.java
tests/src/com/android/providers/downloads/FakeSystemFacade.java

index da51e9d..34d6ad1 100644 (file)
@@ -85,6 +85,10 @@ import java.net.MalformedURLException;
 import java.net.ProtocolException;
 import java.net.URL;
 import java.net.URLConnection;
+import java.security.GeneralSecurityException;
+
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLContext;
 
 /**
  * Task which executes a given {@link DownloadInfo}: making network requests,
@@ -403,6 +407,13 @@ public class DownloadThread extends Thread {
         }
 
         boolean cleartextTrafficPermitted = mSystemFacade.isCleartextTrafficPermitted(mInfo.mUid);
+        SSLContext appContext;
+        try {
+            appContext = mSystemFacade.getSSLContextForPackage(mContext, mInfo.mPackage);
+        } catch (GeneralSecurityException e) {
+            // This should never happen.
+            throw new StopRequestException(STATUS_UNKNOWN_ERROR, "Unable to create SSLContext.");
+        }
         int redirectionCount = 0;
         while (redirectionCount++ < Constants.MAX_REDIRECTS) {
             // Enforce the cleartext traffic opt-out for the UID. This cannot be enforced earlier
@@ -424,6 +435,11 @@ public class DownloadThread extends Thread {
                 conn.setInstanceFollowRedirects(false);
                 conn.setConnectTimeout(DEFAULT_TIMEOUT);
                 conn.setReadTimeout(DEFAULT_TIMEOUT);
+                // If this is going over HTTPS configure the trust to be the same as the calling
+                // package.
+                if (conn instanceof HttpsURLConnection) {
+                    ((HttpsURLConnection)conn).setSSLSocketFactory(appContext.getSocketFactory());
+                }
 
                 addRequestHeaders(conn, resuming);
 
index 2203eef..df1d245 100644 (file)
@@ -26,6 +26,13 @@ import android.content.pm.PackageManager.NameNotFoundException;
 import android.net.ConnectivityManager;
 import android.net.Network;
 import android.net.NetworkInfo;
+import android.security.NetworkSecurityPolicy;
+import android.security.net.config.ApplicationConfig;
+
+import java.security.GeneralSecurityException;
+
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.TrustManager;
 
 import com.android.internal.util.ArrayUtils;
 
@@ -94,6 +101,21 @@ class RealSystemFacade implements SystemFacade {
         return false;
     }
 
+    @Override
+    public SSLContext getSSLContextForPackage(Context context, String packageName)
+            throws GeneralSecurityException {
+        ApplicationConfig appConfig;
+        try {
+            appConfig = NetworkSecurityPolicy.getApplicationConfigForPackage(context, packageName);
+        } catch (NameNotFoundException e) {
+            // Unknown package -- fallback to the default SSLContext
+            return SSLContext.getDefault();
+        }
+        SSLContext ctx = SSLContext.getInstance("TLS");
+        ctx.init(null, new TrustManager[] {appConfig.getTrustManager()}, null);
+        return ctx;
+    }
+
     /**
      * Returns whether cleartext network traffic (HTTP) is permitted for the provided package.
      */
index 5f020b1..c34317c 100644 (file)
 
 package com.android.providers.downloads;
 
+import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.net.Network;
 import android.net.NetworkInfo;
 
+import java.security.GeneralSecurityException;
+import javax.net.ssl.SSLContext;
+
 interface SystemFacade {
     /**
      * @see System#currentTimeMillis()
@@ -58,4 +62,10 @@ interface SystemFacade {
      * Returns true if cleartext network traffic is permitted for the specified UID.
      */
     public boolean isCleartextTrafficPermitted(int uid);
+
+    /**
+     * Return a {@link SSLContext} configured using the specified package's configuration.
+     */
+    public SSLContext getSSLContextForPackage(Context context, String pckg)
+            throws GeneralSecurityException;
 }
index 3b9a83b..b6b800a 100644 (file)
@@ -4,6 +4,7 @@ import static org.mockito.Matchers.any;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
+import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.net.ConnectivityManager;
@@ -17,8 +18,10 @@ import org.mockito.stubbing.Answer;
 import java.io.IOException;
 import java.net.URL;
 import java.net.URLConnection;
+import java.security.GeneralSecurityException;
 import java.util.ArrayList;
 import java.util.List;
+import javax.net.ssl.SSLContext;
 
 public class FakeSystemFacade implements SystemFacade {
     long mTimeMillis = 0;
@@ -30,6 +33,7 @@ public class FakeSystemFacade implements SystemFacade {
     List<Intent> mBroadcastsSent = new ArrayList<Intent>();
     boolean mCleartextTrafficPermitted = true;
     private boolean mReturnActualTime = false;
+    private SSLContext mSSLContext = null;
 
     public void setUp() {
         mTimeMillis = 0;
@@ -40,6 +44,11 @@ public class FakeSystemFacade implements SystemFacade {
         mRecommendedMaxBytesOverMobile = Long.MAX_VALUE;
         mBroadcastsSent.clear();
         mReturnActualTime = false;
+        try {
+            mSSLContext = SSLContext.getDefault();
+        } catch (GeneralSecurityException e) {
+            throw new RuntimeException(e);
+        }
     }
 
     void incrementTimeMillis(long delta) {
@@ -112,6 +121,15 @@ public class FakeSystemFacade implements SystemFacade {
         return mCleartextTrafficPermitted;
     }
 
+    @Override
+    public SSLContext getSSLContextForPackage(Context context, String pckg) {
+        return mSSLContext;
+    }
+
+    public void setSSLContext(SSLContext context) {
+        mSSLContext = context;
+    }
+
     public void setReturnActualTime(boolean flag) {
         mReturnActualTime = flag;
     }