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 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 */ 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 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 118 protected abstract boolean matchesValue(String value); 119 120 @Override 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 { 128 protected ValueProvider(String tag) { 129 super(tag); 130 } 131 132 public Filter newFilter(XmlPullParser parser) 133 throws IOException, XmlPullParserException { 134 return StringFilter.readFromXml(this, parser); 135 } 136 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 144 public EqualsFilter(ValueProvider valueProvider, String attrValue) { 145 super(valueProvider); 146 mFilterValue = attrValue; 147 } 148 149 @Override 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 158 public ContainsFilter(ValueProvider valueProvider, String attrValue) { 159 super(valueProvider); 160 mFilterValue = attrValue; 161 } 162 163 @Override 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 172 public StartsWithFilter(ValueProvider valueProvider, String attrValue) { 173 super(valueProvider); 174 mFilterValue = attrValue; 175 } 176 177 @Override 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 186 public PatternStringFilter(ValueProvider valueProvider, String attrValue) { 187 super(valueProvider); 188 mPattern = new PatternMatcher(attrValue, PatternMatcher.PATTERN_SIMPLE_GLOB); 189 } 190 191 @Override 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 200 public RegexFilter(ValueProvider valueProvider, String attrValue) { 201 super(valueProvider); 202 this.mPattern = Pattern.compile(attrValue); 203 } 204 205 @Override 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 214 public IsNullFilter(ValueProvider valueProvider, String attrValue) { 215 super(valueProvider); 216 mIsNull = Boolean.parseBoolean(attrValue); 217 } 218 219 public IsNullFilter(ValueProvider valueProvider, boolean isNull) { 220 super(valueProvider); 221 mIsNull = isNull; 222 } 223 224 @Override 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