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