From 99c4a8a6b3852c33828b03cbd0aef0c625957a39 Mon Sep 17 00:00:00 2001 From: Hung-ying Tyan Date: Mon, 1 Feb 2016 15:07:40 +0800 Subject: [PATCH] Mount /vendor and /odm early Right now these two partitions are mounted in the fs stage of the init process. As a result, many vendor/ODM files needed earlier in the boot process (e.g., init..rc, fstab..rc, uevent..rc, SELinux policy files etc) can only live on the root partition. To prevent vendors/ODMs from polluting the root partition, this patch makes it possible to mount the vendor and ODM partitions in the first stage of the init process. The fstab info of both partitions to be mounted early is composed from new kernel cmdline arguments android.early.prefix and android.early.fstab. For example, with: android.early.prefix=/sys/devices/1010000.msdc0/mmc_host/mmc0/mmc0:0001/block/mmcblk0/ android.early.fstab=mmcblk0p10+/odm+ext4+ro+verify\nmmcblk0p09+/vendor+ext4+ro+verify the final fstab string will be: /sys/devices/1010000.msdc0/mmc_host/mmc0/mmc0:0001/block/mmcblk0/mmcblk0p10 /odm ext4 ro verify /sys/devices/1010000.msdc0/mmc_host/mmc0/mmc0:0001/block/mmcblk0/mmcblk0p09 /vendor ext4 ro verify The android.early.prefix is optional. When it is missing, the final fstab string will be directly converted from android.early.fstab. This patch also makes sure that the early mounted partitions are dm-verity enabled so that they are trust worthy to store system files. BUG=27805372 Change-Id: I3cf32482a5ec65445ba3aedab2164c7ba8f12694 --- fs_mgr/fs_mgr.c | 19 +++++++ fs_mgr/fs_mgr_fstab.c | 30 +++++++---- fs_mgr/fs_mgr_verity.cpp | 16 ------ fs_mgr/include/fs_mgr.h | 7 +++ init/devices.cpp | 105 ++++++++++++++++++++++++++++++------- init/devices.h | 7 +++ init/init.cpp | 108 +++++++++++++++++++++++++++++++++++++-- 7 files changed, 244 insertions(+), 48 deletions(-) diff --git a/fs_mgr/fs_mgr.c b/fs_mgr/fs_mgr.c index 1b3893f51..4a8568934 100644 --- a/fs_mgr/fs_mgr.c +++ b/fs_mgr/fs_mgr.c @@ -905,3 +905,22 @@ int fs_mgr_get_crypt_info(struct fstab *fstab, char *key_loc, char *real_blk_dev return 0; } + +int fs_mgr_early_setup_verity(struct fstab_rec *fstab_rec) +{ + if ((fstab_rec->fs_mgr_flags & MF_VERIFY) && device_is_secure()) { + int rc = fs_mgr_setup_verity(fstab_rec); + if (device_is_debuggable() && rc == FS_MGR_SETUP_VERITY_DISABLED) { + INFO("Verity disabled"); + return FS_MGR_EARLY_SETUP_VERITY_NO_VERITY; + } else if (rc == FS_MGR_SETUP_VERITY_SUCCESS) { + return FS_MGR_EARLY_SETUP_VERITY_SUCCESS; + } else { + return FS_MGR_EARLY_SETUP_VERITY_FAIL; + } + } else if (device_is_secure()) { + ERROR("Verity must be enabled for early mounted partitions on secured devices.\n"); + return FS_MGR_EARLY_SETUP_VERITY_FAIL; + } + return FS_MGR_EARLY_SETUP_VERITY_NO_VERITY; +} diff --git a/fs_mgr/fs_mgr_fstab.c b/fs_mgr/fs_mgr_fstab.c index 5b92db710..b30235467 100644 --- a/fs_mgr/fs_mgr_fstab.c +++ b/fs_mgr/fs_mgr_fstab.c @@ -210,9 +210,8 @@ static int parse_flags(char *flags, struct flag_list *fl, return f; } -struct fstab *fs_mgr_read_fstab(const char *fstab_path) +struct fstab *fs_mgr_read_fstab_file(FILE *fstab_file) { - FILE *fstab_file; int cnt, entries; ssize_t len; size_t alloc_len = 0; @@ -224,12 +223,6 @@ struct fstab *fs_mgr_read_fstab(const char *fstab_path) #define FS_OPTIONS_LEN 1024 char tmp_fs_options[FS_OPTIONS_LEN]; - fstab_file = fopen(fstab_path, "r"); - if (!fstab_file) { - ERROR("Cannot open file %s\n", fstab_path); - return 0; - } - entries = 0; while ((len = getline(&line, &alloc_len, fstab_file)) != -1) { /* if the last character is a newline, shorten the string by 1 byte */ @@ -255,7 +248,6 @@ struct fstab *fs_mgr_read_fstab(const char *fstab_path) /* Allocate and init the fstab structure */ fstab = calloc(1, sizeof(struct fstab)); fstab->num_entries = entries; - fstab->fstab_filename = strdup(fstab_path); fstab->recs = calloc(fstab->num_entries, sizeof(struct fstab_rec)); fseek(fstab_file, 0, SEEK_SET); @@ -338,18 +330,34 @@ struct fstab *fs_mgr_read_fstab(const char *fstab_path) ERROR("Error updating for slotselect\n"); goto err; } - fclose(fstab_file); free(line); return fstab; err: - fclose(fstab_file); free(line); if (fstab) fs_mgr_free_fstab(fstab); return NULL; } +struct fstab *fs_mgr_read_fstab(const char *fstab_path) +{ + FILE *fstab_file; + struct fstab *fstab; + + fstab_file = fopen(fstab_path, "r"); + if (!fstab_file) { + ERROR("Cannot open file %s\n", fstab_path); + return NULL; + } + fstab = fs_mgr_read_fstab_file(fstab_file); + if (fstab) { + fstab->fstab_filename = strdup(fstab_path); + } + fclose(fstab_file); + return fstab; +} + void fs_mgr_free_fstab(struct fstab *fstab) { int i; diff --git a/fs_mgr/fs_mgr_verity.cpp b/fs_mgr/fs_mgr_verity.cpp index 67104cc3a..767b3b3e8 100644 --- a/fs_mgr/fs_mgr_verity.cpp +++ b/fs_mgr/fs_mgr_verity.cpp @@ -341,17 +341,6 @@ static int resume_verity_table(struct dm_ioctl *io, char *name, int fd) return 0; } -static int test_access(char *device) { - int tries = 25; - while (tries--) { - if (!access(device, F_OK) || errno != ENOENT) { - return 0; - } - usleep(40 * 1000); - } - return -1; -} - static int check_verity_restart(const char *fname) { char buffer[VERITY_KMSG_BUFSIZE + 1]; @@ -1031,11 +1020,6 @@ loaded: fstab->blk_device = verity_blk_name; verity_blk_name = 0; - // make sure we've set everything up properly - if (test_access(fstab->blk_device) < 0) { - goto out; - } - retval = FS_MGR_SETUP_VERITY_SUCCESS; out: diff --git a/fs_mgr/include/fs_mgr.h b/fs_mgr/include/fs_mgr.h index 3c27edefb..17d02770e 100644 --- a/fs_mgr/include/fs_mgr.h +++ b/fs_mgr/include/fs_mgr.h @@ -17,6 +17,7 @@ #ifndef __CORE_FS_MGR_H #define __CORE_FS_MGR_H +#include #include #include #include @@ -72,6 +73,7 @@ struct fstab_rec { typedef void (*fs_mgr_verity_state_callback)(struct fstab_rec *fstab, const char *mount_point, int mode, int status); +struct fstab *fs_mgr_read_fstab_file(FILE *fstab_file); struct fstab *fs_mgr_read_fstab(const char *fstab_path); void fs_mgr_free_fstab(struct fstab *fstab); @@ -111,6 +113,11 @@ int fs_mgr_swapon_all(struct fstab *fstab); int fs_mgr_do_format(struct fstab_rec *fstab, bool reserve_footer); +#define FS_MGR_EARLY_SETUP_VERITY_NO_VERITY -2 +#define FS_MGR_EARLY_SETUP_VERITY_FAIL -1 +#define FS_MGR_EARLY_SETUP_VERITY_SUCCESS 0 +int fs_mgr_early_setup_verity(struct fstab_rec *fstab); + #ifdef __cplusplus } #endif diff --git a/init/devices.cpp b/init/devices.cpp index 373177efd..830b74cb2 100644 --- a/init/devices.cpp +++ b/init/devices.cpp @@ -44,6 +44,7 @@ #include #include +#include #include #include @@ -601,14 +602,17 @@ static const char *parse_device_name(struct uevent *uevent, unsigned int len) return name; } +#define DEVPATH_LEN 96 +#define MAX_DEV_NAME 64 + static void handle_block_device_event(struct uevent *uevent) { const char *base = "/dev/block/"; const char *name; - char devpath[96]; + char devpath[DEVPATH_LEN]; char **links = NULL; - name = parse_device_name(uevent, 64); + name = parse_device_name(uevent, MAX_DEV_NAME); if (!name) return; @@ -622,8 +626,6 @@ static void handle_block_device_event(struct uevent *uevent) uevent->major, uevent->minor, links); } -#define DEVPATH_LEN 96 - static bool assemble_devpath(char *devpath, const char *dirname, const char *devname) { @@ -657,7 +659,7 @@ static void handle_generic_device_event(struct uevent *uevent) char devpath[DEVPATH_LEN] = {0}; char **links = NULL; - name = parse_device_name(uevent, 64); + name = parse_device_name(uevent, MAX_DEV_NAME); if (!name) return; @@ -900,7 +902,8 @@ static void handle_firmware_event(struct uevent *uevent) } #define UEVENT_MSG_LEN 2048 -void handle_device_fd() + +static inline void handle_device_fd_with(void (handle_uevent)(struct uevent*)) { char msg[UEVENT_MSG_LEN+2]; int n; @@ -913,21 +916,28 @@ void handle_device_fd() struct uevent uevent; parse_event(msg, &uevent); - - if (selinux_status_updated() > 0) { - struct selabel_handle *sehandle2; - sehandle2 = selinux_android_file_context_handle(); - if (sehandle2) { - selabel_close(sehandle); - sehandle = sehandle2; - } - } - - handle_device_event(&uevent); - handle_firmware_event(&uevent); + handle_uevent(&uevent); } } +void handle_device_fd() +{ + handle_device_fd_with( + [](struct uevent *uevent) { + if (selinux_status_updated() > 0) { + struct selabel_handle *sehandle2; + sehandle2 = selinux_android_file_context_handle(); + if (sehandle2) { + selabel_close(sehandle); + sehandle = sehandle2; + } + } + + handle_device_event(uevent); + handle_firmware_event(uevent); + }); +} + /* Coldboot walks parts of the /sys tree and pokes the uevent files ** to cause the kernel to regenerate device add events that happened ** before init's device manager was started @@ -979,6 +989,65 @@ static void coldboot(const char *path) } } +static void early_uevent_handler(struct uevent *uevent, const char *base, bool is_block) +{ + const char *name; + char devpath[DEVPATH_LEN]; + + if (is_block && strncmp(uevent->subsystem, "block", 5)) + return; + + name = parse_device_name(uevent, MAX_DEV_NAME); + if (!name) { + LOG(ERROR) << "Failed to parse dev name from uevent: " << uevent->action + << " " << uevent->partition_name << " " << uevent->partition_num + << " " << uevent->major << ":" << uevent->minor; + return; + } + + snprintf(devpath, sizeof(devpath), "%s%s", base, name); + make_dir(base, 0755); + + dev_t dev = makedev(uevent->major, uevent->minor); + mode_t mode = 0600 | (is_block ? S_IFBLK : S_IFCHR); + mknod(devpath, mode, dev); +} + +void early_create_dev(const std::string& syspath, early_device_type dev_type) +{ + android::base::unique_fd dfd(open(syspath.c_str(), O_RDONLY)); + if (dfd < 0) { + LOG(ERROR) << "Failed to open " << syspath; + return; + } + + android::base::unique_fd fd(openat(dfd, "uevent", O_WRONLY)); + if (fd < 0) { + LOG(ERROR) << "Failed to open " << syspath << "/uevent"; + return; + } + + fcntl(device_fd, F_SETFL, O_NONBLOCK); + + write(fd, "add\n", 4); + handle_device_fd_with(dev_type == EARLY_BLOCK_DEV ? + [](struct uevent *uevent) { + early_uevent_handler(uevent, "/dev/block/", true); + } : + [](struct uevent *uevent) { + early_uevent_handler(uevent, "/dev/", false); + }); +} + +int early_device_socket_open() { + device_fd = uevent_open_socket(256*1024, true); + return device_fd < 0; +} + +void early_device_socket_close() { + close(device_fd); +} + void device_init() { sehandle = selinux_android_file_context_handle(); selinux_status_open(true); diff --git a/init/devices.h b/init/devices.h index 6cb0a77c4..8e9ab7d2e 100644 --- a/init/devices.h +++ b/init/devices.h @@ -21,6 +21,13 @@ extern void handle_device_fd(); extern void device_init(void); + +enum early_device_type { EARLY_BLOCK_DEV, EARLY_CHAR_DEV }; + +extern int early_device_socket_open(); +extern void early_device_socket_close(); +extern void early_create_dev(const std::string& syspath, early_device_type dev_type); + extern int add_dev_perms(const char *name, const char *attr, mode_t perm, unsigned int uid, unsigned int gid, unsigned short prefix, diff --git a/init/init.cpp b/init/init.cpp index 78d71a818..957527bd9 100644 --- a/init/init.cpp +++ b/init/init.cpp @@ -54,6 +54,7 @@ #include "action.h" #include "bootchart.h" #include "devices.h" +#include "fs_mgr.h" #include "import_parser.h" #include "init.h" #include "init_parser.h" @@ -342,9 +343,6 @@ static void process_kernel_dt() { } static void process_kernel_cmdline() { - // Don't expose the raw commandline to unprivileged processes. - chmod("/proc/cmdline", 0440); - // The first pass does the common stuff, and finds if we are in qemu. // The second pass is only necessary for qemu to export all kernel params // as properties. @@ -461,6 +459,104 @@ static void set_usb_controller() { } } +/* Returns a new path consisting of base_path and the file name in reference_path. */ +static std::string get_path(const std::string& base_path, const std::string& reference_path) { + std::string::size_type pos = reference_path.rfind('/'); + if (pos == std::string::npos) { + return base_path + '/' + reference_path; + } else { + return base_path + reference_path.substr(pos); + } +} + +/* Imports the fstab info from cmdline. */ +static std::string import_cmdline_fstab() { + std::string prefix, fstab, fstab_full; + + import_kernel_cmdline(false, + [&](const std::string& key, const std::string& value, bool in_qemu __attribute__((__unused__))) { + if (key == "android.early.prefix") { + prefix = value; + } else if (key == "android.early.fstab") { + fstab = value; + } + }); + if (!fstab.empty()) { + // Convert "mmcblk0p09+/odm+ext4+ro+verify" to "mmcblk0p09 /odm ext4 ro verify" + std::replace(fstab.begin(), fstab.end(), '+', ' '); + for (const auto& entry : android::base::Split(fstab, "\n")) { + fstab_full += prefix + entry + '\n'; + } + } + return fstab_full; +} + +/* Early mount vendor and ODM partitions. The fstab info is read from kernel cmdline. */ +static void early_mount() { + std::string fstab_string = import_cmdline_fstab(); + if (fstab_string.empty()) { + LOG(INFO) << "Failed to load vendor fstab from kernel cmdline"; + return; + } + FILE *fstab_file = fmemopen((void *)fstab_string.c_str(), fstab_string.length(), "r"); + if (!fstab_file) { + PLOG(ERROR) << "Failed to open fstab string as FILE"; + return; + } + std::unique_ptr fstab(fs_mgr_read_fstab_file(fstab_file), fs_mgr_free_fstab); + fclose(fstab_file); + if (!fstab) { + LOG(ERROR) << "Failed to parse fstab string: " << fstab_string; + return; + } + LOG(INFO) << "Loaded vendor fstab from cmdline"; + + if (early_device_socket_open()) { + LOG(ERROR) << "Failed to open device uevent socket"; + return; + } + + /* Create /dev/device-mapper for dm-verity */ + early_create_dev("/sys/devices/virtual/misc/device-mapper", EARLY_CHAR_DEV); + + for (int i = 0; i < fstab->num_entries; ++i) { + struct fstab_rec *rec = &fstab->recs[i]; + std::string mount_point = rec->mount_point; + std::string syspath = rec->blk_device; + + if (mount_point != "/vendor" && mount_point != "/odm") + continue; + + /* Create mount target under /dev/block/ from sysfs via uevent */ + LOG(INFO) << "Mounting " << mount_point << " from " << syspath << "..."; + char *devpath = strdup(get_path("/dev/block", syspath).c_str()); + if (!devpath) { + PLOG(ERROR) << "Failed to strdup dev path in early mount " << syspath; + continue; + } + rec->blk_device = devpath; + early_create_dev(syspath, EARLY_BLOCK_DEV); + + int rc = fs_mgr_early_setup_verity(rec); + if (rc == FS_MGR_EARLY_SETUP_VERITY_SUCCESS) { + /* Mount target is changed to /dev/block/dm-; initiate its creation from sysfs counterpart */ + early_create_dev(get_path("/sys/devices/virtual/block", rec->blk_device), EARLY_BLOCK_DEV); + } else if (rc == FS_MGR_EARLY_SETUP_VERITY_FAIL) { + LOG(ERROR) << "Failed to set up dm-verity on " << rec->blk_device; + continue; + } else { /* FS_MGR_EARLY_SETUP_VERITY_NO_VERITY */ + LOG(INFO) << "dm-verity disabled on debuggable device; mount directly on " << rec->blk_device; + } + + mkdir(mount_point.c_str(), 0755); + rc = mount(rec->blk_device, mount_point.c_str(), rec->fs_type, rec->flags, rec->fs_options); + if (rc) { + PLOG(ERROR) << "Failed to mount on " << rec->blk_device; + } + } + early_device_socket_close(); +} + int main(int argc, char** argv) { if (!strcmp(basename(argv[0]), "ueventd")) { return ueventd_main(argc, argv); @@ -477,6 +573,9 @@ int main(int argc, char** argv) { bool is_first_stage = (argc == 1) || (strcmp(argv[1], "--second-stage") != 0); + // Don't expose the raw commandline to unprivileged processes. + chmod("/proc/cmdline", 0440); + // Get the basic filesystem setup we need put together in the initramdisk // on / and then we'll let the rc file figure out the rest. if (is_first_stage) { @@ -489,6 +588,7 @@ int main(int argc, char** argv) { mount("sysfs", "/sys", "sysfs", 0, NULL); mount("selinuxfs", "/sys/fs/selinux", "selinuxfs", 0, NULL); mknod("/dev/kmsg", S_IFCHR | 0600, makedev(1, 11)); + early_mount(); } // Now that tmpfs is mounted on /dev and we have /dev/kmsg, we can actually @@ -541,6 +641,8 @@ int main(int argc, char** argv) { restorecon("/dev/__properties__"); restorecon("/property_contexts"); restorecon_recursive("/sys"); + restorecon_recursive("/dev/block"); + restorecon("/dev/device-mapper"); epoll_fd = epoll_create1(EPOLL_CLOEXEC); if (epoll_fd == -1) {