Merge "Introduce property types"
This commit is contained in:
commit
a0ffad60ef
|
@ -75,6 +75,7 @@ cc_library_static {
|
|||
"persistent_properties.cpp",
|
||||
"persistent_properties.proto",
|
||||
"property_service.cpp",
|
||||
"property_type.cpp",
|
||||
"security.cpp",
|
||||
"selinux.cpp",
|
||||
"service.cpp",
|
||||
|
@ -178,6 +179,7 @@ cc_test {
|
|||
"init_test.cpp",
|
||||
"persistent_properties_test.cpp",
|
||||
"property_service_test.cpp",
|
||||
"property_type_test.cpp",
|
||||
"result_test.cpp",
|
||||
"rlimit_parser_test.cpp",
|
||||
"service_test.cpp",
|
||||
|
|
|
@ -58,6 +58,7 @@
|
|||
|
||||
#include "init.h"
|
||||
#include "persistent_properties.h"
|
||||
#include "property_type.h"
|
||||
#include "util.h"
|
||||
|
||||
using android::base::ReadFileToString;
|
||||
|
@ -95,14 +96,9 @@ void property_init() {
|
|||
LOG(FATAL) << "Failed to load serialized property info file";
|
||||
}
|
||||
}
|
||||
static bool check_mac_perms(const std::string& name, char* sctx, struct ucred* cr) {
|
||||
if (!sctx) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const char* target_context = nullptr;
|
||||
property_info_area->GetPropertyInfo(name.c_str(), &target_context, nullptr);
|
||||
if (target_context == nullptr) {
|
||||
static bool CheckMacPerms(const std::string& name, const char* target_context,
|
||||
const char* source_context, struct ucred* cr) {
|
||||
if (!target_context || !source_context) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -111,29 +107,12 @@ static bool check_mac_perms(const std::string& name, char* sctx, struct ucred* c
|
|||
audit_data.name = name.c_str();
|
||||
audit_data.cr = cr;
|
||||
|
||||
bool has_access =
|
||||
(selinux_check_access(sctx, target_context, "property_service", "set", &audit_data) == 0);
|
||||
bool has_access = (selinux_check_access(source_context, target_context, "property_service",
|
||||
"set", &audit_data) == 0);
|
||||
|
||||
return has_access;
|
||||
}
|
||||
|
||||
static int check_control_mac_perms(const char *name, char *sctx, struct ucred *cr)
|
||||
{
|
||||
/*
|
||||
* Create a name prefix out of ctl.<service name>
|
||||
* The new prefix allows the use of the existing
|
||||
* property service backend labeling while avoiding
|
||||
* mislabels based on true property prefixes.
|
||||
*/
|
||||
char ctl_name[PROP_VALUE_MAX+4];
|
||||
int ret = snprintf(ctl_name, sizeof(ctl_name), "ctl.%s", name);
|
||||
|
||||
if (ret < 0 || (size_t) ret >= sizeof(ctl_name))
|
||||
return 0;
|
||||
|
||||
return check_mac_perms(ctl_name, sctx, cr);
|
||||
}
|
||||
|
||||
bool is_legal_property_name(const std::string& name) {
|
||||
size_t namelen = name.size();
|
||||
|
||||
|
@ -422,52 +401,70 @@ static void handle_property_set(SocketConnection& socket,
|
|||
struct ucred cr = socket.cred();
|
||||
char* source_ctx = nullptr;
|
||||
getpeercon(socket.socket(), &source_ctx);
|
||||
std::string source_context = source_ctx;
|
||||
freecon(source_ctx);
|
||||
|
||||
if (StartsWith(name, "ctl.")) {
|
||||
if (check_control_mac_perms(value.c_str(), source_ctx, &cr)) {
|
||||
// ctl. properties have their name ctl.<action> and their value is the name of the service to
|
||||
// apply that action to. Permissions for these actions are based on the service, so we must
|
||||
// create a fake name of ctl.<service> to check permissions.
|
||||
auto control_string = "ctl." + value;
|
||||
const char* target_context = nullptr;
|
||||
const char* type = nullptr;
|
||||
property_info_area->GetPropertyInfo(control_string.c_str(), &target_context, &type);
|
||||
if (!CheckMacPerms(control_string, target_context, source_context.c_str(), &cr)) {
|
||||
LOG(ERROR) << "sys_prop(" << cmd_name << "): Unable to " << (name.c_str() + 4)
|
||||
<< " service ctl [" << value << "]"
|
||||
<< " uid:" << cr.uid << " gid:" << cr.gid << " pid:" << cr.pid;
|
||||
if (!legacy_protocol) {
|
||||
socket.SendUint32(PROP_ERROR_HANDLE_CONTROL_MESSAGE);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
handle_control_message(name.c_str() + 4, value.c_str());
|
||||
if (!legacy_protocol) {
|
||||
socket.SendUint32(PROP_SUCCESS);
|
||||
socket.SendUint32(PROP_SUCCESS);
|
||||
}
|
||||
} else {
|
||||
LOG(ERROR) << "sys_prop(" << cmd_name << "): Unable to " << (name.c_str() + 4)
|
||||
<< " service ctl [" << value << "]"
|
||||
<< " uid:" << cr.uid
|
||||
<< " gid:" << cr.gid
|
||||
<< " pid:" << cr.pid;
|
||||
if (!legacy_protocol) {
|
||||
socket.SendUint32(PROP_ERROR_HANDLE_CONTROL_MESSAGE);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (check_mac_perms(name, source_ctx, &cr)) {
|
||||
const char* target_context = nullptr;
|
||||
const char* type = nullptr;
|
||||
property_info_area->GetPropertyInfo(name.c_str(), &target_context, &type);
|
||||
if (!CheckMacPerms(name, target_context, source_context.c_str(), &cr)) {
|
||||
LOG(ERROR) << "sys_prop(" << cmd_name << "): permission denied uid:" << cr.uid
|
||||
<< " name:" << name;
|
||||
if (!legacy_protocol) {
|
||||
socket.SendUint32(PROP_ERROR_PERMISSION_DENIED);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (type == nullptr || !CheckType(type, value)) {
|
||||
LOG(ERROR) << "sys_prop(" << cmd_name << "): type check failed, type: '"
|
||||
<< (type ?: "(null)") << "' value: '" << value << "'";
|
||||
if (!legacy_protocol) {
|
||||
socket.SendUint32(PROP_ERROR_INVALID_VALUE);
|
||||
}
|
||||
return;
|
||||
}
|
||||
// sys.powerctl is a special property that is used to make the device reboot. We want to log
|
||||
// any process that sets this property to be able to accurately blame the cause of a shutdown.
|
||||
if (name == "sys.powerctl") {
|
||||
std::string cmdline_path = StringPrintf("proc/%d/cmdline", cr.pid);
|
||||
std::string process_cmdline;
|
||||
std::string process_log_string;
|
||||
if (ReadFileToString(cmdline_path, &process_cmdline)) {
|
||||
// Since cmdline is null deliminated, .c_str() conveniently gives us just the process path.
|
||||
process_log_string = StringPrintf(" (%s)", process_cmdline.c_str());
|
||||
}
|
||||
LOG(INFO) << "Received sys.powerctl='" << value << "' from pid: " << cr.pid
|
||||
<< process_log_string;
|
||||
std::string cmdline_path = StringPrintf("proc/%d/cmdline", cr.pid);
|
||||
std::string process_cmdline;
|
||||
std::string process_log_string;
|
||||
if (ReadFileToString(cmdline_path, &process_cmdline)) {
|
||||
// Since cmdline is null deliminated, .c_str() conveniently gives us just the process path.
|
||||
process_log_string = StringPrintf(" (%s)", process_cmdline.c_str());
|
||||
}
|
||||
LOG(INFO) << "Received sys.powerctl='" << value << "' from pid: " << cr.pid
|
||||
<< process_log_string;
|
||||
}
|
||||
|
||||
uint32_t result = property_set(name, value);
|
||||
if (!legacy_protocol) {
|
||||
socket.SendUint32(result);
|
||||
socket.SendUint32(result);
|
||||
}
|
||||
} else {
|
||||
LOG(ERROR) << "sys_prop(" << cmd_name << "): permission denied uid:" << cr.uid << " name:" << name;
|
||||
if (!legacy_protocol) {
|
||||
socket.SendUint32(PROP_ERROR_PERMISSION_DENIED);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
freecon(source_ctx);
|
||||
}
|
||||
|
||||
static void handle_property_set_fd() {
|
||||
|
@ -764,9 +761,10 @@ void CreateSerializedPropertyInfo() {
|
|||
}
|
||||
LoadPropertyInfoFromFile("/nonplat_property_contexts", &property_infos);
|
||||
}
|
||||
|
||||
auto serialized_contexts = std::string();
|
||||
auto error = std::string();
|
||||
if (!BuildTrie(property_infos, "u:object_r:default_prop:s0", "\\s*", &serialized_contexts,
|
||||
if (!BuildTrie(property_infos, "u:object_r:default_prop:s0", "string", &serialized_contexts,
|
||||
&error)) {
|
||||
LOG(ERROR) << "Unable to serialize property contexts: " << error;
|
||||
return;
|
||||
|
|
|
@ -0,0 +1,81 @@
|
|||
//
|
||||
// Copyright (C) 2017 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 "property_type.h"
|
||||
|
||||
#include <android-base/parsedouble.h>
|
||||
#include <android-base/parseint.h>
|
||||
#include <android-base/strings.h>
|
||||
|
||||
using android::base::ParseDouble;
|
||||
using android::base::ParseInt;
|
||||
using android::base::ParseUint;
|
||||
using android::base::Split;
|
||||
|
||||
namespace android {
|
||||
namespace init {
|
||||
|
||||
bool CheckType(const std::string& type_string, const std::string& value) {
|
||||
auto type_strings = Split(type_string, " ");
|
||||
if (type_strings.empty()) {
|
||||
return false;
|
||||
}
|
||||
auto type = type_strings[0];
|
||||
|
||||
if (type == "string") {
|
||||
return true;
|
||||
}
|
||||
if (type == "bool") {
|
||||
return value == "true" || value == "false" || value == "1" || value == "0";
|
||||
}
|
||||
if (type == "int") {
|
||||
int64_t parsed;
|
||||
return ParseInt(value, &parsed);
|
||||
}
|
||||
if (type == "uint") {
|
||||
uint64_t parsed;
|
||||
if (value.empty() || value.front() == '-') {
|
||||
return false;
|
||||
}
|
||||
return ParseUint(value, &parsed);
|
||||
}
|
||||
if (type == "double") {
|
||||
double parsed;
|
||||
return ParseDouble(value.c_str(), &parsed);
|
||||
}
|
||||
if (type == "size") {
|
||||
auto it = value.begin();
|
||||
while (it != value.end() && isdigit(*it)) {
|
||||
it++;
|
||||
}
|
||||
if (it == value.begin() || it == value.end() || (*it != 'g' && *it != 'k' && *it != 'm')) {
|
||||
return false;
|
||||
}
|
||||
it++;
|
||||
return it == value.end();
|
||||
}
|
||||
if (type == "enum") {
|
||||
for (auto it = std::next(type_strings.begin()); it != type_strings.end(); ++it) {
|
||||
if (*it == value) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace init
|
||||
} // namespace android
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright (C) 2017 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 _INIT_PROPERTY_TYPE_H
|
||||
#define _INIT_PROPERTY_TYPE_H
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace android {
|
||||
namespace init {
|
||||
|
||||
bool CheckType(const std::string& type_string, const std::string& value);
|
||||
|
||||
} // namespace init
|
||||
} // namespace android
|
||||
|
||||
#endif
|
|
@ -0,0 +1,95 @@
|
|||
/*
|
||||
* Copyright (C) 2017 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 "property_type.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
namespace android {
|
||||
namespace init {
|
||||
|
||||
TEST(property_type, CheckType_string) {
|
||||
EXPECT_TRUE(CheckType("string", ""));
|
||||
EXPECT_TRUE(CheckType("string", "-234"));
|
||||
EXPECT_TRUE(CheckType("string", "234"));
|
||||
EXPECT_TRUE(CheckType("string", "true"));
|
||||
EXPECT_TRUE(CheckType("string", "false"));
|
||||
EXPECT_TRUE(CheckType("string", "45645634563456345634563456"));
|
||||
EXPECT_TRUE(CheckType("string", "some other string"));
|
||||
}
|
||||
|
||||
TEST(property_type, CheckType_int) {
|
||||
EXPECT_FALSE(CheckType("int", ""));
|
||||
EXPECT_FALSE(CheckType("int", "abc"));
|
||||
EXPECT_FALSE(CheckType("int", "-abc"));
|
||||
EXPECT_TRUE(CheckType("int", "0"));
|
||||
EXPECT_TRUE(CheckType("int", std::to_string(std::numeric_limits<int64_t>::min())));
|
||||
EXPECT_TRUE(CheckType("int", std::to_string(std::numeric_limits<int64_t>::max())));
|
||||
EXPECT_TRUE(CheckType("int", "123"));
|
||||
EXPECT_TRUE(CheckType("int", "-123"));
|
||||
}
|
||||
|
||||
TEST(property_type, CheckType_uint) {
|
||||
EXPECT_FALSE(CheckType("uint", ""));
|
||||
EXPECT_FALSE(CheckType("uint", "abc"));
|
||||
EXPECT_FALSE(CheckType("uint", "-abc"));
|
||||
EXPECT_TRUE(CheckType("uint", "0"));
|
||||
EXPECT_TRUE(CheckType("uint", std::to_string(std::numeric_limits<uint64_t>::max())));
|
||||
EXPECT_TRUE(CheckType("uint", "123"));
|
||||
EXPECT_FALSE(CheckType("uint", "-123"));
|
||||
}
|
||||
|
||||
TEST(property_type, CheckType_double) {
|
||||
EXPECT_FALSE(CheckType("double", ""));
|
||||
EXPECT_FALSE(CheckType("double", "abc"));
|
||||
EXPECT_FALSE(CheckType("double", "-abc"));
|
||||
EXPECT_TRUE(CheckType("double", "0.0"));
|
||||
EXPECT_TRUE(CheckType("double", std::to_string(std::numeric_limits<double>::min())));
|
||||
EXPECT_TRUE(CheckType("double", std::to_string(std::numeric_limits<double>::max())));
|
||||
EXPECT_TRUE(CheckType("double", "123.1"));
|
||||
EXPECT_TRUE(CheckType("double", "-123.1"));
|
||||
}
|
||||
|
||||
TEST(property_type, CheckType_size) {
|
||||
EXPECT_FALSE(CheckType("size", ""));
|
||||
EXPECT_FALSE(CheckType("size", "ab"));
|
||||
EXPECT_FALSE(CheckType("size", "abcd"));
|
||||
EXPECT_FALSE(CheckType("size", "0"));
|
||||
|
||||
EXPECT_TRUE(CheckType("size", "512g"));
|
||||
EXPECT_TRUE(CheckType("size", "512k"));
|
||||
EXPECT_TRUE(CheckType("size", "512m"));
|
||||
|
||||
EXPECT_FALSE(CheckType("size", "512gggg"));
|
||||
EXPECT_FALSE(CheckType("size", "512mgk"));
|
||||
EXPECT_FALSE(CheckType("size", "g"));
|
||||
EXPECT_FALSE(CheckType("size", "m"));
|
||||
}
|
||||
|
||||
TEST(property_type, CheckType_enum) {
|
||||
EXPECT_FALSE(CheckType("enum abc", ""));
|
||||
EXPECT_FALSE(CheckType("enum abc", "ab"));
|
||||
EXPECT_FALSE(CheckType("enum abc", "abcd"));
|
||||
EXPECT_FALSE(CheckType("enum 123 456 789", "0"));
|
||||
|
||||
EXPECT_TRUE(CheckType("enum abc", "abc"));
|
||||
EXPECT_TRUE(CheckType("enum 123 456 789", "123"));
|
||||
EXPECT_TRUE(CheckType("enum 123 456 789", "456"));
|
||||
EXPECT_TRUE(CheckType("enum 123 456 789", "789"));
|
||||
}
|
||||
|
||||
} // namespace init
|
||||
} // namespace android
|
|
@ -1,9 +1,26 @@
|
|||
//
|
||||
// Copyright (C) 2017 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 <property_info_serializer/property_info_serializer.h>
|
||||
|
||||
#include <android-base/strings.h>
|
||||
|
||||
#include "space_tokenizer.h"
|
||||
|
||||
using android::base::Join;
|
||||
using android::base::Split;
|
||||
using android::base::StartsWith;
|
||||
using android::base::Trim;
|
||||
|
@ -11,6 +28,34 @@ using android::base::Trim;
|
|||
namespace android {
|
||||
namespace properties {
|
||||
|
||||
namespace {
|
||||
|
||||
bool IsTypeValid(const std::vector<std::string>& type_strings) {
|
||||
if (type_strings.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// There must be at least one string following 'enum'
|
||||
if (type_strings[0] == "enum") {
|
||||
return type_strings.size() > 1;
|
||||
}
|
||||
|
||||
// There should not be any string following any other types.
|
||||
if (type_strings.size() != 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check that the type matches one of remaining valid types.
|
||||
static const char* const no_parameter_types[] = {"string", "bool", "int",
|
||||
"uint", "double", "size"};
|
||||
for (const auto& type : no_parameter_types) {
|
||||
if (type_strings[0] == type) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ParsePropertyInfoLine(const std::string& line, PropertyInfoEntry* out, std::string* error) {
|
||||
auto tokenizer = SpaceTokenizer(line);
|
||||
|
||||
|
@ -26,14 +71,28 @@ bool ParsePropertyInfoLine(const std::string& line, PropertyInfoEntry* out, std:
|
|||
return false;
|
||||
}
|
||||
|
||||
// It is not an error to not find these, as older files will not contain them.
|
||||
// It is not an error to not find exact_match or a type, as older files will not contain them.
|
||||
auto exact_match = tokenizer.GetNext();
|
||||
auto type = tokenizer.GetRemaining();
|
||||
// We reformat type to be space deliminated regardless of the input whitespace for easier storage
|
||||
// and subsequent parsing.
|
||||
auto type_strings = std::vector<std::string>{};
|
||||
auto type = tokenizer.GetNext();
|
||||
while (!type.empty()) {
|
||||
type_strings.emplace_back(type);
|
||||
type = tokenizer.GetNext();
|
||||
}
|
||||
|
||||
*out = {property, context, type, exact_match == "exact"};
|
||||
if (!type_strings.empty() && !IsTypeValid(type_strings)) {
|
||||
*error = "Type '" + Join(type_strings, " ") + "' is not valid";
|
||||
return false;
|
||||
}
|
||||
|
||||
*out = {property, context, Join(type_strings, " "), exact_match == "exact"};
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void ParsePropertyInfoFile(const std::string& file_contents,
|
||||
std::vector<PropertyInfoEntry>* property_infos,
|
||||
std::vector<std::string>* errors) {
|
||||
|
|
Loading…
Reference in New Issue