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/adb/remount_service.c b/adb/remount_service.c
index 4cb41e7..ad61284 100644
--- a/adb/remount_service.c
+++ b/adb/remount_service.c
@@ -72,6 +72,8 @@
static int remount_system()
{
char *dev;
+ int fd;
+ int OFF = 0;
if (system_ro == 0) {
return 0;
@@ -82,6 +84,13 @@
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 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;
diff --git a/toolbox/mount.c b/toolbox/mount.c
index b7adce2..164efcd 100644
--- a/toolbox/mount.c
+++ b/toolbox/mount.c
@@ -137,6 +137,24 @@
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 @@
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) {