1 /* 2 * Copyright (C) 2013 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.firewall; 18 19 import android.content.ComponentName; 20 import android.content.Intent; 21 import android.net.Uri; 22 import android.os.PatternMatcher; 23 import org.xmlpull.v1.XmlPullParser; 24 import org.xmlpull.v1.XmlPullParserException; 25 26 import java.io.IOException; 27 import java.util.regex.Pattern; 28 29 abstract class StringFilter implements Filter { 30 private static final String ATTR_EQUALS = "equals"; 31 private static final String ATTR_STARTS_WITH = "startsWith"; 32 private static final String ATTR_CONTAINS = "contains"; 33 private static final String ATTR_PATTERN = "pattern"; 34 private static final String ATTR_REGEX = "regex"; 35 private static final String ATTR_IS_NULL = "isNull"; 36 37 private final ValueProvider mValueProvider; 38 StringFilter(ValueProvider valueProvider)39 private StringFilter(ValueProvider valueProvider) { 40 this.mValueProvider = valueProvider; 41 } 42 43 /** 44 * Constructs a new StringFilter based on the string filter attribute on the current 45 * element, and the given StringValueMatcher. 46 * 47 * The current node should contain exactly 1 string filter attribute. E.g. equals, 48 * contains, etc. Otherwise, an XmlPullParserException will be thrown. 49 * 50 * @param parser An XmlPullParser object positioned at an element that should 51 * contain a string filter attribute 52 * @return This StringFilter object 53 */ readFromXml(ValueProvider valueProvider, XmlPullParser parser)54 public static StringFilter readFromXml(ValueProvider valueProvider, XmlPullParser parser) 55 throws IOException, XmlPullParserException { 56 StringFilter filter = null; 57 58 for (int i=0; i<parser.getAttributeCount(); i++) { 59 StringFilter newFilter = getFilter(valueProvider, parser, i); 60 if (newFilter != null) { 61 if (filter != null) { 62 throw new XmlPullParserException("Multiple string filter attributes found"); 63 } 64 filter = newFilter; 65 } 66 } 67 68 if (filter == null) { 69 // if there are no string filter attributes, we default to isNull="false" so that an 70 // empty filter is equivalent to an existence check 71 filter = new IsNullFilter(valueProvider, false); 72 } 73 74 return filter; 75 } 76 getFilter(ValueProvider valueProvider, XmlPullParser parser, int attributeIndex)77 private static StringFilter getFilter(ValueProvider valueProvider, XmlPullParser parser, 78 int attributeIndex) { 79 String attributeName = parser.getAttributeName(attributeIndex); 80 81 switch (attributeName.charAt(0)) { 82 case 'e': 83 if (!attributeName.equals(ATTR_EQUALS)) { 84 return null; 85 } 86 return new EqualsFilter(valueProvider, parser.getAttributeValue(attributeIndex)); 87 case 'i': 88 if (!attributeName.equals(ATTR_IS_NULL)) { 89 return null; 90 } 91 return new IsNullFilter(valueProvider, parser.getAttributeValue(attributeIndex)); 92 case 's': 93 if (!attributeName.equals(ATTR_STARTS_WITH)) { 94 return null; 95 } 96 return new StartsWithFilter(valueProvider, 97 parser.getAttributeValue(attributeIndex)); 98 case 'c': 99 if (!attributeName.equals(ATTR_CONTAINS)) { 100 return null; 101 } 102 return new ContainsFilter(valueProvider, parser.getAttributeValue(attributeIndex)); 103 case 'p': 104 if (!attributeName.equals(ATTR_PATTERN)) { 105 return null; 106 } 107 return new PatternStringFilter(valueProvider, 108 parser.getAttributeValue(attributeIndex)); 109 case 'r': 110 if (!attributeName.equals(ATTR_REGEX)) { 111 return null; 112 } 113 return new RegexFilter(valueProvider, parser.getAttributeValue(attributeIndex)); 114 } 115 return null; 116 } 117 matchesValue(String value)118 protected abstract boolean matchesValue(String value); 119 120 @Override matches(IntentFirewall ifw, ComponentName resolvedComponent, Intent intent, int callerUid, int callerPid, String resolvedType, int receivingUid)121 public boolean matches(IntentFirewall ifw, ComponentName resolvedComponent, Intent intent, 122 int callerUid, int callerPid, String resolvedType, int receivingUid) { 123 String value = mValueProvider.getValue(resolvedComponent, intent, resolvedType); 124 return matchesValue(value); 125 } 126 127 private static abstract class ValueProvider extends FilterFactory { ValueProvider(String tag)128 protected ValueProvider(String tag) { 129 super(tag); 130 } 131 newFilter(XmlPullParser parser)132 public Filter newFilter(XmlPullParser parser) 133 throws IOException, XmlPullParserException { 134 return StringFilter.readFromXml(this, parser); 135 } 136 getValue(ComponentName resolvedComponent, Intent intent, String resolvedType)137 public abstract String getValue(ComponentName resolvedComponent, Intent intent, 138 String resolvedType); 139 } 140 141 private static class EqualsFilter extends StringFilter { 142 private final String mFilterValue; 143 EqualsFilter(ValueProvider valueProvider, String attrValue)144 public EqualsFilter(ValueProvider valueProvider, String attrValue) { 145 super(valueProvider); 146 mFilterValue = attrValue; 147 } 148 149 @Override matchesValue(String value)150 public boolean matchesValue(String value) { 151 return value != null && value.equals(mFilterValue); 152 } 153 } 154 155 private static class ContainsFilter extends StringFilter { 156 private final String mFilterValue; 157 ContainsFilter(ValueProvider valueProvider, String attrValue)158 public ContainsFilter(ValueProvider valueProvider, String attrValue) { 159 super(valueProvider); 160 mFilterValue = attrValue; 161 } 162 163 @Override matchesValue(String value)164 public boolean matchesValue(String value) { 165 return value != null && value.contains(mFilterValue); 166 } 167 } 168 169 private static class StartsWithFilter extends StringFilter { 170 private final String mFilterValue; 171 StartsWithFilter(ValueProvider valueProvider, String attrValue)172 public StartsWithFilter(ValueProvider valueProvider, String attrValue) { 173 super(valueProvider); 174 mFilterValue = attrValue; 175 } 176 177 @Override matchesValue(String value)178 public boolean matchesValue(String value) { 179 return value != null && value.startsWith(mFilterValue); 180 } 181 } 182 183 private static class PatternStringFilter extends StringFilter { 184 private final PatternMatcher mPattern; 185 PatternStringFilter(ValueProvider valueProvider, String attrValue)186 public PatternStringFilter(ValueProvider valueProvider, String attrValue) { 187 super(valueProvider); 188 mPattern = new PatternMatcher(attrValue, PatternMatcher.PATTERN_SIMPLE_GLOB); 189 } 190 191 @Override matchesValue(String value)192 public boolean matchesValue(String value) { 193 return value != null && mPattern.match(value); 194 } 195 } 196 197 private static class RegexFilter extends StringFilter { 198 private final Pattern mPattern; 199 RegexFilter(ValueProvider valueProvider, String attrValue)200 public RegexFilter(ValueProvider valueProvider, String attrValue) { 201 super(valueProvider); 202 this.mPattern = Pattern.compile(attrValue); 203 } 204 205 @Override matchesValue(String value)206 public boolean matchesValue(String value) { 207 return value != null && mPattern.matcher(value).matches(); 208 } 209 } 210 211 private static class IsNullFilter extends StringFilter { 212 private final boolean mIsNull; 213 IsNullFilter(ValueProvider valueProvider, String attrValue)214 public IsNullFilter(ValueProvider valueProvider, String attrValue) { 215 super(valueProvider); 216 mIsNull = Boolean.parseBoolean(attrValue); 217 } 218 IsNullFilter(ValueProvider valueProvider, boolean isNull)219 public IsNullFilter(ValueProvider valueProvider, boolean isNull) { 220 super(valueProvider); 221 mIsNull = isNull; 222 } 223 224 @Override matchesValue(String value)225 public boolean matchesValue(String value) { 226 return (value == null) == mIsNull; 227 } 228 } 229 230 public static final ValueProvider COMPONENT = new ValueProvider("component") { 231 @Override 232 public String getValue(ComponentName resolvedComponent, Intent intent, 233 String resolvedType) { 234 if (resolvedComponent != null) { 235 return resolvedComponent.flattenToString(); 236 } 237 return null; 238 } 239 }; 240 241 public static final ValueProvider COMPONENT_NAME = new ValueProvider("component-name") { 242 @Override 243 public String getValue(ComponentName resolvedComponent, Intent intent, 244 String resolvedType) { 245 if (resolvedComponent != null) { 246 return resolvedComponent.getClassName(); 247 } 248 return null; 249 } 250 }; 251 252 public static final ValueProvider COMPONENT_PACKAGE = new ValueProvider("component-package") { 253 @Override 254 public String getValue(ComponentName resolvedComponent, Intent intent, 255 String resolvedType) { 256 if (resolvedComponent != null) { 257 return resolvedComponent.getPackageName(); 258 } 259 return null; 260 } 261 }; 262 263 public static final FilterFactory ACTION = new ValueProvider("action") { 264 @Override 265 public String getValue(ComponentName resolvedComponent, Intent intent, 266 String resolvedType) { 267 return intent.getAction(); 268 } 269 }; 270 271 public static final ValueProvider DATA = new ValueProvider("data") { 272 @Override 273 public String getValue(ComponentName resolvedComponent, Intent intent, 274 String resolvedType) { 275 Uri data = intent.getData(); 276 if (data != null) { 277 return data.toString(); 278 } 279 return null; 280 } 281 }; 282 283 public static final ValueProvider MIME_TYPE = new ValueProvider("mime-type") { 284 @Override 285 public String getValue(ComponentName resolvedComponent, Intent intent, 286 String resolvedType) { 287 return resolvedType; 288 } 289 }; 290 291 public static final ValueProvider SCHEME = new ValueProvider("scheme") { 292 @Override 293 public String getValue(ComponentName resolvedComponent, Intent intent, 294 String resolvedType) { 295 Uri data = intent.getData(); 296 if (data != null) { 297 return data.getScheme(); 298 } 299 return null; 300 } 301 }; 302 303 public static final ValueProvider SSP = new ValueProvider("scheme-specific-part") { 304 @Override 305 public String getValue(ComponentName resolvedComponent, Intent intent, 306 String resolvedType) { 307 Uri data = intent.getData(); 308 if (data != null) { 309 return data.getSchemeSpecificPart(); 310 } 311 return null; 312 } 313 }; 314 315 public static final ValueProvider HOST = new ValueProvider("host") { 316 @Override 317 public String getValue(ComponentName resolvedComponent, Intent intent, 318 String resolvedType) { 319 Uri data = intent.getData(); 320 if (data != null) { 321 return data.getHost(); 322 } 323 return null; 324 } 325 }; 326 327 public static final ValueProvider PATH = new ValueProvider("path") { 328 @Override 329 public String getValue(ComponentName resolvedComponent, Intent intent, 330 String resolvedType) { 331 Uri data = intent.getData(); 332 if (data != null) { 333 return data.getPath(); 334 } 335 return null; 336 } 337 }; 338 } 339