xorl %eax, %eax

GRKERNSEC_ROFS Runtime Read-only Mount Protection and GRKERNSEC_SYSCTL

leave a comment »

This protection is designed for secure embedded systems as we can read in its description:

config GRKERNSEC_ROFS
	bool "Runtime read-only mount protection"
	help
	  If you say Y here, a sysctl option with name "romount_protect" will
	  be created.  By setting this option to 1 at runtime, filesystems
	  will be protected in the following ways:
	  * No new writable mounts will be allowed
	  * Existing read-only mounts won't be able to be remounted read/write
	  * Write operations will be denied on all block devices
	  This option acts independently of grsec_lock: once it is set to 1,
	  it cannot be turned off.  Therefore, please be mindful of the resulting
	  behavior if this option is enabled in an init script on a read-only
	  filesystem.  This feature is mainly intended for secure embedded systems.

Of course, there might be other uses for it too but this is not the scope of this post. Its code is comprised by two functions located at grsecurity/grsec_mount.c file.

int
gr_handle_rofs_mount(struct dentry *dentry, struct vfsmount *mnt, int mnt_flags)
{
#ifdef CONFIG_GRKERNSEC_ROFS
	if (grsec_enable_rofs && !(mnt_flags & MNT_READONLY)) {
		gr_log_fs_generic(GR_DO_AUDIT, GR_ROFS_MOUNT_MSG, dentry, mnt);
		return -EPERM;
	} else
		return 0;
#endif
	return 0;
}

int
gr_handle_rofs_blockwrite(struct dentry *dentry, struct vfsmount *mnt, int acc_mode)
{
#ifdef CONFIG_GRKERNSEC_ROFS
	if (grsec_enable_rofs && (acc_mode & MAY_WRITE) &&
	    dentry->d_inode && S_ISBLK(dentry->d_inode->i_mode)) {
		gr_log_fs_generic(GR_DO_AUDIT, GR_ROFS_BLOCKWRITE_MSG, dentry, mnt);
		return -EPERM;
	} else
		return 0;
#endif
	return 0;
}

The first one, gr_handle_rofs_mount() is a simple logging routine that will return “Permission Denied” after logging the event if a read-only filesystem is used and obviously, the protection is enabled. The second one is the gr_handle_rofs_blockwrite() that will perform the same operation if the protection is enabled, the access list mode includes ‘MAY_WRITE’ flag, the directory entry’s inode exist and it’s dealing with a block device.
To use this through sysctl interface you can use ‘romount_protect’ entry which is enabled regardless of ‘GRKERNSEC_SYSCTL’ which is taken into account for all the other grsecurity options. Here is its description:

config GRKERNSEC_SYSCTL
	bool "Sysctl support"
	help
	  If you say Y here, you will be able to change the options that
	  grsecurity runs with at bootup, without having to recompile your
	  kernel.  You can echo values to files in /proc/sys/kernel/grsecurity
	  to enable (1) or disable (0) various features.  All the sysctl entries
	  are mutable until the "grsec_lock" entry is set to a non-zero value.
	  All features enabled in the kernel configuration are disabled at boot
	  if you do not say Y to the "Turn on features by default" option.
	  All options should be set at startup, and the grsec_lock entry should
	  be set to a non-zero value after all the options are set.
	  *THIS IS EXTREMELY IMPORTANT*

But if we move to kernel/sysctl.c we’ll see that:

 static struct ctl_table kern_table[] = {
#if defined(CONFIG_GRKERNSEC_SYSCTL) || defined(CONFIG_GRKERNSEC_ROFS)
	{
		.procname	= "grsecurity",
		.mode		= 0500,
		.child		= grsecurity_table,
	},
#endif
   ...
/*
 * NOTE: do not add new entries to this table unless you have read
 * Documentation/sysctl/ctl_unnumbered.txt
 */
        { .ctl_name = 0 }
};

which is defined at grsecurity/grsec_sysctl.c:

#if defined(CONFIG_GRKERNSEC_SYSCTL) || defined(CONFIG_GRKERNSEC_ROFS)
struct ctl_table grsecurity_table[] = {
   ...
#ifdef CONFIG_GRKERNSEC_ROFS
	{
		.procname	= "romount_protect",
		.data		= &grsec_enable_rofs,
		.maxlen		= sizeof(int),
		.mode		= 0600,
		.proc_handler	= &proc_dointvec_minmax,
		.extra1		= &one,
		.extra2		= &one,
	},
#endif
   ...
	{ }
};
#endif

Meaning that if ‘GRKERNSEC_ROFS’ will have its sysctl entry even if ‘GRKERNSEC_SYSCTL’ is disabled. Now, the newly added functions are used firstly at fs/namespace.c where the do_mount() routine is placed.

long do_mount(char *dev_name, char *dir_name, char *type_page,
                  unsigned long flags, void *data_page)
{
        struct path path;
        int retval = 0;
        int mnt_flags = 0;
    ...
        flags &= ~(MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_ACTIVE | MS_BORN |
                   MS_NOATIME | MS_NODIRATIME | MS_RELATIME| MS_KERNMOUNT |
                   MS_STRICTATIME);

	if (gr_handle_rofs_mount(path.dentry, path.mnt, mnt_flags)) {
		retval = -EPERM;
		goto dput_out;
	}
    ...
        return retval;
}

And it will perform the previously described check and possibly disallow access to the given entry. Also, there is a patch applied to fs/namei.c file that will include this code:

static struct file *do_last(struct nameidata *nd, struct path *path,
                            int open_flag, int acc_mode,
                            int mode, const char *pathname)
{
        struct dentry *dir = nd->path.dentry;
        int flag = open_to_namei_flags(open_flag);
        struct file *filp;
        int error = -EISDIR;

        switch (nd->last_type) {
        case LAST_DOTDOT:
    ...
                /* fallthrough */
        case LAST_BIND:
                audit_inode(pathname, dir);

		if (gr_handle_rofs_blockwrite(nd->path.dentry, nd->path.mnt, acc_mode)) {
			error = -EPERM;
			goto exit;
		}
    ...
                goto ok;
        }
    ...
        return ERR_PTR(error);
}

Which is the equivalent code for block devices write operations.

Written by xorl

November 19, 2010 at 03:57

Posted in grsecurity, linux, security

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s