diff --git a/init/Android.bp b/init/Android.bp index 69b4ee4a7..60394efaf 100644 --- a/init/Android.bp +++ b/init/Android.bp @@ -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 = ["*"] diff --git a/init/action.cpp b/init/action.cpp index 11335ca14..f782b51c1 100644 --- a/init/action.cpp +++ b/init/action.cpp @@ -18,11 +18,16 @@ #include #include -#include #include #include "util.h" +#if defined(__ANDROID__) +#include +#else +#include "host_init_stubs.h" +#endif + using android::base::Join; namespace android { diff --git a/init/action_parser.cpp b/init/action_parser.cpp index 8a4b518f5..a2c96718b 100644 --- a/init/action_parser.cpp +++ b/init/action_parser.cpp @@ -16,11 +16,16 @@ #include "action_parser.h" -#include #include #include "stable_properties.h" +#if defined(__ANDROID__) +#include +#else +#include "host_init_stubs.h" +#endif + using android::base::GetBoolProperty; using android::base::StartsWith; diff --git a/init/builtins.cpp b/init/builtins.cpp index 1040b47b0..51c2c9bd1 100644 --- a/init/builtins.cpp +++ b/init/builtins.cpp @@ -968,8 +968,8 @@ static Result 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 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::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 diff --git a/init/capabilities.cpp b/init/capabilities.cpp index 50987db7c..a91cd1dd1 100644 --- a/init/capabilities.cpp +++ b/init/capabilities.cpp @@ -14,7 +14,6 @@ #include "capabilities.h" -#include #include #include @@ -72,10 +71,15 @@ static const std::map 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; } diff --git a/init/capabilities.h b/init/capabilities.h index fc80c9864..891e0ac98 100644 --- a/init/capabilities.h +++ b/init/capabilities.h @@ -21,6 +21,17 @@ #include #include +#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 { diff --git a/init/host_init_parser.cpp b/init/host_init_parser.cpp new file mode 100644 index 000000000..5232b7e5e --- /dev/null +++ b/init/host_init_parser.cpp @@ -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 + +#include + +#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 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] << " "; + 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(&sl, nullptr)); + parser.AddSectionParser("on", std::make_unique(&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); +} diff --git a/init/host_init_stubs.cpp b/init/host_init_stubs.cpp new file mode 100644 index 000000000..cde747de0 --- /dev/null +++ b/init/host_init_stubs.cpp @@ -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 diff --git a/init/host_init_stubs.h b/init/host_init_stubs.h new file mode 100644 index 000000000..af96aea15 --- /dev/null +++ b/init/host_init_stubs.h @@ -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 +#include +#include + +#include + +// 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 diff --git a/init/parser.cpp b/init/parser.cpp index 4c69bac3f..4453aaac1 100644 --- a/init/parser.cpp +++ b/init/parser.cpp @@ -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 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 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 diff --git a/init/parser.h b/init/parser.h index 110a4688a..f6e237f57 100644 --- a/init/parser.h +++ b/init/parser.h @@ -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 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> section_parsers_; std::vector> line_callbacks_; diff --git a/init/property_service.cpp b/init/property_service.cpp index 0cf61840f..338c05aec 100644 --- a/init/property_service.cpp +++ b/init/property_service.cpp @@ -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; } diff --git a/init/property_service.h b/init/property_service.h index 8161b40e3..d4973fc89 100644 --- a/init/property_service.h +++ b/init/property_service.h @@ -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 diff --git a/init/service.cpp b/init/service.cpp index 35dd3196c..09d8dae11 100644 --- a/init/service.cpp +++ b/init/service.cpp @@ -24,7 +24,6 @@ #include #include #include -#include #include #include #include @@ -33,8 +32,6 @@ #include #include #include -#include -#include #include #include #include @@ -42,15 +39,23 @@ #include #include -#include "init.h" -#include "property_service.h" #include "rlimit_parser.h" #include "util.h" +#if defined(__ANDROID__) +#include + +#include + +#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 diff --git a/init/subcontext.cpp b/init/subcontext.cpp index f3b643ad7..faab36848 100644 --- a/init/subcontext.cpp +++ b/init/subcontext.cpp @@ -27,12 +27,14 @@ #include #include "action.h" -#include "property_service.h" -#include "selinux.h" #include "util.h" -#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_ -#include +#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> 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 { diff --git a/init/util.cpp b/init/util.cpp index d80cb1ef6..4455b2eb1 100644 --- a/init/util.cpp +++ b/init/util.cpp @@ -33,7 +33,6 @@ #include #include -#include #include #include #include @@ -42,7 +41,14 @@ #include #include "reboot.h" + +#if defined(__ANDROID__) +#include + #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 diff --git a/init/util.h b/init/util.h index 2cfcf6c78..07e4864ac 100644 --- a/init/util.h +++ b/init/util.h @@ -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