Skip to content

Commit c067690

Browse files
committed
Extract code to build a list of blacklisted content providers/authorities
The list is now built on first access instead of when a SafeContentResolver instance is created.
1 parent b97ab0d commit c067690

2 files changed

Lines changed: 90 additions & 48 deletions

File tree

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
/*
2+
* Copyright (C) 2016 cketti
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package de.cketti.safecontentresolver;
17+
18+
19+
import java.util.Collections;
20+
import java.util.HashSet;
21+
import java.util.Set;
22+
23+
import android.content.Context;
24+
import android.content.pm.PackageInfo;
25+
import android.content.pm.PackageManager;
26+
import android.content.pm.PackageManager.NameNotFoundException;
27+
import android.content.pm.ProviderInfo;
28+
import android.os.Bundle;
29+
30+
31+
/**
32+
* Stores a (lazily built) list of all of the app's content providers that are not SafeContentResolver-whitelisted.
33+
*/
34+
class Blacklist {
35+
private static final String META_DATA_KEY_ALLOW_INTERNAL_ACCESS =
36+
"de.cketti.safecontentresolver.ALLOW_INTERNAL_ACCESS";
37+
38+
39+
private final Context context;
40+
private Set<String> blacklistedAuthorities;
41+
42+
43+
Blacklist(Context context) {
44+
this.context = context;
45+
}
46+
47+
synchronized boolean isBlacklisted(String authority) {
48+
if (blacklistedAuthorities == null) {
49+
blacklistedAuthorities = findBlacklistedContentProviderAuthorities();
50+
}
51+
52+
return blacklistedAuthorities.contains(authority);
53+
}
54+
55+
private Set<String> findBlacklistedContentProviderAuthorities() {
56+
ProviderInfo[] providers = getProviderInfo(context);
57+
58+
Set<String> blacklistedAuthorities = new HashSet<>(providers.length);
59+
for (ProviderInfo providerInfo : providers) {
60+
if (!isContentProviderWhitelisted(providerInfo)) {
61+
String[] authorities = providerInfo.authority.split(";");
62+
Collections.addAll(blacklistedAuthorities, authorities);
63+
}
64+
}
65+
66+
return blacklistedAuthorities;
67+
}
68+
69+
private ProviderInfo[] getProviderInfo(Context context) {
70+
try {
71+
PackageManager packageManager = context.getPackageManager();
72+
String packageName = context.getPackageName();
73+
PackageInfo packageInfo = packageManager.getPackageInfo(packageName,
74+
PackageManager.GET_PROVIDERS | PackageManager.GET_META_DATA);
75+
76+
ProviderInfo[] providers = packageInfo.providers;
77+
return providers != null ? providers : new ProviderInfo[0];
78+
} catch (NameNotFoundException e) {
79+
throw new RuntimeException(e);
80+
}
81+
}
82+
83+
private boolean isContentProviderWhitelisted(ProviderInfo providerInfo) {
84+
Bundle metaData = providerInfo.metaData;
85+
return metaData != null && metaData.getBoolean(META_DATA_KEY_ALLOW_INTERNAL_ACCESS, false);
86+
}
87+
}

SafeContentResolver/src/main/java/de/cketti/safecontentresolver/SafeContentResolver.java

Lines changed: 3 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -22,20 +22,12 @@
2222
import java.io.FileNotFoundException;
2323
import java.io.IOException;
2424
import java.io.InputStream;
25-
import java.util.Collections;
26-
import java.util.HashSet;
27-
import java.util.Set;
2825

2926
import android.content.ContentResolver;
3027
import android.content.Context;
3128
import android.content.Intent;
32-
import android.content.pm.PackageInfo;
33-
import android.content.pm.PackageManager;
34-
import android.content.pm.PackageManager.NameNotFoundException;
35-
import android.content.pm.ProviderInfo;
3629
import android.content.res.AssetFileDescriptor;
3730
import android.net.Uri;
38-
import android.os.Bundle;
3931
import android.os.ParcelFileDescriptor;
4032
import android.support.annotation.NonNull;
4133
import android.support.annotation.Nullable;
@@ -80,12 +72,8 @@
8072
* </p>
8173
*/
8274
public abstract class SafeContentResolver {
83-
private static final String META_DATA_KEY_ALLOW_INTERNAL_ACCESS =
84-
"de.cketti.safecontentresolver.ALLOW_INTERNAL_ACCESS";
85-
86-
8775
private final ContentResolver contentResolver;
88-
private final Set<String> blacklistedAuthorities;
76+
private final Blacklist blacklist;
8977

9078

9179
/**
@@ -107,7 +95,7 @@ public static SafeContentResolver newInstance(@NonNull Context context) {
10795

10896
protected SafeContentResolver(@NonNull Context context) {
10997
this.contentResolver = context.getContentResolver();
110-
this.blacklistedAuthorities = getBlacklistedContentProviderAuthorities(context);
98+
this.blacklist = new Blacklist(context);
11199
}
112100

113101
/**
@@ -136,7 +124,7 @@ public InputStream openInputStream(@NonNull Uri uri) throws FileNotFoundExceptio
136124
String scheme = uri.getScheme();
137125
if (ContentResolver.SCHEME_CONTENT.equals(scheme)) {
138126
String authority = uri.getAuthority();
139-
if (blacklistedAuthorities.contains(authority)) {
127+
if (blacklist.isBlacklisted(authority)) {
140128
throw new FileNotFoundException("content URI is owned by the application itself. " +
141129
"Content provider is not whitelisted: " + authority);
142130
}
@@ -164,37 +152,4 @@ public InputStream openInputStream(@NonNull Uri uri) throws FileNotFoundExceptio
164152
}
165153

166154
protected abstract int getFileUidOrThrow(@NonNull FileDescriptor fileDescriptor) throws FileNotFoundException;
167-
168-
private Set<String> getBlacklistedContentProviderAuthorities(Context context) {
169-
ProviderInfo[] providers = getProviderInfo(context);
170-
171-
Set<String> blacklistedAuthorities = new HashSet<>(providers.length);
172-
for (ProviderInfo providerInfo : providers) {
173-
if (!isContentProviderWhitelisted(providerInfo)) {
174-
String[] authorities = providerInfo.authority.split(";");
175-
Collections.addAll(blacklistedAuthorities, authorities);
176-
}
177-
}
178-
179-
return blacklistedAuthorities;
180-
}
181-
182-
private ProviderInfo[] getProviderInfo(Context context) {
183-
try {
184-
PackageManager packageManager = context.getPackageManager();
185-
String packageName = context.getPackageName();
186-
PackageInfo packageInfo = packageManager.getPackageInfo(packageName,
187-
PackageManager.GET_PROVIDERS | PackageManager.GET_META_DATA);
188-
189-
ProviderInfo[] providers = packageInfo.providers;
190-
return providers != null ? providers : new ProviderInfo[0];
191-
} catch (NameNotFoundException e) {
192-
throw new RuntimeException(e);
193-
}
194-
}
195-
196-
private boolean isContentProviderWhitelisted(ProviderInfo providerInfo) {
197-
Bundle metaData = providerInfo.metaData;
198-
return metaData != null && metaData.getBoolean(META_DATA_KEY_ALLOW_INTERNAL_ACCESS, false);
199-
}
200155
}

0 commit comments

Comments
 (0)