summaryrefslogtreecommitdiffstats
path: root/services/java/com/android/server/firewall/IntentFirewall.java
diff options
context:
space:
mode:
Diffstat (limited to 'services/java/com/android/server/firewall/IntentFirewall.java')
-rw-r--r--services/java/com/android/server/firewall/IntentFirewall.java315
1 files changed, 315 insertions, 0 deletions
diff --git a/services/java/com/android/server/firewall/IntentFirewall.java b/services/java/com/android/server/firewall/IntentFirewall.java
new file mode 100644
index 0000000..ebbbd86
--- /dev/null
+++ b/services/java/com/android/server/firewall/IntentFirewall.java
@@ -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;
+ }
+}