Merge "Initial implementation of IntentFirewall functionality" into jb-mr2-dev
This commit is contained in:
@ -117,7 +117,7 @@ public abstract class IntentResolver<F extends IntentFilter, R extends Object> {
|
||||
boolean printedHeader = false;
|
||||
F filter;
|
||||
for (int i=0; i<N && (filter=a[i]) != null; i++) {
|
||||
if (packageName != null && !packageName.equals(packageForFilter(filter))) {
|
||||
if (packageName != null && !isPackageForFilter(packageName, filter)) {
|
||||
continue;
|
||||
}
|
||||
if (title != null) {
|
||||
@ -357,11 +357,11 @@ public abstract class IntentResolver<F extends IntentFilter, R extends Object> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the package that owns this filter. This must be implemented to
|
||||
* provide correct filtering of Intents that have specified a package name
|
||||
* they are to be delivered to.
|
||||
* Returns whether this filter is owned by this package. This must be
|
||||
* implemented to provide correct filtering of Intents that have
|
||||
* specified a package name they are to be delivered to.
|
||||
*/
|
||||
protected abstract String packageForFilter(F filter);
|
||||
protected abstract boolean isPackageForFilter(String packageName, F filter);
|
||||
|
||||
protected abstract F[] newArray(int size);
|
||||
|
||||
@ -556,7 +556,7 @@ public abstract class IntentResolver<F extends IntentFilter, R extends Object> {
|
||||
}
|
||||
|
||||
// Is delivery being limited to filters owned by a particular package?
|
||||
if (packageName != null && !packageName.equals(packageForFilter(filter))) {
|
||||
if (packageName != null && !isPackageForFilter(packageName, filter)) {
|
||||
if (debug) {
|
||||
Slog.v(TAG, " Filter is not from package " + packageName + "; skipping");
|
||||
}
|
||||
@ -710,8 +710,8 @@ public abstract class IntentResolver<F extends IntentFilter, R extends Object> {
|
||||
}
|
||||
|
||||
private final IntentResolverOld<F, R> mOldResolver = new IntentResolverOld<F, R>() {
|
||||
@Override protected String packageForFilter(F filter) {
|
||||
return IntentResolver.this.packageForFilter(filter);
|
||||
@Override protected boolean isPackageForFilter(String packageName, F filter) {
|
||||
return IntentResolver.this.isPackageForFilter(packageName, filter);
|
||||
}
|
||||
@Override protected boolean allowFilterResult(F filter, List<R> dest) {
|
||||
return IntentResolver.this.allowFilterResult(filter, dest);
|
||||
|
@ -106,7 +106,7 @@ public abstract class IntentResolverOld<F extends IntentFilter, R extends Object
|
||||
boolean printedHeader = false;
|
||||
for (int i=0; i<N; i++) {
|
||||
F filter = a.get(i);
|
||||
if (packageName != null && !packageName.equals(packageForFilter(filter))) {
|
||||
if (packageName != null && !isPackageForFilter(packageName, filter)) {
|
||||
continue;
|
||||
}
|
||||
if (title != null) {
|
||||
@ -336,11 +336,11 @@ public abstract class IntentResolverOld<F extends IntentFilter, R extends Object
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the package that owns this filter. This must be implemented to
|
||||
* provide correct filtering of Intents that have specified a package name
|
||||
* they are to be delivered to.
|
||||
* Returns whether this filter is owned by this package. This must be
|
||||
* implemented to provide correct filtering of Intents that have
|
||||
* specified a package name they are to be delivered to.
|
||||
*/
|
||||
protected abstract String packageForFilter(F filter);
|
||||
protected abstract boolean isPackageForFilter(String packageName, F filter);
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected R newResult(F filter, int match, int userId) {
|
||||
@ -529,7 +529,7 @@ public abstract class IntentResolverOld<F extends IntentFilter, R extends Object
|
||||
}
|
||||
|
||||
// Is delivery being limited to filters owned by a particular package?
|
||||
if (packageName != null && !packageName.equals(packageForFilter(filter))) {
|
||||
if (packageName != null && !isPackageForFilter(packageName, filter)) {
|
||||
if (debug) {
|
||||
Slog.v(TAG, " Filter is not from package " + packageName + "; skipping");
|
||||
}
|
||||
|
@ -30,6 +30,7 @@ import com.android.server.ProcessMap;
|
||||
import com.android.server.SystemServer;
|
||||
import com.android.server.Watchdog;
|
||||
import com.android.server.am.ActivityStack.ActivityState;
|
||||
import com.android.server.firewall.IntentFirewall;
|
||||
import com.android.server.pm.UserManagerService;
|
||||
import com.android.server.wm.AppTransition;
|
||||
import com.android.server.wm.WindowManagerService;
|
||||
@ -274,6 +275,8 @@ public final class ActivityManagerService extends ActivityManagerNative
|
||||
|
||||
public ActivityStack mMainStack;
|
||||
|
||||
public IntentFirewall mIntentFirewall;
|
||||
|
||||
private final boolean mHeadless;
|
||||
|
||||
// Whether we should show our dialogs (ANR, crash, etc) or just perform their
|
||||
@ -570,8 +573,8 @@ public final class ActivityManagerService extends ActivityManagerNative
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String packageForFilter(BroadcastFilter filter) {
|
||||
return filter.packageName;
|
||||
protected boolean isPackageForFilter(String packageName, BroadcastFilter filter) {
|
||||
return packageName.equals(filter.packageName);
|
||||
}
|
||||
};
|
||||
|
||||
@ -1472,7 +1475,8 @@ public final class ActivityManagerService extends ActivityManagerNative
|
||||
m.mContext = context;
|
||||
m.mFactoryTest = factoryTest;
|
||||
m.mMainStack = new ActivityStack(m, context, true, thr.mLooper);
|
||||
|
||||
m.mIntentFirewall = new IntentFirewall(m.new IntentFirewallInterface());
|
||||
|
||||
m.mBatteryStatsService.publish(context);
|
||||
m.mUsageStatsService.publish(context);
|
||||
m.mAppOpsService.publish(context);
|
||||
@ -4948,6 +4952,14 @@ public final class ActivityManagerService extends ActivityManagerNative
|
||||
}
|
||||
}
|
||||
|
||||
class IntentFirewallInterface implements IntentFirewall.AMSInterface {
|
||||
public int checkComponentPermission(String permission, int pid, int uid,
|
||||
int owningUid, boolean exported) {
|
||||
return ActivityManagerService.this.checkComponentPermission(permission, pid, uid,
|
||||
owningUid, exported);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This can be called with or without the global lock held.
|
||||
*/
|
||||
|
@ -2489,6 +2489,7 @@ final class ActivityStack {
|
||||
int err = ActivityManager.START_SUCCESS;
|
||||
|
||||
ProcessRecord callerApp = null;
|
||||
|
||||
if (caller != null) {
|
||||
callerApp = mService.getRecordForAppLocked(caller);
|
||||
if (callerApp != null) {
|
||||
@ -2592,34 +2593,37 @@ final class ActivityStack {
|
||||
throw new SecurityException(msg);
|
||||
}
|
||||
|
||||
boolean abort = !mService.mIntentFirewall.checkStartActivity(intent,
|
||||
callerApp==null?null:callerApp.info, callingPackage, callingUid, callingPid,
|
||||
resolvedType, aInfo);
|
||||
|
||||
if (mMainStack) {
|
||||
if (mService.mController != null) {
|
||||
boolean abort = false;
|
||||
try {
|
||||
// The Intent we give to the watcher has the extra data
|
||||
// stripped off, since it can contain private information.
|
||||
Intent watchIntent = intent.cloneFilter();
|
||||
abort = !mService.mController.activityStarting(watchIntent,
|
||||
abort |= !mService.mController.activityStarting(watchIntent,
|
||||
aInfo.applicationInfo.packageName);
|
||||
} catch (RemoteException e) {
|
||||
mService.mController = null;
|
||||
}
|
||||
|
||||
if (abort) {
|
||||
if (resultRecord != null) {
|
||||
sendActivityResultLocked(-1,
|
||||
resultRecord, resultWho, requestCode,
|
||||
Activity.RESULT_CANCELED, null);
|
||||
}
|
||||
// We pretend to the caller that it was really started, but
|
||||
// they will just get a cancel result.
|
||||
mDismissKeyguardOnNextActivity = false;
|
||||
ActivityOptions.abort(options);
|
||||
return ActivityManager.START_SUCCESS;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (abort) {
|
||||
if (resultRecord != null) {
|
||||
sendActivityResultLocked(-1,
|
||||
resultRecord, resultWho, requestCode,
|
||||
Activity.RESULT_CANCELED, null);
|
||||
}
|
||||
// We pretend to the caller that it was really started, but
|
||||
// they will just get a cancel result.
|
||||
mDismissKeyguardOnNextActivity = false;
|
||||
ActivityOptions.abort(options);
|
||||
return ActivityManager.START_SUCCESS;
|
||||
}
|
||||
|
||||
ActivityRecord r = new ActivityRecord(mService, this, callerApp, callingUid, callingPackage,
|
||||
intent, resolvedType, aInfo, mService.mConfiguration,
|
||||
resultRecord, resultWho, requestCode, componentSpecified);
|
||||
|
47
services/java/com/android/server/firewall/AndFilter.java
Normal file
47
services/java/com/android/server/firewall/AndFilter.java
Normal file
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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.pm.ApplicationInfo;
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
class AndFilter extends FilterList {
|
||||
@Override
|
||||
public boolean matches(IntentFirewall ifw, Intent intent, ApplicationInfo callerApp,
|
||||
String callerPackage, int callerUid, int callerPid, String resolvedType,
|
||||
ApplicationInfo resolvedApp) {
|
||||
for (int i=0; i<children.size(); i++) {
|
||||
if (!children.get(i).matches(ifw, intent, callerApp, callerPackage, callerUid,
|
||||
callerPid, resolvedType, resolvedApp)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static final FilterFactory FACTORY = new FilterFactory("and") {
|
||||
@Override
|
||||
public Filter newFilter(XmlPullParser parser)
|
||||
throws IOException, XmlPullParserException {
|
||||
return new AndFilter().readFromXml(parser);
|
||||
}
|
||||
};
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* 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.pm.ApplicationInfo;
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Set;
|
||||
|
||||
class CategoryFilter implements Filter {
|
||||
private static final String ATTR_NAME = "name";
|
||||
|
||||
private final String mCategoryName;
|
||||
|
||||
private CategoryFilter(String categoryName) {
|
||||
mCategoryName = categoryName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(IntentFirewall ifw, Intent intent, ApplicationInfo callerApp, String callerPackage,
|
||||
int callerUid, int callerPid, String resolvedType, ApplicationInfo resolvedApp) {
|
||||
Set<String> categories = intent.getCategories();
|
||||
if (categories == null) {
|
||||
return false;
|
||||
}
|
||||
return categories.contains(mCategoryName);
|
||||
}
|
||||
|
||||
public static final FilterFactory FACTORY = new FilterFactory("category") {
|
||||
@Override
|
||||
public Filter newFilter(XmlPullParser parser)
|
||||
throws IOException, XmlPullParserException {
|
||||
String categoryName = parser.getAttributeValue(null, ATTR_NAME);
|
||||
if (categoryName == null) {
|
||||
throw new XmlPullParserException("Category name must be specified.",
|
||||
parser, null);
|
||||
}
|
||||
return new CategoryFilter(categoryName);
|
||||
}
|
||||
};
|
||||
}
|
42
services/java/com/android/server/firewall/Filter.java
Normal file
42
services/java/com/android/server/firewall/Filter.java
Normal file
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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.pm.ApplicationInfo;
|
||||
|
||||
interface Filter {
|
||||
/**
|
||||
* Does the given intent + context info match this filter?
|
||||
*
|
||||
* @param ifw The IntentFirewall instance
|
||||
* @param intent The intent being started/bound/broadcast
|
||||
* @param callerApp An ApplicationInfo of an application in the caller's process. This may not
|
||||
* be the specific app that is actually sending the intent. This also may be
|
||||
* null, if the caller is the system process, or an unrecognized process (e.g.
|
||||
* am start)
|
||||
* @param callerPackage The package name of the component sending the intent. This value is
|
||||
* provided by the caller and might be forged/faked.
|
||||
* @param callerUid
|
||||
* @param callerPid
|
||||
* @param resolvedType The resolved mime type of the intent
|
||||
* @param resolvedApp The application that contains the resolved component that the intent is
|
||||
*/
|
||||
boolean matches(IntentFirewall ifw, Intent intent, ApplicationInfo callerApp,
|
||||
String callerPackage, int callerUid, int callerPid, String resolvedType,
|
||||
ApplicationInfo resolvedApp);
|
||||
}
|
40
services/java/com/android/server/firewall/FilterFactory.java
Normal file
40
services/java/com/android/server/firewall/FilterFactory.java
Normal file
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* 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 org.xmlpull.v1.XmlPullParser;
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public abstract class FilterFactory {
|
||||
private final String mTag;
|
||||
|
||||
protected FilterFactory(String tag) {
|
||||
if (tag == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
mTag = tag;
|
||||
}
|
||||
|
||||
public String getTagName() {
|
||||
return mTag;
|
||||
}
|
||||
|
||||
public abstract Filter newFilter(XmlPullParser parser)
|
||||
throws IOException, XmlPullParserException;
|
||||
}
|
41
services/java/com/android/server/firewall/FilterList.java
Normal file
41
services/java/com/android/server/firewall/FilterList.java
Normal file
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* 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 com.android.internal.util.XmlUtils;
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
|
||||
abstract class FilterList implements Filter {
|
||||
protected final ArrayList<Filter> children = new ArrayList<Filter>();
|
||||
|
||||
public FilterList readFromXml(XmlPullParser parser) throws IOException, XmlPullParserException {
|
||||
int outerDepth = parser.getDepth();
|
||||
while (XmlUtils.nextElementWithin(parser, outerDepth)) {
|
||||
readChild(parser);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
protected void readChild(XmlPullParser parser) throws IOException, XmlPullParserException {
|
||||
Filter filter = IntentFirewall.parseFilter(parser);
|
||||
children.add(filter);
|
||||
}
|
||||
}
|
315
services/java/com/android/server/firewall/IntentFirewall.java
Normal file
315
services/java/com/android/server/firewall/IntentFirewall.java
Normal file
@ -0,0 +1,315 @@
|
||||
/*
|
||||
* 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";
|
||||
|
||||
private static final String RULES_FILENAME = "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.SENDER_PACKAGE,
|
||||
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;
|
||||
File dataSystemDir = new File(Environment.getDataDirectory(), "system");
|
||||
File rulesFile = new File(dataSystemDir, RULES_FILENAME);
|
||||
readRules(rulesFile);
|
||||
}
|
||||
|
||||
public boolean checkStartActivity(Intent intent, ApplicationInfo callerApp,
|
||||
String callerPackage, 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, callerPackage, 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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
60
services/java/com/android/server/firewall/NotFilter.java
Normal file
60
services/java/com/android/server/firewall/NotFilter.java
Normal file
@ -0,0 +1,60 @@
|
||||
/*
|
||||
* 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.pm.ApplicationInfo;
|
||||
import com.android.internal.util.XmlUtils;
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
class NotFilter implements Filter {
|
||||
private final Filter mChild;
|
||||
|
||||
private NotFilter(Filter child) {
|
||||
mChild = child;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(IntentFirewall ifw, Intent intent, ApplicationInfo callerApp,
|
||||
String callerPackage, int callerUid, int callerPid, String resolvedType,
|
||||
ApplicationInfo resolvedApp) {
|
||||
return !mChild.matches(ifw, intent, callerApp, callerPackage, callerUid, callerPid,
|
||||
resolvedType, resolvedApp);
|
||||
}
|
||||
|
||||
public static final FilterFactory FACTORY = new FilterFactory("not") {
|
||||
@Override
|
||||
public Filter newFilter(XmlPullParser parser)
|
||||
throws IOException, XmlPullParserException {
|
||||
Filter child = null;
|
||||
int outerDepth = parser.getDepth();
|
||||
while (XmlUtils.nextElementWithin(parser, outerDepth)) {
|
||||
Filter filter = IntentFirewall.parseFilter(parser);
|
||||
if (child == null) {
|
||||
child = filter;
|
||||
} else {
|
||||
throw new XmlPullParserException(
|
||||
"<not> tag can only contain a single child filter.", parser, null);
|
||||
}
|
||||
}
|
||||
return new NotFilter(child);
|
||||
}
|
||||
};
|
||||
}
|
47
services/java/com/android/server/firewall/OrFilter.java
Normal file
47
services/java/com/android/server/firewall/OrFilter.java
Normal file
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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.pm.ApplicationInfo;
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
class OrFilter extends FilterList {
|
||||
@Override
|
||||
public boolean matches(IntentFirewall ifw, Intent intent, ApplicationInfo callerApp,
|
||||
String callerPackage, int callerUid, int callerPid, String resolvedType,
|
||||
ApplicationInfo resolvedApp) {
|
||||
for (int i=0; i<children.size(); i++) {
|
||||
if (children.get(i).matches(ifw, intent, callerApp, callerPackage, callerUid, callerPid,
|
||||
resolvedType, resolvedApp)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static final FilterFactory FACTORY = new FilterFactory("or") {
|
||||
@Override
|
||||
public Filter newFilter(XmlPullParser parser)
|
||||
throws IOException, XmlPullParserException {
|
||||
return new OrFilter().readFromXml(parser);
|
||||
}
|
||||
};
|
||||
}
|
111
services/java/com/android/server/firewall/PortFilter.java
Normal file
111
services/java/com/android/server/firewall/PortFilter.java
Normal file
@ -0,0 +1,111 @@
|
||||
/*
|
||||
* 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.pm.ApplicationInfo;
|
||||
import android.net.Uri;
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
class PortFilter implements Filter {
|
||||
private static final String ATTR_EQUALS = "equals";
|
||||
private static final String ATTR_MIN = "min";
|
||||
private static final String ATTR_MAX = "max";
|
||||
|
||||
private static final int NO_BOUND = -1;
|
||||
|
||||
// both bounds are inclusive
|
||||
private final int mLowerBound;
|
||||
private final int mUpperBound;
|
||||
|
||||
private PortFilter(int lowerBound, int upperBound) {
|
||||
mLowerBound = lowerBound;
|
||||
mUpperBound = upperBound;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(IntentFirewall ifw, Intent intent, ApplicationInfo callerApp,
|
||||
String callerPackage, int callerUid, int callerPid, String resolvedType,
|
||||
ApplicationInfo resolvedApp) {
|
||||
int port = -1;
|
||||
Uri uri = intent.getData();
|
||||
if (uri != null) {
|
||||
port = uri.getPort();
|
||||
}
|
||||
return port != -1 &&
|
||||
(mLowerBound == NO_BOUND || mLowerBound <= port) &&
|
||||
(mUpperBound == NO_BOUND || mUpperBound >= port);
|
||||
}
|
||||
|
||||
public static final FilterFactory FACTORY = new FilterFactory("port") {
|
||||
@Override
|
||||
public Filter newFilter(XmlPullParser parser)
|
||||
throws IOException, XmlPullParserException {
|
||||
int lowerBound = NO_BOUND;
|
||||
int upperBound = NO_BOUND;
|
||||
|
||||
String equalsValue = parser.getAttributeValue(null, ATTR_EQUALS);
|
||||
if (equalsValue != null) {
|
||||
int value;
|
||||
try {
|
||||
value = Integer.parseInt(equalsValue);
|
||||
} catch (NumberFormatException ex) {
|
||||
throw new XmlPullParserException("Invalid port value: " + equalsValue,
|
||||
parser, null);
|
||||
}
|
||||
lowerBound = value;
|
||||
upperBound = value;
|
||||
}
|
||||
|
||||
String lowerBoundString = parser.getAttributeValue(null, ATTR_MIN);
|
||||
String upperBoundString = parser.getAttributeValue(null, ATTR_MAX);
|
||||
if (lowerBoundString != null || upperBoundString != null) {
|
||||
if (equalsValue != null) {
|
||||
throw new XmlPullParserException(
|
||||
"Port filter cannot use both equals and range filtering",
|
||||
parser, null);
|
||||
}
|
||||
|
||||
if (lowerBoundString != null) {
|
||||
try {
|
||||
lowerBound = Integer.parseInt(lowerBoundString);
|
||||
} catch (NumberFormatException ex) {
|
||||
throw new XmlPullParserException(
|
||||
"Invalid minimum port value: " + lowerBoundString,
|
||||
parser, null);
|
||||
}
|
||||
}
|
||||
|
||||
if (upperBoundString != null) {
|
||||
try {
|
||||
upperBound = Integer.parseInt(upperBoundString);
|
||||
} catch (NumberFormatException ex) {
|
||||
throw new XmlPullParserException(
|
||||
"Invalid maximum port value: " + upperBoundString,
|
||||
parser, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// an empty port filter is explicitly allowed, and checks for the existence of a port
|
||||
return new PortFilter(lowerBound, upperBound);
|
||||
}
|
||||
};
|
||||
}
|
115
services/java/com/android/server/firewall/SenderFilter.java
Normal file
115
services/java/com/android/server/firewall/SenderFilter.java
Normal file
@ -0,0 +1,115 @@
|
||||
/*
|
||||
* 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.pm.ApplicationInfo;
|
||||
import android.os.Process;
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
class SenderFilter {
|
||||
private static final String ATTR_TYPE = "type";
|
||||
|
||||
private static final String VAL_SIGNATURE = "signature";
|
||||
private static final String VAL_SYSTEM = "system";
|
||||
private static final String VAL_SYSTEM_OR_SIGNATURE = "system|signature";
|
||||
private static final String VAL_USER_ID = "userId";
|
||||
|
||||
static boolean isSystemApp(ApplicationInfo callerApp, int callerUid, int callerPid) {
|
||||
if (callerUid == Process.SYSTEM_UID ||
|
||||
callerPid == Process.myPid()) {
|
||||
return true;
|
||||
}
|
||||
if (callerApp == null) {
|
||||
return false;
|
||||
}
|
||||
return (callerApp.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
|
||||
}
|
||||
|
||||
public static final FilterFactory FACTORY = new FilterFactory("sender") {
|
||||
@Override
|
||||
public Filter newFilter(XmlPullParser parser) throws IOException, XmlPullParserException {
|
||||
String typeString = parser.getAttributeValue(null, ATTR_TYPE);
|
||||
if (typeString == null) {
|
||||
throw new XmlPullParserException("type attribute must be specified for <sender>",
|
||||
parser, null);
|
||||
}
|
||||
if (typeString.equals(VAL_SYSTEM)) {
|
||||
return SYSTEM;
|
||||
} else if (typeString.equals(VAL_SIGNATURE)) {
|
||||
return SIGNATURE;
|
||||
} else if (typeString.equals(VAL_SYSTEM_OR_SIGNATURE)) {
|
||||
return SYSTEM_OR_SIGNATURE;
|
||||
} else if (typeString.equals(VAL_USER_ID)) {
|
||||
return USER_ID;
|
||||
}
|
||||
throw new XmlPullParserException(
|
||||
"Invalid type attribute for <sender>: " + typeString, parser, null);
|
||||
}
|
||||
};
|
||||
|
||||
private static final Filter SIGNATURE = new Filter() {
|
||||
@Override
|
||||
public boolean matches(IntentFirewall ifw, Intent intent, ApplicationInfo callerApp,
|
||||
String callerPackage, int callerUid, int callerPid, String resolvedType,
|
||||
ApplicationInfo resolvedApp) {
|
||||
if (callerApp == null) {
|
||||
return false;
|
||||
}
|
||||
return ifw.signaturesMatch(callerUid, resolvedApp.uid);
|
||||
}
|
||||
};
|
||||
|
||||
private static final Filter SYSTEM = new Filter() {
|
||||
@Override
|
||||
public boolean matches(IntentFirewall ifw, Intent intent, ApplicationInfo callerApp,
|
||||
String callerPackage, int callerUid, int callerPid, String resolvedType,
|
||||
ApplicationInfo resolvedApp) {
|
||||
if (callerApp == null) {
|
||||
// if callerApp is null, the caller is the system process
|
||||
return false;
|
||||
}
|
||||
return isSystemApp(callerApp, callerUid, callerPid);
|
||||
}
|
||||
};
|
||||
|
||||
private static final Filter SYSTEM_OR_SIGNATURE = new Filter() {
|
||||
@Override
|
||||
public boolean matches(IntentFirewall ifw, Intent intent, ApplicationInfo callerApp,
|
||||
String callerPackage, int callerUid, int callerPid, String resolvedType,
|
||||
ApplicationInfo resolvedApp) {
|
||||
return isSystemApp(callerApp, callerUid, callerPid) ||
|
||||
ifw.signaturesMatch(callerUid, resolvedApp.uid);
|
||||
}
|
||||
};
|
||||
|
||||
private static final Filter USER_ID = new Filter() {
|
||||
@Override
|
||||
public boolean matches(IntentFirewall ifw, Intent intent, ApplicationInfo callerApp,
|
||||
String callerPackage, int callerUid, int callerPid, String resolvedType,
|
||||
ApplicationInfo resolvedApp) {
|
||||
// This checks whether the caller is either the system process, or has the same user id
|
||||
// I.e. the same app, or an app that uses the same shared user id.
|
||||
// This is the same set of applications that would be able to access the component if
|
||||
// it wasn't exported.
|
||||
return ifw.checkComponentPermission(null, callerPid, callerUid, resolvedApp.uid, false);
|
||||
}
|
||||
};
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* 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.pm.ApplicationInfo;
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
class SenderPermissionFilter implements Filter {
|
||||
private static final String ATTR_NAME = "name";
|
||||
|
||||
private final String mPermission;
|
||||
|
||||
private SenderPermissionFilter(String permission) {
|
||||
mPermission = permission;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(IntentFirewall ifw, Intent intent, ApplicationInfo callerApp,
|
||||
String callerPackage, int callerUid, int callerPid, String resolvedType,
|
||||
ApplicationInfo resolvedApp) {
|
||||
// We assume the component is exported here. If the component is not exported, then
|
||||
// ActivityManager would only resolve to this component for callers from the same uid.
|
||||
// In this case, it doesn't matter whether the component is exported or not.
|
||||
return ifw.checkComponentPermission(mPermission, callerPid, callerUid, resolvedApp.uid,
|
||||
true);
|
||||
}
|
||||
|
||||
public static final FilterFactory FACTORY = new FilterFactory("sender-permission") {
|
||||
@Override
|
||||
public Filter newFilter(XmlPullParser parser)
|
||||
throws IOException, XmlPullParserException {
|
||||
String permission = parser.getAttributeValue(null, ATTR_NAME);
|
||||
if (permission == null) {
|
||||
throw new XmlPullParserException("Permission name must be specified.",
|
||||
parser, null);
|
||||
}
|
||||
return new SenderPermissionFilter(permission);
|
||||
}
|
||||
};
|
||||
}
|
353
services/java/com/android/server/firewall/StringFilter.java
Normal file
353
services/java/com/android/server/firewall/StringFilter.java
Normal file
@ -0,0 +1,353 @@
|
||||
/*
|
||||
* 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.ComponentName;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.net.Uri;
|
||||
import android.os.PatternMatcher;
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
abstract class StringFilter implements Filter {
|
||||
private static final String ATTR_EQUALS = "equals";
|
||||
private static final String ATTR_STARTS_WITH = "startsWith";
|
||||
private static final String ATTR_CONTAINS = "contains";
|
||||
private static final String ATTR_PATTERN = "pattern";
|
||||
private static final String ATTR_REGEX = "regex";
|
||||
private static final String ATTR_IS_NULL = "isNull";
|
||||
|
||||
private final ValueProvider mValueProvider;
|
||||
|
||||
private StringFilter(ValueProvider valueProvider) {
|
||||
this.mValueProvider = valueProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new StringFilter based on the string filter attribute on the current
|
||||
* element, and the given StringValueMatcher.
|
||||
*
|
||||
* The current node should contain exactly 1 string filter attribute. E.g. equals,
|
||||
* contains, etc. Otherwise, an XmlPullParserException will be thrown.
|
||||
*
|
||||
* @param parser An XmlPullParser object positioned at an element that should
|
||||
* contain a string filter attribute
|
||||
* @return This StringFilter object
|
||||
*/
|
||||
public static StringFilter readFromXml(ValueProvider valueProvider, XmlPullParser parser)
|
||||
throws IOException, XmlPullParserException {
|
||||
StringFilter filter = null;
|
||||
|
||||
for (int i=0; i<parser.getAttributeCount(); i++) {
|
||||
StringFilter newFilter = getFilter(valueProvider, parser, i);
|
||||
if (newFilter != null) {
|
||||
if (filter != null) {
|
||||
throw new XmlPullParserException("Multiple string filter attributes found");
|
||||
}
|
||||
filter = newFilter;
|
||||
}
|
||||
}
|
||||
|
||||
if (filter == null) {
|
||||
// if there are no string filter attributes, we default to isNull="false" so that an
|
||||
// empty filter is equivalent to an existence check
|
||||
filter = new IsNullFilter(valueProvider, false);
|
||||
}
|
||||
|
||||
return filter;
|
||||
}
|
||||
|
||||
private static StringFilter getFilter(ValueProvider valueProvider, XmlPullParser parser,
|
||||
int attributeIndex) {
|
||||
String attributeName = parser.getAttributeName(attributeIndex);
|
||||
|
||||
switch (attributeName.charAt(0)) {
|
||||
case 'e':
|
||||
if (!attributeName.equals(ATTR_EQUALS)) {
|
||||
return null;
|
||||
}
|
||||
return new EqualsFilter(valueProvider, parser.getAttributeValue(attributeIndex));
|
||||
case 'i':
|
||||
if (!attributeName.equals(ATTR_IS_NULL)) {
|
||||
return null;
|
||||
}
|
||||
return new IsNullFilter(valueProvider, parser.getAttributeValue(attributeIndex));
|
||||
case 's':
|
||||
if (!attributeName.equals(ATTR_STARTS_WITH)) {
|
||||
return null;
|
||||
}
|
||||
return new StartsWithFilter(valueProvider,
|
||||
parser.getAttributeValue(attributeIndex));
|
||||
case 'c':
|
||||
if (!attributeName.equals(ATTR_CONTAINS)) {
|
||||
return null;
|
||||
}
|
||||
return new ContainsFilter(valueProvider, parser.getAttributeValue(attributeIndex));
|
||||
case 'p':
|
||||
if (!attributeName.equals(ATTR_PATTERN)) {
|
||||
return null;
|
||||
}
|
||||
return new PatternStringFilter(valueProvider,
|
||||
parser.getAttributeValue(attributeIndex));
|
||||
case 'r':
|
||||
if (!attributeName.equals(ATTR_REGEX)) {
|
||||
return null;
|
||||
}
|
||||
return new RegexFilter(valueProvider, parser.getAttributeValue(attributeIndex));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected abstract boolean matchesValue(String value);
|
||||
|
||||
@Override
|
||||
public boolean matches(IntentFirewall ifw, Intent intent, ApplicationInfo callerApp, String callerPackage,
|
||||
int callerUid, int callerPid, String resolvedType, ApplicationInfo resolvedApp) {
|
||||
String value = mValueProvider.getValue(intent, callerApp, callerPackage, resolvedType,
|
||||
resolvedApp);
|
||||
return matchesValue(value);
|
||||
}
|
||||
|
||||
private static abstract class ValueProvider extends FilterFactory {
|
||||
protected ValueProvider(String tag) {
|
||||
super(tag);
|
||||
}
|
||||
|
||||
public Filter newFilter(XmlPullParser parser)
|
||||
throws IOException, XmlPullParserException {
|
||||
return StringFilter.readFromXml(this, parser);
|
||||
}
|
||||
|
||||
public abstract String getValue(Intent intent, ApplicationInfo callerApp,
|
||||
String callerPackage, String resolvedType, ApplicationInfo resolvedApp);
|
||||
}
|
||||
|
||||
private static class EqualsFilter extends StringFilter {
|
||||
private final String mFilterValue;
|
||||
|
||||
public EqualsFilter(ValueProvider valueProvider, String attrValue) {
|
||||
super(valueProvider);
|
||||
mFilterValue = attrValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matchesValue(String value) {
|
||||
return value != null && value.equals(mFilterValue);
|
||||
}
|
||||
}
|
||||
|
||||
private static class ContainsFilter extends StringFilter {
|
||||
private final String mFilterValue;
|
||||
|
||||
public ContainsFilter(ValueProvider valueProvider, String attrValue) {
|
||||
super(valueProvider);
|
||||
mFilterValue = attrValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matchesValue(String value) {
|
||||
return value != null && value.contains(mFilterValue);
|
||||
}
|
||||
}
|
||||
|
||||
private static class StartsWithFilter extends StringFilter {
|
||||
private final String mFilterValue;
|
||||
|
||||
public StartsWithFilter(ValueProvider valueProvider, String attrValue) {
|
||||
super(valueProvider);
|
||||
mFilterValue = attrValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matchesValue(String value) {
|
||||
return value != null && value.startsWith(mFilterValue);
|
||||
}
|
||||
}
|
||||
|
||||
private static class PatternStringFilter extends StringFilter {
|
||||
private final PatternMatcher mPattern;
|
||||
|
||||
public PatternStringFilter(ValueProvider valueProvider, String attrValue) {
|
||||
super(valueProvider);
|
||||
mPattern = new PatternMatcher(attrValue, PatternMatcher.PATTERN_SIMPLE_GLOB);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matchesValue(String value) {
|
||||
return value != null && mPattern.match(value);
|
||||
}
|
||||
}
|
||||
|
||||
private static class RegexFilter extends StringFilter {
|
||||
private final Pattern mPattern;
|
||||
|
||||
public RegexFilter(ValueProvider valueProvider, String attrValue) {
|
||||
super(valueProvider);
|
||||
this.mPattern = Pattern.compile(attrValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matchesValue(String value) {
|
||||
return value != null && mPattern.matcher(value).matches();
|
||||
}
|
||||
}
|
||||
|
||||
private static class IsNullFilter extends StringFilter {
|
||||
private final boolean mIsNull;
|
||||
|
||||
public IsNullFilter(ValueProvider valueProvider, String attrValue) {
|
||||
super(valueProvider);
|
||||
mIsNull = Boolean.parseBoolean(attrValue);
|
||||
}
|
||||
|
||||
public IsNullFilter(ValueProvider valueProvider, boolean isNull) {
|
||||
super(valueProvider);
|
||||
mIsNull = isNull;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matchesValue(String value) {
|
||||
return (value == null) == mIsNull;
|
||||
}
|
||||
}
|
||||
|
||||
public static final ValueProvider COMPONENT = new ValueProvider("component") {
|
||||
@Override
|
||||
public String getValue(Intent intent, ApplicationInfo callerApp, String callerPackage,
|
||||
String resolvedType, ApplicationInfo resolvedApp) {
|
||||
ComponentName cn = intent.getComponent();
|
||||
if (cn != null) {
|
||||
return cn.flattenToString();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
public static final ValueProvider COMPONENT_NAME = new ValueProvider("component-name") {
|
||||
@Override
|
||||
public String getValue(Intent intent, ApplicationInfo callerApp, String callerPackage,
|
||||
String resolvedType, ApplicationInfo resolvedApp) {
|
||||
ComponentName cn = intent.getComponent();
|
||||
if (cn != null) {
|
||||
return cn.getClassName();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
public static final ValueProvider COMPONENT_PACKAGE = new ValueProvider("component-package") {
|
||||
@Override
|
||||
public String getValue(Intent intent, ApplicationInfo callerApp, String callerPackage,
|
||||
String resolvedType, ApplicationInfo resolvedApp) {
|
||||
ComponentName cn = intent.getComponent();
|
||||
if (cn != null) {
|
||||
return cn.getPackageName();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
public static final ValueProvider SENDER_PACKAGE = new ValueProvider("sender-package") {
|
||||
@Override
|
||||
public String getValue(Intent intent, ApplicationInfo callerApp, String callerPackage,
|
||||
String resolvedType, ApplicationInfo resolvedApp) {
|
||||
// TODO: We can't trust this value, so maybe should check all packages in the caller process?
|
||||
return callerPackage;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
public static final FilterFactory ACTION = new ValueProvider("action") {
|
||||
@Override
|
||||
public String getValue(Intent intent, ApplicationInfo callerApp, String callerPackage,
|
||||
String resolvedType, ApplicationInfo resolvedApp) {
|
||||
return intent.getAction();
|
||||
}
|
||||
};
|
||||
|
||||
public static final ValueProvider DATA = new ValueProvider("data") {
|
||||
@Override
|
||||
public String getValue(Intent intent, ApplicationInfo callerApp, String callerPackage,
|
||||
String resolvedType, ApplicationInfo resolvedApp) {
|
||||
Uri data = intent.getData();
|
||||
if (data != null) {
|
||||
return data.toString();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
public static final ValueProvider MIME_TYPE = new ValueProvider("mime-type") {
|
||||
@Override
|
||||
public String getValue(Intent intent, ApplicationInfo callerApp, String callerPackage,
|
||||
String resolvedType, ApplicationInfo resolvedApp) {
|
||||
return resolvedType;
|
||||
}
|
||||
};
|
||||
|
||||
public static final ValueProvider SCHEME = new ValueProvider("scheme") {
|
||||
@Override
|
||||
public String getValue(Intent intent, ApplicationInfo callerApp, String callerPackage,
|
||||
String resolvedType, ApplicationInfo resolvedApp) {
|
||||
Uri data = intent.getData();
|
||||
if (data != null) {
|
||||
return data.getScheme();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
public static final ValueProvider SSP = new ValueProvider("scheme-specific-part") {
|
||||
@Override
|
||||
public String getValue(Intent intent, ApplicationInfo callerApp, String callerPackage,
|
||||
String resolvedType, ApplicationInfo resolvedApp) {
|
||||
Uri data = intent.getData();
|
||||
if (data != null) {
|
||||
return data.getSchemeSpecificPart();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
public static final ValueProvider HOST = new ValueProvider("host") {
|
||||
@Override
|
||||
public String getValue(Intent intent, ApplicationInfo callerApp, String callerPackage,
|
||||
String resolvedType, ApplicationInfo resolvedApp) {
|
||||
Uri data = intent.getData();
|
||||
if (data != null) {
|
||||
return data.getHost();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
public static final ValueProvider PATH = new ValueProvider("path") {
|
||||
@Override
|
||||
public String getValue(Intent intent, ApplicationInfo callerApp, String callerPackage,
|
||||
String resolvedType, ApplicationInfo resolvedApp) {
|
||||
Uri data = intent.getData();
|
||||
if (data != null) {
|
||||
return data.getPath();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
@ -5410,8 +5410,9 @@ public class PackageManagerService extends IPackageManager.Stub {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String packageForFilter(PackageParser.ActivityIntentInfo info) {
|
||||
return info.activity.owner.packageName;
|
||||
protected boolean isPackageForFilter(String packageName,
|
||||
PackageParser.ActivityIntentInfo info) {
|
||||
return packageName.equals(info.activity.owner.packageName);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -5607,8 +5608,9 @@ public class PackageManagerService extends IPackageManager.Stub {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String packageForFilter(PackageParser.ServiceIntentInfo info) {
|
||||
return info.service.owner.packageName;
|
||||
protected boolean isPackageForFilter(String packageName,
|
||||
PackageParser.ServiceIntentInfo info) {
|
||||
return packageName.equals(info.service.owner.packageName);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -27,8 +27,8 @@ public class PreferredIntentResolver
|
||||
return new PreferredActivity[size];
|
||||
}
|
||||
@Override
|
||||
protected String packageForFilter(PreferredActivity filter) {
|
||||
return filter.mPref.mComponent.getPackageName();
|
||||
protected boolean isPackageForFilter(String packageName, PreferredActivity filter) {
|
||||
return packageName.equals(filter.mPref.mComponent.getPackageName());
|
||||
}
|
||||
@Override
|
||||
protected void dumpFilter(PrintWriter out, String prefix,
|
||||
|
Reference in New Issue
Block a user