/* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #define LOG_TAG "VehiclePropertyAccessControl" #include <string> #include <stdint.h> #include <sys/types.h> #include <IVehicleNetwork.h> #include "VehiclePropertyAccessControl.h" #include <hardware/vehicle.h> #include <private/android_filesystem_config.h> #include <vehicle-internal.h> //#define DBG_EVENT //#define DBG_VERBOSE #ifdef DBG_EVENT #define EVENT_LOG(x...) ALOGD(x) #else #define EVENT_LOG(x...) #endif #ifdef DBG_VERBOSE #define LOG_VERBOSE(x...) ALOGD(x) #else #define LOG_VERBOSE(x...) #endif namespace android { VehiclePropertyAccessControl::VehiclePropertyAccessControl() { } VehiclePropertyAccessControl::~VehiclePropertyAccessControl() { int index; int size; for (auto& i: mVehicleAccessControlMap) { delete(&i); } mVehicleAccessControlMap.clear(); } // Returns true if the given string, s, is a hex number that starts with 0x. // Otherwise false is returned. bool VehiclePropertyAccessControl::isHexNotation(std::string const& s) { return s.compare(0, 2, "0x") == 0 && s.size() > 2 && s.find_first_not_of("0123456789abcdefABCDEF", 2) == std::string::npos; } // Converts the string representation, access, to an integer form and store it // in value. true is returned if the parameter, access, is "r", "w", "rw" or // "wr". Otherwise false is returned. The parameters property and uid are // only used for logging in the event that the string, access, was not // recognized. bool VehiclePropertyAccessControl::accessToInt(int32_t* const value, const xmlChar* property, const xmlChar* uid, const xmlChar* access) { if (!value || !property || !uid || !access) { ALOGE("Internal Error\n"); return false; } if (xmlStrcmp(access, (const xmlChar *)"r") == 0) { *value = VEHICLE_PROP_ACCESS_READ; } else if (xmlStrcmp(access, (const xmlChar *)"w") == 0) { *value = VEHICLE_PROP_ACCESS_WRITE; } else if ((xmlStrcmp(access, (const xmlChar *)"rw") == 0) || (xmlStrcmp(access, (const xmlChar *)"wr") == 0)) { *value = VEHICLE_PROP_ACCESS_READ_WRITE; } else { ALOGE("Unknown access tag %s for UID %s in PROPERTY %s\n",access, uid, property); return false; } return true; } // Adds the property/uid pair to the mVehicleAccessControlMap map if the pair // doesn't already exist. If the pair does exist, the access is updated. bool VehiclePropertyAccessControl::updateOrCreate(int32_t uid, int32_t property, int32_t access) { // check if property exists if (mVehicleAccessControlMap.count(property) == 0) { std::map<int32_t, int32_t>* uid_access = new std::map<int32_t, int32_t>(); mVehicleAccessControlMap[property] = uid_access; } // Get the propertyAccessMap std::map<int32_t, int32_t>* uidAccessMap = mVehicleAccessControlMap[property]; // Now check if uid exists if (uidAccessMap->count(uid) == 0) { (*uidAccessMap)[uid] = access; // uid was not found return false; } // The Property, Uid pair exist. So update the access (*uidAccessMap)[uid] = access; return true; } // Start parsing the xml file and populating the mVehicleAccessControlMap // map. The parameter, a_node, must point to the first <PROPERTY> tag. // true is returned if the parsing completed else false. bool VehiclePropertyAccessControl::populate(xmlNode * a_node) { xmlNode* cur_node = NULL; xmlNode* child = NULL; xmlChar* property = NULL; xmlChar* property_value_str = NULL; xmlChar* uid = NULL; xmlChar* uid_value_str = NULL; xmlChar* access = NULL; int32_t property_value; int32_t uid_value; int32_t access_value; if (!a_node) { ALOGE("Internal Error"); return false; } // Loop over all the PROPERTY tags for (cur_node = a_node; cur_node; cur_node = cur_node->next) { if ((xmlStrcmp(cur_node->name, (const xmlChar *)"PROPERTY") == 0) && (cur_node->type == XML_ELEMENT_NODE)) { // Free the old property tag xmlFree(property); // get new property tag name attribute property = xmlGetProp(cur_node, (const xmlChar *)"name"); if (!property) { ALOGE("PROPERTY given without name attribute"); continue; } // get new property tag value attribute property_value_str = xmlGetProp(cur_node, (const xmlChar*)"value"); if (!property_value_str) { ALOGE("PROPERTY given without value attribute"); continue; } std::string tmp_str((const char*)property_value_str); if (isHexNotation(tmp_str)) { property_value = std::stoul(tmp_str, nullptr, 16); } else { property_value = std::stoul(tmp_str, nullptr, 10); } // Loop over all UID tags for (child = cur_node->children; child; child = child->next) { if ((xmlStrcmp(child->name, (const xmlChar*)"UID")==0) && (child->type == XML_ELEMENT_NODE)) { if (property != NULL) { // Free the old uid tag xmlFree(uid); // Free the old access tag xmlFree(access); // get new uid tag uid = xmlGetProp(child, (const xmlChar*)"name"); // get new uid tag uid_value_str = xmlGetProp(child, (const xmlChar*)"value"); // get new access tag access = xmlGetProp(child, (const xmlChar *)"access"); if (uid == NULL) { ALOGE( "UID tag for property %s given without name attribute\n", property); } else if (uid_value_str == NULL) { ALOGE( "UID tag for property %s given without value attribute\n", property); } else if (access == NULL) { ALOGE( "UID tag for property %s given without access attribute\n", property); } else { std::string tmp_str((const char *)uid_value_str); if (isHexNotation(tmp_str)) { uid_value = std::stoul(tmp_str, nullptr, 16); } else { uid_value = std::stoul(tmp_str, nullptr, 10); } bool re1 = accessToInt(&access_value, property, uid, access); if (re1) { if (!updateOrCreate(uid_value, property_value, access_value)) { LOG_VERBOSE( "Property %08x was added: uid=%d access=%d\n", property_value, uid_value, access_value); } else { LOG_VERBOSE("Property %08x was updated: uid=%d access=%d\n", property_value, uid_value, access_value); } } } } } } } } xmlFree(property); xmlFree(uid); xmlFree(access); return true; } // This method initializes the class by parsing the mandatory // /system/etc/vns/vns_policy.xml file and then the optional // /system/etc/vns/vendor_vns_policy.xml if found. // false is returned if vns_policy.xml was not found or is // invalid else true is returned. bool VehiclePropertyAccessControl::init() { static const char* default_policy = "/system/etc/vns/vns_policy.xml"; static const char* vendor_policy = "/system/etc/vns/vendor_vns_policy.xml"; if (!process(default_policy)) { return false; } if (process(vendor_policy)) { ALOGE("Vendor VNS Policy was applied\n"); } return true; } // Processes the vns_policy.xml or vendor_vns_policy.xml files // and returns true on success else false is returned. bool VehiclePropertyAccessControl::process(const char* policy) { xmlDoc* doc = NULL; xmlNode* root_element = NULL; doc = xmlReadFile(policy, NULL, 0); if (doc == NULL) { ALOGE("Could not find %s\n", policy); return false; } root_element = xmlDocGetRootElement(doc); if (!root_element) { ALOGE("Not a valid config file %s\n", policy); xmlFreeDoc(doc); return false; } if (xmlStrcmp(root_element->name, (const xmlChar *)"ALLOW") != 0) { ALOGE("Not a valid config file %s\n", policy); xmlFreeDoc(doc); return false; } bool ret = populate(root_element->children); xmlFreeDoc(doc); return ret; } void VehiclePropertyAccessControl::dump(String8& msg) { std::string perm; int32_t property; int32_t uid; int32_t access; std::map<int32_t, int32_t> *uid_access_map; for (auto& i: mVehicleAccessControlMap) { property = i.first; uid_access_map = mVehicleAccessControlMap[property]; for (auto& j: *uid_access_map) { uid = j.first; access = (*uid_access_map)[uid]; switch(access) { case VEHICLE_PROP_ACCESS_READ: perm = "read"; break; case VEHICLE_PROP_ACCESS_WRITE: perm = "write"; break; case VEHICLE_PROP_ACCESS_READ_WRITE: perm = "read/write"; break; default: perm="unknown"; } msg.appendFormat("UID %d: property 0x%08x, access %s\n", uid, property, perm.c_str()); } } } // Test if the given uid has (read or write) access to the given property. If it // does, true is returned and false is returned if it doesn't have access or the // property or uid is unknown. bool VehiclePropertyAccessControl::testAccess(int32_t property, int32_t uid, bool isWrite) { // Check if the property exists if (mVehicleAccessControlMap.count(property) == 0) { // property was not found return false; } // Get the uidAccessMap std::map<int32_t, int32_t>* uidAccessMap = mVehicleAccessControlMap[property]; // Now check if uid exists if (uidAccessMap->count(uid) == 0) { // uid was not found return false; } // Get Access to this Property int32_t access = (*uidAccessMap)[uid]; // Test if the UID has access to the property if (isWrite) { if ((access == VEHICLE_PROP_ACCESS_WRITE) || (access == VEHICLE_PROP_ACCESS_READ_WRITE)) { return true; } else { return false; } } else { if ((access == VEHICLE_PROP_ACCESS_READ) || (access == VEHICLE_PROP_ACCESS_READ_WRITE)) { return true; } else { return false; } } } };