init: add host side parser for init

Create a host side parser for init such that init rc files can be
verified for syntax correctness before being used on the device.

Bug: 36970783
Test: run the parser on init files on host

Change-Id: I7e8772e278ebaff727057308596ebacf28b6fdda
This commit is contained in:
Tom Cherry 2018-02-13 16:50:08 -08:00
parent 304dacae8a
commit de6bd50d42
17 changed files with 369 additions and 56 deletions

View file

@ -194,4 +194,59 @@ cc_benchmark {
static_libs: ["libinit"],
}
// Host Verifier
// ------------------------------------------------------------------------------
genrule {
name: "generated_stub_builtin_function_map",
out: ["generated_stub_builtin_function_map.h"],
srcs: ["builtins.cpp"],
cmd: "sed -n '/Builtin-function-map start/{:a;n;/Builtin-function-map end/q;p;ba}' $(in) | sed -e 's/do_[^}]*/do_stub/g' > $(out)"
}
cc_binary {
name: "host_init_verifier",
host_supported: true,
cpp_std: "experimental",
cflags: [
"-Wall",
"-Wextra",
"-Wno-unused-parameter",
"-Werror",
],
static_libs: [
"libbase",
"libselinux",
],
whole_static_libs: ["libcap"],
shared_libs: [
"libprotobuf-cpp-lite",
"libhidl-gen-utils",
"libprocessgroup",
"liblog",
"libcutils",
],
srcs: [
"action.cpp",
"action_manager.cpp",
"action_parser.cpp",
"capabilities.cpp",
"descriptors.cpp",
"import_parser.cpp",
"host_init_parser.cpp",
"host_init_stubs.cpp",
"parser.cpp",
"rlimit_parser.cpp",
"tokenizer.cpp",
"service.cpp",
"subcontext.cpp",
"subcontext.proto",
"util.cpp",
],
proto: {
type: "lite",
},
generated_headers: ["generated_stub_builtin_function_map"],
}
subdirs = ["*"]

View file

