1 /*
2  * Copyright (C) 2016 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 #define LOG_TAG "VehiclePropertyAccessControl"
18 #include <string>
19 #include <stdint.h>
20 #include <sys/types.h>
21 #include <IVehicleNetwork.h>
22 #include "VehiclePropertyAccessControl.h"
23 #include <hardware/vehicle.h>
24 #include <private/android_filesystem_config.h>
25 #include <vehicle-internal.h>
26 
27 //#define DBG_EVENT
28 //#define DBG_VERBOSE
29 #ifdef DBG_EVENT
30 #define EVENT_LOG(x...) ALOGD(x)
31 #else
32 #define EVENT_LOG(x...)
33 #endif
34 #ifdef DBG_VERBOSE
35 #define LOG_VERBOSE(x...) ALOGD(x)
36 #else
37 #define LOG_VERBOSE(x...)
38 #endif
39 
40 
41 namespace android {
42 
VehiclePropertyAccessControl()43 VehiclePropertyAccessControl::VehiclePropertyAccessControl() {
44 }
45 
~VehiclePropertyAccessControl()46 VehiclePropertyAccessControl::~VehiclePropertyAccessControl() {
47     int index;
48     int size;
49 
50     for (auto& i: mVehicleAccessControlMap) {
51         delete(&i);
52     }
53 
54     mVehicleAccessControlMap.clear();
55 }
56 
57 // Returns true if the given string, s, is a hex number that starts with 0x.
58 // Otherwise false is returned.
isHexNotation(std::string const & s)59 bool VehiclePropertyAccessControl::isHexNotation(std::string const& s) {
60     return s.compare(0, 2, "0x") == 0
61             && s.size() > 2
62             && s.find_first_not_of("0123456789abcdefABCDEF", 2)
63             == std::string::npos;
64 }
65 
66 // Converts the string representation, access, to an integer form and store it
67 // in value. true is returned if the parameter, access, is "r", "w", "rw" or
68 // "wr". Otherwise false is returned. The parameters property and uid are
69 // only used for logging in the event that the string, access, was not
70 // recognized.
accessToInt(int32_t * const value,const xmlChar * property,const xmlChar * uid,const xmlChar * access)71 bool VehiclePropertyAccessControl::accessToInt(int32_t* const value,
72                                                const xmlChar* property,
73                                                const xmlChar* uid,
74                                                const xmlChar* access) {
75     if (!value || !property || !uid || !access) {
76         ALOGE("Internal Error\n");
77         return false;
78     }
79 
80     if (xmlStrcmp(access, (const xmlChar *)"r") == 0) {
81         *value = VEHICLE_PROP_ACCESS_READ;
82     }
83     else if (xmlStrcmp(access, (const xmlChar *)"w") == 0) {
84         *value = VEHICLE_PROP_ACCESS_WRITE;
85     }
86     else if ((xmlStrcmp(access, (const xmlChar *)"rw") == 0)
87             || (xmlStrcmp(access, (const xmlChar *)"wr") == 0)) {
88         *value = VEHICLE_PROP_ACCESS_READ_WRITE;
89     }
90     else {
91         ALOGE("Unknown access tag %s for UID %s in PROPERTY %s\n",access, uid,
92               property);
93         return false;
94     }
95 
96     return true;
97 }
98 
99 // Adds the property/uid pair to the mVehicleAccessControlMap map if the pair
100 // doesn't already exist. If the pair does exist, the access is updated.
updateOrCreate(int32_t uid,int32_t property,int32_t access)101 bool VehiclePropertyAccessControl::updateOrCreate(int32_t uid, int32_t property,
102                                                   int32_t access) {
103     // check if property exists
104     if (mVehicleAccessControlMap.count(property) == 0) {
105         std::map<int32_t, int32_t>* uid_access =
106                 new std::map<int32_t, int32_t>();
107         mVehicleAccessControlMap[property] = uid_access;
108     }
109 
110     // Get the propertyAccessMap
111     std::map<int32_t, int32_t>* uidAccessMap =
112             mVehicleAccessControlMap[property];
113 
114     // Now check if uid exists
115     if (uidAccessMap->count(uid) == 0) {
116         (*uidAccessMap)[uid] = access;
117         // uid was not found
118         return false;
119     }
120 
121     // The Property, Uid pair exist. So update the access
122     (*uidAccessMap)[uid] = access;
123 
124     return true;
125 }
126 
127 // Start parsing the xml file and populating the mVehicleAccessControlMap
128 // map. The parameter, a_node, must point to the first <PROPERTY> tag.
129 // true is returned if the parsing completed else false.
populate(xmlNode * a_node)130 bool VehiclePropertyAccessControl::populate(xmlNode * a_node) {
131     xmlNode* cur_node = NULL;
132     xmlNode* child = NULL;
133     xmlChar* property = NULL;
134     xmlChar* property_value_str = NULL;
135     xmlChar* uid = NULL;
136     xmlChar* uid_value_str = NULL;
137     xmlChar* access = NULL;
138     int32_t property_value;
139     int32_t uid_value;
140     int32_t access_value;
141 
142     if (!a_node) {
143         ALOGE("Internal Error");
144         return false;
145     }
146 
147     // Loop over all the PROPERTY tags
148     for (cur_node = a_node; cur_node; cur_node = cur_node->next) {
149         if ((xmlStrcmp(cur_node->name, (const xmlChar *)"PROPERTY") == 0) &&
150                 (cur_node->type == XML_ELEMENT_NODE)) {
151             // Free the old property tag
152             xmlFree(property);
153             // get new property tag name attribute
154             property = xmlGetProp(cur_node, (const xmlChar *)"name");
155             if (!property) {
156                 ALOGE("PROPERTY given without name attribute");
157                 continue;
158             }
159 
160             // get new property tag value attribute
161             property_value_str = xmlGetProp(cur_node, (const xmlChar*)"value");
162             if (!property_value_str) {
163                 ALOGE("PROPERTY given without value attribute");
164                 continue;
165             }
166 
167             std::string tmp_str((const char*)property_value_str);
168             if (isHexNotation(tmp_str)) {
169                 property_value = std::stoul(tmp_str, nullptr, 16);
170             } else {
171                 property_value = std::stoul(tmp_str, nullptr, 10);
172             }
173 
174             // Loop over all UID tags
175             for (child = cur_node->children; child; child = child->next) {
176                 if ((xmlStrcmp(child->name, (const xmlChar*)"UID")==0) &&
177                         (child->type == XML_ELEMENT_NODE)) {
178                     if (property != NULL) {
179                         // Free the old uid tag
180                         xmlFree(uid);
181                         // Free the old access tag
182                         xmlFree(access);
183                         // get new uid tag
184                         uid = xmlGetProp(child, (const xmlChar*)"name");
185                         // get new uid tag
186                         uid_value_str = xmlGetProp(child,
187                                                    (const xmlChar*)"value");
188                         // get new access tag
189                         access = xmlGetProp(child, (const xmlChar *)"access");
190 
191                         if (uid == NULL) {
192                             ALOGE(
193                                 "UID tag for property %s given without name attribute\n",
194                                 property);
195                         } else if (uid_value_str == NULL) {
196                             ALOGE(
197                                 "UID tag for property %s given without value attribute\n",
198                                 property);
199                         } else if (access == NULL) {
200                             ALOGE(
201                                 "UID tag for property %s given without access attribute\n",
202                                 property);
203                         } else {
204                             std::string tmp_str((const char *)uid_value_str);
205                             if (isHexNotation(tmp_str)) {
206                                 uid_value = std::stoul(tmp_str, nullptr, 16);
207                             } else {
208                                 uid_value = std::stoul(tmp_str, nullptr, 10);
209                             }
210 
211                             bool re1 = accessToInt(&access_value, property, uid,
212                                                    access);
213                             if (re1) {
214                                 if (!updateOrCreate(uid_value, property_value,
215                                                     access_value)) {
216                                     LOG_VERBOSE(
217                                         "Property %08x was added: uid=%d access=%d\n",
218                                         property_value, uid_value, access_value);
219                                 } else {
220                                     LOG_VERBOSE("Property %08x was updated: uid=%d access=%d\n",
221                                           property_value, uid_value, access_value);
222                                 }
223                             }
224                         }
225                     }
226                 }
227             }
228         }
229     }
230 
231     xmlFree(property);
232     xmlFree(uid);
233     xmlFree(access);
234 
235     return true;
236 }
237 
238 // This method initializes the class by parsing the mandatory
239 // /system/etc/vns/vns_policy.xml file and then the optional
240 // /system/etc/vns/vendor_vns_policy.xml if found.
241 // false is returned if vns_policy.xml was not found or is
242 // invalid else true is returned.
init()243 bool VehiclePropertyAccessControl::init() {
244     static const char* default_policy = "/system/etc/vns/vns_policy.xml";
245     static const char* vendor_policy = "/system/etc/vns/vendor_vns_policy.xml";
246 
247     if (!process(default_policy)) {
248         return false;
249     }
250 
251     if (process(vendor_policy)) {
252         ALOGE("Vendor VNS Policy was applied\n");
253     }
254 
255     return true;
256 }
257 
258 // Processes the vns_policy.xml or vendor_vns_policy.xml files
259 // and returns true on success else false is returned.
process(const char * policy)260 bool VehiclePropertyAccessControl::process(const char* policy) {
261     xmlDoc* doc = NULL;
262     xmlNode* root_element = NULL;
263 
264     doc = xmlReadFile(policy, NULL, 0);
265     if (doc == NULL) {
266         ALOGE("Could not find %s\n", policy);
267         return false;
268     }
269 
270     root_element = xmlDocGetRootElement(doc);
271     if (!root_element) {
272         ALOGE("Not a valid config file %s\n", policy);
273         xmlFreeDoc(doc);
274         return false;
275     }
276 
277     if (xmlStrcmp(root_element->name, (const xmlChar *)"ALLOW") != 0) {
278         ALOGE("Not a valid config file %s\n", policy);
279         xmlFreeDoc(doc);
280         return false;
281     }
282 
283     bool ret = populate(root_element->children);
284 
285     xmlFreeDoc(doc);
286 
287     return ret;
288 }
289 
dump(String8 & msg)290 void VehiclePropertyAccessControl::dump(String8& msg) {
291     std::string perm;
292     int32_t property;
293     int32_t uid;
294     int32_t access;
295     std::map<int32_t, int32_t> *uid_access_map;
296 
297     for (auto& i: mVehicleAccessControlMap) {
298         property = i.first;
299         uid_access_map = mVehicleAccessControlMap[property];
300         for (auto& j: *uid_access_map) {
301             uid = j.first;
302             access = (*uid_access_map)[uid];
303             switch(access) {
304                 case VEHICLE_PROP_ACCESS_READ: perm = "read"; break;
305                 case VEHICLE_PROP_ACCESS_WRITE: perm = "write"; break;
306                 case VEHICLE_PROP_ACCESS_READ_WRITE: perm = "read/write"; break;
307                 default: perm="unknown";
308             }
309             msg.appendFormat("UID %d: property 0x%08x, access %s\n", uid,
310                              property, perm.c_str());
311         }
312     }
313 }
314 
315 // Test if the given uid has (read or write) access to the given property. If it
316 // does, true is returned and false is returned if it doesn't have access or the
317 // property or uid is unknown.
testAccess(int32_t property,int32_t uid,bool isWrite)318 bool VehiclePropertyAccessControl::testAccess(int32_t property, int32_t uid,
319                                               bool isWrite) {
320     // Check if the property exists
321     if (mVehicleAccessControlMap.count(property) == 0) {
322         // property was not found
323         return false;
324     }
325 
326     // Get the uidAccessMap
327     std::map<int32_t, int32_t>* uidAccessMap =
328             mVehicleAccessControlMap[property];
329 
330     // Now check if uid exists
331     if (uidAccessMap->count(uid) == 0) {
332         // uid was not found
333         return false;
334     }
335 
336     // Get Access to this Property
337     int32_t access = (*uidAccessMap)[uid];
338 
339     // Test if the UID has access to the property
340     if (isWrite) {
341         if ((access == VEHICLE_PROP_ACCESS_WRITE)
342                 || (access == VEHICLE_PROP_ACCESS_READ_WRITE)) {
343             return true;
344         } else {
345             return false;
346         }
347     } else {
348         if ((access == VEHICLE_PROP_ACCESS_READ)
349                 || (access == VEHICLE_PROP_ACCESS_READ_WRITE)) {
350             return true;
351         } else {
352             return false;
353         }
354     }
355 }
356 
357 };
358