Sam Hurst | c7152db | 2016-02-29 09:35:22 -0800 | [diff] [blame] | 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 | |
| 43 | VehiclePropertyAccessControl::VehiclePropertyAccessControl() { |
| 44 | } |
| 45 | |
| 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. |
| 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. |
| 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. |
| 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. |
| 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 | |
Keun-young Park | d36a995 | 2016-05-24 10:03:59 -0700 | [diff] [blame] | 174 | // property with this set to true will not call get when it is subscribed. |
| 175 | property_value_str = xmlGetProp(cur_node, (const xmlChar*)"no_auto_get"); |
| 176 | if (property_value_str) { |
| 177 | if (xmlStrcmp(property_value_str, (const xmlChar*)"true")==0) { |
| 178 | mPropertiesWithNoAutoGet.insert(property_value); |
| 179 | } |
| 180 | } |
| 181 | |
Sam Hurst | c7152db | 2016-02-29 09:35:22 -0800 | [diff] [blame] | 182 | // Loop over all UID tags |
| 183 | for (child = cur_node->children; child; child = child->next) { |
| 184 | if ((xmlStrcmp(child->name, (const xmlChar*)"UID")==0) && |
| 185 | (child->type == XML_ELEMENT_NODE)) { |
| 186 | if (property != NULL) { |
| 187 | // Free the old uid tag |
| 188 | xmlFree(uid); |
| 189 | // Free the old access tag |
| 190 | xmlFree(access); |
| 191 | // get new uid tag |
| 192 | uid = xmlGetProp(child, (const xmlChar*)"name"); |
| 193 | // get new uid tag |
| 194 | uid_value_str = xmlGetProp(child, |
| 195 | (const xmlChar*)"value"); |
| 196 | // get new access tag |
| 197 | access = xmlGetProp(child, (const xmlChar *)"access"); |
| 198 | |
| 199 | if (uid == NULL) { |
| 200 | ALOGE( |
| 201 | "UID tag for property %s given without name attribute\n", |
| 202 | property); |
| 203 | } else if (uid_value_str == NULL) { |
| 204 | ALOGE( |
| 205 | "UID tag for property %s given without value attribute\n", |
| 206 | property); |
| 207 | } else if (access == NULL) { |
| 208 | ALOGE( |
| 209 | "UID tag for property %s given without access attribute\n", |
| 210 | property); |
| 211 | } else { |
| 212 | std::string tmp_str((const char *)uid_value_str); |
| 213 | if (isHexNotation(tmp_str)) { |
| 214 | uid_value = std::stoul(tmp_str, nullptr, 16); |
| 215 | } else { |
| 216 | uid_value = std::stoul(tmp_str, nullptr, 10); |
| 217 | } |
| 218 | |
| 219 | bool re1 = accessToInt(&access_value, property, uid, |
| 220 | access); |
| 221 | if (re1) { |
| 222 | if (!updateOrCreate(uid_value, property_value, |
| 223 | access_value)) { |
Keun-young Park | 76eeb52 | 2016-03-08 17:59:17 -0800 | [diff] [blame] | 224 | LOG_VERBOSE( |
Sam Hurst | c7152db | 2016-02-29 09:35:22 -0800 | [diff] [blame] | 225 | "Property %08x was added: uid=%d access=%d\n", |
| 226 | property_value, uid_value, access_value); |
| 227 | } else { |
Keun-young Park | 76eeb52 | 2016-03-08 17:59:17 -0800 | [diff] [blame] | 228 | LOG_VERBOSE("Property %08x was updated: uid=%d access=%d\n", |
Sam Hurst | c7152db | 2016-02-29 09:35:22 -0800 | [diff] [blame] | 229 | property_value, uid_value, access_value); |
| 230 | } |
| 231 | } |
| 232 | } |
| 233 | } |
| 234 | } |
| 235 | } |
| 236 | } |
| 237 | } |
| 238 | |
| 239 | xmlFree(property); |
| 240 | xmlFree(uid); |
| 241 | xmlFree(access); |
| 242 | |
| 243 | return true; |
| 244 | } |
| 245 | |
| 246 | // This method initializes the class by parsing the mandatory |
| 247 | // /system/etc/vns/vns_policy.xml file and then the optional |
| 248 | // /system/etc/vns/vendor_vns_policy.xml if found. |
| 249 | // false is returned if vns_policy.xml was not found or is |
| 250 | // invalid else true is returned. |
| 251 | bool VehiclePropertyAccessControl::init() { |
| 252 | static const char* default_policy = "/system/etc/vns/vns_policy.xml"; |
| 253 | static const char* vendor_policy = "/system/etc/vns/vendor_vns_policy.xml"; |
| 254 | |
| 255 | if (!process(default_policy)) { |
| 256 | return false; |
| 257 | } |
| 258 | |
| 259 | if (process(vendor_policy)) { |
| 260 | ALOGE("Vendor VNS Policy was applied\n"); |
| 261 | } |
| 262 | |
| 263 | return true; |
| 264 | } |
| 265 | |
| 266 | // Processes the vns_policy.xml or vendor_vns_policy.xml files |
| 267 | // and returns true on success else false is returned. |
| 268 | bool VehiclePropertyAccessControl::process(const char* policy) { |
| 269 | xmlDoc* doc = NULL; |
| 270 | xmlNode* root_element = NULL; |
| 271 | |
| 272 | doc = xmlReadFile(policy, NULL, 0); |
| 273 | if (doc == NULL) { |
| 274 | ALOGE("Could not find %s\n", policy); |
| 275 | return false; |
| 276 | } |
| 277 | |
| 278 | root_element = xmlDocGetRootElement(doc); |
| 279 | if (!root_element) { |
| 280 | ALOGE("Not a valid config file %s\n", policy); |
| 281 | xmlFreeDoc(doc); |
| 282 | return false; |
| 283 | } |
| 284 | |
| 285 | if (xmlStrcmp(root_element->name, (const xmlChar *)"ALLOW") != 0) { |
| 286 | ALOGE("Not a valid config file %s\n", policy); |
| 287 | xmlFreeDoc(doc); |
| 288 | return false; |
| 289 | } |
| 290 | |
| 291 | bool ret = populate(root_element->children); |
| 292 | |
| 293 | xmlFreeDoc(doc); |
| 294 | |
| 295 | return ret; |
| 296 | } |
| 297 | |
| 298 | void VehiclePropertyAccessControl::dump(String8& msg) { |
| 299 | std::string perm; |
| 300 | int32_t property; |
| 301 | int32_t uid; |
| 302 | int32_t access; |
| 303 | std::map<int32_t, int32_t> *uid_access_map; |
| 304 | |
| 305 | for (auto& i: mVehicleAccessControlMap) { |
| 306 | property = i.first; |
| 307 | uid_access_map = mVehicleAccessControlMap[property]; |
| 308 | for (auto& j: *uid_access_map) { |
| 309 | uid = j.first; |
| 310 | access = (*uid_access_map)[uid]; |
| 311 | switch(access) { |
| 312 | case VEHICLE_PROP_ACCESS_READ: perm = "read"; break; |
| 313 | case VEHICLE_PROP_ACCESS_WRITE: perm = "write"; break; |
| 314 | case VEHICLE_PROP_ACCESS_READ_WRITE: perm = "read/write"; break; |
| 315 | default: perm="unknown"; |
| 316 | } |
| 317 | msg.appendFormat("UID %d: property 0x%08x, access %s\n", uid, |
| 318 | property, perm.c_str()); |
| 319 | } |
| 320 | } |
| 321 | } |
| 322 | |
| 323 | // Test if the given uid has (read or write) access to the given property. If it |
| 324 | // does, true is returned and false is returned if it doesn't have access or the |
| 325 | // property or uid is unknown. |
| 326 | bool VehiclePropertyAccessControl::testAccess(int32_t property, int32_t uid, |
| 327 | bool isWrite) { |
| 328 | // Check if the property exists |
| 329 | if (mVehicleAccessControlMap.count(property) == 0) { |
| 330 | // property was not found |
| 331 | return false; |
| 332 | } |
| 333 | |
| 334 | // Get the uidAccessMap |
| 335 | std::map<int32_t, int32_t>* uidAccessMap = |
| 336 | mVehicleAccessControlMap[property]; |
| 337 | |
| 338 | // Now check if uid exists |
| 339 | if (uidAccessMap->count(uid) == 0) { |
| 340 | // uid was not found |
| 341 | return false; |
| 342 | } |
| 343 | |
| 344 | // Get Access to this Property |
| 345 | int32_t access = (*uidAccessMap)[uid]; |
| 346 | |
| 347 | // Test if the UID has access to the property |
| 348 | if (isWrite) { |
| 349 | if ((access == VEHICLE_PROP_ACCESS_WRITE) |
| 350 | || (access == VEHICLE_PROP_ACCESS_READ_WRITE)) { |
| 351 | return true; |
| 352 | } else { |
| 353 | return false; |
| 354 | } |
| 355 | } else { |
| 356 | if ((access == VEHICLE_PROP_ACCESS_READ) |
| 357 | || (access == VEHICLE_PROP_ACCESS_READ_WRITE)) { |
| 358 | return true; |
| 359 | } else { |
| 360 | return false; |
| 361 | } |
| 362 | } |
| 363 | } |
| 364 | |
Keun-young Park | d36a995 | 2016-05-24 10:03:59 -0700 | [diff] [blame] | 365 | bool VehiclePropertyAccessControl::isAutoGetEnabled(int32_t property) { |
| 366 | return mPropertiesWithNoAutoGet.count(property) == 0; |
| 367 | } |
| 368 | |
Sam Hurst | c7152db | 2016-02-29 09:35:22 -0800 | [diff] [blame] | 369 | }; |