/*
|
* 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;
|
}
|
};
|
}
|