Loading vehicle/2.0/default/Android.mk +2 −0 Original line number Diff line number Diff line Loading @@ -22,6 +22,7 @@ module_prefix = android.hardware.vehicle@2.0 include $(CLEAR_VARS) LOCAL_MODULE := $(module_prefix)-manager-lib LOCAL_SRC_FILES := \ vehicle_hal_manager/AccessControlConfigParser.cpp \ vehicle_hal_manager/SubscriptionManager.cpp \ vehicle_hal_manager/VehicleHalManager.cpp \ Loading Loading @@ -67,6 +68,7 @@ LOCAL_MODULE:= $(module_prefix)-manager-unit-tests LOCAL_WHOLE_STATIC_LIBRARIES := $(module_prefix)-manager-lib LOCAL_SRC_FILES:= \ tests/AccessControlConfigParser_test.cpp \ tests/VehicleObjectPool_test.cpp \ tests/VehiclePropConfigIndex_test.cpp \ tests/SubscriptionManager_test.cpp \ Loading vehicle/2.0/default/tests/AccessControlConfigParser_test.cpp 0 → 100644 +149 −0 Original line number Diff line number Diff line /* * 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. */ #include <gtest/gtest.h> #include <memory> #include <fstream> #include <unordered_set> #include "vehicle_hal_manager/AccessControlConfigParser.h" namespace android { namespace hardware { namespace vehicle { namespace V2_0 { namespace { class AccessControlConfigParserTest : public ::testing::Test { protected: void SetUp() override { std::vector<VehicleProperty> supportedProperties { VehicleProperty::HVAC_FAN_SPEED, VehicleProperty::HVAC_FAN_DIRECTION, }; parser.reset(new AccessControlConfigParser(supportedProperties)); } public: PropertyAclMap aclMap; std::unique_ptr<AccessControlConfigParser> parser; }; TEST_F(AccessControlConfigParserTest, basicParsing) { std::stringstream file; file << "S:0x0500 1000 RW" << std::endl; ASSERT_TRUE(parser->parseFromStream(&file, &aclMap)); ASSERT_EQ(1, aclMap.size()); auto it = aclMap.find(VehicleProperty::HVAC_FAN_SPEED); ASSERT_NE(aclMap.end(), it); ASSERT_EQ(VehiclePropertyAccess::READ_WRITE, it->second.access); ASSERT_EQ(VehicleProperty::HVAC_FAN_SPEED, it->second.propId); ASSERT_EQ(1000u, it->second.uid); } TEST_F(AccessControlConfigParserTest, multipleUids) { std::stringstream file; file << "Set AID_AUDIO 1004" << std::endl << "Set AID_SYSTEM 1000" << std::endl << "S:0x0500 AID_SYSTEM RW" << std::endl << "S:0x0500 AID_AUDIO RW" << std::endl << "S:0x0500 0xbeef R" << std::endl; // Read-only. std::unordered_set<unsigned> expectedUids {1000, 1004, 0xbeef}; ASSERT_TRUE(parser->parseFromStream(&file, &aclMap)); auto range = aclMap.equal_range(VehicleProperty::HVAC_FAN_SPEED); for (auto it = range.first; it != range.second; ++it) { auto& acl = it->second; ASSERT_EQ(1, expectedUids.count(acl.uid)) << " uid: " << std::hex << acl.uid; if (acl.uid == 0xbeef) { ASSERT_EQ(VehiclePropertyAccess::READ, acl.access); } else { ASSERT_EQ(VehiclePropertyAccess::READ_WRITE, acl.access); } } } TEST_F(AccessControlConfigParserTest, fileContainsJunk) { std::stringstream file; file << "This string will be ignored with warning in the log" << std::endl << "# However comments are quit legitimate" << std::endl << "S:0x0500 0xbeef R # YAY" << std::endl; ASSERT_FALSE(parser->parseFromStream(&file, &aclMap)); ASSERT_EQ(1, aclMap.size()); auto it = aclMap.find(VehicleProperty::HVAC_FAN_SPEED); ASSERT_NE(aclMap.end(), it); ASSERT_EQ(VehiclePropertyAccess::READ, it->second.access); ASSERT_EQ(VehicleProperty::HVAC_FAN_SPEED, it->second.propId); ASSERT_EQ(0xbeef, it->second.uid); } TEST_F(AccessControlConfigParserTest, badIntegerFormat) { std::stringstream file; file << "S:0x0500 A12 RW " << std::endl; ASSERT_FALSE(parser->parseFromStream(&file, &aclMap)); ASSERT_EQ(0, aclMap.size()); } TEST_F(AccessControlConfigParserTest, ignoreNotSupportedProperties) { std::stringstream file; file << "S:0x0666 1000 RW " << std::endl; ASSERT_FALSE(parser->parseFromStream(&file, &aclMap)); ASSERT_EQ(0, aclMap.size()); } TEST_F(AccessControlConfigParserTest, multipleCalls) { std::stringstream configFile; configFile << "S:0x0500 1000 RW" << std::endl; ASSERT_TRUE(parser->parseFromStream(&configFile, &aclMap)); ASSERT_EQ(1, aclMap.size()); std::stringstream configFile2; configFile2 << "S:0x0501 1004 RW" << std::endl; ASSERT_TRUE(parser->parseFromStream(&configFile2, &aclMap)); ASSERT_EQ(2, aclMap.size()); auto it = aclMap.find(VehicleProperty::HVAC_FAN_SPEED); ASSERT_NE(aclMap.end(), it); ASSERT_EQ(VehiclePropertyAccess::READ_WRITE, it->second.access); ASSERT_EQ(VehicleProperty::HVAC_FAN_SPEED, it->second.propId); ASSERT_EQ(1000u, it->second.uid); it = aclMap.find(VehicleProperty::HVAC_FAN_DIRECTION); ASSERT_NE(aclMap.end(), it); ASSERT_EQ(VehiclePropertyAccess::READ_WRITE, it->second.access); ASSERT_EQ(VehicleProperty::HVAC_FAN_DIRECTION, it->second.propId); ASSERT_EQ(1004u, it->second.uid); } } // namespace anonymous } // namespace V2_0 } // namespace vehicle } // namespace hardware } // namespace android vehicle/2.0/default/vehicle_hal_manager/AccessControlConfigParser.cpp 0 → 100644 +224 −0 Original line number Diff line number Diff line /* * 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 "android.hardware.vehicle@2.0-impl" #include <android/log.h> #include <fstream> #include <sstream> #include <iostream> #include "AccessControlConfigParser.h" namespace android { namespace hardware { namespace vehicle { namespace V2_0 { AccessControlConfigParser::AccessControlConfigParser( const std::vector<VehicleProperty>& properties) { // Property Id in the config file doesn't include information about // type and area. So we want to create a map from these kind of // *stripped* properties to the whole VehicleProperty. // We also want to filter out ACL to the properties that supported // by concrete Vehicle HAL implementation. for (VehicleProperty vehicleProperty : properties) { auto numProp = static_cast<int>(vehicleProperty); numProp &= ~static_cast<int>(VehiclePropertyType::MASK) & ~static_cast<int>(VehicleArea::MASK); mStrippedToVehiclePropertyMap.emplace(numProp, vehicleProperty); } } bool AccessControlConfigParser::parseFromStream( std::istream* stream, PropertyAclMap* propertyAclMap) { std::list<std::string> tokens; std::string line; int lineNo = 0; bool warnings = false; for (;std::getline(*stream, line); lineNo++) { split(line, &tokens); if (!processTokens(&tokens, propertyAclMap)) { warnings = true; ALOGW("Failed to parse line %d : %s", lineNo, line.c_str()); } } return !warnings; } bool AccessControlConfigParser::processTokens(std::list<std::string>* tokens, PropertyAclMap* propertyAclMap) { std::string token = readNextToken(tokens); if (token.empty() || token[0] == '#') { // Ignore comment. return true; } if (token == "Set") { std::string alias = readNextToken(tokens); std::string strUid = readNextToken(tokens); if (alias.empty() || strUid.empty()) { ALOGW("Expected alias and UID must be specified"); return false; } int uid; if (!parseInt(strUid.c_str(), &uid)) { ALOGW("Invalid UID: %d", uid); } mUidMap.emplace(std::move(alias), uid); } else if (token.size() > 2 && token[1] == ':') { VehiclePropertyGroup propGroup; if (!parsePropertyGroup(token[0], &propGroup)) { return false; } std::string strUid = readNextToken(tokens); std::string strAccess = readNextToken(tokens); if (strUid.empty() || strAccess.empty()) { ALOGW("Expected UID and access for property: %s", token.c_str()); } PropertyAcl acl; if (parsePropertyId(token.substr(2), propGroup, &acl.propId) && parseUid(strUid, &acl.uid) && parseAccess(strAccess, &acl.access)) { propertyAclMap->emplace(acl.propId, std::move(acl)); } else { return false; } } else { ALOGW("Unexpected token: %s", token.c_str()); return false; } return true; } bool AccessControlConfigParser::parsePropertyGroup( char group, VehiclePropertyGroup* outPropertyGroup) const { switch (group) { case 'S': // Fall through. case 's': *outPropertyGroup = VehiclePropertyGroup::SYSTEM; break; case 'V': // Fall through. case 'v': *outPropertyGroup = VehiclePropertyGroup::VENDOR; break; default: ALOGW("Unexpected group: %c", group); return false; } return true; } bool AccessControlConfigParser::parsePropertyId( const std::string& strPropId, VehiclePropertyGroup propertyGroup, VehicleProperty* outVehicleProperty) const { int propId; if (!parseInt(strPropId.c_str(), &propId)) { ALOGW("Failed to convert property id to integer: %s", strPropId.c_str()); return false; } propId |= static_cast<int>(propertyGroup); auto it = mStrippedToVehiclePropertyMap.find(propId); if (it == mStrippedToVehiclePropertyMap.end()) { ALOGW("Property Id not found or not supported: 0x%x", propId); return false; } *outVehicleProperty = it->second; return true; } bool AccessControlConfigParser::parseInt(const char* strValue, int* outIntValue) { char* end; long num = std::strtol(strValue, &end, 0 /* auto detect base */); bool success = *end == 0 && errno != ERANGE; if (success) { *outIntValue = static_cast<int>(num); } return success; } bool AccessControlConfigParser::parseUid(const std::string& strUid, unsigned* outUid) const { auto element = mUidMap.find(strUid); if (element != mUidMap.end()) { *outUid = element->second; } else { int val; if (!parseInt(strUid.c_str(), &val)) { ALOGW("Failed to convert UID '%s' to integer", strUid.c_str()); return false; } *outUid = static_cast<unsigned>(val); } return true; } bool AccessControlConfigParser::parseAccess( const std::string& strAccess, VehiclePropertyAccess* outAccess) const { if (strAccess.size() == 0 || strAccess.size() > 2) { ALOGW("Unknown access mode '%s'", strAccess.c_str()); return false; } int32_t access = static_cast<int32_t>(VehiclePropertyAccess::NONE); for (char c : strAccess) { if (c == 'R' || c == 'r') { access |= VehiclePropertyAccess::READ; } else if (c == 'W' || c == 'w') { access |= VehiclePropertyAccess::WRITE; } else { ALOGW("Unknown access mode: %c", c); return false; } } *outAccess = static_cast<VehiclePropertyAccess>(access); return true; } void AccessControlConfigParser::split(const std::string& line, std::list<std::string>* outTokens) { outTokens->clear(); std::istringstream iss(line); while (!iss.eof()) { std::string token; iss >> token; outTokens->push_back(std::move(token)); } } std::string AccessControlConfigParser::readNextToken( std::list<std::string>* tokens) const { if (tokens->empty()) { return ""; } std::string token = tokens->front(); tokens->pop_front(); return token; } } // namespace V2_0 } // namespace vehicle } // namespace hardware } // namespace android vehicle/2.0/default/vehicle_hal_manager/AccessControlConfigParser.h 0 → 100644 +110 −0 Original line number Diff line number Diff line /* * 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. */ #ifndef android_hardware_vehicle_V2_0_AccessControlConfigParser_H_ #define android_hardware_vehicle_V2_0_AccessControlConfigParser_H_ #include <string> #include <vector> #include <unordered_map> #include <list> #include <android/hardware/vehicle/2.0/types.h> namespace android { namespace hardware { namespace vehicle { namespace V2_0 { struct PropertyAcl { VehicleProperty propId; unsigned uid; VehiclePropertyAccess access; }; using PropertyAclMap = std::unordered_multimap<VehicleProperty, PropertyAcl>; /** * Parser for per-property access control in vehicle HAL. * * It supports the following format: * Set ALIAS_NAME UID * {S,V}:0x0305 {ALIAS_NAME,UID} {R,W,RW} * * ALIAS_NAME is just an alias for UID * S - for system properties (VehiclePropertyGroup::SYSTEM) * V - for vendor properties (VehiclePropertyGroup::VENDOR) * * Example: * * Set AID_AUDIO 1004 * Set AID_MY_APP 10022 * * S:0x0305 AID_AUDIO RW * S:0x0305 10021 R * V:0x0101 AID_MY_APP R */ class AccessControlConfigParser { public: /** * Creates an instance of AccessControlConfigParser * * @param properties - properties supported by HAL implementation */ AccessControlConfigParser(const std::vector<VehicleProperty>& properties); /** * Parses config content from given stream and writes results to * propertyAclMap. */ bool parseFromStream(std::istream* stream, PropertyAclMap* propertyAclMap); private: bool processTokens(std::list<std::string>* tokens, PropertyAclMap* propertyAclMap); bool parsePropertyGroup(char group, VehiclePropertyGroup* outPropertyGroup) const; bool parsePropertyId(const std::string& strPropId, VehiclePropertyGroup propertyGroup, VehicleProperty* outVehicleProperty) const; bool parseUid(const std::string& strUid, unsigned* outUid) const; bool parseAccess(const std::string& strAccess, VehiclePropertyAccess* outAccess) const; std::string readNextToken(std::list<std::string>* tokens) const; static bool parseInt(const char* strValue, int* outIntValue); static void split(const std::string& line, std::list<std::string>* outTokens); private: std::unordered_map<std::string, unsigned> mUidMap {}; // Contains UID // aliases. // Map property ids w/o TYPE and AREA to VehicleProperty. std::unordered_map<int, VehicleProperty> mStrippedToVehiclePropertyMap; }; } // namespace V2_0 } // namespace vehicle } // namespace hardware } // namespace android #endif // android_hardware_vehicle_V2_0_AccessControlConfigParser_H_ vehicle/2.0/default/vehicle_hal_manager/VehicleHalManager.cpp +60 −16 Original line number Diff line number Diff line Loading @@ -22,6 +22,8 @@ #include <hidl/Status.h> #include <future> #include <bitset> #include <fstream> #include <private/android_filesystem_config.h> #include "VehicleHalManager.h" Loading Loading @@ -52,10 +54,8 @@ Return<void> VehicleHalManager::getAllPropConfigs( const_cast<VehiclePropConfig *>(halConfig.data()), halConfig.size()); ALOGI("getAllPropConfigs calling callback"); _hidl_cb(hidlConfigs); ALOGI("getAllPropConfigs done"); return Void(); } Loading Loading @@ -88,7 +88,7 @@ Return<void> VehicleHalManager::get( return Void(); } if (!checkReadPermission(*config, getCallee())) { if (!checkReadPermission(*config, getCaller())) { _hidl_cb(StatusCode::INVALID_ARG, kEmptyValue); return Void(); } Loading @@ -109,7 +109,7 @@ Return<StatusCode> VehicleHalManager::set(const VehiclePropValue &value) { return StatusCode::INVALID_ARG; } if (!checkWritePermission(*config, getCallee())) { if (!checkWritePermission(*config, getCaller())) { return StatusCode::INVALID_ARG; } Loading Loading @@ -194,7 +194,21 @@ void VehicleHalManager::init() { _1, _2, _3)); // Initialize index with vehicle configurations received from VehicleHal. mConfigIndex.reset(new VehiclePropConfigIndex(mHal->listProperties())); auto supportedPropConfigs = mHal->listProperties(); mConfigIndex.reset(new VehiclePropConfigIndex(supportedPropConfigs)); std::vector<VehicleProperty> supportedProperties( supportedPropConfigs.size()); for (const auto& config : supportedPropConfigs) { supportedProperties.push_back(config.prop); } AccessControlConfigParser aclParser(supportedProperties); const char* configs[] = { "/system/etc/vehicle_access.conf", "/vendor/etc/vehicle_access.conf" }; for (const char* filename : configs) { readAndParseAclConfig(filename, &aclParser, &mPropertyAclMap); } } VehicleHalManager::~VehicleHalManager() { Loading Loading @@ -292,24 +306,43 @@ bool VehicleHalManager::isSubscribable(const VehiclePropConfig& config, return true; } bool checkAcl(const PropertyAclMap& aclMap, uid_t callerUid, VehicleProperty propertyId, VehiclePropertyAccess requiredAccess) { if (callerUid == AID_SYSTEM && isSystemProperty(propertyId)) { return true; } auto range = aclMap.equal_range(propertyId); for (auto it = range.first; it != range.second; ++it) { auto& acl = it->second; if (acl.uid == callerUid && (acl.access & requiredAccess)) { return true; } } return false; } bool VehicleHalManager::checkWritePermission(const VehiclePropConfig &config, const Callee& callee) { const Caller& caller) const { if (!(config.access & VehiclePropertyAccess::WRITE)) { ALOGW("Property 0%x has no write access", config.prop); return false; } //TODO(pavelm): check pid/uid has write access return true; return checkAcl(mPropertyAclMap, caller.uid, config.prop, VehiclePropertyAccess::WRITE); } bool VehicleHalManager::checkReadPermission(const VehiclePropConfig &config, const Callee& callee) { const Caller& caller) const { if (!(config.access & VehiclePropertyAccess::READ)) { ALOGW("Property 0%x has no read access", config.prop); return false; } //TODO(pavelm): check pid/uid has read access return true; return checkAcl(mPropertyAclMap, caller.uid, config.prop, VehiclePropertyAccess::READ); } void VehicleHalManager::handlePropertySetEvent(const VehiclePropValue& value) { Loading @@ -326,13 +359,24 @@ const VehiclePropConfig* VehicleHalManager::getPropConfigOrNull( ? &mConfigIndex->getConfig(prop) : nullptr; } Callee VehicleHalManager::getCallee() { Callee callee; Caller VehicleHalManager::getCaller() { Caller caller; IPCThreadState* self = IPCThreadState::self(); callee.pid = self->getCallingPid(); callee.uid = self->getCallingUid(); caller.pid = self->getCallingPid(); caller.uid = self->getCallingUid(); return caller; } return callee; void VehicleHalManager::readAndParseAclConfig(const char* filename, AccessControlConfigParser* parser, PropertyAclMap* outAclMap) { std::ifstream file(filename); if (file.is_open()) { ALOGI("Parsing file: %s", filename); parser->parseFromStream(&file, outAclMap); file.close(); } } } // namespace V2_0 Loading Loading
vehicle/2.0/default/Android.mk +2 −0 Original line number Diff line number Diff line Loading @@ -22,6 +22,7 @@ module_prefix = android.hardware.vehicle@2.0 include $(CLEAR_VARS) LOCAL_MODULE := $(module_prefix)-manager-lib LOCAL_SRC_FILES := \ vehicle_hal_manager/AccessControlConfigParser.cpp \ vehicle_hal_manager/SubscriptionManager.cpp \ vehicle_hal_manager/VehicleHalManager.cpp \ Loading Loading @@ -67,6 +68,7 @@ LOCAL_MODULE:= $(module_prefix)-manager-unit-tests LOCAL_WHOLE_STATIC_LIBRARIES := $(module_prefix)-manager-lib LOCAL_SRC_FILES:= \ tests/AccessControlConfigParser_test.cpp \ tests/VehicleObjectPool_test.cpp \ tests/VehiclePropConfigIndex_test.cpp \ tests/SubscriptionManager_test.cpp \ Loading
vehicle/2.0/default/tests/AccessControlConfigParser_test.cpp 0 → 100644 +149 −0 Original line number Diff line number Diff line /* * 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. */ #include <gtest/gtest.h> #include <memory> #include <fstream> #include <unordered_set> #include "vehicle_hal_manager/AccessControlConfigParser.h" namespace android { namespace hardware { namespace vehicle { namespace V2_0 { namespace { class AccessControlConfigParserTest : public ::testing::Test { protected: void SetUp() override { std::vector<VehicleProperty> supportedProperties { VehicleProperty::HVAC_FAN_SPEED, VehicleProperty::HVAC_FAN_DIRECTION, }; parser.reset(new AccessControlConfigParser(supportedProperties)); } public: PropertyAclMap aclMap; std::unique_ptr<AccessControlConfigParser> parser; }; TEST_F(AccessControlConfigParserTest, basicParsing) { std::stringstream file; file << "S:0x0500 1000 RW" << std::endl; ASSERT_TRUE(parser->parseFromStream(&file, &aclMap)); ASSERT_EQ(1, aclMap.size()); auto it = aclMap.find(VehicleProperty::HVAC_FAN_SPEED); ASSERT_NE(aclMap.end(), it); ASSERT_EQ(VehiclePropertyAccess::READ_WRITE, it->second.access); ASSERT_EQ(VehicleProperty::HVAC_FAN_SPEED, it->second.propId); ASSERT_EQ(1000u, it->second.uid); } TEST_F(AccessControlConfigParserTest, multipleUids) { std::stringstream file; file << "Set AID_AUDIO 1004" << std::endl << "Set AID_SYSTEM 1000" << std::endl << "S:0x0500 AID_SYSTEM RW" << std::endl << "S:0x0500 AID_AUDIO RW" << std::endl << "S:0x0500 0xbeef R" << std::endl; // Read-only. std::unordered_set<unsigned> expectedUids {1000, 1004, 0xbeef}; ASSERT_TRUE(parser->parseFromStream(&file, &aclMap)); auto range = aclMap.equal_range(VehicleProperty::HVAC_FAN_SPEED); for (auto it = range.first; it != range.second; ++it) { auto& acl = it->second; ASSERT_EQ(1, expectedUids.count(acl.uid)) << " uid: " << std::hex << acl.uid; if (acl.uid == 0xbeef) { ASSERT_EQ(VehiclePropertyAccess::READ, acl.access); } else { ASSERT_EQ(VehiclePropertyAccess::READ_WRITE, acl.access); } } } TEST_F(AccessControlConfigParserTest, fileContainsJunk) { std::stringstream file; file << "This string will be ignored with warning in the log" << std::endl << "# However comments are quit legitimate" << std::endl << "S:0x0500 0xbeef R # YAY" << std::endl; ASSERT_FALSE(parser->parseFromStream(&file, &aclMap)); ASSERT_EQ(1, aclMap.size()); auto it = aclMap.find(VehicleProperty::HVAC_FAN_SPEED); ASSERT_NE(aclMap.end(), it); ASSERT_EQ(VehiclePropertyAccess::READ, it->second.access); ASSERT_EQ(VehicleProperty::HVAC_FAN_SPEED, it->second.propId); ASSERT_EQ(0xbeef, it->second.uid); } TEST_F(AccessControlConfigParserTest, badIntegerFormat) { std::stringstream file; file << "S:0x0500 A12 RW " << std::endl; ASSERT_FALSE(parser->parseFromStream(&file, &aclMap)); ASSERT_EQ(0, aclMap.size()); } TEST_F(AccessControlConfigParserTest, ignoreNotSupportedProperties) { std::stringstream file; file << "S:0x0666 1000 RW " << std::endl; ASSERT_FALSE(parser->parseFromStream(&file, &aclMap)); ASSERT_EQ(0, aclMap.size()); } TEST_F(AccessControlConfigParserTest, multipleCalls) { std::stringstream configFile; configFile << "S:0x0500 1000 RW" << std::endl; ASSERT_TRUE(parser->parseFromStream(&configFile, &aclMap)); ASSERT_EQ(1, aclMap.size()); std::stringstream configFile2; configFile2 << "S:0x0501 1004 RW" << std::endl; ASSERT_TRUE(parser->parseFromStream(&configFile2, &aclMap)); ASSERT_EQ(2, aclMap.size()); auto it = aclMap.find(VehicleProperty::HVAC_FAN_SPEED); ASSERT_NE(aclMap.end(), it); ASSERT_EQ(VehiclePropertyAccess::READ_WRITE, it->second.access); ASSERT_EQ(VehicleProperty::HVAC_FAN_SPEED, it->second.propId); ASSERT_EQ(1000u, it->second.uid); it = aclMap.find(VehicleProperty::HVAC_FAN_DIRECTION); ASSERT_NE(aclMap.end(), it); ASSERT_EQ(VehiclePropertyAccess::READ_WRITE, it->second.access); ASSERT_EQ(VehicleProperty::HVAC_FAN_DIRECTION, it->second.propId); ASSERT_EQ(1004u, it->second.uid); } } // namespace anonymous } // namespace V2_0 } // namespace vehicle } // namespace hardware } // namespace android
vehicle/2.0/default/vehicle_hal_manager/AccessControlConfigParser.cpp 0 → 100644 +224 −0 Original line number Diff line number Diff line /* * 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 "android.hardware.vehicle@2.0-impl" #include <android/log.h> #include <fstream> #include <sstream> #include <iostream> #include "AccessControlConfigParser.h" namespace android { namespace hardware { namespace vehicle { namespace V2_0 { AccessControlConfigParser::AccessControlConfigParser( const std::vector<VehicleProperty>& properties) { // Property Id in the config file doesn't include information about // type and area. So we want to create a map from these kind of // *stripped* properties to the whole VehicleProperty. // We also want to filter out ACL to the properties that supported // by concrete Vehicle HAL implementation. for (VehicleProperty vehicleProperty : properties) { auto numProp = static_cast<int>(vehicleProperty); numProp &= ~static_cast<int>(VehiclePropertyType::MASK) & ~static_cast<int>(VehicleArea::MASK); mStrippedToVehiclePropertyMap.emplace(numProp, vehicleProperty); } } bool AccessControlConfigParser::parseFromStream( std::istream* stream, PropertyAclMap* propertyAclMap) { std::list<std::string> tokens; std::string line; int lineNo = 0; bool warnings = false; for (;std::getline(*stream, line); lineNo++) { split(line, &tokens); if (!processTokens(&tokens, propertyAclMap)) { warnings = true; ALOGW("Failed to parse line %d : %s", lineNo, line.c_str()); } } return !warnings; } bool AccessControlConfigParser::processTokens(std::list<std::string>* tokens, PropertyAclMap* propertyAclMap) { std::string token = readNextToken(tokens); if (token.empty() || token[0] == '#') { // Ignore comment. return true; } if (token == "Set") { std::string alias = readNextToken(tokens); std::string strUid = readNextToken(tokens); if (alias.empty() || strUid.empty()) { ALOGW("Expected alias and UID must be specified"); return false; } int uid; if (!parseInt(strUid.c_str(), &uid)) { ALOGW("Invalid UID: %d", uid); } mUidMap.emplace(std::move(alias), uid); } else if (token.size() > 2 && token[1] == ':') { VehiclePropertyGroup propGroup; if (!parsePropertyGroup(token[0], &propGroup)) { return false; } std::string strUid = readNextToken(tokens); std::string strAccess = readNextToken(tokens); if (strUid.empty() || strAccess.empty()) { ALOGW("Expected UID and access for property: %s", token.c_str()); } PropertyAcl acl; if (parsePropertyId(token.substr(2), propGroup, &acl.propId) && parseUid(strUid, &acl.uid) && parseAccess(strAccess, &acl.access)) { propertyAclMap->emplace(acl.propId, std::move(acl)); } else { return false; } } else { ALOGW("Unexpected token: %s", token.c_str()); return false; } return true; } bool AccessControlConfigParser::parsePropertyGroup( char group, VehiclePropertyGroup* outPropertyGroup) const { switch (group) { case 'S': // Fall through. case 's': *outPropertyGroup = VehiclePropertyGroup::SYSTEM; break; case 'V': // Fall through. case 'v': *outPropertyGroup = VehiclePropertyGroup::VENDOR; break; default: ALOGW("Unexpected group: %c", group); return false; } return true; } bool AccessControlConfigParser::parsePropertyId( const std::string& strPropId, VehiclePropertyGroup propertyGroup, VehicleProperty* outVehicleProperty) const { int propId; if (!parseInt(strPropId.c_str(), &propId)) { ALOGW("Failed to convert property id to integer: %s", strPropId.c_str()); return false; } propId |= static_cast<int>(propertyGroup); auto it = mStrippedToVehiclePropertyMap.find(propId); if (it == mStrippedToVehiclePropertyMap.end()) { ALOGW("Property Id not found or not supported: 0x%x", propId); return false; } *outVehicleProperty = it->second; return true; } bool AccessControlConfigParser::parseInt(const char* strValue, int* outIntValue) { char* end; long num = std::strtol(strValue, &end, 0 /* auto detect base */); bool success = *end == 0 && errno != ERANGE; if (success) { *outIntValue = static_cast<int>(num); } return success; } bool AccessControlConfigParser::parseUid(const std::string& strUid, unsigned* outUid) const { auto element = mUidMap.find(strUid); if (element != mUidMap.end()) { *outUid = element->second; } else { int val; if (!parseInt(strUid.c_str(), &val)) { ALOGW("Failed to convert UID '%s' to integer", strUid.c_str()); return false; } *outUid = static_cast<unsigned>(val); } return true; } bool AccessControlConfigParser::parseAccess( const std::string& strAccess, VehiclePropertyAccess* outAccess) const { if (strAccess.size() == 0 || strAccess.size() > 2) { ALOGW("Unknown access mode '%s'", strAccess.c_str()); return false; } int32_t access = static_cast<int32_t>(VehiclePropertyAccess::NONE); for (char c : strAccess) { if (c == 'R' || c == 'r') { access |= VehiclePropertyAccess::READ; } else if (c == 'W' || c == 'w') { access |= VehiclePropertyAccess::WRITE; } else { ALOGW("Unknown access mode: %c", c); return false; } } *outAccess = static_cast<VehiclePropertyAccess>(access); return true; } void AccessControlConfigParser::split(const std::string& line, std::list<std::string>* outTokens) { outTokens->clear(); std::istringstream iss(line); while (!iss.eof()) { std::string token; iss >> token; outTokens->push_back(std::move(token)); } } std::string AccessControlConfigParser::readNextToken( std::list<std::string>* tokens) const { if (tokens->empty()) { return ""; } std::string token = tokens->front(); tokens->pop_front(); return token; } } // namespace V2_0 } // namespace vehicle } // namespace hardware } // namespace android
vehicle/2.0/default/vehicle_hal_manager/AccessControlConfigParser.h 0 → 100644 +110 −0 Original line number Diff line number Diff line /* * 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. */ #ifndef android_hardware_vehicle_V2_0_AccessControlConfigParser_H_ #define android_hardware_vehicle_V2_0_AccessControlConfigParser_H_ #include <string> #include <vector> #include <unordered_map> #include <list> #include <android/hardware/vehicle/2.0/types.h> namespace android { namespace hardware { namespace vehicle { namespace V2_0 { struct PropertyAcl { VehicleProperty propId; unsigned uid; VehiclePropertyAccess access; }; using PropertyAclMap = std::unordered_multimap<VehicleProperty, PropertyAcl>; /** * Parser for per-property access control in vehicle HAL. * * It supports the following format: * Set ALIAS_NAME UID * {S,V}:0x0305 {ALIAS_NAME,UID} {R,W,RW} * * ALIAS_NAME is just an alias for UID * S - for system properties (VehiclePropertyGroup::SYSTEM) * V - for vendor properties (VehiclePropertyGroup::VENDOR) * * Example: * * Set AID_AUDIO 1004 * Set AID_MY_APP 10022 * * S:0x0305 AID_AUDIO RW * S:0x0305 10021 R * V:0x0101 AID_MY_APP R */ class AccessControlConfigParser { public: /** * Creates an instance of AccessControlConfigParser * * @param properties - properties supported by HAL implementation */ AccessControlConfigParser(const std::vector<VehicleProperty>& properties); /** * Parses config content from given stream and writes results to * propertyAclMap. */ bool parseFromStream(std::istream* stream, PropertyAclMap* propertyAclMap); private: bool processTokens(std::list<std::string>* tokens, PropertyAclMap* propertyAclMap); bool parsePropertyGroup(char group, VehiclePropertyGroup* outPropertyGroup) const; bool parsePropertyId(const std::string& strPropId, VehiclePropertyGroup propertyGroup, VehicleProperty* outVehicleProperty) const; bool parseUid(const std::string& strUid, unsigned* outUid) const; bool parseAccess(const std::string& strAccess, VehiclePropertyAccess* outAccess) const; std::string readNextToken(std::list<std::string>* tokens) const; static bool parseInt(const char* strValue, int* outIntValue); static void split(const std::string& line, std::list<std::string>* outTokens); private: std::unordered_map<std::string, unsigned> mUidMap {}; // Contains UID // aliases. // Map property ids w/o TYPE and AREA to VehicleProperty. std::unordered_map<int, VehicleProperty> mStrippedToVehiclePropertyMap; }; } // namespace V2_0 } // namespace vehicle } // namespace hardware } // namespace android #endif // android_hardware_vehicle_V2_0_AccessControlConfigParser_H_
vehicle/2.0/default/vehicle_hal_manager/VehicleHalManager.cpp +60 −16 Original line number Diff line number Diff line Loading @@ -22,6 +22,8 @@ #include <hidl/Status.h> #include <future> #include <bitset> #include <fstream> #include <private/android_filesystem_config.h> #include "VehicleHalManager.h" Loading Loading @@ -52,10 +54,8 @@ Return<void> VehicleHalManager::getAllPropConfigs( const_cast<VehiclePropConfig *>(halConfig.data()), halConfig.size()); ALOGI("getAllPropConfigs calling callback"); _hidl_cb(hidlConfigs); ALOGI("getAllPropConfigs done"); return Void(); } Loading Loading @@ -88,7 +88,7 @@ Return<void> VehicleHalManager::get( return Void(); } if (!checkReadPermission(*config, getCallee())) { if (!checkReadPermission(*config, getCaller())) { _hidl_cb(StatusCode::INVALID_ARG, kEmptyValue); return Void(); } Loading @@ -109,7 +109,7 @@ Return<StatusCode> VehicleHalManager::set(const VehiclePropValue &value) { return StatusCode::INVALID_ARG; } if (!checkWritePermission(*config, getCallee())) { if (!checkWritePermission(*config, getCaller())) { return StatusCode::INVALID_ARG; } Loading Loading @@ -194,7 +194,21 @@ void VehicleHalManager::init() { _1, _2, _3)); // Initialize index with vehicle configurations received from VehicleHal. mConfigIndex.reset(new VehiclePropConfigIndex(mHal->listProperties())); auto supportedPropConfigs = mHal->listProperties(); mConfigIndex.reset(new VehiclePropConfigIndex(supportedPropConfigs)); std::vector<VehicleProperty> supportedProperties( supportedPropConfigs.size()); for (const auto& config : supportedPropConfigs) { supportedProperties.push_back(config.prop); } AccessControlConfigParser aclParser(supportedProperties); const char* configs[] = { "/system/etc/vehicle_access.conf", "/vendor/etc/vehicle_access.conf" }; for (const char* filename : configs) { readAndParseAclConfig(filename, &aclParser, &mPropertyAclMap); } } VehicleHalManager::~VehicleHalManager() { Loading Loading @@ -292,24 +306,43 @@ bool VehicleHalManager::isSubscribable(const VehiclePropConfig& config, return true; } bool checkAcl(const PropertyAclMap& aclMap, uid_t callerUid, VehicleProperty propertyId, VehiclePropertyAccess requiredAccess) { if (callerUid == AID_SYSTEM && isSystemProperty(propertyId)) { return true; } auto range = aclMap.equal_range(propertyId); for (auto it = range.first; it != range.second; ++it) { auto& acl = it->second; if (acl.uid == callerUid && (acl.access & requiredAccess)) { return true; } } return false; } bool VehicleHalManager::checkWritePermission(const VehiclePropConfig &config, const Callee& callee) { const Caller& caller) const { if (!(config.access & VehiclePropertyAccess::WRITE)) { ALOGW("Property 0%x has no write access", config.prop); return false; } //TODO(pavelm): check pid/uid has write access return true; return checkAcl(mPropertyAclMap, caller.uid, config.prop, VehiclePropertyAccess::WRITE); } bool VehicleHalManager::checkReadPermission(const VehiclePropConfig &config, const Callee& callee) { const Caller& caller) const { if (!(config.access & VehiclePropertyAccess::READ)) { ALOGW("Property 0%x has no read access", config.prop); return false; } //TODO(pavelm): check pid/uid has read access return true; return checkAcl(mPropertyAclMap, caller.uid, config.prop, VehiclePropertyAccess::READ); } void VehicleHalManager::handlePropertySetEvent(const VehiclePropValue& value) { Loading @@ -326,13 +359,24 @@ const VehiclePropConfig* VehicleHalManager::getPropConfigOrNull( ? &mConfigIndex->getConfig(prop) : nullptr; } Callee VehicleHalManager::getCallee() { Callee callee; Caller VehicleHalManager::getCaller() { Caller caller; IPCThreadState* self = IPCThreadState::self(); callee.pid = self->getCallingPid(); callee.uid = self->getCallingUid(); caller.pid = self->getCallingPid(); caller.uid = self->getCallingUid(); return caller; } return callee; void VehicleHalManager::readAndParseAclConfig(const char* filename, AccessControlConfigParser* parser, PropertyAclMap* outAclMap) { std::ifstream file(filename); if (file.is_open()) { ALOGI("Parsing file: %s", filename); parser->parseFromStream(&file, outAclMap); file.close(); } } } // namespace V2_0 Loading