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
diff --git a/fs_mgr/fs_mgr.c b/fs_mgr/fs_mgr.c
index fecc556..0848342 100644
--- a/fs_mgr/fs_mgr.c
+++ b/fs_mgr/fs_mgr.c
@@ -487,6 +487,43 @@
}
}
+/*
+ * 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 @@
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 @@
} 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;