| /* |
| * 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.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, ComponentName resolvedComponent, Intent intent, |
| int callerUid, int callerPid, String resolvedType, int receivingUid) { |
| String value = mValueProvider.getValue(resolvedComponent, intent, resolvedType); |
| 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(ComponentName resolvedComponent, Intent intent, |
| String resolvedType); |
| } |
| |
| 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(ComponentName resolvedComponent, Intent intent, |
| String resolvedType) { |
| if (resolvedComponent != null) { |
| return resolvedComponent.flattenToString(); |
| } |
| return null; |
| } |
| }; |
| |
| public static final ValueProvider COMPONENT_NAME = new ValueProvider("component-name") { |
| @Override |
| public String getValue(ComponentName resolvedComponent, Intent intent, |
| String resolvedType) { |
| if (resolvedComponent != null) { |
| return resolvedComponent.getClassName(); |
| } |
| return null; |
| } |
| }; |
| |
| public static final ValueProvider COMPONENT_PACKAGE = new ValueProvider("component-package") { |
| @Override |
| public String getValue(ComponentName resolvedComponent, Intent intent, |
| String resolvedType) { |
| if (resolvedComponent != null) { |
| return resolvedComponent.getPackageName(); |
| } |
| return null; |
| } |
| }; |
| |
| public static final FilterFactory ACTION = new ValueProvider("action") { |
| @Override |
| public String getValue(ComponentName resolvedComponent, Intent intent, |
| String resolvedType) { |
| return intent.getAction(); |
| } |
| }; |
| |
| public static final ValueProvider DATA = new ValueProvider("data") { |
| @Override |
| public String getValue(ComponentName resolvedComponent, Intent intent, |
| String resolvedType) { |
| 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(ComponentName resolvedComponent, Intent intent, |
| String resolvedType) { |
| return resolvedType; |
| } |
| }; |
| |
| public static final ValueProvider SCHEME = new ValueProvider("scheme") { |
| @Override |
| public String getValue(ComponentName resolvedComponent, Intent intent, |
| String resolvedType) { |
| 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(ComponentName resolvedComponent, Intent intent, |
| String resolvedType) { |
| Uri data = intent.getData(); |
| if (data != null) { |
| return data.getSchemeSpecificPart(); |
| } |
| return null; |
| } |
| }; |
| |
| public static final ValueProvider HOST = new ValueProvider("host") { |
| @Override |
| public String getValue(ComponentName resolvedComponent, Intent intent, |
| String resolvedType) { |
| Uri data = intent.getData(); |
| if (data != null) { |
| return data.getHost(); |
| } |
| return null; |
| } |
| }; |
| |
| public static final ValueProvider PATH = new ValueProvider("path") { |
| @Override |
| public String getValue(ComponentName resolvedComponent, Intent intent, |
| String resolvedType) { |
| Uri data = intent.getData(); |
| if (data != null) { |
| return data.getPath(); |
| } |
| return null; |
| } |
| }; |
| } |