@ -18,11 +18,16 @@
#include <android-base/chrono_utils.h>
#include <android-base/logging.h>
#include <android-base/properties.h>
#include <android-base/strings.h>
#include "util.h"
#if defined(__ANDROID__)
#include <android-base/properties.h>
#else
#include "host_init_stubs.h"
#endif
using android::base::Join;
namespace android {

View file

@ -16,11 +16,16 @@
#include "action_parser.h"
#include <android-base/properties.h>
#include <android-base/strings.h>
#include "stable_properties.h"
#if defined(__ANDROID__)
#include <android-base/properties.h>
#else
#include "host_init_stubs.h"
#endif
using android::base::GetBoolProperty;
using android::base::StartsWith;

View file

@ -968,8 +968,8 @@ static Result<Success> do_wait_for_prop(const BuiltinArguments& args) {
const char* value = args[2].c_str();
size_t value_len = strlen(value);
if (!is_legal_property_name(name)) {
return Error() << "is_legal_property_name(" << name << ") failed";
if (!IsLegalPropertyName(name)) {
return Error() << "IsLegalPropertyName(" << name << ") failed";
}
if (value_len >= PROP_VALUE_MAX) {
return Error() << "value too long";
@ -1018,6 +1018,7 @@ static Result<Success> do_init_user0(const BuiltinArguments& args) {
{"exec", "/system/bin/vdc", "--wait", "cryptfs", "init_user0"});
}
// Builtin-function-map start
const BuiltinFunctionMap::Map& BuiltinFunctionMap::map() const {
constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
// clang-format off
@ -1075,6 +1076,7 @@ const BuiltinFunctionMap::Map& BuiltinFunctionMap::map() const {
// clang-format on
return builtin_functions;
}
// Builtin-function-map end
} // namespace init
} // namespace android

View file

@ -14,7 +14,6 @@
#include "capabilities.h"
#include <sys/capability.h>
#include <sys/prctl.h>
#include <map>
@ -72,10 +71,15 @@ static const std::map<std::string, int> cap_map = {
static_assert(CAP_LAST_CAP == CAP_AUDIT_READ, "CAP_LAST_CAP is not CAP_AUDIT_READ");
static bool ComputeCapAmbientSupported() {
#if defined(__ANDROID__)
return prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET, CAP_CHOWN, 0, 0) >= 0;
#else
return true;
#endif
}
static unsigned int ComputeLastValidCap() {
#if defined(__ANDROID__)
// Android does not support kernels < 3.8. 'CAP_WAKE_ALARM' has been present since 3.0, see
// http://lxr.free-electrons.com/source/include/linux/capability.h?v=3.0#L360.
unsigned int last_valid_cap = CAP_WAKE_ALARM;
@ -83,6 +87,9 @@ static unsigned int ComputeLastValidCap() {
// |last_valid_cap| will be the first failing value.
return last_valid_cap - 1;
#else
return CAP_LAST_CAP;
#endif
}
static bool DropBoundingSet(const CapSet& to_keep) {
@ -139,6 +146,7 @@ static bool SetProcCaps(const CapSet& to_keep, bool add_setpcap) {
}
static bool SetAmbientCaps(const CapSet& to_raise) {
#if defined(__ANDROID__)
for (size_t cap = 0; cap < to_raise.size(); ++cap) {
if (to_raise.test(cap)) {
if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, cap, 0, 0) != 0) {
@ -147,6 +155,7 @@ static bool SetAmbientCaps(const CapSet& to_raise) {
}
}
}
#endif
return true;
}

View file

@ -21,6 +21,17 @@
#include <string>
#include <type_traits>
#if !defined(__ANDROID__)
#ifndef CAP_BLOCK_SUSPEND
#define CAP_BLOCK_SUSPEND 36
#endif
#ifndef CAP_AUDIT_READ
#define CAP_AUDIT_READ 37
#endif
#undef CAP_LAST_CAP
#define CAP_LAST_CAP CAP_AUDIT_READ
#endif
namespace android {
namespace init {

82
init/host_init_parser.cpp Normal file
View file

@ -0,0 +1,82 @@
//
// Copyright (C) 2018 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 <pwd.h>
#include <android-base/logging.h>
#include "action.h"
#include "action_manager.h"
#include "action_parser.h"
#include "parser.h"
#include "result.h"
#include "service.h"
// The host passwd file won't have the Android entries, so we fake success here.
passwd* getpwnam(const char* login) { // NOLINT: implementing bad function.
char dummy_buf[] = "dummy";
static passwd dummy_passwd = {
.pw_name = dummy_buf,
.pw_dir = dummy_buf,
.pw_shell = dummy_buf,
.pw_uid = 123,
.pw_gid = 123,
};
return &dummy_passwd;
}
namespace android {
namespace init {
static Result<Success> do_stub(const BuiltinArguments& args) {
return Success();
}
#include "generated_stub_builtin_function_map.h"
int main(int argc, char** argv) {
android::base::InitLogging(argv, &android::base::StderrLogger);
if (argc != 2) {
LOG(ERROR) << "Usage: " << argv[0] << " <init file to parse>";
return -1;
}
const BuiltinFunctionMap function_map;
Action::set_function_map(&function_map);
ActionManager& am = ActionManager::GetInstance();
ServiceList& sl = ServiceList::GetInstance();
Parser parser;
parser.AddSectionParser("service", std::make_unique<ServiceParser>(&sl, nullptr));
parser.AddSectionParser("on", std::make_unique<ActionParser>(&am, nullptr));
size_t num_errors = 0;
if (!parser.ParseConfig(argv[1], &num_errors)) {
LOG(ERROR) << "Failed to find script";
return -1;
}
if (num_errors > 0) {
LOG(ERROR) << "Parse failed with " << num_errors << " errors";
return -1;
}
LOG(INFO) << "Parse success!";
return 0;
}
} // namespace init
} // namespace android
int main(int argc, char** argv) {
android::init::main(argc, argv);
}

59
init/host_init_stubs.cpp Normal file
View file

@ -0,0 +1,59 @@
/*
* Copyright (C) 2018 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 "host_init_stubs.h"
// unistd.h
int setgroups(size_t __size, const gid_t* __list) {
return 0;
}
namespace android {
namespace base {
std::string GetProperty(const std::string&, const std::string& default_value) {
return default_value;
}
bool GetBoolProperty(const std::string&, bool default_value) {
return default_value;
}
} // namespace base
} // namespace android
namespace android {
namespace init {
// init.h
std::string default_console = "/dev/console";
// property_service.h
uint32_t (*property_set)(const std::string& name, const std::string& value) = nullptr;
uint32_t HandlePropertySet(const std::string&, const std::string&, const std::string&,
const ucred&) {
return 0;
}
// selinux.h
void SelabelInitialize() {}
bool SelabelLookupFileContext(const std::string& key, int type, std::string* result) {
return false;
}
} // namespace init
} // namespace android

60
init/host_init_stubs.h Normal file
View file

@ -0,0 +1,60 @@
/*
* Copyright (C) 2018 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_HOST_INIT_STUBS_H
#define _INIT_HOST_INIT_STUBS_H
#include <stddef.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <string>
// sys/system_properties.h
#define PROP_VALUE_MAX 92
// unistd.h
int setgroups(size_t __size, const gid_t* __list);
// android-base/properties.h
namespace android {
namespace base {
std::string GetProperty(const std::string& key, const std::string& default_value);
bool GetBoolProperty(const std::string& key, bool default_value);
} // namespace base
} // namespace android
namespace android {
namespace init {
// init.h
extern std::string default_console;
// property_service.h
extern uint32_t (*property_set)(const std::string& name, const std::string& value);
uint32_t HandlePropertySet(const std::string& name, const std::string& value,
const std::string& source_context, const ucred& cr);
// selinux.h
void SelabelInitialize();
bool SelabelLookupFileContext(const std::string& key, int type, std::string* result);
} // namespace init
} // namespace android
#endif

View file

@ -39,7 +39,7 @@ void Parser::AddSingleLineParser(const std::string& prefix, LineCallback callbac
line_callbacks_.emplace_back(prefix, callback);
}
void Parser::ParseData(const std::string& filename, const std::string& data) {
void Parser::ParseData(const std::string& filename, const std::string& data, size_t* parse_errors) {
// TODO: Use a parser with const input and remove this copy
std::vector<char> data_copy(data.begin(), data.end());
data_copy.push_back('\0');
@ -57,6 +57,7 @@ void Parser::ParseData(const std::string& filename, const std::string& data) {
if (section_parser == nullptr) return;
if (auto result = section_parser->EndSection(); !result) {
(*parse_errors)++;
LOG(ERROR) << filename << ": " << section_start_line << ": " << result.error();
}
@ -80,6 +81,7 @@ void Parser::ParseData(const std::string& filename, const std::string& data) {
end_section();
if (auto result = callback(std::move(args)); !result) {
(*parse_errors)++;
LOG(ERROR) << filename << ": " << state.line << ": " << result.error();
}
break;
@ -92,12 +94,14 @@ void Parser::ParseData(const std::string& filename, const std::string& data) {
if (auto result =
section_parser->ParseSection(std::move(args), filename, state.line);
!result) {
(*parse_errors)++;
LOG(ERROR) << filename << ": " << state.line << ": " << result.error();
section_parser = nullptr;
}
} else if (section_parser) {
if (auto result = section_parser->ParseLineSection(std::move(args), state.line);
!result) {
(*parse_errors)++;
LOG(ERROR) << filename << ": " << state.line << ": " << result.error();
}
}
@ -110,7 +114,7 @@ void Parser::ParseData(const std::string& filename, const std::string& data) {
}
}
bool Parser::ParseConfigFile(const std::string& path) {
bool Parser::ParseConfigFile(const std::string& path, size_t* parse_errors) {
LOG(INFO) << "Parsing file " << path << "...";
android::base::Timer t;
auto config_contents = ReadFile(path);
@ -120,7 +124,7 @@ bool Parser::ParseConfigFile(const std::string& path) {
}
config_contents->push_back('\n'); // TODO: fix parse_config.
ParseData(path, *config_contents);
ParseData(path, *config_contents, parse_errors);
for (const auto& [section_name, section_parser] : section_parsers_) {
section_parser->EndFile();
}
@ -129,7 +133,7 @@ bool Parser::ParseConfigFile(const std::string& path) {
return true;
}
bool Parser::ParseConfigDir(const std::string& path) {
bool Parser::ParseConfigDir(const std::string& path, size_t* parse_errors) {
LOG(INFO) << "Parsing directory " << path << "...";
std::unique_ptr<DIR, decltype(&closedir)> config_dir(opendir(path.c_str()), closedir);
if (!config_dir) {
@ -149,7 +153,7 @@ bool Parser::ParseConfigDir(const std::string& path) {
// Sort first so we load files in a consistent order (bug 31996208)
std::sort(files.begin(), files.end());
for (const auto& file : files) {
if (!ParseConfigFile(file)) {
if (!ParseConfigFile(file, parse_errors)) {
LOG(ERROR) << "could not import file '" << file << "'";
}
}
@ -157,10 +161,16 @@ bool Parser::ParseConfigDir(const std::string& path) {
}
bool Parser::ParseConfig(const std::string& path) {
size_t parse_errors;
return ParseConfig(path, &parse_errors);
}
bool Parser::ParseConfig(const std::string& path, size_t* parse_errors) {
*parse_errors = 0;
if (is_dir(path.c_str())) {
return ParseConfigDir(path);
return ParseConfigDir(path, parse_errors);
}
return ParseConfigFile(path);
return ParseConfigFile(path, parse_errors);
}
} // namespace init

View file

@ -72,13 +72,14 @@ class Parser {
Parser();
bool ParseConfig(const std::string& path);
bool ParseConfig(const std::string& path, size_t* parse_errors);
void AddSectionParser(const std::string& name, std::unique_ptr<SectionParser> parser);
void AddSingleLineParser(const std::string& prefix, LineCallback callback);
private:
void ParseData(const std::string& filename, const std::string& data);
bool ParseConfigFile(const std::string& path);
bool ParseConfigDir(const std::string& path);
void ParseData(const std::string& filename, const std::string& data, size_t* parse_errors);
bool ParseConfigFile(const std::string& path, size_t* parse_errors);
bool ParseConfigDir(const std::string& path, size_t* parse_errors);
std::map<std::string, std::unique_ptr<SectionParser>> section_parsers_;
std::vector<std::pair<std::string, LineCallback>> line_callbacks_;

View file

@ -117,35 +117,10 @@ static bool CheckMacPerms(const std::string& name, const char* target_context,
return has_access;
}
bool is_legal_property_name(const std::string& name) {
size_t namelen = name.size();
if (namelen < 1) return false;
if (name[0] == '.') return false;
if (name[namelen - 1] == '.') return false;
/* Only allow alphanumeric, plus '.', '-', '@', ':', or '_' */
/* Don't allow ".." to appear in a property name */
for (size_t i = 0; i < namelen; i++) {
if (name[i] == '.') {
// i=0 is guaranteed to never have a dot. See above.
if (name[i-1] == '.') return false;
continue;
}
if (name[i] == '_' || name[i] == '-' || name[i] == '@' || name[i] == ':') continue;
if (name[i] >= 'a' && name[i] <= 'z') continue;
if (name[i] >= 'A' && name[i] <= 'Z') continue;
if (name[i] >= '0' && name[i] <= '9') continue;
return false;
}
return true;
}
static uint32_t PropertySetImpl(const std::string& name, const std::string& value) {
size_t valuelen = value.size();
if (!is_legal_property_name(name)) {
if (!IsLegalPropertyName(name)) {
LOG(ERROR) << "property_set(\"" << name << "\", \"" << value << "\") failed: bad name";
return PROP_ERROR_INVALID_NAME;
}
@ -416,7 +391,7 @@ class SocketConnection {
// This returns one of the enum of PROP_SUCCESS or PROP_ERROR*.
uint32_t HandlePropertySet(const std::string& name, const std::string& value,
const std::string& source_context, const ucred& cr) {
if (!is_legal_property_name(name)) {
if (!IsLegalPropertyName(name)) {
LOG(ERROR) << "PropertySet: illegal property name \"" << name << "\"";
return PROP_ERROR_INVALID_NAME;
}

View file

@ -41,7 +41,6 @@ void property_load_boot_defaults(void);
void load_persist_props(void);
void load_system_props(void);
void start_property_service(void);
bool is_legal_property_name(const std::string& name);
} // namespace init
} // namespace android

View file

@ -24,7 +24,6 @@
#include <sys/prctl.h>
#include <sys/resource.h>
#include <sys/stat.h>
#include <sys/system_properties.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <termios.h>
@ -33,8 +32,6 @@
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/parseint.h>
#include <android-base/properties.h>
#include <android-base/scopeguard.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <hidl-util/FQName.h>
@ -42,15 +39,23 @@
#include <selinux/selinux.h>
#include <system/thread_defs.h>
#include "init.h"
#include "property_service.h"
#include "rlimit_parser.h"
#include "util.h"
#if defined(__ANDROID__)
#include <sys/system_properties.h>
#include <android-base/properties.h>
#include "init.h"
#include "property_service.h"
#else
#include "host_init_stubs.h"
#endif
using android::base::boot_clock;
using android::base::GetProperty;
using android::base::Join;
using android::base::make_scope_guard;
using android::base::ParseInt;
using android::base::StartsWith;
using android::base::StringPrintf;
@ -1168,7 +1173,7 @@ bool ServiceParser::IsValidName(const std::string& name) const {
// Property values can contain any characters, but may only be a certain length.
// (The latter restriction is needed because `start` and `stop` work by writing
// the service name to the "ctl.start" and "ctl.stop" properties.)
return is_legal_property_name("init.svc." + name) && name.size() <= PROP_VALUE_MAX;
return IsLegalPropertyName("init.svc." + name) && name.size() <= PROP_VALUE_MAX;
}
} // namespace init

View file

@ -27,12 +27,14 @@
#include <selinux/android.h>
#include "action.h"
#include "property_service.h"
#include "selinux.h"
#include "util.h"
#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
#include <sys/_system_properties.h>
#if defined(__ANDROID__)
#include "property_service.h"
#include "selinux.h"
#else
#include "host_init_stubs.h"
#endif
using android::base::GetExecutablePath;
using android::base::Join;
@ -83,7 +85,7 @@ std::vector<std::pair<std::string, std::string>> properties_to_set;
uint32_t SubcontextPropertySet(const std::string& name, const std::string& value) {
properties_to_set.emplace_back(name, value);
return PROP_SUCCESS;
return 0;
}
class SubcontextProcess {

View file

@ -33,7 +33,6 @@
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
@ -42,7 +41,14 @@
#include <selinux/android.h>
#include "reboot.h"
#if defined(__ANDROID__)
#include <android-base/properties.h>
#include "selinux.h"
#else
#include "host_init_stubs.h"
#endif
#ifdef _INIT_INIT_H
#error "Do not include init.h in files used by ueventd or watchdogd; it will expose init's globals"
@ -409,5 +415,30 @@ bool is_android_dt_value_expected(const std::string& sub_path, const std::string
return false;
}
bool IsLegalPropertyName(const std::string& name) {
size_t namelen = name.size();
if (namelen < 1) return false;
if (name[0] == '.') return false;
if (name[namelen - 1] == '.') return false;
/* Only allow alphanumeric, plus '.', '-', '@', ':', or '_' */
/* Don't allow ".." to appear in a property name */
for (size_t i = 0; i < namelen; i++) {
if (name[i] == '.') {
// i=0 is guaranteed to never have a dot. See above.
if (name[i - 1] == '.') return false;
continue;
}
if (name[i] == '_' || name[i] == '-' || name[i] == '@' || name[i] == ':') continue;
if (name[i] >= 'a' && name[i] <= 'z') continue;
if (name[i] >= 'A' && name[i] <= 'Z') continue;
if (name[i] >= '0' && name[i] <= '9') continue;
return false;
}
return true;
}
} // namespace init
} // namespace android

View file

@ -62,6 +62,8 @@ const std::string& get_android_dt_dir();
bool read_android_dt_file(const std::string& sub_path, std::string* dt_content);
bool is_android_dt_value_expected(const std::string& sub_path, const std::string& expected_content);
bool IsLegalPropertyName(const std::string& name);
} // namespace init
} // namespace android