StringFilter.java revision 9158825f9c41869689d6b1786d7c7aa8bdd524ce
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
17package com.android.server.firewall;
18
19import android.content.ComponentName;
20import android.content.Intent;
21import android.net.Uri;
22import android.os.PatternMatcher;
23import org.xmlpull.v1.XmlPullParser;
24import org.xmlpull.v1.XmlPullParserException;
25
26import java.io.IOException;
27import java.util.regex.Pattern;
28
29abstract 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