Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 593f0f93 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Implementing per-property access control"

parents a4d8fdd1 7cb0bf36
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -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 \

@@ -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 \
+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
+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
+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_
+60 −16
Original line number Diff line number Diff line
@@ -22,6 +22,8 @@
#include <hidl/Status.h>
#include <future>
#include <bitset>
#include <fstream>
#include <private/android_filesystem_config.h>

#include "VehicleHalManager.h"

@@ -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();
}

@@ -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();
    }
@@ -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;
    }

@@ -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() {
@@ -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) {
@@ -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