blob: 2e99d0f921093351aabbec2d1f7e93685e2d20ab [file] [log] [blame]
Khaled Abdelmohsen7c56df62019-10-31 16:59:43 +00001/*
2 * Copyright (C) 2019 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.integrity.parser;
18
Song Pan75147d52019-11-19 00:57:46 +000019import android.content.integrity.AtomicFormula;
20import android.content.integrity.CompoundFormula;
21import android.content.integrity.Formula;
22import android.content.integrity.Rule;
Khaled Abdelmohsen216109e2019-11-04 17:49:18 +000023import android.util.Xml;
24
Khaled Abdelmohsen216109e2019-11-04 17:49:18 +000025import org.xmlpull.v1.XmlPullParser;
26import org.xmlpull.v1.XmlPullParserException;
Khaled Abdelmohsen7c56df62019-10-31 16:59:43 +000027
Khaled Abdelmohsen216109e2019-11-04 17:49:18 +000028import java.io.IOException;
29import java.io.InputStream;
30import java.io.StringReader;
31import java.nio.charset.StandardCharsets;
32import java.util.ArrayList;
33import java.util.List;
Khaled Abdelmohsene39fa0f2019-11-25 17:24:22 +000034import java.util.Optional;
Khaled Abdelmohsen216109e2019-11-04 17:49:18 +000035
Song Pan75147d52019-11-19 00:57:46 +000036/** A helper class to parse rules into the {@link Rule} model from Xml representation. */
Khaled Abdelmohsen7c56df62019-10-31 16:59:43 +000037public final class RuleXmlParser implements RuleParser {
38
Khaled Abdelmohsen216109e2019-11-04 17:49:18 +000039 public static final String TAG = "RuleXmlParser";
40
Khaled Abdelmohsenb05d7062019-11-19 01:22:23 +000041 private static final String NAMESPACE = "";
Khaled Abdelmohsen6d77f942019-11-12 16:47:01 +000042 private static final String RULE_LIST_TAG = "RL";
43 private static final String RULE_TAG = "R";
44 private static final String OPEN_FORMULA_TAG = "OF";
45 private static final String ATOMIC_FORMULA_TAG = "AF";
Khaled Abdelmohsenb05d7062019-11-19 01:22:23 +000046 private static final String EFFECT_ATTRIBUTE = "E";
47 private static final String KEY_ATTRIBUTE = "K";
48 private static final String OPERATOR_ATTRIBUTE = "O";
49 private static final String VALUE_ATTRIBUTE = "V";
50 private static final String CONNECTOR_ATTRIBUTE = "C";
Khaled Abdelmohsen1efff872019-11-25 16:44:20 +000051 private static final String IS_HASHED_VALUE_ATTRIBUTE = "H";
Khaled Abdelmohsen216109e2019-11-04 17:49:18 +000052
Khaled Abdelmohsen7c56df62019-10-31 16:59:43 +000053 @Override
Khaled Abdelmohsen6ecfebc2019-12-03 15:00:49 +000054 public List<Rule> parse(byte[] ruleBytes) throws RuleParseException {
Khaled Abdelmohsen216109e2019-11-04 17:49:18 +000055 try {
56 XmlPullParser xmlPullParser = Xml.newPullParser();
Khaled Abdelmohsen6ecfebc2019-12-03 15:00:49 +000057 xmlPullParser.setInput(new StringReader(new String(ruleBytes, StandardCharsets.UTF_8)));
Khaled Abdelmohsen216109e2019-11-04 17:49:18 +000058 return parseRules(xmlPullParser);
Khaled Abdelmohsen21a96052019-11-06 12:23:22 +000059 } catch (Exception e) {
60 throw new RuleParseException(e.getMessage(), e);
Khaled Abdelmohsen216109e2019-11-04 17:49:18 +000061 }
Khaled Abdelmohsen7c56df62019-10-31 16:59:43 +000062 }
63
64 @Override
Khaled Abdelmohsen21a96052019-11-06 12:23:22 +000065 public List<Rule> parse(InputStream inputStream) throws RuleParseException {
Khaled Abdelmohsen216109e2019-11-04 17:49:18 +000066 try {
67 XmlPullParser xmlPullParser = Xml.newPullParser();
68 xmlPullParser.setInput(inputStream, StandardCharsets.UTF_8.name());
69 return parseRules(xmlPullParser);
Khaled Abdelmohsen21a96052019-11-06 12:23:22 +000070 } catch (Exception e) {
71 throw new RuleParseException(e.getMessage(), e);
Khaled Abdelmohsen216109e2019-11-04 17:49:18 +000072 }
Khaled Abdelmohsen216109e2019-11-04 17:49:18 +000073 }
74
Khaled Abdelmohsen12b110e2019-11-05 17:24:48 +000075 private static List<Rule> parseRules(XmlPullParser parser)
76 throws IOException, XmlPullParserException {
Khaled Abdelmohsen216109e2019-11-04 17:49:18 +000077 List<Rule> rules = new ArrayList<>();
78
79 // Skipping the first event type, which is always {@link XmlPullParser.START_DOCUMENT}
80 parser.next();
81
Khaled Abdelmohsen6d77f942019-11-12 16:47:01 +000082 // Processing the first tag; which should always be a RuleList <RL> tag.
Khaled Abdelmohsen216109e2019-11-04 17:49:18 +000083 String nodeName = parser.getName();
Khaled Abdelmohsen6d77f942019-11-12 16:47:01 +000084 // Validating that the XML is starting with a RuleList <RL> tag.
Khaled Abdelmohsen216109e2019-11-04 17:49:18 +000085 // Note: This is the only breaking validation to run against XML files in the platform.
86 // All rules inside are assumed to be validated at the server. If a rule is found to be
87 // corrupt in the XML, it will be skipped to the next rule.
88 if (!nodeName.equals(RULE_LIST_TAG)) {
89 throw new RuntimeException(
Song Pan75147d52019-11-19 00:57:46 +000090 String.format(
91 "Rules must start with RuleList <RL> tag. Found: %s at %s",
Khaled Abdelmohsen6d77f942019-11-12 16:47:01 +000092 nodeName, parser.getPositionDescription()));
Khaled Abdelmohsen216109e2019-11-04 17:49:18 +000093 }
94
95 int eventType;
96 while ((eventType = parser.next()) != XmlPullParser.END_DOCUMENT) {
97 nodeName = parser.getName();
98 if (eventType != XmlPullParser.START_TAG || !nodeName.equals(RULE_TAG)) {
99 continue;
100 }
Khaled Abdelmohsen21a96052019-11-06 12:23:22 +0000101 rules.add(parseRule(parser));
Khaled Abdelmohsen216109e2019-11-04 17:49:18 +0000102 }
103
104 return rules;
105 }
106
Khaled Abdelmohsen21a96052019-11-06 12:23:22 +0000107 private static Rule parseRule(XmlPullParser parser) throws IOException, XmlPullParserException {
108 Formula formula = null;
Khaled Abdelmohsene39fa0f2019-11-25 17:24:22 +0000109 int effect = Integer.parseInt(extractAttributeValue(parser, EFFECT_ATTRIBUTE).orElse("-1"));
Khaled Abdelmohsen12b110e2019-11-05 17:24:48 +0000110
Khaled Abdelmohsen21a96052019-11-06 12:23:22 +0000111 int eventType;
112 while ((eventType = parser.next()) != XmlPullParser.END_DOCUMENT) {
113 String nodeName = parser.getName();
Khaled Abdelmohsen12b110e2019-11-05 17:24:48 +0000114
Khaled Abdelmohsen21a96052019-11-06 12:23:22 +0000115 if (eventType == XmlPullParser.END_TAG && parser.getName().equals(RULE_TAG)) {
116 break;
Khaled Abdelmohsen12b110e2019-11-05 17:24:48 +0000117 }
118
Khaled Abdelmohsen21a96052019-11-06 12:23:22 +0000119 if (eventType == XmlPullParser.START_TAG) {
120 switch (nodeName) {
121 case OPEN_FORMULA_TAG:
122 formula = parseOpenFormula(parser);
123 break;
124 case ATOMIC_FORMULA_TAG:
125 formula = parseAtomicFormula(parser);
126 break;
Khaled Abdelmohsen21a96052019-11-06 12:23:22 +0000127 default:
128 throw new RuntimeException(
129 String.format("Found unexpected tag: %s", nodeName));
130 }
131 } else {
132 throw new RuntimeException(
133 String.format("Found unexpected event type: %d", eventType));
134 }
Khaled Abdelmohsen12b110e2019-11-05 17:24:48 +0000135 }
Khaled Abdelmohsen21a96052019-11-06 12:23:22 +0000136
137 return new Rule(formula, effect);
Khaled Abdelmohsen12b110e2019-11-05 17:24:48 +0000138 }
139
Khaled Abdelmohsen21a96052019-11-06 12:23:22 +0000140 private static Formula parseOpenFormula(XmlPullParser parser)
141 throws IOException, XmlPullParserException {
Song Pan75147d52019-11-19 00:57:46 +0000142 int connector =
143 Integer.parseInt(extractAttributeValue(parser, CONNECTOR_ATTRIBUTE).orElse("-1"));
Khaled Abdelmohsen21a96052019-11-06 12:23:22 +0000144 List<Formula> formulas = new ArrayList<>();
145
146 int eventType;
147 while ((eventType = parser.next()) != XmlPullParser.END_DOCUMENT) {
148 String nodeName = parser.getName();
149
150 if (eventType == XmlPullParser.END_TAG && parser.getName().equals(OPEN_FORMULA_TAG)) {
151 break;
152 }
153
154 if (eventType == XmlPullParser.START_TAG) {
155 switch (nodeName) {
Khaled Abdelmohsen21a96052019-11-06 12:23:22 +0000156 case ATOMIC_FORMULA_TAG:
157 formulas.add(parseAtomicFormula(parser));
158 break;
159 case OPEN_FORMULA_TAG:
160 formulas.add(parseOpenFormula(parser));
161 break;
162 default:
163 throw new RuntimeException(
164 String.format("Found unexpected tag: %s", nodeName));
165 }
166 } else {
167 throw new RuntimeException(
168 String.format("Found unexpected event type: %d", eventType));
169 }
170 }
171
Song Pan75147d52019-11-19 00:57:46 +0000172 return new CompoundFormula(connector, formulas);
Khaled Abdelmohsen7c56df62019-10-31 16:59:43 +0000173 }
Khaled Abdelmohsen12b110e2019-11-05 17:24:48 +0000174
175 private static Formula parseAtomicFormula(XmlPullParser parser)
176 throws IOException, XmlPullParserException {
Khaled Abdelmohsene39fa0f2019-11-25 17:24:22 +0000177 int key = Integer.parseInt(extractAttributeValue(parser, KEY_ATTRIBUTE).orElse("-1"));
Song Pan75147d52019-11-19 00:57:46 +0000178 int operator =
179 Integer.parseInt(extractAttributeValue(parser, OPERATOR_ATTRIBUTE).orElse("-1"));
Khaled Abdelmohsene39fa0f2019-11-25 17:24:22 +0000180 String value = extractAttributeValue(parser, VALUE_ATTRIBUTE).orElse(null);
Song Pan75147d52019-11-19 00:57:46 +0000181 String isHashedValue =
182 extractAttributeValue(parser, IS_HASHED_VALUE_ATTRIBUTE).orElse(null);
Khaled Abdelmohsen12b110e2019-11-05 17:24:48 +0000183
Khaled Abdelmohsen12b110e2019-11-05 17:24:48 +0000184 int eventType;
185 while ((eventType = parser.next()) != XmlPullParser.END_DOCUMENT) {
Khaled Abdelmohsen12b110e2019-11-05 17:24:48 +0000186 if (eventType == XmlPullParser.END_TAG && parser.getName().equals(ATOMIC_FORMULA_TAG)) {
187 break;
188 }
Khaled Abdelmohsen12b110e2019-11-05 17:24:48 +0000189 }
Khaled Abdelmohsen1efff872019-11-25 16:44:20 +0000190 return constructAtomicFormulaBasedOnKey(key, operator, value, isHashedValue);
Khaled Abdelmohsen12b110e2019-11-05 17:24:48 +0000191 }
192
Song Pan75147d52019-11-19 00:57:46 +0000193 private static Formula constructAtomicFormulaBasedOnKey(
194 @AtomicFormula.Key int key,
195 @AtomicFormula.Operator int operator,
196 String value,
197 String isHashedValue) {
Khaled Abdelmohsen12b110e2019-11-05 17:24:48 +0000198 switch (key) {
199 case AtomicFormula.PACKAGE_NAME:
200 case AtomicFormula.INSTALLER_NAME:
201 case AtomicFormula.APP_CERTIFICATE:
202 case AtomicFormula.INSTALLER_CERTIFICATE:
Song Pan75147d52019-11-19 00:57:46 +0000203 return new AtomicFormula.StringAtomicFormula(
204 key, value, Boolean.parseBoolean(isHashedValue));
Khaled Abdelmohsen12b110e2019-11-05 17:24:48 +0000205 case AtomicFormula.PRE_INSTALLED:
206 return new AtomicFormula.BooleanAtomicFormula(key, Boolean.parseBoolean(value));
207 case AtomicFormula.VERSION_CODE:
208 return new AtomicFormula.IntAtomicFormula(key, operator, Integer.parseInt(value));
209 default:
Khaled Abdelmohsen21a96052019-11-06 12:23:22 +0000210 throw new RuntimeException(String.format("Found unexpected key: %d", key));
Khaled Abdelmohsen12b110e2019-11-05 17:24:48 +0000211 }
212 }
213
Khaled Abdelmohsene39fa0f2019-11-25 17:24:22 +0000214 private static Optional<String> extractAttributeValue(XmlPullParser parser, String attribute) {
215 return Optional.ofNullable(parser.getAttributeValue(NAMESPACE, attribute));
Khaled Abdelmohsen12b110e2019-11-05 17:24:48 +0000216 }
Khaled Abdelmohsen7c56df62019-10-31 16:59:43 +0000217}