OLD | NEW |
1 // Copyright 2017 The Chromium Authors. All rights reserved. | 1 // Copyright 2017 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 package org.chromium.chrome.browser.customtabs; | 5 package org.chromium.chrome.browser.customtabs; |
6 | 6 |
7 import android.content.pm.PackageInfo; | 7 import android.content.pm.PackageInfo; |
8 import android.content.pm.PackageManager; | 8 import android.content.pm.PackageManager; |
9 import android.net.Uri; | 9 import android.net.Uri; |
10 import android.support.annotation.NonNull; | 10 import android.support.annotation.NonNull; |
(...skipping 12 matching lines...) Expand all Loading... |
23 | 23 |
24 import java.io.ByteArrayInputStream; | 24 import java.io.ByteArrayInputStream; |
25 import java.io.InputStream; | 25 import java.io.InputStream; |
26 import java.security.MessageDigest; | 26 import java.security.MessageDigest; |
27 import java.security.NoSuchAlgorithmException; | 27 import java.security.NoSuchAlgorithmException; |
28 import java.security.cert.CertificateEncodingException; | 28 import java.security.cert.CertificateEncodingException; |
29 import java.security.cert.CertificateException; | 29 import java.security.cert.CertificateException; |
30 import java.security.cert.CertificateFactory; | 30 import java.security.cert.CertificateFactory; |
31 import java.security.cert.X509Certificate; | 31 import java.security.cert.X509Certificate; |
32 import java.util.HashMap; | 32 import java.util.HashMap; |
| 33 import java.util.HashSet; |
33 import java.util.Locale; | 34 import java.util.Locale; |
34 import java.util.Map; | 35 import java.util.Map; |
| 36 import java.util.Set; |
35 | 37 |
36 /** | 38 /** |
37 * Used to verify postMessage origin for a designated package name. | 39 * Used to verify postMessage origin for a designated package name. |
38 * | 40 * |
39 * Uses Digital Asset Links to confirm that the given origin is associated with
the package name as | 41 * Uses Digital Asset Links to confirm that the given origin is associated with
the package name as |
40 * a postMessage origin. It caches any origin that has been verified during the
current application | 42 * a postMessage origin. It caches any origin that has been verified during the
current application |
41 * lifecycle and reuses that without making any new network requests. | 43 * lifecycle and reuses that without making any new network requests. |
42 * | 44 * |
43 * The lifecycle of this object is governed by the owner. The owner has to call | 45 * The lifecycle of this object is governed by the owner. The owner has to call |
44 * {@link OriginVerifier#cleanUp()} for proper cleanup of dependencies. | 46 * {@link OriginVerifier#cleanUp()} for proper cleanup of dependencies. |
45 */ | 47 */ |
46 @JNINamespace("customtabs") | 48 @JNINamespace("customtabs") |
47 class OriginVerifier { | 49 class OriginVerifier { |
48 private static final String TAG = "OriginVerifier"; | 50 private static final String TAG = "OriginVerifier"; |
49 private static final char[] HEX_CHAR_LOOKUP = "0123456789ABCDEF".toCharArray
(); | 51 private static final char[] HEX_CHAR_LOOKUP = "0123456789ABCDEF".toCharArray
(); |
50 private static Map<String, Uri> sCachedOriginMap; | 52 private static Map<String, Set<Uri>> sPackageToCachedOrigins; |
51 private final OriginVerificationListener mListener; | 53 private final OriginVerificationListener mListener; |
52 private final String mPackageName; | 54 private final String mPackageName; |
53 private final String mSignatureFingerprint; | 55 private final String mSignatureFingerprint; |
54 private long mNativeOriginVerifier = 0; | 56 private long mNativeOriginVerifier = 0; |
55 private Uri mOrigin; | 57 private Uri mOrigin; |
56 | 58 |
57 /** | 59 /** Small helper class to post a result of origin verification. */ |
58 * To be used for prepopulating verified origin for testing functionality. | 60 private class VerifiedCallback implements Runnable { |
59 * @param packageName The package name to prepopulate for. | 61 private final boolean mResult; |
60 * @param origin The origin to add as verified. | 62 |
61 */ | 63 public VerifiedCallback(boolean result) { |
62 @VisibleForTesting | 64 this.mResult = result; |
63 static void prePopulateVerifiedOriginForTesting(String packageName, Uri orig
in) { | 65 } |
64 cacheVerifiedOriginIfNeeded(packageName, origin); | 66 |
| 67 @Override |
| 68 public void run() { |
| 69 originVerified(mResult); |
| 70 } |
65 } | 71 } |
66 | 72 |
67 private static Uri getPostMessageOriginFromVerifiedOrigin( | 73 private static Uri getPostMessageOriginFromVerifiedOrigin( |
68 String packageName, Uri verifiedOrigin) { | 74 String packageName, Uri verifiedOrigin) { |
69 return Uri.parse(IntentHandler.ANDROID_APP_REFERRER_SCHEME + "://" | 75 return Uri.parse(IntentHandler.ANDROID_APP_REFERRER_SCHEME + "://" |
70 + verifiedOrigin.getHost() + "/" + packageName); | 76 + verifiedOrigin.getHost() + "/" + packageName); |
71 } | 77 } |
72 | 78 |
73 private static void cacheVerifiedOriginIfNeeded(String packageName, Uri orig
in) { | 79 /** Clears all known relations. */ |
74 if (sCachedOriginMap == null) sCachedOriginMap = new HashMap<>(); | 80 @VisibleForTesting |
75 if (!sCachedOriginMap.containsKey(packageName)) { | 81 static void reset() { |
76 sCachedOriginMap.put(packageName, origin); | 82 ThreadUtils.assertOnUiThread(); |
77 } | 83 if (sPackageToCachedOrigins != null) sPackageToCachedOrigins.clear(); |
78 } | 84 } |
79 | 85 |
80 /** | 86 /** |
| 87 * Mark an origin as verified for a package. |
| 88 * @param packageName The package name to prepopulate for. |
| 89 * @param origin The origin to add as verified. |
| 90 */ |
| 91 static void addVerifiedOriginForPackage(String packageName, Uri origin) { |
| 92 ThreadUtils.assertOnUiThread(); |
| 93 if (sPackageToCachedOrigins == null) sPackageToCachedOrigins = new HashM
ap<>(); |
| 94 Set<Uri> cachedOrigins = sPackageToCachedOrigins.get(packageName); |
| 95 if (cachedOrigins == null) { |
| 96 cachedOrigins = new HashSet<Uri>(); |
| 97 sPackageToCachedOrigins.put(packageName, cachedOrigins); |
| 98 } |
| 99 cachedOrigins.add(origin); |
| 100 } |
| 101 |
| 102 /** |
| 103 * Returns whether an origin is first-party relative to a given package name
. |
| 104 * |
| 105 * This only returns data from previously cached relations, and does not |
| 106 * trigger an asynchronous validation. |
| 107 * |
| 108 * @param packageName The package name |
| 109 * @param origin The origin to verify |
| 110 */ |
| 111 static boolean isValidOrigin(String packageName, Uri origin) { |
| 112 ThreadUtils.assertOnUiThread(); |
| 113 if (sPackageToCachedOrigins == null) return false; |
| 114 Set<Uri> cachedOrigins = sPackageToCachedOrigins.get(packageName); |
| 115 if (cachedOrigins == null) return false; |
| 116 return cachedOrigins.contains(origin); |
| 117 } |
| 118 |
| 119 /** |
81 * Callback interface for getting verification results. | 120 * Callback interface for getting verification results. |
82 */ | 121 */ |
83 public interface OriginVerificationListener { | 122 public interface OriginVerificationListener { |
84 /** | 123 /** |
85 * To be posted on the handler thread after the verification finishes. | 124 * To be posted on the handler thread after the verification finishes. |
86 * @param packageName The package name for the origin verification query
for this result. | 125 * @param packageName The package name for the origin verification query
for this result. |
87 * @param origin The origin that was declared on the query for this resu
lt. | 126 * @param origin The origin that was declared on the query for this resu
lt. |
88 * @param verified Whether the given origin was verified to correspond t
o the given package. | 127 * @param verified Whether the given origin was verified to correspond t
o the given package. |
89 */ | 128 */ |
90 void onOriginVerified(String packageName, Uri origin, boolean verified); | 129 void onOriginVerified(String packageName, Uri origin, boolean verified); |
(...skipping 14 matching lines...) Expand all Loading... |
105 /** | 144 /** |
106 * Verify the claimed origin for the cached package name asynchronously. Thi
s will end up | 145 * Verify the claimed origin for the cached package name asynchronously. Thi
s will end up |
107 * making a network request for non-cached origins with a URLFetcher using t
he last used | 146 * making a network request for non-cached origins with a URLFetcher using t
he last used |
108 * profile as context. | 147 * profile as context. |
109 * @param origin The postMessage origin the application is claiming to have.
Can't be null. | 148 * @param origin The postMessage origin the application is claiming to have.
Can't be null. |
110 */ | 149 */ |
111 public void start(@NonNull Uri origin) { | 150 public void start(@NonNull Uri origin) { |
112 ThreadUtils.assertOnUiThread(); | 151 ThreadUtils.assertOnUiThread(); |
113 mOrigin = origin; | 152 mOrigin = origin; |
114 if (!UrlConstants.HTTPS_SCHEME.equals(mOrigin.getScheme().toLowerCase(Lo
cale.US))) { | 153 if (!UrlConstants.HTTPS_SCHEME.equals(mOrigin.getScheme().toLowerCase(Lo
cale.US))) { |
115 ThreadUtils.postOnUiThread(new Runnable() { | 154 ThreadUtils.runOnUiThread(new VerifiedCallback(false)); |
116 @Override | |
117 public void run() { | |
118 originVerified(false); | |
119 } | |
120 }); | |
121 return; | 155 return; |
122 } | 156 } |
123 | 157 |
124 // If this origin is cached as verified already, use that. | 158 // If this origin is cached as verified already, use that. |
125 Uri cachedOrigin = getCachedOriginIfExists(); | 159 if (isValidOrigin(mPackageName, origin)) { |
126 if (cachedOrigin != null && cachedOrigin.equals(origin)) { | 160 ThreadUtils.runOnUiThread(new VerifiedCallback(true)); |
127 ThreadUtils.postOnUiThread(new Runnable() { | |
128 @Override | |
129 public void run() { | |
130 originVerified(true); | |
131 } | |
132 }); | |
133 return; | 161 return; |
134 } | 162 } |
135 if (mNativeOriginVerifier != 0) cleanUp(); | 163 if (mNativeOriginVerifier != 0) cleanUp(); |
136 if (!BrowserStartupController.get(LibraryProcessType.PROCESS_BROWSER) | 164 if (!BrowserStartupController.get(LibraryProcessType.PROCESS_BROWSER) |
137 .isStartupSuccessfullyCompleted()) { | 165 .isStartupSuccessfullyCompleted()) { |
138 // Early return for testing without native. | 166 // Early return for testing without native. |
139 return; | 167 return; |
140 } | 168 } |
141 mNativeOriginVerifier = nativeInit(Profile.getLastUsedProfile().getOrigi
nalProfile()); | 169 mNativeOriginVerifier = nativeInit(Profile.getLastUsedProfile().getOrigi
nalProfile()); |
142 assert mNativeOriginVerifier != 0; | 170 assert mNativeOriginVerifier != 0; |
143 boolean success = nativeVerifyOrigin( | 171 boolean success = nativeVerifyOrigin( |
144 mNativeOriginVerifier, mPackageName, mSignatureFingerprint, mOri
gin.toString()); | 172 mNativeOriginVerifier, mPackageName, mSignatureFingerprint, mOri
gin.toString()); |
145 if (!success) { | 173 if (!success) ThreadUtils.runOnUiThread(new VerifiedCallback(false)); |
146 ThreadUtils.postOnUiThread(new Runnable() { | |
147 @Override | |
148 public void run() { | |
149 originVerified(false); | |
150 } | |
151 }); | |
152 } | |
153 } | 174 } |
154 | 175 |
155 /** | 176 /** |
156 * Cleanup native dependencies on this object. | 177 * Cleanup native dependencies on this object. |
157 */ | 178 */ |
158 void cleanUp() { | 179 void cleanUp() { |
159 if (mNativeOriginVerifier == 0) return; | 180 if (mNativeOriginVerifier == 0) return; |
160 nativeDestroy(mNativeOriginVerifier); | 181 nativeDestroy(mNativeOriginVerifier); |
161 mNativeOriginVerifier = 0; | 182 mNativeOriginVerifier = 0; |
162 } | 183 } |
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
212 hexString.append(HEX_CHAR_LOOKUP[(byteArray[i] & 0xf0) >>> 4]); | 233 hexString.append(HEX_CHAR_LOOKUP[(byteArray[i] & 0xf0) >>> 4]); |
213 hexString.append(HEX_CHAR_LOOKUP[byteArray[i] & 0xf]); | 234 hexString.append(HEX_CHAR_LOOKUP[byteArray[i] & 0xf]); |
214 if (i < (byteArray.length - 1)) hexString.append(':'); | 235 if (i < (byteArray.length - 1)) hexString.append(':'); |
215 } | 236 } |
216 return hexString.toString(); | 237 return hexString.toString(); |
217 } | 238 } |
218 | 239 |
219 @CalledByNative | 240 @CalledByNative |
220 private void originVerified(boolean originVerified) { | 241 private void originVerified(boolean originVerified) { |
221 if (originVerified) { | 242 if (originVerified) { |
222 cacheVerifiedOriginIfNeeded(mPackageName, mOrigin); | 243 addVerifiedOriginForPackage(mPackageName, mOrigin); |
223 mOrigin = getPostMessageOriginFromVerifiedOrigin(mPackageName, mOrig
in); | 244 mOrigin = getPostMessageOriginFromVerifiedOrigin(mPackageName, mOrig
in); |
224 } | 245 } |
225 mListener.onOriginVerified(mPackageName, mOrigin, originVerified); | 246 mListener.onOriginVerified(mPackageName, mOrigin, originVerified); |
226 cleanUp(); | 247 cleanUp(); |
227 } | 248 } |
228 | 249 |
229 private Uri getCachedOriginIfExists() { | |
230 if (sCachedOriginMap == null) return null; | |
231 return sCachedOriginMap.get(mPackageName); | |
232 } | |
233 | |
234 private native long nativeInit(Profile profile); | 250 private native long nativeInit(Profile profile); |
235 private native boolean nativeVerifyOrigin(long nativeOriginVerifier, String
packageName, | 251 private native boolean nativeVerifyOrigin(long nativeOriginVerifier, String
packageName, |
236 String signatureFingerprint, String origin); | 252 String signatureFingerprint, String origin); |
237 private native void nativeDestroy(long nativeOriginVerifier); | 253 private native void nativeDestroy(long nativeOriginVerifier); |
238 } | 254 } |
OLD | NEW |