1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
|
/*
* Copyright (C) 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.firewall;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.os.Environment;
import android.os.ServiceManager;
import android.util.Slog;
import android.util.Xml;
import com.android.internal.util.XmlUtils;
import com.android.server.IntentResolver;
import com.android.server.pm.PackageManagerService;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
public class IntentFirewall {
private static final String TAG = "IntentFirewall";
// e.g. /data/system/ifw/ifw.xml or /data/secure/system/ifw/ifw.xml
private static final File RULES_FILE =
new File(Environment.getSystemSecureDirectory(), "ifw/ifw.xml");
private static final String TAG_RULES = "rules";
private static final String TAG_ACTIVITY = "activity";
private static final String TAG_SERVICE = "service";
private static final String TAG_BROADCAST = "broadcast";
private static final HashMap<String, FilterFactory> factoryMap;
private final AMSInterface mAms;
private final IntentResolver<FirewallIntentFilter, Rule> mActivityResolver =
new FirewallIntentResolver();
private final IntentResolver<FirewallIntentFilter, Rule> mServiceResolver =
new FirewallIntentResolver();
private final IntentResolver<FirewallIntentFilter, Rule> mBroadcastResolver =
new FirewallIntentResolver();
static {
FilterFactory[] factories = new FilterFactory[] {
AndFilter.FACTORY,
OrFilter.FACTORY,
NotFilter.FACTORY,
StringFilter.ACTION,
StringFilter.COMPONENT,
StringFilter.COMPONENT_NAME,
StringFilter.COMPONENT_PACKAGE,
StringFilter.DATA,
StringFilter.HOST,
StringFilter.MIME_TYPE,
StringFilter.PATH,
StringFilter.SSP,
CategoryFilter.FACTORY,
SenderFilter.FACTORY,
SenderPermissionFilter.FACTORY,
PortFilter.FACTORY
};
// load factor ~= .75
factoryMap = new HashMap<String, FilterFactory>(factories.length * 4 / 3);
for (int i=0; i<factories.length; i++) {
FilterFactory factory = factories[i];
factoryMap.put(factory.getTagName(), factory);
}
}
public IntentFirewall(AMSInterface ams) {
mAms = ams;
readRules(getRulesFile());
}
public boolean checkStartActivity(Intent intent, ApplicationInfo callerApp, int callerUid,
int callerPid, String resolvedType, ActivityInfo resolvedActivity) {
List<Rule> matchingRules = mActivityResolver.queryIntent(intent, resolvedType, false, 0);
boolean log = false;
boolean block = false;
for (int i=0; i< matchingRules.size(); i++) {
Rule rule = matchingRules.get(i);
if (rule.matches(this, intent, callerApp, callerUid, callerPid, resolvedType,
resolvedActivity.applicationInfo)) {
block |= rule.getBlock();
log |= rule.getLog();
// if we've already determined that we should both block and log, there's no need
// to continue trying rules
if (block && log) {
break;
}
}
}
if (log) {
// TODO: log info about intent to event log
}
return !block;
}
public static File getRulesFile() {
return RULES_FILE;
}
private void readRules(File rulesFile) {
FileInputStream fis;
try {
fis = new FileInputStream(rulesFile);
} catch (FileNotFoundException ex) {
// Nope, no rules. Nothing else to do!
return;
}
try {
XmlPullParser parser = Xml.newPullParser();
parser.setInput(fis, null);
XmlUtils.beginDocument(parser, TAG_RULES);
int outerDepth = parser.getDepth();
while (XmlUtils.nextElementWithin(parser, outerDepth)) {
IntentResolver<FirewallIntentFilter, Rule> resolver = null;
String tagName = parser.getName();
if (tagName.equals(TAG_ACTIVITY)) {
resolver = mActivityResolver;
} else if (tagName.equals(TAG_SERVICE)) {
resolver = mServiceResolver;
} else if (tagName.equals(TAG_BROADCAST)) {
resolver = mBroadcastResolver;
}
if (resolver != null) {
Rule rule = new Rule();
try {
rule.readFromXml(parser);
} catch (XmlPullParserException ex) {
Slog.e(TAG, "Error reading intent firewall rule", ex);
continue;
} catch (IOException ex) {
Slog.e(TAG, "Error reading intent firewall rule", ex);
continue;
}
for (int i=0; i<rule.getIntentFilterCount(); i++) {
resolver.addFilter(rule.getIntentFilter(i));
}
}
}
} catch (XmlPullParserException ex) {
Slog.e(TAG, "Error reading intent firewall rules", ex);
} catch (IOException ex) {
Slog.e(TAG, "Error reading intent firewall rules", ex);
} finally {
try {
fis.close();
} catch (IOException ex) {
Slog.e(TAG, "Error while closing " + rulesFile, ex);
}
}
}
static Filter parseFilter(XmlPullParser parser) throws IOException, XmlPullParserException {
String elementName = parser.getName();
FilterFactory factory = factoryMap.get(elementName);
if (factory == null) {
throw new XmlPullParserException("Unknown element in filter list: " + elementName);
}
return factory.newFilter(parser);
}
private static class Rule extends AndFilter {
private static final String TAG_INTENT_FILTER = "intent-filter";
private static final String ATTR_BLOCK = "block";
private static final String ATTR_LOG = "log";
private final ArrayList<FirewallIntentFilter> mIntentFilters =
new ArrayList<FirewallIntentFilter>(1);
private boolean block;
private boolean log;
@Override
public Rule readFromXml(XmlPullParser parser) throws IOException, XmlPullParserException {
block = Boolean.parseBoolean(parser.getAttributeValue(null, ATTR_BLOCK));
log = Boolean.parseBoolean(parser.getAttributeValue(null, ATTR_LOG));
super.readFromXml(parser);
return this;
}
@Override
protected void readChild(XmlPullParser parser) throws IOException, XmlPullParserException {
if (parser.getName().equals(TAG_INTENT_FILTER)) {
FirewallIntentFilter intentFilter = new FirewallIntentFilter(this);
intentFilter.readFromXml(parser);
mIntentFilters.add(intentFilter);
} else {
super.readChild(parser);
}
}
public int getIntentFilterCount() {
return mIntentFilters.size();
}
public FirewallIntentFilter getIntentFilter(int index) {
return mIntentFilters.get(index);
}
public boolean getBlock() {
return block;
}
public boolean getLog() {
return log;
}
}
private static class FirewallIntentFilter extends IntentFilter {
private final Rule rule;
public FirewallIntentFilter(Rule rule) {
this.rule = rule;
}
}
private static class FirewallIntentResolver
extends IntentResolver<FirewallIntentFilter, Rule> {
@Override
protected boolean allowFilterResult(FirewallIntentFilter filter, List<Rule> dest) {
return !dest.contains(filter.rule);
}
@Override
protected boolean isPackageForFilter(String packageName, FirewallIntentFilter filter) {
return true;
}
@Override
protected FirewallIntentFilter[] newArray(int size) {
return new FirewallIntentFilter[size];
}
@Override
protected Rule newResult(FirewallIntentFilter filter, int match, int userId) {
return filter.rule;
}
@Override
protected void sortResults(List<Rule> results) {
// there's no need to sort the results
return;
}
}
/**
* This interface contains the methods we need from ActivityManagerService. This allows AMS to
* export these methods to us without making them public, and also makes it easier to test this
* component.
*/
public interface AMSInterface {
int checkComponentPermission(String permission, int pid, int uid,
int owningUid, boolean exported);
}
/**
* Checks if the caller has access to a component
*
* @param permission If present, the caller must have this permission
* @param pid The pid of the caller
* @param uid The uid of the caller
* @param owningUid The uid of the application that owns the component
* @param exported Whether the component is exported
* @return True if the caller can access the described component
*/
boolean checkComponentPermission(String permission, int pid, int uid, int owningUid,
boolean exported) {
return mAms.checkComponentPermission(permission, pid, uid, owningUid, exported) ==
PackageManager.PERMISSION_GRANTED;
}
boolean signaturesMatch(int uid1, int uid2) {
PackageManagerService pm = (PackageManagerService)ServiceManager.getService("package");
return pm.checkUidSignatures(uid1, uid2) == PackageManager.SIGNATURE_MATCH;
}
}
|