From e18c0d508a6d8b4376c6f0b8c22600e5aca37f69 Mon Sep 17 00:00:00 2001 From: Nick Kralevich Date: Tue, 16 Apr 2013 16:41:32 -0700 Subject: [PATCH] fs_mgr: make block devices read-only When a filesystem is mounted read-only, make the underlying block device read-only too. This helps prevent an attacker who is able to change permissions on the files in /dev (for example, symlink attack) from modifying the block device. In particular, this change would have stopped the LG Thrill / Optimus 3D rooting exploit (http://vulnfactory.org/blog/2012/02/26/rooting-the-lg-thrill-optimus-3d/) as that exploit modified the raw block device corresponding to /system. This change also makes UID=0 less powerful. Block devices cannot be made writable again without CAP_SYS_ADMIN, so an escalation to UID=0 by itself doesn't give full root access. adb/mount: Prior to mounting something read-write, remove the read-only restrictions on the underlying block device. This avoids messing up developer workflows. Change-Id: I135098a8fe06f327336f045aab0d48ed9de33807 --- adb/remount_service.c | 9 +++++++++ fs_mgr/fs_mgr.c | 47 ++++++++++++++++++++++++++++++++++++++----- toolbox/mount.c | 22 ++++++++++++++++++++ 3 files changed, 73 insertions(+), 5 deletions(-) diff --git a/adb/remount_service.c b/adb/remount_service.c index 4cb41e7d1..ad61284b6 100644 --- a/adb/remount_service.c +++ b/adb/remount_service.c @@ -72,6 +72,8 @@ static char *find_mount(const char *dir) static int remount_system() { char *dev; + int fd; + int OFF = 0; if (system_ro == 0) { return 0; @@ -82,6 +84,13 @@ static int remount_system() if (!dev) return -1; + fd = unix_open(dev, O_RDONLY); + if (fd < 0) + return -1; + + ioctl(fd, BLKROSET, &OFF); + adb_close(fd); + system_ro = mount(dev, "/system", "none", MS_REMOUNT, NULL); free(dev); diff --git a/fs_mgr/fs_mgr.c b/fs_mgr/fs_mgr.c index fecc556a3..08483426f 100644 --- a/fs_mgr/fs_mgr.c +++ b/fs_mgr/fs_mgr.c @@ -487,6 +487,43 @@ static void remove_trailing_slashes(char *n) } } +/* + * Mark the given block device as read-only, using the BLKROSET ioctl. + * Return 0 on success, and -1 on error. + */ +static void fs_set_blk_ro(const char *blockdev) +{ + int fd; + int ON = 1; + + fd = open(blockdev, O_RDONLY); + if (fd < 0) { + // should never happen + return; + } + + ioctl(fd, BLKROSET, &ON); + close(fd); +} + +/* + * __mount(): wrapper around the mount() system call which also + * sets the underlying block device to read-only if the mount is read-only. + * See "man 2 mount" for return values. + */ +static int __mount(const char *source, const char *target, + const char *filesystemtype, unsigned long mountflags, + const void *data) +{ + int ret = mount(source, target, filesystemtype, mountflags, data); + + if ((ret == 0) && (mountflags & MS_RDONLY) != 0) { + fs_set_blk_ro(source); + } + + return ret; +} + static int fs_match(char *in1, char *in2) { char *n1; @@ -539,9 +576,9 @@ int fs_mgr_mount_all(struct fstab *fstab) fstab->recs[i].mount_point); } - mret = mount(fstab->recs[i].blk_device, fstab->recs[i].mount_point, - fstab->recs[i].fs_type, fstab->recs[i].flags, - fstab->recs[i].fs_options); + mret = __mount(fstab->recs[i].blk_device, fstab->recs[i].mount_point, + fstab->recs[i].fs_type, fstab->recs[i].flags, + fstab->recs[i].fs_options); if (!mret) { /* Success! Go get the next one */ continue; @@ -621,8 +658,8 @@ int fs_mgr_do_mount(struct fstab *fstab, char *n_name, char *n_blk_device, } else { m = fstab->recs[i].mount_point; } - if (mount(n_blk_device, m, fstab->recs[i].fs_type, - fstab->recs[i].flags, fstab->recs[i].fs_options)) { + if (__mount(n_blk_device, m, fstab->recs[i].fs_type, + fstab->recs[i].flags, fstab->recs[i].fs_options)) { ERROR("Cannot mount filesystem on %s at %s\n", n_blk_device, m); goto out; diff --git a/toolbox/mount.c b/toolbox/mount.c index b7adce2d9..164efcd1a 100644 --- a/toolbox/mount.c +++ b/toolbox/mount.c @@ -137,6 +137,24 @@ parse_mount_options(char *arg, unsigned long rwflag, struct extra_opts *extra, i return rwflag; } +/* + * Mark the given block device as read-write, using the BLKROSET ioctl. + */ +static void fs_set_blk_rw(const char *blockdev) +{ + int fd; + int OFF = 0; + + fd = open(blockdev, O_RDONLY); + if (fd < 0) { + // should never happen + return; + } + + ioctl(fd, BLKROSET, &OFF); + close(fd); +} + static char *progname; static struct extra_opts extra; @@ -178,6 +196,10 @@ do_mount(char *dev, char *dir, char *type, unsigned long rwflag, void *data, int dev = loopdev; } + if ((rwflag & MS_RDONLY) == 0) { + fs_set_blk_rw(dev); + } + while ((s = strsep(&type, ",")) != NULL) { retry: if (mount(dev, dir, s, rwflag, data) == -1) {