init: split security functions out of init.cpp
This change splits out the selinux initialization and supporting functionality into selinux.cpp and splits the security related initialization of the rng, etc to security.cpp. It also provides additional documentation for SEPolicy loading as this has been requested by some teams. It additionally cleans up sehandle and sehandle_prop. The former is static within selinux.cpp and new wrapper functions are created around selabel_lookup*() to better serve the users. The latter is moved to property_service.cpp as it is isolated to that file for its usage. Test: boot bullhead Merged-In: Idc95d493cebc681fbe686b5160502f36af149f60 Change-Id: Idc95d493cebc681fbe686b5160502f36af149f60 (cherry picked from commit 9afb86b25d8675927cb37c86119a7ecf19f74819)
This commit is contained in:
parent
1f6a481462
commit
c317009410
|
@ -70,6 +70,8 @@ cc_library_static {
|
|||
"log.cpp",
|
||||
"parser.cpp",
|
||||
"property_service.cpp",
|
||||
"security.cpp",
|
||||
"selinux.cpp",
|
||||
"service.cpp",
|
||||
"tokenizer.cpp",
|
||||
"uevent_listener.cpp",
|
||||
|
|
|
@ -225,23 +225,22 @@ static int do_insmod(const std::vector<std::string>& args) {
|
|||
return insmod(filename.c_str(), options.c_str(), flags);
|
||||
}
|
||||
|
||||
// mkdir <path> [mode] [owner] [group]
|
||||
static int do_mkdir(const std::vector<std::string>& args) {
|
||||
mode_t mode = 0755;
|
||||
int ret;
|
||||
|
||||
/* mkdir <path> [mode] [owner] [group] */
|
||||
|
||||
if (args.size() >= 3) {
|
||||
mode = std::strtoul(args[2].c_str(), 0, 8);
|
||||
}
|
||||
|
||||
ret = make_dir(args[1].c_str(), mode, sehandle);
|
||||
/* chmod in case the directory already exists */
|
||||
if (ret == -1 && errno == EEXIST) {
|
||||
ret = fchmodat(AT_FDCWD, args[1].c_str(), mode, AT_SYMLINK_NOFOLLOW);
|
||||
}
|
||||
if (ret == -1) {
|
||||
return -errno;
|
||||
if (!make_dir(args[1], mode)) {
|
||||
/* chmod in case the directory already exists */
|
||||
if (errno == EEXIST) {
|
||||
if (fchmodat(AT_FDCWD, args[1].c_str(), mode, AT_SYMLINK_NOFOLLOW) == -1) {
|
||||
return -errno;
|
||||
}
|
||||
} else {
|
||||
return -errno;
|
||||
}
|
||||
}
|
||||
|
||||
if (args.size() >= 4) {
|
||||
|
@ -266,8 +265,7 @@ static int do_mkdir(const std::vector<std::string>& args) {
|
|||
|
||||
/* chown may have cleared S_ISUID and S_ISGID, chmod again */
|
||||
if (mode & (S_ISUID | S_ISGID)) {
|
||||
ret = fchmodat(AT_FDCWD, args[1].c_str(), mode, AT_SYMLINK_NOFOLLOW);
|
||||
if (ret == -1) {
|
||||
if (fchmodat(AT_FDCWD, args[1].c_str(), mode, AT_SYMLINK_NOFOLLOW) == -1) {
|
||||
return -errno;
|
||||
}
|
||||
}
|
||||
|
@ -895,17 +893,6 @@ static int do_wait_for_prop(const std::vector<std::string>& args) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback to make a directory from the ext4 code
|
||||
*/
|
||||
static int do_installkeys_ensure_dir_exists(const char* dir) {
|
||||
if (make_dir(dir, 0700, sehandle) && errno != EEXIST) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool is_file_crypto() {
|
||||
return android::base::GetProperty("ro.crypto.type", "") == "file";
|
||||
}
|
||||
|
@ -915,7 +902,7 @@ static int do_installkey(const std::vector<std::string>& args) {
|
|||
return 0;
|
||||
}
|
||||
auto unencrypted_dir = args[1] + e4crypt_unencrypted_folder;
|
||||
if (do_installkeys_ensure_dir_exists(unencrypted_dir.c_str())) {
|
||||
if (!make_dir(unencrypted_dir, 0700) && errno != EEXIST) {
|
||||
PLOG(ERROR) << "Failed to create " << unencrypted_dir;
|
||||
return -1;
|
||||
}
|
||||
|
|
|
@ -86,8 +86,7 @@ int SocketInfo::Create(const std::string& context) const {
|
|||
int flags =
|
||||
((types[0] == "stream" ? SOCK_STREAM : (types[0] == "dgram" ? SOCK_DGRAM : SOCK_SEQPACKET)));
|
||||
bool passcred = types.size() > 1 && types[1] == "passcred";
|
||||
return CreateSocket(name().c_str(), flags, passcred, perm(), uid(), gid(), context.c_str(),
|
||||
sehandle);
|
||||
return CreateSocket(name().c_str(), flags, passcred, perm(), uid(), gid(), context.c_str());
|
||||
}
|
||||
|
||||
const std::string SocketInfo::key() const {
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
#include <selinux/android.h>
|
||||
#include <selinux/selinux.h>
|
||||
|
||||
#include "selinux.h"
|
||||
#include "ueventd.h"
|
||||
#include "util.h"
|
||||
|
||||
|
@ -224,18 +225,13 @@ void DeviceHandler::MakeDevice(const std::string& path, bool block, int major, i
|
|||
auto[mode, uid, gid] = GetDevicePermissions(path, links);
|
||||
mode |= (block ? S_IFBLK : S_IFCHR);
|
||||
|
||||
char* secontext = nullptr;
|
||||
if (sehandle_) {
|
||||
std::vector<const char*> c_links;
|
||||
for (const auto& link : links) {
|
||||
c_links.emplace_back(link.c_str());
|
||||
}
|
||||
c_links.emplace_back(nullptr);
|
||||
if (selabel_lookup_best_match(sehandle_, &secontext, path.c_str(), &c_links[0], mode)) {
|
||||
PLOG(ERROR) << "Device '" << path << "' not created; cannot find SELinux label";
|
||||
return;
|
||||
}
|
||||
setfscreatecon(secontext);
|
||||
std::string secontext;
|
||||
if (!SelabelLookupFileContextBestMatch(path, links, mode, &secontext)) {
|
||||
PLOG(ERROR) << "Device '" << path << "' not created; cannot find SELinux label";
|
||||
return;
|
||||
}
|
||||
if (!secontext.empty()) {
|
||||
setfscreatecon(secontext.c_str());
|
||||
}
|
||||
|
||||
dev_t dev = makedev(major, minor);
|
||||
|
@ -250,7 +246,7 @@ void DeviceHandler::MakeDevice(const std::string& path, bool block, int major, i
|
|||
}
|
||||
/* If the node already exists update its SELinux label to handle cases when
|
||||
* it was created with the wrong context during coldboot procedure. */
|
||||
if (mknod(path.c_str(), mode, dev) && (errno == EEXIST) && secontext) {
|
||||
if (mknod(path.c_str(), mode, dev) && (errno == EEXIST) && !secontext.empty()) {
|
||||
char* fcon = nullptr;
|
||||
int rc = lgetfilecon(path.c_str(), &fcon);
|
||||
if (rc < 0) {
|
||||
|
@ -258,10 +254,10 @@ void DeviceHandler::MakeDevice(const std::string& path, bool block, int major, i
|
|||
goto out;
|
||||
}
|
||||
|
||||
bool different = strcmp(fcon, secontext) != 0;
|
||||
bool different = fcon != secontext;
|
||||
freecon(fcon);
|
||||
|
||||
if (different && lsetfilecon(path.c_str(), secontext)) {
|
||||
if (different && lsetfilecon(path.c_str(), secontext.c_str())) {
|
||||
PLOG(ERROR) << "Cannot set '" << secontext << "' SELinux label on '" << path
|
||||
<< "' device";
|
||||
}
|
||||
|
@ -273,8 +269,7 @@ out:
|
|||
PLOG(FATAL) << "setegid(AID_ROOT) failed";
|
||||
}
|
||||
|
||||
if (secontext) {
|
||||
freecon(secontext);
|
||||
if (!secontext.empty()) {
|
||||
setfscreatecon(nullptr);
|
||||
}
|
||||
}
|
||||
|
@ -351,7 +346,7 @@ void DeviceHandler::HandleDevice(const std::string& action, const std::string& d
|
|||
if (action == "add") {
|
||||
MakeDevice(devpath, block, major, minor, links);
|
||||
for (const auto& link : links) {
|
||||
if (mkdir_recursive(Dirname(link), 0755, sehandle_)) {
|
||||
if (!mkdir_recursive(Dirname(link), 0755)) {
|
||||
PLOG(ERROR) << "Failed to create directory " << Dirname(link);
|
||||
}
|
||||
|
||||
|
@ -415,7 +410,7 @@ void DeviceHandler::HandleDeviceEvent(const Uevent& uevent) {
|
|||
devpath = "/dev/" + Basename(uevent.path);
|
||||
}
|
||||
|
||||
mkdir_recursive(Dirname(devpath), 0755, sehandle_);
|
||||
mkdir_recursive(Dirname(devpath), 0755);
|
||||
|
||||
HandleDevice(uevent.action, devpath, block, uevent.major, uevent.minor, links);
|
||||
}
|
||||
|
@ -426,7 +421,6 @@ DeviceHandler::DeviceHandler(std::vector<Permissions> dev_permissions,
|
|||
: dev_permissions_(std::move(dev_permissions)),
|
||||
sysfs_permissions_(std::move(sysfs_permissions)),
|
||||
subsystems_(std::move(subsystems)),
|
||||
sehandle_(selinux_android_file_context_handle()),
|
||||
skip_restorecon_(skip_restorecon),
|
||||
sysfs_mount_point_("/sys") {}
|
||||
|
||||
|
|
|
@ -124,7 +124,6 @@ class DeviceHandler {
|
|||
std::vector<Permissions> dev_permissions_;
|
||||
std::vector<SysfsPermissions> sysfs_permissions_;
|
||||
std::vector<Subsystem> subsystems_;
|
||||
selabel_handle* sehandle_;
|
||||
bool skip_restorecon_;
|
||||
std::string sysfs_mount_point_;
|
||||
};
|
||||
|
|
|
@ -35,13 +35,13 @@ class DeviceHandlerTester {
|
|||
device_handler_.sysfs_mount_point_ = fake_sys_root.path;
|
||||
|
||||
std::string platform_device_dir = fake_sys_root.path + platform_device;
|
||||
mkdir_recursive(platform_device_dir, 0777, nullptr);
|
||||
mkdir_recursive(platform_device_dir, 0777);
|
||||
|
||||
std::string platform_bus = fake_sys_root.path + "/bus/platform"s;
|
||||
mkdir_recursive(platform_bus, 0777, nullptr);
|
||||
mkdir_recursive(platform_bus, 0777);
|
||||
symlink(platform_bus.c_str(), (platform_device_dir + "/subsystem").c_str());
|
||||
|
||||
mkdir_recursive(android::base::Dirname(fake_sys_root.path + uevent.path), 0777, nullptr);
|
||||
mkdir_recursive(android::base::Dirname(fake_sys_root.path + uevent.path), 0777);
|
||||
|
||||
std::vector<std::string> result;
|
||||
result = device_handler_.GetBlockDeviceSymlinks(uevent);
|
||||
|
|
611
init/init.cpp
611
init/init.cpp
|
@ -16,27 +16,17 @@
|
|||
|
||||
#include "init.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <inttypes.h>
|
||||
#include <libgen.h>
|
||||
#include <paths.h>
|
||||
#include <seccomp_policy.h>
|
||||
#include <signal.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/epoll.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/sysmacros.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/un.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <android-base/chrono_utils.h>
|
||||
|
@ -44,25 +34,22 @@
|
|||
#include <android-base/logging.h>
|
||||
#include <android-base/properties.h>
|
||||
#include <android-base/strings.h>
|
||||
#include <android-base/unique_fd.h>
|
||||
#include <keyutils.h>
|
||||
#include <libavb/libavb.h>
|
||||
#include <private/android_filesystem_config.h>
|
||||
#include <selinux/android.h>
|
||||
#include <selinux/selinux.h>
|
||||
|
||||
#include <fstream>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
#include "bootchart.h"
|
||||
#include "import_parser.h"
|
||||
#include "init_first_stage.h"
|
||||
#include "keychords.h"
|
||||
#include "log.h"
|
||||
#include "property_service.h"
|
||||
#include "reboot.h"
|
||||
#include "security.h"
|
||||
#include "selinux.h"
|
||||
#include "signal_handler.h"
|
||||
#include "ueventd.h"
|
||||
#include "util.h"
|
||||
|
@ -73,14 +60,10 @@ using namespace std::string_literals;
|
|||
using android::base::boot_clock;
|
||||
using android::base::GetProperty;
|
||||
using android::base::Timer;
|
||||
using android::base::unique_fd;
|
||||
|
||||
namespace android {
|
||||
namespace init {
|
||||
|
||||
struct selabel_handle *sehandle;
|
||||
struct selabel_handle *sehandle_prop;
|
||||
|
||||
static int property_triggers_enabled = 0;
|
||||
|
||||
static char qemu[32];
|
||||
|
@ -277,194 +260,6 @@ static int wait_for_coldboot_done_action(const std::vector<std::string>& args) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Writes 512 bytes of output from Hardware RNG (/dev/hw_random, backed
|
||||
* by Linux kernel's hw_random framework) into Linux RNG's via /dev/urandom.
|
||||
* Does nothing if Hardware RNG is not present.
|
||||
*
|
||||
* Since we don't yet trust the quality of Hardware RNG, these bytes are not
|
||||
* mixed into the primary pool of Linux RNG and the entropy estimate is left
|
||||
* unmodified.
|
||||
*
|
||||
* If the HW RNG device /dev/hw_random is present, we require that at least
|
||||
* 512 bytes read from it are written into Linux RNG. QA is expected to catch
|
||||
* devices/configurations where these I/O operations are blocking for a long
|
||||
* time. We do not reboot or halt on failures, as this is a best-effort
|
||||
* attempt.
|
||||
*/
|
||||
static int mix_hwrng_into_linux_rng_action(const std::vector<std::string>& args) {
|
||||
unique_fd hwrandom_fd(
|
||||
TEMP_FAILURE_RETRY(open("/dev/hw_random", O_RDONLY | O_NOFOLLOW | O_CLOEXEC)));
|
||||
if (hwrandom_fd == -1) {
|
||||
if (errno == ENOENT) {
|
||||
LOG(INFO) << "/dev/hw_random not found";
|
||||
// It's not an error to not have a Hardware RNG.
|
||||
return 0;
|
||||
}
|
||||
PLOG(ERROR) << "Failed to open /dev/hw_random";
|
||||
return -1;
|
||||
}
|
||||
|
||||
unique_fd urandom_fd(
|
||||
TEMP_FAILURE_RETRY(open("/dev/urandom", O_WRONLY | O_NOFOLLOW | O_CLOEXEC)));
|
||||
if (urandom_fd == -1) {
|
||||
PLOG(ERROR) << "Failed to open /dev/urandom";
|
||||
return -1;
|
||||
}
|
||||
|
||||
char buf[512];
|
||||
size_t total_bytes_written = 0;
|
||||
while (total_bytes_written < sizeof(buf)) {
|
||||
ssize_t chunk_size =
|
||||
TEMP_FAILURE_RETRY(read(hwrandom_fd, buf, sizeof(buf) - total_bytes_written));
|
||||
if (chunk_size == -1) {
|
||||
PLOG(ERROR) << "Failed to read from /dev/hw_random";
|
||||
return -1;
|
||||
} else if (chunk_size == 0) {
|
||||
LOG(ERROR) << "Failed to read from /dev/hw_random: EOF";
|
||||
return -1;
|
||||
}
|
||||
|
||||
chunk_size = TEMP_FAILURE_RETRY(write(urandom_fd, buf, chunk_size));
|
||||
if (chunk_size == -1) {
|
||||
PLOG(ERROR) << "Failed to write to /dev/urandom";
|
||||
return -1;
|
||||
}
|
||||
total_bytes_written += chunk_size;
|
||||
}
|
||||
|
||||
LOG(INFO) << "Mixed " << total_bytes_written << " bytes from /dev/hw_random into /dev/urandom";
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void security_failure() {
|
||||
LOG(ERROR) << "Security failure...";
|
||||
panic();
|
||||
}
|
||||
|
||||
static bool set_highest_available_option_value(std::string path, int min, int max)
|
||||
{
|
||||
std::ifstream inf(path, std::fstream::in);
|
||||
if (!inf) {
|
||||
LOG(ERROR) << "Cannot open for reading: " << path;
|
||||
return false;
|
||||
}
|
||||
|
||||
int current = max;
|
||||
while (current >= min) {
|
||||
// try to write out new value
|
||||
std::string str_val = std::to_string(current);
|
||||
std::ofstream of(path, std::fstream::out);
|
||||
if (!of) {
|
||||
LOG(ERROR) << "Cannot open for writing: " << path;
|
||||
return false;
|
||||
}
|
||||
of << str_val << std::endl;
|
||||
of.close();
|
||||
|
||||
// check to make sure it was recorded
|
||||
inf.seekg(0);
|
||||
std::string str_rec;
|
||||
inf >> str_rec;
|
||||
if (str_val.compare(str_rec) == 0) {
|
||||
break;
|
||||
}
|
||||
current--;
|
||||
}
|
||||
inf.close();
|
||||
|
||||
if (current < min) {
|
||||
LOG(ERROR) << "Unable to set minimum option value " << min << " in " << path;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#define MMAP_RND_PATH "/proc/sys/vm/mmap_rnd_bits"
|
||||
#define MMAP_RND_COMPAT_PATH "/proc/sys/vm/mmap_rnd_compat_bits"
|
||||
|
||||
/* __attribute__((unused)) due to lack of mips support: see mips block
|
||||
* in set_mmap_rnd_bits_action */
|
||||
static bool __attribute__((unused)) set_mmap_rnd_bits_min(int start, int min, bool compat) {
|
||||
std::string path;
|
||||
if (compat) {
|
||||
path = MMAP_RND_COMPAT_PATH;
|
||||
} else {
|
||||
path = MMAP_RND_PATH;
|
||||
}
|
||||
|
||||
return set_highest_available_option_value(path, min, start);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set /proc/sys/vm/mmap_rnd_bits and potentially
|
||||
* /proc/sys/vm/mmap_rnd_compat_bits to the maximum supported values.
|
||||
* Returns -1 if unable to set these to an acceptable value.
|
||||
*
|
||||
* To support this sysctl, the following upstream commits are needed:
|
||||
*
|
||||
* d07e22597d1d mm: mmap: add new /proc tunable for mmap_base ASLR
|
||||
* e0c25d958f78 arm: mm: support ARCH_MMAP_RND_BITS
|
||||
* 8f0d3aa9de57 arm64: mm: support ARCH_MMAP_RND_BITS
|
||||
* 9e08f57d684a x86: mm: support ARCH_MMAP_RND_BITS
|
||||
* ec9ee4acd97c drivers: char: random: add get_random_long()
|
||||
* 5ef11c35ce86 mm: ASLR: use get_random_long()
|
||||
*/
|
||||
static int set_mmap_rnd_bits_action(const std::vector<std::string>& args) {
|
||||
/* values are arch-dependent */
|
||||
#if defined(USER_MODE_LINUX)
|
||||
/* uml does not support mmap_rnd_bits */
|
||||
return 0;
|
||||
#elif defined(__aarch64__)
|
||||
/* arm64 supports 18 - 33 bits depending on pagesize and VA_SIZE */
|
||||
if (set_mmap_rnd_bits_min(33, 24, false)
|
||||
&& set_mmap_rnd_bits_min(16, 16, true)) {
|
||||
return 0;
|
||||
}
|
||||
#elif defined(__x86_64__)
|
||||
/* x86_64 supports 28 - 32 bits */
|
||||
if (set_mmap_rnd_bits_min(32, 32, false)
|
||||
&& set_mmap_rnd_bits_min(16, 16, true)) {
|
||||
return 0;
|
||||
}
|
||||
#elif defined(__arm__) || defined(__i386__)
|
||||
/* check to see if we're running on 64-bit kernel */
|
||||
bool h64 = !access(MMAP_RND_COMPAT_PATH, F_OK);
|
||||
/* supported 32-bit architecture must have 16 bits set */
|
||||
if (set_mmap_rnd_bits_min(16, 16, h64)) {
|
||||
return 0;
|
||||
}
|
||||
#elif defined(__mips__) || defined(__mips64__)
|
||||
// TODO: add mips support b/27788820
|
||||
return 0;
|
||||
#else
|
||||
LOG(ERROR) << "Unknown architecture";
|
||||
#endif
|
||||
|
||||
LOG(ERROR) << "Unable to set adequate mmap entropy value!";
|
||||
security_failure();
|
||||
return -1;
|
||||
}
|
||||
|
||||
#define KPTR_RESTRICT_PATH "/proc/sys/kernel/kptr_restrict"
|
||||
#define KPTR_RESTRICT_MINVALUE 2
|
||||
#define KPTR_RESTRICT_MAXVALUE 4
|
||||
|
||||
/* Set kptr_restrict to the highest available level.
|
||||
*
|
||||
* Aborts if unable to set this to an acceptable value.
|
||||
*/
|
||||
static int set_kptr_restrict_action(const std::vector<std::string>& args)
|
||||
{
|
||||
std::string path = KPTR_RESTRICT_PATH;
|
||||
|
||||
if (!set_highest_available_option_value(path, KPTR_RESTRICT_MINVALUE, KPTR_RESTRICT_MAXVALUE)) {
|
||||
LOG(ERROR) << "Unable to set adequate kptr_restrict value!";
|
||||
security_failure();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int keychord_init_action(const std::vector<std::string>& args)
|
||||
{
|
||||
keychord_init();
|
||||
|
@ -582,386 +377,6 @@ static void global_seccomp() {
|
|||
});
|
||||
}
|
||||
|
||||
static void selinux_init_all_handles(void)
|
||||
{
|
||||
sehandle = selinux_android_file_context_handle();
|
||||
selinux_android_set_sehandle(sehandle);
|
||||
sehandle_prop = selinux_android_prop_context_handle();
|
||||
}
|
||||
|
||||
enum selinux_enforcing_status { SELINUX_PERMISSIVE, SELINUX_ENFORCING };
|
||||
|
||||
static selinux_enforcing_status selinux_status_from_cmdline() {
|
||||
selinux_enforcing_status status = SELINUX_ENFORCING;
|
||||
|
||||
import_kernel_cmdline(false, [&](const std::string& key, const std::string& value, bool in_qemu) {
|
||||
if (key == "androidboot.selinux" && value == "permissive") {
|
||||
status = SELINUX_PERMISSIVE;
|
||||
}
|
||||
});
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static bool selinux_is_enforcing(void)
|
||||
{
|
||||
if (ALLOW_PERMISSIVE_SELINUX) {
|
||||
return selinux_status_from_cmdline() == SELINUX_ENFORCING;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static int audit_callback(void *data, security_class_t /*cls*/, char *buf, size_t len) {
|
||||
|
||||
property_audit_data *d = reinterpret_cast<property_audit_data*>(data);
|
||||
|
||||
if (!d || !d->name || !d->cr) {
|
||||
LOG(ERROR) << "audit_callback invoked with null data arguments!";
|
||||
return 0;
|
||||
}
|
||||
|
||||
snprintf(buf, len, "property=%s pid=%d uid=%d gid=%d", d->name,
|
||||
d->cr->pid, d->cr->uid, d->cr->gid);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Forks, executes the provided program in the child, and waits for the completion in the parent.
|
||||
* Child's stderr is captured and logged using LOG(ERROR).
|
||||
*
|
||||
* Returns true if the child exited with status code 0, returns false otherwise.
|
||||
*/
|
||||
static bool fork_execve_and_wait_for_completion(const char* filename, char* const argv[],
|
||||
char* const envp[]) {
|
||||
// Create a pipe used for redirecting child process's output.
|
||||
// * pipe_fds[0] is the FD the parent will use for reading.
|
||||
// * pipe_fds[1] is the FD the child will use for writing.
|
||||
int pipe_fds[2];
|
||||
if (pipe(pipe_fds) == -1) {
|
||||
PLOG(ERROR) << "Failed to create pipe";
|
||||
return false;
|
||||
}
|
||||
|
||||
pid_t child_pid = fork();
|
||||
if (child_pid == -1) {
|
||||
PLOG(ERROR) << "Failed to fork for " << filename;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (child_pid == 0) {
|
||||
// fork succeeded -- this is executing in the child process
|
||||
|
||||
// Close the pipe FD not used by this process
|
||||
TEMP_FAILURE_RETRY(close(pipe_fds[0]));
|
||||
|
||||
// Redirect stderr to the pipe FD provided by the parent
|
||||
if (TEMP_FAILURE_RETRY(dup2(pipe_fds[1], STDERR_FILENO)) == -1) {
|
||||
PLOG(ERROR) << "Failed to redirect stderr of " << filename;
|
||||
_exit(127);
|
||||
return false;
|
||||
}
|
||||
TEMP_FAILURE_RETRY(close(pipe_fds[1]));
|
||||
|
||||
if (execve(filename, argv, envp) == -1) {
|
||||
PLOG(ERROR) << "Failed to execve " << filename;
|
||||
return false;
|
||||
}
|
||||
// Unreachable because execve will have succeeded and replaced this code
|
||||
// with child process's code.
|
||||
_exit(127);
|
||||
return false;
|
||||
} else {
|
||||
// fork succeeded -- this is executing in the original/parent process
|
||||
|
||||
// Close the pipe FD not used by this process
|
||||
TEMP_FAILURE_RETRY(close(pipe_fds[1]));
|
||||
|
||||
// Log the redirected output of the child process.
|
||||
// It's unfortunate that there's no standard way to obtain an istream for a file descriptor.
|
||||
// As a result, we're buffering all output and logging it in one go at the end of the
|
||||
// invocation, instead of logging it as it comes in.
|
||||
const int child_out_fd = pipe_fds[0];
|
||||
std::string child_output;
|
||||
if (!android::base::ReadFdToString(child_out_fd, &child_output)) {
|
||||
PLOG(ERROR) << "Failed to capture full output of " << filename;
|
||||
}
|
||||
TEMP_FAILURE_RETRY(close(child_out_fd));
|
||||
if (!child_output.empty()) {
|
||||
// Log captured output, line by line, because LOG expects to be invoked for each line
|
||||
std::istringstream in(child_output);
|
||||
std::string line;
|
||||
while (std::getline(in, line)) {
|
||||
LOG(ERROR) << filename << ": " << line;
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for child to terminate
|
||||
int status;
|
||||
if (TEMP_FAILURE_RETRY(waitpid(child_pid, &status, 0)) != child_pid) {
|
||||
PLOG(ERROR) << "Failed to wait for " << filename;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (WIFEXITED(status)) {
|
||||
int status_code = WEXITSTATUS(status);
|
||||
if (status_code == 0) {
|
||||
return true;
|
||||
} else {
|
||||
LOG(ERROR) << filename << " exited with status " << status_code;
|
||||
}
|
||||
} else if (WIFSIGNALED(status)) {
|
||||
LOG(ERROR) << filename << " killed by signal " << WTERMSIG(status);
|
||||
} else if (WIFSTOPPED(status)) {
|
||||
LOG(ERROR) << filename << " stopped by signal " << WSTOPSIG(status);
|
||||
} else {
|
||||
LOG(ERROR) << "waitpid for " << filename << " returned unexpected status: " << status;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool read_first_line(const char* file, std::string* line) {
|
||||
line->clear();
|
||||
|
||||
std::string contents;
|
||||
if (!android::base::ReadFileToString(file, &contents, true /* follow symlinks */)) {
|
||||
return false;
|
||||
}
|
||||
std::istringstream in(contents);
|
||||
std::getline(in, *line);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool selinux_find_precompiled_split_policy(std::string* file) {
|
||||
file->clear();
|
||||
|
||||
static constexpr const char precompiled_sepolicy[] = "/vendor/etc/selinux/precompiled_sepolicy";
|
||||
if (access(precompiled_sepolicy, R_OK) == -1) {
|
||||
return false;
|
||||
}
|
||||
std::string actual_plat_id;
|
||||
if (!read_first_line("/system/etc/selinux/plat_and_mapping_sepolicy.cil.sha256",
|
||||
&actual_plat_id)) {
|
||||
PLOG(INFO) << "Failed to read "
|
||||
"/system/etc/selinux/plat_and_mapping_sepolicy.cil.sha256";
|
||||
return false;
|
||||
}
|
||||
std::string precompiled_plat_id;
|
||||
if (!read_first_line("/vendor/etc/selinux/precompiled_sepolicy.plat_and_mapping.sha256",
|
||||
&precompiled_plat_id)) {
|
||||
PLOG(INFO) << "Failed to read "
|
||||
"/vendor/etc/selinux/"
|
||||
"precompiled_sepolicy.plat_and_mapping.sha256";
|
||||
return false;
|
||||
}
|
||||
if ((actual_plat_id.empty()) || (actual_plat_id != precompiled_plat_id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*file = precompiled_sepolicy;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool selinux_get_vendor_mapping_version(std::string* plat_vers) {
|
||||
if (!read_first_line("/vendor/etc/selinux/plat_sepolicy_vers.txt", plat_vers)) {
|
||||
PLOG(ERROR) << "Failed to read /vendor/etc/selinux/plat_sepolicy_vers.txt";
|
||||
return false;
|
||||
}
|
||||
if (plat_vers->empty()) {
|
||||
LOG(ERROR) << "No version present in plat_sepolicy_vers.txt";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static constexpr const char plat_policy_cil_file[] = "/system/etc/selinux/plat_sepolicy.cil";
|
||||
|
||||
static bool selinux_is_split_policy_device() { return access(plat_policy_cil_file, R_OK) != -1; }
|
||||
|
||||
/*
|
||||
* Loads SELinux policy split across platform/system and non-platform/vendor files.
|
||||
*
|
||||
* Returns true upon success, false otherwise (failure cause is logged).
|
||||
*/
|
||||
static bool selinux_load_split_policy() {
|
||||
// IMPLEMENTATION NOTE: Split policy consists of three CIL files:
|
||||
// * platform -- policy needed due to logic contained in the system image,
|
||||
// * non-platform -- policy needed due to logic contained in the vendor image,
|
||||
// * mapping -- mapping policy which helps preserve forward-compatibility of non-platform policy
|
||||
// with newer versions of platform policy.
|
||||
//
|
||||
// secilc is invoked to compile the above three policy files into a single monolithic policy
|
||||
// file. This file is then loaded into the kernel.
|
||||
|
||||
// Load precompiled policy from vendor image, if a matching policy is found there. The policy
|
||||
// must match the platform policy on the system image.
|
||||
std::string precompiled_sepolicy_file;
|
||||
if (selinux_find_precompiled_split_policy(&precompiled_sepolicy_file)) {
|
||||
android::base::unique_fd fd(
|
||||
open(precompiled_sepolicy_file.c_str(), O_RDONLY | O_CLOEXEC | O_BINARY));
|
||||
if (fd != -1) {
|
||||
if (selinux_android_load_policy_from_fd(fd, precompiled_sepolicy_file.c_str()) < 0) {
|
||||
LOG(ERROR) << "Failed to load SELinux policy from " << precompiled_sepolicy_file;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// No suitable precompiled policy could be loaded
|
||||
|
||||
LOG(INFO) << "Compiling SELinux policy";
|
||||
|
||||
// Determine the highest policy language version supported by the kernel
|
||||
set_selinuxmnt("/sys/fs/selinux");
|
||||
int max_policy_version = security_policyvers();
|
||||
if (max_policy_version == -1) {
|
||||
PLOG(ERROR) << "Failed to determine highest policy version supported by kernel";
|
||||
return false;
|
||||
}
|
||||
|
||||
// We store the output of the compilation on /dev because this is the most convenient tmpfs
|
||||
// storage mount available this early in the boot sequence.
|
||||
char compiled_sepolicy[] = "/dev/sepolicy.XXXXXX";
|
||||
android::base::unique_fd compiled_sepolicy_fd(mkostemp(compiled_sepolicy, O_CLOEXEC));
|
||||
if (compiled_sepolicy_fd < 0) {
|
||||
PLOG(ERROR) << "Failed to create temporary file " << compiled_sepolicy;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Determine which mapping file to include
|
||||
std::string vend_plat_vers;
|
||||
if (!selinux_get_vendor_mapping_version(&vend_plat_vers)) {
|
||||
return false;
|
||||
}
|
||||
std::string mapping_file("/system/etc/selinux/mapping/" + vend_plat_vers + ".cil");
|
||||
// clang-format off
|
||||
const char* compile_args[] = {
|
||||
"/system/bin/secilc",
|
||||
plat_policy_cil_file,
|
||||
"-M", "true", "-G", "-N",
|
||||
// Target the highest policy language version supported by the kernel
|
||||
"-c", std::to_string(max_policy_version).c_str(),
|
||||
mapping_file.c_str(),
|
||||
"/vendor/etc/selinux/nonplat_sepolicy.cil",
|
||||
"-o", compiled_sepolicy,
|
||||
// We don't care about file_contexts output by the compiler
|
||||
"-f", "/sys/fs/selinux/null", // /dev/null is not yet available
|
||||
nullptr};
|
||||
// clang-format on
|
||||
|
||||
if (!fork_execve_and_wait_for_completion(compile_args[0], (char**)compile_args, (char**)ENV)) {
|
||||
unlink(compiled_sepolicy);
|
||||
return false;
|
||||
}
|
||||
unlink(compiled_sepolicy);
|
||||
|
||||
LOG(INFO) << "Loading compiled SELinux policy";
|
||||
if (selinux_android_load_policy_from_fd(compiled_sepolicy_fd, compiled_sepolicy) < 0) {
|
||||
LOG(ERROR) << "Failed to load SELinux policy from " << compiled_sepolicy;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Loads SELinux policy from a monolithic file.
|
||||
*
|
||||
* Returns true upon success, false otherwise (failure cause is logged).
|
||||
*/
|
||||
static bool selinux_load_monolithic_policy() {
|
||||
LOG(VERBOSE) << "Loading SELinux policy from monolithic file";
|
||||
if (selinux_android_load_policy() < 0) {
|
||||
PLOG(ERROR) << "Failed to load monolithic SELinux policy";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Loads SELinux policy into the kernel.
|
||||
*
|
||||
* Returns true upon success, false otherwise (failure cause is logged).
|
||||
*/
|
||||
static bool selinux_load_policy() {
|
||||
return selinux_is_split_policy_device() ? selinux_load_split_policy()
|
||||
: selinux_load_monolithic_policy();
|
||||
}
|
||||
|
||||
static void selinux_initialize(bool in_kernel_domain) {
|
||||
Timer t;
|
||||
|
||||
selinux_callback cb;
|
||||
cb.func_log = selinux_klog_callback;
|
||||
selinux_set_callback(SELINUX_CB_LOG, cb);
|
||||
cb.func_audit = audit_callback;
|
||||
selinux_set_callback(SELINUX_CB_AUDIT, cb);
|
||||
|
||||
if (in_kernel_domain) {
|
||||
LOG(INFO) << "Loading SELinux policy";
|
||||
if (!selinux_load_policy()) {
|
||||
panic();
|
||||
}
|
||||
|
||||
bool kernel_enforcing = (security_getenforce() == 1);
|
||||
bool is_enforcing = selinux_is_enforcing();
|
||||
if (kernel_enforcing != is_enforcing) {
|
||||
if (security_setenforce(is_enforcing)) {
|
||||
PLOG(ERROR) << "security_setenforce(%s) failed" << (is_enforcing ? "true" : "false");
|
||||
security_failure();
|
||||
}
|
||||
}
|
||||
|
||||
std::string err;
|
||||
if (!WriteFile("/sys/fs/selinux/checkreqprot", "0", &err)) {
|
||||
LOG(ERROR) << err;
|
||||
security_failure();
|
||||
}
|
||||
|
||||
// init's first stage can't set properties, so pass the time to the second stage.
|
||||
setenv("INIT_SELINUX_TOOK", std::to_string(t.duration().count()).c_str(), 1);
|
||||
} else {
|
||||
selinux_init_all_handles();
|
||||
}
|
||||
}
|
||||
|
||||
// The files and directories that were created before initial sepolicy load or
|
||||
// files on ramdisk need to have their security context restored to the proper
|
||||
// value. This must happen before /dev is populated by ueventd.
|
||||
static void selinux_restore_context() {
|
||||
LOG(INFO) << "Running restorecon...";
|
||||
selinux_android_restorecon("/dev", 0);
|
||||
selinux_android_restorecon("/dev/kmsg", 0);
|
||||
if constexpr (WORLD_WRITABLE_KMSG) {
|
||||
selinux_android_restorecon("/dev/kmsg_debug", 0);
|
||||
}
|
||||
selinux_android_restorecon("/dev/socket", 0);
|
||||
selinux_android_restorecon("/dev/random", 0);
|
||||
selinux_android_restorecon("/dev/urandom", 0);
|
||||
selinux_android_restorecon("/dev/__properties__", 0);
|
||||
|
||||
selinux_android_restorecon("/file_contexts.bin", 0);
|
||||
selinux_android_restorecon("/plat_file_contexts", 0);
|
||||
selinux_android_restorecon("/nonplat_file_contexts", 0);
|
||||
selinux_android_restorecon("/plat_property_contexts", 0);
|
||||
selinux_android_restorecon("/nonplat_property_contexts", 0);
|
||||
selinux_android_restorecon("/plat_seapp_contexts", 0);
|
||||
selinux_android_restorecon("/nonplat_seapp_contexts", 0);
|
||||
selinux_android_restorecon("/plat_service_contexts", 0);
|
||||
selinux_android_restorecon("/nonplat_service_contexts", 0);
|
||||
selinux_android_restorecon("/plat_hwservice_contexts", 0);
|
||||
selinux_android_restorecon("/nonplat_hwservice_contexts", 0);
|
||||
selinux_android_restorecon("/sepolicy", 0);
|
||||
selinux_android_restorecon("/vndservice_contexts", 0);
|
||||
|
||||
selinux_android_restorecon("/dev/block", SELINUX_ANDROID_RESTORECON_RECURSE);
|
||||
selinux_android_restorecon("/dev/device-mapper", 0);
|
||||
|
||||
selinux_android_restorecon("/sbin/mke2fs_static", 0);
|
||||
selinux_android_restorecon("/sbin/e2fsdroid_static", 0);
|
||||
}
|
||||
|
||||
// Set the UDC controller for the ConfigFS USB Gadgets.
|
||||
// Read the UDC controller in use from "/sys/class/udc".
|
||||
// In case of multiple UDC controllers select the first one.
|
||||
|
@ -1067,13 +482,14 @@ int main(int argc, char** argv) {
|
|||
global_seccomp();
|
||||
|
||||
// Set up SELinux, loading the SELinux policy.
|
||||
selinux_initialize(true);
|
||||
SelinuxSetupKernelLogging();
|
||||
SelinuxInitialize();
|
||||
|
||||
// We're in the kernel domain, so re-exec init to transition to the init domain now
|
||||
// that the SELinux policy has been loaded.
|
||||
if (selinux_android_restorecon("/init", 0) == -1) {
|
||||
PLOG(ERROR) << "restorecon failed";
|
||||
security_failure();
|
||||
PLOG(ERROR) << "restorecon failed of /init failed";
|
||||
panic();
|
||||
}
|
||||
|
||||
setenv("INIT_SECOND_STAGE", "true", 1);
|
||||
|
@ -1089,7 +505,7 @@ int main(int argc, char** argv) {
|
|||
// execv() only returns if an error happened, in which case we
|
||||
// panic and never fall through this conditional.
|
||||
PLOG(ERROR) << "execv(\"" << path << "\") failed";
|
||||
security_failure();
|
||||
panic();
|
||||
}
|
||||
|
||||
// At this point we're in the second stage of init.
|
||||
|
@ -1130,8 +546,9 @@ int main(int argc, char** argv) {
|
|||
unsetenv("INIT_AVB_VERSION");
|
||||
|
||||
// Now set up SELinux for second stage.
|
||||
selinux_initialize(false);
|
||||
selinux_restore_context();
|
||||
SelinuxSetupKernelLogging();
|
||||
SelabelInitialize();
|
||||
SelinuxRestoreContext();
|
||||
|
||||
epoll_fd = epoll_create1(EPOLL_CLOEXEC);
|
||||
if (epoll_fd == -1) {
|
||||
|
@ -1163,9 +580,9 @@ int main(int argc, char** argv) {
|
|||
// Queue an action that waits for coldboot done so we know ueventd has set up all of /dev...
|
||||
am.QueueBuiltinAction(wait_for_coldboot_done_action, "wait_for_coldboot_done");
|
||||
// ... so that we can start queuing up actions that require stuff from /dev.
|
||||
am.QueueBuiltinAction(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
|
||||
am.QueueBuiltinAction(set_mmap_rnd_bits_action, "set_mmap_rnd_bits");
|
||||
am.QueueBuiltinAction(set_kptr_restrict_action, "set_kptr_restrict");
|
||||
am.QueueBuiltinAction(MixHwrngIntoLinuxRngAction, "MixHwrngIntoLinuxRng");
|
||||
am.QueueBuiltinAction(SetMmapRndBitsAction, "SetMmapRndBits");
|
||||
am.QueueBuiltinAction(SetKptrRestrictAction, "SetKptrRestrict");
|
||||
am.QueueBuiltinAction(keychord_init_action, "keychord_init");
|
||||
am.QueueBuiltinAction(console_init_action, "console_init");
|
||||
|
||||
|
@ -1174,7 +591,7 @@ int main(int argc, char** argv) {
|
|||
|
||||
// Repeat mix_hwrng_into_linux_rng in case /dev/hw_random or /dev/random
|
||||
// wasn't ready immediately after wait_for_coldboot_done
|
||||
am.QueueBuiltinAction(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
|
||||
am.QueueBuiltinAction(MixHwrngIntoLinuxRngAction, "MixHwrngIntoLinuxRng");
|
||||
|
||||
// Don't mount filesystems or start core system services in charger mode.
|
||||
std::string bootmode = GetProperty("ro.bootmode", "");
|
||||
|
|
|
@ -18,8 +18,7 @@
|
|||
#define _INIT_INIT_H
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <selinux/label.h>
|
||||
#include <vector>
|
||||
|
||||
#include "action.h"
|
||||
#include "parser.h"
|
||||
|
@ -33,8 +32,6 @@ namespace init {
|
|||
// TODO: Have an Init class and remove all globals.
|
||||
extern const char *ENV[32];
|
||||
extern std::string default_console;
|
||||
extern struct selabel_handle *sehandle;
|
||||
extern struct selabel_handle *sehandle_prop;
|
||||
|
||||
extern std::vector<std::string> late_import_paths;
|
||||
|
||||
|
|
|
@ -68,6 +68,8 @@ static int persistent_properties_loaded = 0;
|
|||
|
||||
static int property_set_fd = -1;
|
||||
|
||||
static struct selabel_handle* sehandle_prop;
|
||||
|
||||
void property_init() {
|
||||
if (__system_property_area_init()) {
|
||||
LOG(ERROR) << "Failed to initialize property area";
|
||||
|
@ -733,11 +735,30 @@ void load_system_props() {
|
|||
load_recovery_id_prop();
|
||||
}
|
||||
|
||||
static int SelinuxAuditCallback(void* data, security_class_t /*cls*/, char* buf, size_t len) {
|
||||
property_audit_data* d = reinterpret_cast<property_audit_data*>(data);
|
||||
|
||||
if (!d || !d->name || !d->cr) {
|
||||
LOG(ERROR) << "AuditCallback invoked with null data arguments!";
|
||||
return 0;
|
||||
}
|
||||
|
||||
snprintf(buf, len, "property=%s pid=%d uid=%d gid=%d", d->name, d->cr->pid, d->cr->uid,
|
||||
d->cr->gid);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void start_property_service() {
|
||||
sehandle_prop = selinux_android_prop_context_handle();
|
||||
|
||||
selinux_callback cb;
|
||||
cb.func_audit = SelinuxAuditCallback;
|
||||
selinux_set_callback(SELINUX_CB_AUDIT, cb);
|
||||
|
||||
property_set("ro.property_service.version", "2");
|
||||
|
||||
property_set_fd = CreateSocket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
|
||||
false, 0666, 0, 0, nullptr, sehandle);
|
||||
false, 0666, 0, 0, nullptr);
|
||||
if (property_set_fd == -1) {
|
||||
PLOG(ERROR) << "start_property_service socket creation failed";
|
||||
exit(1);
|
||||
|
|
|
@ -48,6 +48,7 @@
|
|||
#include <fs_mgr.h>
|
||||
#include <logwrap/logwrap.h>
|
||||
#include <private/android_filesystem_config.h>
|
||||
#include <selinux/selinux.h>
|
||||
|
||||
#include "capabilities.h"
|
||||
#include "init.h"
|
||||
|
|
|
@ -0,0 +1,209 @@
|
|||
/*
|
||||
* 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 "security.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <fstream>
|
||||
|
||||
#include <android-base/logging.h>
|
||||
#include <android-base/unique_fd.h>
|
||||
|
||||
#include "util.h"
|
||||
|
||||
using android::base::unique_fd;
|
||||
|
||||
namespace android {
|
||||
namespace init {
|
||||
|
||||
// Writes 512 bytes of output from Hardware RNG (/dev/hw_random, backed
|
||||
// by Linux kernel's hw_random framework) into Linux RNG's via /dev/urandom.
|
||||
// Does nothing if Hardware RNG is not present.
|
||||
//
|
||||
// Since we don't yet trust the quality of Hardware RNG, these bytes are not
|
||||
// mixed into the primary pool of Linux RNG and the entropy estimate is left
|
||||
// unmodified.
|
||||
//
|
||||
// If the HW RNG device /dev/hw_random is present, we require that at least
|
||||
// 512 bytes read from it are written into Linux RNG. QA is expected to catch
|
||||
// devices/configurations where these I/O operations are blocking for a long
|
||||
// time. We do not reboot or halt on failures, as this is a best-effort
|
||||
// attempt.
|
||||
int MixHwrngIntoLinuxRngAction(const std::vector<std::string>& args) {
|
||||
unique_fd hwrandom_fd(
|
||||
TEMP_FAILURE_RETRY(open("/dev/hw_random", O_RDONLY | O_NOFOLLOW | O_CLOEXEC)));
|
||||
if (hwrandom_fd == -1) {
|
||||
if (errno == ENOENT) {
|
||||
LOG(INFO) << "/dev/hw_random not found";
|
||||
// It's not an error to not have a Hardware RNG.
|
||||
return 0;
|
||||
}
|
||||
PLOG(ERROR) << "Failed to open /dev/hw_random";
|
||||
return -1;
|
||||
}
|
||||
|
||||
unique_fd urandom_fd(
|
||||
TEMP_FAILURE_RETRY(open("/dev/urandom", O_WRONLY | O_NOFOLLOW | O_CLOEXEC)));
|
||||
if (urandom_fd == -1) {
|
||||
PLOG(ERROR) << "Failed to open /dev/urandom";
|
||||
return -1;
|
||||
}
|
||||
|
||||
char buf[512];
|
||||
size_t total_bytes_written = 0;
|
||||
while (total_bytes_written < sizeof(buf)) {
|
||||
ssize_t chunk_size =
|
||||
TEMP_FAILURE_RETRY(read(hwrandom_fd, buf, sizeof(buf) - total_bytes_written));
|
||||
if (chunk_size == -1) {
|
||||
PLOG(ERROR) << "Failed to read from /dev/hw_random";
|
||||
return -1;
|
||||
} else if (chunk_size == 0) {
|
||||
LOG(ERROR) << "Failed to read from /dev/hw_random: EOF";
|
||||
return -1;
|
||||
}
|
||||
|
||||
chunk_size = TEMP_FAILURE_RETRY(write(urandom_fd, buf, chunk_size));
|
||||
if (chunk_size == -1) {
|
||||
PLOG(ERROR) << "Failed to write to /dev/urandom";
|
||||
return -1;
|
||||
}
|
||||
total_bytes_written += chunk_size;
|
||||
}
|
||||
|
||||
LOG(INFO) << "Mixed " << total_bytes_written << " bytes from /dev/hw_random into /dev/urandom";
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool SetHighestAvailableOptionValue(std::string path, int min, int max) {
|
||||
std::ifstream inf(path, std::fstream::in);
|
||||
if (!inf) {
|
||||
LOG(ERROR) << "Cannot open for reading: " << path;
|
||||
return false;
|
||||
}
|
||||
|
||||
int current = max;
|
||||
while (current >= min) {
|
||||
// try to write out new value
|
||||
std::string str_val = std::to_string(current);
|
||||
std::ofstream of(path, std::fstream::out);
|
||||
if (!of) {
|
||||
LOG(ERROR) << "Cannot open for writing: " << path;
|
||||
return false;
|
||||
}
|
||||
of << str_val << std::endl;
|
||||
of.close();
|
||||
|
||||
// check to make sure it was recorded
|
||||
inf.seekg(0);
|
||||
std::string str_rec;
|
||||
inf >> str_rec;
|
||||
if (str_val.compare(str_rec) == 0) {
|
||||
break;
|
||||
}
|
||||
current--;
|
||||
}
|
||||
inf.close();
|
||||
|
||||
if (current < min) {
|
||||
LOG(ERROR) << "Unable to set minimum option value " << min << " in " << path;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#define MMAP_RND_PATH "/proc/sys/vm/mmap_rnd_bits"
|
||||
#define MMAP_RND_COMPAT_PATH "/proc/sys/vm/mmap_rnd_compat_bits"
|
||||
|
||||
// __attribute__((unused)) due to lack of mips support: see mips block in SetMmapRndBitsAction
|
||||
static bool __attribute__((unused)) SetMmapRndBitsMin(int start, int min, bool compat) {
|
||||
std::string path;
|
||||
if (compat) {
|
||||
path = MMAP_RND_COMPAT_PATH;
|
||||
} else {
|
||||
path = MMAP_RND_PATH;
|
||||
}
|
||||
|
||||
return SetHighestAvailableOptionValue(path, min, start);
|
||||
}
|
||||
|
||||
// Set /proc/sys/vm/mmap_rnd_bits and potentially
|
||||
// /proc/sys/vm/mmap_rnd_compat_bits to the maximum supported values.
|
||||
// Returns -1 if unable to set these to an acceptable value.
|
||||
//
|
||||
// To support this sysctl, the following upstream commits are needed:
|
||||
//
|
||||
// d07e22597d1d mm: mmap: add new /proc tunable for mmap_base ASLR
|
||||
// e0c25d958f78 arm: mm: support ARCH_MMAP_RND_BITS
|
||||
// 8f0d3aa9de57 arm64: mm: support ARCH_MMAP_RND_BITS
|
||||
// 9e08f57d684a x86: mm: support ARCH_MMAP_RND_BITS
|
||||
// ec9ee4acd97c drivers: char: random: add get_random_long()
|
||||
// 5ef11c35ce86 mm: ASLR: use get_random_long()
|
||||
int SetMmapRndBitsAction(const std::vector<std::string>& args) {
|
||||
// values are arch-dependent
|
||||
#if defined(USER_MODE_LINUX)
|
||||
// uml does not support mmap_rnd_bits
|
||||
return 0;
|
||||
#elif defined(__aarch64__)
|
||||
// arm64 supports 18 - 33 bits depending on pagesize and VA_SIZE
|
||||
if (SetMmapRndBitsMin(33, 24, false) && SetMmapRndBitsMin(16, 16, true)) {
|
||||
return 0;
|
||||
}
|
||||
#elif defined(__x86_64__)
|
||||
// x86_64 supports 28 - 32 bits
|
||||
if (SetMmapRndBitsMin(32, 32, false) && SetMmapRndBitsMin(16, 16, true)) {
|
||||
return 0;
|
||||
}
|
||||
#elif defined(__arm__) || defined(__i386__)
|
||||
// check to see if we're running on 64-bit kernel
|
||||
bool h64 = !access(MMAP_RND_COMPAT_PATH, F_OK);
|
||||
// supported 32-bit architecture must have 16 bits set
|
||||
if (SetMmapRndBitsMin(16, 16, h64)) {
|
||||
return 0;
|
||||
}
|
||||
#elif defined(__mips__) || defined(__mips64__)
|
||||
// TODO: add mips support b/27788820
|
||||
return 0;
|
||||
#else
|
||||
LOG(ERROR) << "Unknown architecture";
|
||||
#endif
|
||||
|
||||
LOG(ERROR) << "Unable to set adequate mmap entropy value!";
|
||||
panic();
|
||||
return -1;
|
||||
}
|
||||
|
||||
#define KPTR_RESTRICT_PATH "/proc/sys/kernel/kptr_restrict"
|
||||
#define KPTR_RESTRICT_MINVALUE 2
|
||||
#define KPTR_RESTRICT_MAXVALUE 4
|
||||
|
||||
// Set kptr_restrict to the highest available level.
|
||||
//
|
||||
// Aborts if unable to set this to an acceptable value.
|
||||
int SetKptrRestrictAction(const std::vector<std::string>& args) {
|
||||
std::string path = KPTR_RESTRICT_PATH;
|
||||
|
||||
if (!SetHighestAvailableOptionValue(path, KPTR_RESTRICT_MINVALUE, KPTR_RESTRICT_MAXVALUE)) {
|
||||
LOG(ERROR) << "Unable to set adequate kptr_restrict value!";
|
||||
panic();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace init
|
||||
} // namespace android
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* 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_SECURITY_H
|
||||
#define _INIT_SECURITY_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace android {
|
||||
namespace init {
|
||||
|
||||
int MixHwrngIntoLinuxRngAction(const std::vector<std::string>& args);
|
||||
int SetMmapRndBitsAction(const std::vector<std::string>& args);
|
||||
int SetKptrRestrictAction(const std::vector<std::string>& args);
|
||||
|
||||
} // namespace init
|
||||
} // namespace android
|
||||
|
||||
#endif
|
|
@ -0,0 +1,463 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
// This file contains the functions that initialize SELinux during boot as well as helper functions
|
||||
// for SELinux operation for init.
|
||||
|
||||
// When the system boots, there is no SEPolicy present and init is running in the kernel domain.
|
||||
// Init loads the SEPolicy from the file system, restores the context of /init based on this
|
||||
// SEPolicy, and finally exec()'s itself to run in the proper domain.
|
||||
|
||||
// The SEPolicy on Android comes in two variants: monolithic and split.
|
||||
|
||||
// The monolithic policy variant is for legacy non-treble devices that contain a single SEPolicy
|
||||
// file located at /sepolicy and is directly loaded into the kernel SELinux subsystem.
|
||||
|
||||
// The split policy is for supporting treble devices. It splits the SEPolicy across files on
|
||||
// /system/etc/selinux (the 'plat' portion of the policy) and /vendor/etc/selinux (the 'nonplat'
|
||||
// portion of the policy). This is necessary to allow the system image to be updated independently
|
||||
// of the vendor image, while maintaining contributions from both partitions in the SEPolicy. This
|
||||
// is especially important for VTS testing, where the SEPolicy on the Google System Image may not be
|
||||
// identical to the system image shipped on a vendor's device.
|
||||
|
||||
// The split SEPolicy is loaded as described below:
|
||||
// 1) There is a precompiled SEPolicy located at /vendor/etc/selinux/precompiled_sepolicy.
|
||||
// Stored along with this file is the sha256 hash of the parts of the SEPolicy on /system that
|
||||
// were used to compile this precompiled policy. The system partition contains a similar sha256
|
||||
// of the parts of the SEPolicy that it currently contains. If these two hashes match, then the
|
||||
// system loads this precompiled_sepolicy directly.
|
||||
// 2) If these hashes do not match, then /system has been updated out of sync with /vendor and the
|
||||
// init needs to compile the SEPolicy. /system contains the SEPolicy compiler, secilc, and it
|
||||
// is used by the LoadSplitPolicy() function below to compile the SEPolicy to a temp directory
|
||||
// and load it. That function contains even more documentation with the specific implementation
|
||||
// details of how the SEPolicy is compiled if needed.
|
||||
|
||||
#include "selinux.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <paths.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <android-base/chrono_utils.h>
|
||||
#include <android-base/file.h>
|
||||
#include <android-base/logging.h>
|
||||
#include <android-base/unique_fd.h>
|
||||
#include <selinux/android.h>
|
||||
|
||||
#include "log.h"
|
||||
#include "util.h"
|
||||
|
||||
using android::base::Timer;
|
||||
using android::base::unique_fd;
|
||||
|
||||
namespace android {
|
||||
namespace init {
|
||||
|
||||
static struct selabel_handle* sehandle = nullptr;
|
||||
|
||||
namespace {
|
||||
|
||||
enum EnforcingStatus { SELINUX_PERMISSIVE, SELINUX_ENFORCING };
|
||||
|
||||
EnforcingStatus StatusFromCmdline() {
|
||||
EnforcingStatus status = SELINUX_ENFORCING;
|
||||
|
||||
import_kernel_cmdline(false,
|
||||
[&](const std::string& key, const std::string& value, bool in_qemu) {
|
||||
if (key == "androidboot.selinux" && value == "permissive") {
|
||||
status = SELINUX_PERMISSIVE;
|
||||
}
|
||||
});
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
bool IsEnforcing() {
|
||||
if (ALLOW_PERMISSIVE_SELINUX) {
|
||||
return StatusFromCmdline() == SELINUX_ENFORCING;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Forks, executes the provided program in the child, and waits for the completion in the parent.
|
||||
// Child's stderr is captured and logged using LOG(ERROR).
|
||||
bool ForkExecveAndWaitForCompletion(const char* filename, char* const argv[]) {
|
||||
// Create a pipe used for redirecting child process's output.
|
||||
// * pipe_fds[0] is the FD the parent will use for reading.
|
||||
// * pipe_fds[1] is the FD the child will use for writing.
|
||||
int pipe_fds[2];
|
||||
if (pipe(pipe_fds) == -1) {
|
||||
PLOG(ERROR) << "Failed to create pipe";
|
||||
return false;
|
||||
}
|
||||
|
||||
pid_t child_pid = fork();
|
||||
if (child_pid == -1) {
|
||||
PLOG(ERROR) << "Failed to fork for " << filename;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (child_pid == 0) {
|
||||
// fork succeeded -- this is executing in the child process
|
||||
|
||||
// Close the pipe FD not used by this process
|
||||
TEMP_FAILURE_RETRY(close(pipe_fds[0]));
|
||||
|
||||
// Redirect stderr to the pipe FD provided by the parent
|
||||
if (TEMP_FAILURE_RETRY(dup2(pipe_fds[1], STDERR_FILENO)) == -1) {
|
||||
PLOG(ERROR) << "Failed to redirect stderr of " << filename;
|
||||
_exit(127);
|
||||
return false;
|
||||
}
|
||||
TEMP_FAILURE_RETRY(close(pipe_fds[1]));
|
||||
|
||||
const char* envp[] = {_PATH_DEFPATH, nullptr};
|
||||
if (execve(filename, argv, (char**)envp) == -1) {
|
||||
PLOG(ERROR) << "Failed to execve " << filename;
|
||||
return false;
|
||||
}
|
||||
// Unreachable because execve will have succeeded and replaced this code
|
||||
// with child process's code.
|
||||
_exit(127);
|
||||
return false;
|
||||
} else {
|
||||
// fork succeeded -- this is executing in the original/parent process
|
||||
|
||||
// Close the pipe FD not used by this process
|
||||
TEMP_FAILURE_RETRY(close(pipe_fds[1]));
|
||||
|
||||
// Log the redirected output of the child process.
|
||||
// It's unfortunate that there's no standard way to obtain an istream for a file descriptor.
|
||||
// As a result, we're buffering all output and logging it in one go at the end of the
|
||||
// invocation, instead of logging it as it comes in.
|
||||
const int child_out_fd = pipe_fds[0];
|
||||
std::string child_output;
|
||||
if (!android::base::ReadFdToString(child_out_fd, &child_output)) {
|
||||
PLOG(ERROR) << "Failed to capture full output of " << filename;
|
||||
}
|
||||
TEMP_FAILURE_RETRY(close(child_out_fd));
|
||||
if (!child_output.empty()) {
|
||||
// Log captured output, line by line, because LOG expects to be invoked for each line
|
||||
std::istringstream in(child_output);
|
||||
std::string line;
|
||||
while (std::getline(in, line)) {
|
||||
LOG(ERROR) << filename << ": " << line;
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for child to terminate
|
||||
int status;
|
||||
if (TEMP_FAILURE_RETRY(waitpid(child_pid, &status, 0)) != child_pid) {
|
||||
PLOG(ERROR) << "Failed to wait for " << filename;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (WIFEXITED(status)) {
|
||||
int status_code = WEXITSTATUS(status);
|
||||
if (status_code == 0) {
|
||||
return true;
|
||||
} else {
|
||||
LOG(ERROR) << filename << " exited with status " << status_code;
|
||||
}
|
||||
} else if (WIFSIGNALED(status)) {
|
||||
LOG(ERROR) << filename << " killed by signal " << WTERMSIG(status);
|
||||
} else if (WIFSTOPPED(status)) {
|
||||
LOG(ERROR) << filename << " stopped by signal " << WSTOPSIG(status);
|
||||
} else {
|
||||
LOG(ERROR) << "waitpid for " << filename << " returned unexpected status: " << status;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool ReadFirstLine(const char* file, std::string* line) {
|
||||
line->clear();
|
||||
|
||||
std::string contents;
|
||||
if (!android::base::ReadFileToString(file, &contents, true /* follow symlinks */)) {
|
||||
return false;
|
||||
}
|
||||
std::istringstream in(contents);
|
||||
std::getline(in, *line);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FindPrecompiledSplitPolicy(std::string* file) {
|
||||
file->clear();
|
||||
|
||||
static constexpr const char precompiled_sepolicy[] = "/vendor/etc/selinux/precompiled_sepolicy";
|
||||
if (access(precompiled_sepolicy, R_OK) == -1) {
|
||||
return false;
|
||||
}
|
||||
std::string actual_plat_id;
|
||||
if (!ReadFirstLine("/system/etc/selinux/plat_and_mapping_sepolicy.cil.sha256", &actual_plat_id)) {
|
||||
PLOG(INFO) << "Failed to read "
|
||||
"/system/etc/selinux/plat_and_mapping_sepolicy.cil.sha256";
|
||||
return false;
|
||||
}
|
||||
std::string precompiled_plat_id;
|
||||
if (!ReadFirstLine("/vendor/etc/selinux/precompiled_sepolicy.plat_and_mapping.sha256",
|
||||
&precompiled_plat_id)) {
|
||||
PLOG(INFO) << "Failed to read "
|
||||
"/vendor/etc/selinux/"
|
||||
"precompiled_sepolicy.plat_and_mapping.sha256";
|
||||
return false;
|
||||
}
|
||||
if ((actual_plat_id.empty()) || (actual_plat_id != precompiled_plat_id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*file = precompiled_sepolicy;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GetVendorMappingVersion(std::string* plat_vers) {
|
||||
if (!ReadFirstLine("/vendor/etc/selinux/plat_sepolicy_vers.txt", plat_vers)) {
|
||||
PLOG(ERROR) << "Failed to read /vendor/etc/selinux/plat_sepolicy_vers.txt";
|
||||
return false;
|
||||
}
|
||||
if (plat_vers->empty()) {
|
||||
LOG(ERROR) << "No version present in plat_sepolicy_vers.txt";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
constexpr const char plat_policy_cil_file[] = "/system/etc/selinux/plat_sepolicy.cil";
|
||||
|
||||
bool IsSplitPolicyDevice() {
|
||||
return access(plat_policy_cil_file, R_OK) != -1;
|
||||
}
|
||||
|
||||
bool LoadSplitPolicy() {
|
||||
// IMPLEMENTATION NOTE: Split policy consists of three CIL files:
|
||||
// * platform -- policy needed due to logic contained in the system image,
|
||||
// * non-platform -- policy needed due to logic contained in the vendor image,
|
||||
// * mapping -- mapping policy which helps preserve forward-compatibility of non-platform policy
|
||||
// with newer versions of platform policy.
|
||||
//
|
||||
// secilc is invoked to compile the above three policy files into a single monolithic policy
|
||||
// file. This file is then loaded into the kernel.
|
||||
|
||||
// Load precompiled policy from vendor image, if a matching policy is found there. The policy
|
||||
// must match the platform policy on the system image.
|
||||
std::string precompiled_sepolicy_file;
|
||||
if (FindPrecompiledSplitPolicy(&precompiled_sepolicy_file)) {
|
||||
unique_fd fd(open(precompiled_sepolicy_file.c_str(), O_RDONLY | O_CLOEXEC | O_BINARY));
|
||||
if (fd != -1) {
|
||||
if (selinux_android_load_policy_from_fd(fd, precompiled_sepolicy_file.c_str()) < 0) {
|
||||
LOG(ERROR) << "Failed to load SELinux policy from " << precompiled_sepolicy_file;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// No suitable precompiled policy could be loaded
|
||||
|
||||
LOG(INFO) << "Compiling SELinux policy";
|
||||
|
||||
// Determine the highest policy language version supported by the kernel
|
||||
set_selinuxmnt("/sys/fs/selinux");
|
||||
int max_policy_version = security_policyvers();
|
||||
if (max_policy_version == -1) {
|
||||
PLOG(ERROR) << "Failed to determine highest policy version supported by kernel";
|
||||
return false;
|
||||
}
|
||||
|
||||
// We store the output of the compilation on /dev because this is the most convenient tmpfs
|
||||
// storage mount available this early in the boot sequence.
|
||||
char compiled_sepolicy[] = "/dev/sepolicy.XXXXXX";
|
||||
unique_fd compiled_sepolicy_fd(mkostemp(compiled_sepolicy, O_CLOEXEC));
|
||||
if (compiled_sepolicy_fd < 0) {
|
||||
PLOG(ERROR) << "Failed to create temporary file " << compiled_sepolicy;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Determine which mapping file to include
|
||||
std::string vend_plat_vers;
|
||||
if (!GetVendorMappingVersion(&vend_plat_vers)) {
|
||||
return false;
|
||||
}
|
||||
std::string mapping_file("/system/etc/selinux/mapping/" + vend_plat_vers + ".cil");
|
||||
// clang-format off
|
||||
const char* compile_args[] = {
|
||||
"/system/bin/secilc",
|
||||
plat_policy_cil_file,
|
||||
"-M", "true", "-G", "-N",
|
||||
// Target the highest policy language version supported by the kernel
|
||||
"-c", std::to_string(max_policy_version).c_str(),
|
||||
mapping_file.c_str(),
|
||||
"/vendor/etc/selinux/nonplat_sepolicy.cil",
|
||||
"-o", compiled_sepolicy,
|
||||
// We don't care about file_contexts output by the compiler
|
||||
"-f", "/sys/fs/selinux/null", // /dev/null is not yet available
|
||||
nullptr};
|
||||
// clang-format on
|
||||
|
||||
if (!ForkExecveAndWaitForCompletion(compile_args[0], (char**)compile_args)) {
|
||||
unlink(compiled_sepolicy);
|
||||
return false;
|
||||
}
|
||||
unlink(compiled_sepolicy);
|
||||
|
||||
LOG(INFO) << "Loading compiled SELinux policy";
|
||||
if (selinux_android_load_policy_from_fd(compiled_sepolicy_fd, compiled_sepolicy) < 0) {
|
||||
LOG(ERROR) << "Failed to load SELinux policy from " << compiled_sepolicy;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LoadMonolithicPolicy() {
|
||||
LOG(VERBOSE) << "Loading SELinux policy from monolithic file";
|
||||
if (selinux_android_load_policy() < 0) {
|
||||
PLOG(ERROR) << "Failed to load monolithic SELinux policy";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LoadPolicy() {
|
||||
return IsSplitPolicyDevice() ? LoadSplitPolicy() : LoadMonolithicPolicy();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void SelinuxInitialize() {
|
||||
Timer t;
|
||||
|
||||
LOG(INFO) << "Loading SELinux policy";
|
||||
if (!LoadPolicy()) {
|
||||
panic();
|
||||
}
|
||||
|
||||
bool kernel_enforcing = (security_getenforce() == 1);
|
||||
bool is_enforcing = IsEnforcing();
|
||||
if (kernel_enforcing != is_enforcing) {
|
||||
if (security_setenforce(is_enforcing)) {
|
||||
PLOG(ERROR) << "security_setenforce(%s) failed" << (is_enforcing ? "true" : "false");
|
||||
panic();
|
||||
}
|
||||
}
|
||||
|
||||
std::string err;
|
||||
if (!WriteFile("/sys/fs/selinux/checkreqprot", "0", &err)) {
|
||||
LOG(ERROR) << err;
|
||||
panic();
|
||||
}
|
||||
|
||||
// init's first stage can't set properties, so pass the time to the second stage.
|
||||
setenv("INIT_SELINUX_TOOK", std::to_string(t.duration().count()).c_str(), 1);
|
||||
}
|
||||
|
||||
// The files and directories that were created before initial sepolicy load or
|
||||
// files on ramdisk need to have their security context restored to the proper
|
||||
// value. This must happen before /dev is populated by ueventd.
|
||||
void SelinuxRestoreContext() {
|
||||
LOG(INFO) << "Running restorecon...";
|
||||
selinux_android_restorecon("/dev", 0);
|
||||
selinux_android_restorecon("/dev/kmsg", 0);
|
||||
if constexpr (WORLD_WRITABLE_KMSG) {
|
||||
selinux_android_restorecon("/dev/kmsg_debug", 0);
|
||||
}
|
||||
selinux_android_restorecon("/dev/socket", 0);
|
||||
selinux_android_restorecon("/dev/random", 0);
|
||||
selinux_android_restorecon("/dev/urandom", 0);
|
||||
selinux_android_restorecon("/dev/__properties__", 0);
|
||||
|
||||
selinux_android_restorecon("/file_contexts.bin", 0);
|
||||
selinux_android_restorecon("/plat_file_contexts", 0);
|
||||
selinux_android_restorecon("/nonplat_file_contexts", 0);
|
||||
selinux_android_restorecon("/plat_property_contexts", 0);
|
||||
selinux_android_restorecon("/nonplat_property_contexts", 0);
|
||||
selinux_android_restorecon("/plat_seapp_contexts", 0);
|
||||
selinux_android_restorecon("/nonplat_seapp_contexts", 0);
|
||||
selinux_android_restorecon("/plat_service_contexts", 0);
|
||||
selinux_android_restorecon("/nonplat_service_contexts", 0);
|
||||
selinux_android_restorecon("/plat_hwservice_contexts", 0);
|
||||
selinux_android_restorecon("/nonplat_hwservice_contexts", 0);
|
||||
selinux_android_restorecon("/sepolicy", 0);
|
||||
selinux_android_restorecon("/vndservice_contexts", 0);
|
||||
|
||||
selinux_android_restorecon("/dev/block", SELINUX_ANDROID_RESTORECON_RECURSE);
|
||||
selinux_android_restorecon("/dev/device-mapper", 0);
|
||||
|
||||
selinux_android_restorecon("/sbin/mke2fs_static", 0);
|
||||
selinux_android_restorecon("/sbin/e2fsdroid_static", 0);
|
||||
}
|
||||
|
||||
// This function sets up SELinux logging to be written to kmsg, to match init's logging.
|
||||
void SelinuxSetupKernelLogging() {
|
||||
selinux_callback cb;
|
||||
cb.func_log = selinux_klog_callback;
|
||||
selinux_set_callback(SELINUX_CB_LOG, cb);
|
||||
}
|
||||
|
||||
// selinux_android_file_context_handle() takes on the order of 10+ms to run, so we want to cache
|
||||
// its value. selinux_android_restorecon() also needs an sehandle for file context look up. It
|
||||
// will create and store its own copy, but selinux_android_set_sehandle() can be used to provide
|
||||
// one, thus eliminating an extra call to selinux_android_file_context_handle().
|
||||
void SelabelInitialize() {
|
||||
sehandle = selinux_android_file_context_handle();
|
||||
selinux_android_set_sehandle(sehandle);
|
||||
}
|
||||
|
||||
// A C++ wrapper around selabel_lookup() using the cached sehandle.
|
||||
// If sehandle is null, this returns success with an empty context.
|
||||
bool SelabelLookupFileContext(const std::string& key, int type, std::string* result) {
|
||||
result->clear();
|
||||
|
||||
if (!sehandle) return true;
|
||||
|
||||
char* context;
|
||||
if (selabel_lookup(sehandle, &context, key.c_str(), type) != 0) {
|
||||
return false;
|
||||
}
|
||||
*result = context;
|
||||
free(context);
|
||||
return true;
|
||||
}
|
||||
|
||||
// A C++ wrapper around selabel_lookup_best_match() using the cached sehandle.
|
||||
// If sehandle is null, this returns success with an empty context.
|
||||
bool SelabelLookupFileContextBestMatch(const std::string& key,
|
||||
const std::vector<std::string>& aliases, int type,
|
||||
std::string* result) {
|
||||
result->clear();
|
||||
|
||||
if (!sehandle) return true;
|
||||
|
||||
std::vector<const char*> c_aliases;
|
||||
for (const auto& alias : aliases) {
|
||||
c_aliases.emplace_back(alias.c_str());
|
||||
}
|
||||
c_aliases.emplace_back(nullptr);
|
||||
|
||||
char* context;
|
||||
if (selabel_lookup_best_match(sehandle, &context, key.c_str(), &c_aliases[0], type) != 0) {
|
||||
return false;
|
||||
}
|
||||
*result = context;
|
||||
free(context);
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace init
|
||||
} // namespace android
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* 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_SELINUX_H
|
||||
#define _INIT_SELINUX_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace android {
|
||||
namespace init {
|
||||
|
||||
void SelinuxInitialize();
|
||||
void SelinuxRestoreContext();
|
||||
|
||||
void SelinuxSetupKernelLogging();
|
||||
|
||||
void SelabelInitialize();
|
||||
bool SelabelLookupFileContext(const std::string& key, int type, std::string* result);
|
||||
bool SelabelLookupFileContextBestMatch(const std::string& key,
|
||||
const std::vector<std::string>& aliases, int type,
|
||||
std::string* result);
|
||||
|
||||
} // namespace init
|
||||
} // namespace android
|
||||
|
||||
#endif
|
|
@ -36,6 +36,7 @@
|
|||
#include "devices.h"
|
||||
#include "firmware_handler.h"
|
||||
#include "log.h"
|
||||
#include "selinux.h"
|
||||
#include "uevent_listener.h"
|
||||
#include "ueventd_parser.h"
|
||||
#include "util.h"
|
||||
|
@ -257,9 +258,8 @@ int ueventd_main(int argc, char** argv) {
|
|||
|
||||
LOG(INFO) << "ueventd started!";
|
||||
|
||||
selinux_callback cb;
|
||||
cb.func_log = selinux_klog_callback;
|
||||
selinux_set_callback(SELINUX_CB_LOG, cb);
|
||||
SelinuxSetupKernelLogging();
|
||||
SelabelInitialize();
|
||||
|
||||
DeviceHandler device_handler = CreateDeviceHandler();
|
||||
UeventListener uevent_listener;
|
||||
|
|
|
@ -42,6 +42,7 @@
|
|||
#include <selinux/android.h>
|
||||
|
||||
#include "reboot.h"
|
||||
#include "selinux.h"
|
||||
|
||||
#ifdef _INIT_INIT_H
|
||||
#error "Do not include init.h in files used by ueventd or watchdogd; it will expose init's globals"
|
||||
|
@ -89,7 +90,7 @@ bool DecodeUid(const std::string& name, uid_t* uid, std::string* err) {
|
|||
* variable ANDROID_SOCKET_ENV_PREFIX<name> ("ANDROID_SOCKET_foo").
|
||||
*/
|
||||
int CreateSocket(const char* name, int type, bool passcred, mode_t perm, uid_t uid, gid_t gid,
|
||||
const char* socketcon, selabel_handle* sehandle) {
|
||||
const char* socketcon) {
|
||||
if (socketcon) {
|
||||
if (setsockcreatecon(socketcon) == -1) {
|
||||
PLOG(ERROR) << "setsockcreatecon(\"" << socketcon << "\") failed";
|
||||
|
@ -116,11 +117,9 @@ int CreateSocket(const char* name, int type, bool passcred, mode_t perm, uid_t u
|
|||
return -1;
|
||||
}
|
||||
|
||||
char *filecon = NULL;
|
||||
if (sehandle) {
|
||||
if (selabel_lookup(sehandle, &filecon, addr.sun_path, S_IFSOCK) == 0) {
|
||||
setfscreatecon(filecon);
|
||||
}
|
||||
std::string secontext;
|
||||
if (SelabelLookupFileContext(addr.sun_path, S_IFSOCK, &secontext) && !secontext.empty()) {
|
||||
setfscreatecon(secontext.c_str());
|
||||
}
|
||||
|
||||
if (passcred) {
|
||||
|
@ -134,8 +133,9 @@ int CreateSocket(const char* name, int type, bool passcred, mode_t perm, uid_t u
|
|||
int ret = bind(fd, (struct sockaddr *) &addr, sizeof (addr));
|
||||
int savederrno = errno;
|
||||
|
||||
setfscreatecon(NULL);
|
||||
freecon(filecon);
|
||||
if (!secontext.empty()) {
|
||||
setfscreatecon(nullptr);
|
||||
}
|
||||
|
||||
if (ret) {
|
||||
errno = savederrno;
|
||||
|
@ -210,19 +210,19 @@ bool WriteFile(const std::string& path, const std::string& content, std::string*
|
|||
return true;
|
||||
}
|
||||
|
||||
int mkdir_recursive(const std::string& path, mode_t mode, selabel_handle* sehandle) {
|
||||
bool mkdir_recursive(const std::string& path, mode_t mode) {
|
||||
std::string::size_type slash = 0;
|
||||
while ((slash = path.find('/', slash + 1)) != std::string::npos) {
|
||||
auto directory = path.substr(0, slash);
|
||||
struct stat info;
|
||||
if (stat(directory.c_str(), &info) != 0) {
|
||||
auto ret = make_dir(directory.c_str(), mode, sehandle);
|
||||
if (ret && errno != EEXIST) return ret;
|
||||
auto ret = make_dir(directory, mode);
|
||||
if (!ret && errno != EEXIST) return false;
|
||||
}
|
||||
}
|
||||
auto ret = make_dir(path.c_str(), mode, sehandle);
|
||||
if (ret && errno != EEXIST) return ret;
|
||||
return 0;
|
||||
auto ret = make_dir(path, mode);
|
||||
if (!ret && errno != EEXIST) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
int wait_for_file(const char* filename, std::chrono::nanoseconds timeout) {
|
||||
|
@ -249,26 +249,21 @@ void import_kernel_cmdline(bool in_qemu,
|
|||
}
|
||||
}
|
||||
|
||||
int make_dir(const char* path, mode_t mode, selabel_handle* sehandle) {
|
||||
int rc;
|
||||
|
||||
char *secontext = NULL;
|
||||
|
||||
if (sehandle) {
|
||||
selabel_lookup(sehandle, &secontext, path, mode);
|
||||
setfscreatecon(secontext);
|
||||
bool make_dir(const std::string& path, mode_t mode) {
|
||||
std::string secontext;
|
||||
if (SelabelLookupFileContext(path, mode, &secontext) && !secontext.empty()) {
|
||||
setfscreatecon(secontext.c_str());
|
||||
}
|
||||
|
||||
rc = mkdir(path, mode);
|
||||
int rc = mkdir(path.c_str(), mode);
|
||||
|
||||
if (secontext) {
|
||||
if (!secontext.empty()) {
|
||||
int save_errno = errno;
|
||||
freecon(secontext);
|
||||
setfscreatecon(NULL);
|
||||
setfscreatecon(nullptr);
|
||||
errno = save_errno;
|
||||
}
|
||||
|
||||
return rc;
|
||||
return rc == 0;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -37,18 +37,18 @@ namespace android {
|
|||
namespace init {
|
||||
|
||||
int CreateSocket(const char* name, int type, bool passcred, mode_t perm, uid_t uid, gid_t gid,
|
||||
const char* socketcon, selabel_handle* sehandle);
|
||||
const char* socketcon);
|
||||
|
||||
bool ReadFile(const std::string& path, std::string* content, std::string* err);
|
||||
bool WriteFile(const std::string& path, const std::string& content, std::string* err);
|
||||
|
||||
bool DecodeUid(const std::string& name, uid_t* uid, std::string* err);
|
||||
|
||||
int mkdir_recursive(const std::string& pathname, mode_t mode, selabel_handle* sehandle);
|
||||
bool mkdir_recursive(const std::string& pathname, mode_t mode);
|
||||
int wait_for_file(const char *filename, std::chrono::nanoseconds timeout);
|
||||
void import_kernel_cmdline(bool in_qemu,
|
||||
const std::function<void(const std::string&, const std::string&, bool)>&);
|
||||
int make_dir(const char* path, mode_t mode, selabel_handle* sehandle);
|
||||
bool make_dir(const std::string& path, mode_t mode);
|
||||
std::string bytes_to_hex(const uint8_t *bytes, size_t bytes_len);
|
||||
bool is_dir(const char* pathname);
|
||||
bool expand_props(const std::string& src, std::string* dst);
|
||||
|
|
|
@ -170,7 +170,7 @@ TEST(util, is_dir) {
|
|||
TEST(util, mkdir_recursive) {
|
||||
TemporaryDir test_dir;
|
||||
std::string path = android::base::StringPrintf("%s/three/directories/deep", test_dir.path);
|
||||
EXPECT_EQ(0, mkdir_recursive(path, 0755, nullptr));
|
||||
EXPECT_TRUE(mkdir_recursive(path, 0755));
|
||||
std::string path1 = android::base::StringPrintf("%s/three", test_dir.path);
|
||||
EXPECT_TRUE(is_dir(path1.c_str()));
|
||||
std::string path2 = android::base::StringPrintf("%s/three/directories", test_dir.path);
|
||||
|
@ -182,7 +182,7 @@ TEST(util, mkdir_recursive) {
|
|||
TEST(util, mkdir_recursive_extra_slashes) {
|
||||
TemporaryDir test_dir;
|
||||
std::string path = android::base::StringPrintf("%s/three////directories/deep//", test_dir.path);
|
||||
EXPECT_EQ(0, mkdir_recursive(path, 0755, nullptr));
|
||||
EXPECT_TRUE(mkdir_recursive(path, 0755));
|
||||
std::string path1 = android::base::StringPrintf("%s/three", test_dir.path);
|
||||
EXPECT_TRUE(is_dir(path1.c_str()));
|
||||
std::string path2 = android::base::StringPrintf("%s/three/directories", test_dir.path);
|
||||
|
|
Loading…
Reference in New Issue