diff --git a/fs_mgr/fs_mgr.c b/fs_mgr/fs_mgr.c index ab525126e..46d147ab9 100644 --- a/fs_mgr/fs_mgr.c +++ b/fs_mgr/fs_mgr.c @@ -898,3 +898,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 6d44e064d..791ddd217 100644 --- a/fs_mgr/fs_mgr_fstab.c +++ b/fs_mgr/fs_mgr_fstab.c @@ -216,9 +216,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; @@ -230,12 +229,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 */ @@ -261,7 +254,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); @@ -344,18 +336,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 384701139..36918fada 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); @@ -112,6 +114,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) {