[apparmor] [PATCH 3/3] Add mount rules

Kees Cook kees at ubuntu.com
Wed Feb 22 23:29:55 UTC 2012


On Wed, Feb 22, 2012 at 03:04:58PM -0800, John Johansen wrote:
> Add the ability to control mounting and unmounting
> 
> The basic form of the rules are.
> 
>   [audit] [deny] mount [conds]* [device] [ -> [conds] path],
>   [audit] [deny] remount [conds]* [path],
>   [audit] [deny] umount [conds]* [path],
>   [audit] [deny] pivotroot [oldroot=<value>] <path> -> <profile>
> 
>   remount is just a short cut for mount options=remount
> 
>   where [conds] can be
>     fstype=<expr>
>     options=<expr>
> 
> 
>   conds follow the extended conditional syntax of allowing either:
> 
>   * a single value after the equals, which has the same character range as
>     regular IDS (ie most anything but it can't be terminated with a , (comma)
>     and if spaces or other characters are needed it can be quoted
> 
>     eg.
>        options=foo
>        options = foo
>        options="foo bar"
> 
>   * a list of values after the equals, the list of values is enclosed within
>     parenthesis () and its has a slightly reduced character set but again
>     elements can be quoted.
> 
>     the separation between elements is whitespace and commas.
> 
>     eg.
>       options=(foo bar)
>       options=(foo, bar)
>       options=(foo , bar)
>       options=(foo,bar)
> 
> 
> The rules are flexible and follow a similar pattern as network, capability,
> etc.
> 
>   mount,	# allow all mounts, but not umount or pivotroot
> 
>   mount fstype=procfs,  # allow mounting procfs anywhere
> 
>   mount options=(bind, ro) /foo -> /bar,  # readonly bind mount
> 
>   mount /dev/sda -> /mnt,
> 
>   mount /dev/sd** -> /mnt/**,
> 
>   mount fstype=overlayfs options=(rw,upperdir=/tmp/upper/,lowerdir=/) overlay -> /mnt/
> 
>   umount,
> 
>   umount /m*,
> 
> 
> Currently variables and regexs are are supported on the device and mount
> point. ie.
>   mount <devince> -> <mount point>,
> 
> Regexes are supported in fstype and options.  The options have a further
> caveat that regexs only work if the option is fs specific option.
> 
>   eg. options=(upperdir=/tmp/*,lowerdir=/)
> 
> regex's will not currently work against the standard options like ro, rw
> nosuid
> 
> 
> Conditionals (fstype) can only be applied to the device (source) at this
> time and will be disregarded in situations where the mount is manipulating
> an existing mount (bind, remount).
> 
> Options can be specified multiple times
>   mount option=rw option=(nosuid,upperdir=/foo),
> 
> and will be combined together into a single set of values
> 
> The ordering of the standard mount options (rw,ro, ...) does not matter
> but the ordering of fs specific options does.
> 
> Specifying that the value of a particular option does not matter can be
> acheived by providing both the positive and negative forms of and option
>   option=(rw,ro) options=(suid,nosuid)
> 
> For the fs specific options specifying that a particular value does not
> matter is achieve using a regex with alternations.
> 
> Improvements to the syntax and order restrictions are planned for the
> future.
> 
> Signed-off-by: John Johansen <john.johansen at canonical.com>
> ---
>  parser/Makefile                                    |    4 +-
>  parser/mount.c                                     |  511 ++++++++++++++++++++
>  parser/mount.h                                     |  119 +++++
>  parser/parser.h                                    |   26 +
>  parser/parser_lex.l                                |   96 ++++-
>  parser/parser_misc.c                               |   80 +++
>  parser/parser_policy.c                             |   59 ++-
>  parser/parser_regex.c                              |  365 ++++++++++++++
>  parser/parser_variable.c                           |   80 +++-
>  parser/parser_yacc.y                               |  197 ++++++++-
>  parser/tst/simple_tests/mount/ok_1.sd              |    7 +
>  parser/tst/simple_tests/mount/ok_10.sd             |    7 +
>  parser/tst/simple_tests/mount/ok_11.sd             |    7 +
>  parser/tst/simple_tests/mount/ok_12.sd             |    7 +
>  parser/tst/simple_tests/mount/ok_14.sd             |    7 +
>  parser/tst/simple_tests/mount/ok_15.sd             |    7 +
>  parser/tst/simple_tests/mount/ok_16.sd             |    7 +
>  parser/tst/simple_tests/mount/ok_17.sd             |    7 +
>  parser/tst/simple_tests/mount/ok_18.sd             |    7 +
>  parser/tst/simple_tests/mount/ok_19.sd             |    7 +
>  parser/tst/simple_tests/mount/ok_2.sd              |    7 +
>  parser/tst/simple_tests/mount/ok_20.sd             |    7 +
>  parser/tst/simple_tests/mount/ok_21.sd             |    7 +
>  parser/tst/simple_tests/mount/ok_22.sd             |    7 +
>  parser/tst/simple_tests/mount/ok_23.sd             |    7 +
>  parser/tst/simple_tests/mount/ok_3.sd              |    7 +
>  parser/tst/simple_tests/mount/ok_4.sd              |    7 +
>  parser/tst/simple_tests/mount/ok_5.sd              |   10 +
>  parser/tst/simple_tests/mount/ok_8.sd              |    9 +
>  parser/tst/simple_tests/mount/ok_9.sd              |    7 +
>  parser/tst/simple_tests/mount/okay_13.sd           |    7 +
>  parser/tst/simple_tests/mount/okay_6.sd            |    7 +
>  parser/tst/simple_tests/mount/okay_7.sd            |    7 +
>  .../simple_tests/profile/flags/flags_hats_ok.sd    |    8 +-
>  parser/tst/simple_tests/profile/flags/flags_ok.sd  |   12 +-
>  35 files changed, 1670 insertions(+), 53 deletions(-)
>  create mode 100644 parser/mount.c
>  create mode 100644 parser/mount.h
>  create mode 100644 parser/tst/simple_tests/mount/ok_1.sd
>  create mode 100644 parser/tst/simple_tests/mount/ok_10.sd
>  create mode 100644 parser/tst/simple_tests/mount/ok_11.sd
>  create mode 100644 parser/tst/simple_tests/mount/ok_12.sd
>  create mode 100644 parser/tst/simple_tests/mount/ok_14.sd
>  create mode 100644 parser/tst/simple_tests/mount/ok_15.sd
>  create mode 100644 parser/tst/simple_tests/mount/ok_16.sd
>  create mode 100644 parser/tst/simple_tests/mount/ok_17.sd
>  create mode 100644 parser/tst/simple_tests/mount/ok_18.sd
>  create mode 100644 parser/tst/simple_tests/mount/ok_19.sd
>  create mode 100644 parser/tst/simple_tests/mount/ok_2.sd
>  create mode 100644 parser/tst/simple_tests/mount/ok_20.sd
>  create mode 100644 parser/tst/simple_tests/mount/ok_21.sd
>  create mode 100644 parser/tst/simple_tests/mount/ok_22.sd
>  create mode 100644 parser/tst/simple_tests/mount/ok_23.sd
>  create mode 100644 parser/tst/simple_tests/mount/ok_3.sd
>  create mode 100644 parser/tst/simple_tests/mount/ok_4.sd
>  create mode 100644 parser/tst/simple_tests/mount/ok_5.sd
>  create mode 100644 parser/tst/simple_tests/mount/ok_8.sd
>  create mode 100644 parser/tst/simple_tests/mount/ok_9.sd
>  create mode 100644 parser/tst/simple_tests/mount/okay_13.sd
>  create mode 100644 parser/tst/simple_tests/mount/okay_6.sd
>  create mode 100644 parser/tst/simple_tests/mount/okay_7.sd
> 
> diff --git a/parser/Makefile b/parser/Makefile
> index 15cbe62..528d1b3 100644
> --- a/parser/Makefile
> +++ b/parser/Makefile
> @@ -76,8 +76,8 @@ EXTRA_CFLAGS+=-DSUBDOMAIN_CONFDIR=\"${CONFDIR}\"
>  SRCS = parser_common.c parser_include.c parser_interface.c parser_lex.c \
>         parser_main.c parser_misc.c parser_merge.c parser_symtab.c \
>         parser_yacc.c parser_regex.c parser_variable.c parser_policy.c \
> -       parser_alias.c
> -HDRS = parser.h parser_include.h immunix.h
> +       parser_alias.c mount.c
> +HDRS = parser.h parser_include.h immunix.h mount.h
>  TOOLS = apparmor_parser
>  
>  OBJECTS = $(SRCS:.c=.o)
> diff --git a/parser/mount.c b/parser/mount.c
> new file mode 100644
> index 0000000..c30908e
> --- /dev/null
> +++ b/parser/mount.c
> @@ -0,0 +1,511 @@
> +/*
> + *   Copyright (c) 2010

2012.

> + *   Canonical, Ltd. (All rights reserved)
> + *
> + *   This program is free software; you can redistribute it and/or
> + *   modify it under the terms of version 2 of the GNU General Public
> + *   License published by the Free Software Foundation.
> + *
> + *   This program is distributed in the hope that it will be useful,
> + *   but WITHOUT ANY WARRANTY; without even the implied warranty of
> + *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + *   GNU General Public License for more details.
> + *
> + *   You should have received a copy of the GNU General Public License
> + *   along with this program; if not, contact Novell, Inc. or Canonical
> + *   Ltd.
> + */
> +
> +/**
> + * The mount command, its mix of options and flags, its permissions and
> + * mapping are a mess.
> + *      mount [-lhV]
> + *
> + *      mount -a [-fFnrsvw] [-t vfstype] [-O optlist]
> + *
> + *      mount [-fnrsvw] [-o option[,option]...]  device|dir
> + *
> + *      mount [-fnrsvw] [-t vfstype] [-o options] device dir
> + *
> + *----------------------------------------------------------------------
> + * Mount flags of no interest for apparmor mediation
> + * -a, --all
> + * -F fork for similtaneous mount
> + * -f fake, do everything except that actual system call
> + * -h --help
> + * -i, --internal-only
> + * -n mount without writing in /etc/mtab
> + * -O <optlist> limits what is auto mounted
> + * -p, --pass-fd num
> + * -s     Tolerate sloppy mount options
> + * -U uuid
> + * -V --version
> + * --no-canonicalize
> + *
> + *----------------------------------------------------------------------
> + * what do we do with these
> + * -l	list?
> + * -L <label> label
> + * -v --verbose		deprecated
> + *
> + *----------------------------------------------------------------------
> + * Filesystem type
> + * -t <vfstype>
> + *    vfstype=<vfstype>
> + *
> + *----------------------------------------------------------------------
> + * Mount Flags/options (-o --options)
> + *  -o option[,option]
> + * 
> + * The linux kernel has 32 fs - independent mount flags, that mount command
> + * is responsible for stripping out and mapping to a 32 bit flags field.
> + * The mount commands mapping is documented below.
> + *
> + * Unfortunately we can not directly use this mapping as we need to be able
> + * represent, whether none, 1 or both options of a flag can be present for
> + *
> + * eg.
> + *    ro, and rw information is stored in a single bit.  But we need 2 bits
> + *    of information.
> + *    ro - the mount can only be readonly
> + *    rw - the mount can only be rw
> + *    ro/rw - the mount can be either ro/rw
> + *    the forth state of neither ro/rw does not exist, but still we need
> + *    >1 bit to represent the possible choices
> + *
> + * The fs specific mount options are passed into the kernel as a string
> + * to be interpreted by the filesystem.
> + *
> + *
> + * #define MS_RDONLY	 1		Mount read-only
> + *	ro -r --read-only	[source] dest
> + *	rw -w
> + * #define MS_NOSUID	 2		Ignore suid and sgid bits
> + *	nosuid
> + *	suid
> + * #define MS_NODEV	 4		Disallow access to device special files
> + *	nodev
> + *	dev
> + * #define MS_NOEXEC	 8		Disallow program execution
> + *	noexec
> + *	exec
> + * #define MS_SYNCHRONOUS	16	Writes are synced at once
> + *	sync
> + *	async
> + * #define MS_REMOUNT	32		Alter flags of a mounted FS
> + *	remount			source dest
> + * #define MS_MANDLOCK	64		Allow mandatory locks on an FS
> + *	mand
> + *	nomand
> + * #define MS_DIRSYNC	128		Directory modifications are synchronous
> + *	dirsync
> + * #define MS_NOATIME	1024		Do not update access times
> + *	noatime
> + *	atime
> + * #define MS_NODIRATIME	2048	Do not update directory access times
> + *	nodiratime
> + *	diratime
> + * #define MS_BIND		4096
> + *	--bind -B		source dest
> + * #define MS_MOVE		8192
> + *	--move -M		source dest
> + * #define MS_REC		16384
> + *	--rbind -R		source dest
> + *	--make-rshared		dest
> + *	--make-rslave		dest
> + *	--make-rprivate		dest
> + *	--make-runbindable	dest
> + * #define MS_VERBOSE	32768		MS_VERBOSE is deprecated
> + * #define MS_SILENT	32768
> + *	silent
> + *	load
> + * #define MS_POSIXACL	(1<<16)		VFS does not apply the umask
> + *	acl
> + *	noacl
> + * #define MS_UNBINDABLE	(1<<17)	change to unbindable
> + *	--make-unbindable	dest
> + * #define MS_PRIVATE	(1<<18)		change to private
> + *	--make-private		dest
> + * #define MS_SLAVE	(1<<19)		change to slave
> + *	--make-slave		dest
> + * #define MS_SHARED	(1<<20)		change to shared
> + *	--make-shared		dest
> + * #define MS_RELATIME	(1<<21)		Update atime relative to mtime/ctime
> + *	relatime
> + *	norelatime
> + * #define MS_KERNMOUNT	(1<<22)		this is a kern_mount call
> + * #define MS_I_VERSION	(1<<23)		Update inode I_version field
> + *	iversion
> + *	noiversion
> + * #define MS_STRICTATIME	(1<<24)	Always perform atime updates
> + *	strictatime
> + *	nostrictatime
> + * #define MS_NOSEC	(1<<28)
> + * #define MS_BORN		(1<<29)
> + * #define MS_ACTIVE	(1<<30)
> + * #define MS_NOUSER	(1<<31)
> + *	nouser
> + *	user
> + *
> + * other mount options of interest
> + *
> + *   selinux
> + *     context=<context>
> + *     fscontext=<context>
> + *     defcontext=<context>,
> + *     rootcontext=<context>
> + *
> + *   defaults -> rw, suid,  dev,  exec,  auto,  nouser,  async
> + *   owner -> implies nosuid  and  nodev
> + *   users -> implies noexec, nosuid, and nodev
> + *
> + *----------------------------------------------------------------------
> + * AppArmor mount rules
> + *
> + * AppArmor mount rules try to leverage mount syntax within apparmor syntax
> + * this can not be done entirely but it is largely covered.
> + *
> + * The general mount syntax is
> + * [audit] [deny] [owner] mount [conds]* [source] [ -> [conds] path],
> + * [audit] [deny] remount [conds]* [path],
> + * [audit] [deny] umount [conds]* [path],
> + *
> + * Note: leading owner option applies owner condition to both sours and dest
> + *       path.
> + *
> + * where [conds] can be
> + * fstype=<expr>
> + * options=<expr>
> + * owner[=<expr>]
> + *
> + * <expr> := <re> | '(' (<re>[,])+ ')'
> + *
> + * If a condition is not specified then it is assumed to match all possible
> + * entries for it.  ie. a missing fstype means all fstypes are matced.
> + * However if a condition is specified then the rule only grants permission
> + * for mounts matching the specified pattern.
> + *
> + * Egs.
> + * mount,		# allow any mount
> + * mount /dev/foo,	# allow mounting of /dev/foo anywhere
> + * mount options=ro /dev/foo,  #allow mounting /dev/foo as read only
> + * mount options=(ro,foo) /dev/foo,
> + * mount options=ro options=foo /dev/foo,
> + * mount -> /mnt/**,	# allow any mount on dirs under /mnt/
> + * mount options=ro -> /mnt/**, # allow any read only mount under /mnt/
> + * mount fstype=overlayfs options=(rw,upperdir=/tmp/upper/,lowerdir=/) overlay -> /mnt/
> + *
> + *----------------------------------------------------------------------
> + * pivotroot
> + *   pivotroot [oldroot=<value>] <path> -> <profile>
> + *   pivotroot <path> -> {  }
> + *
> + *----------------------------------------------------------------------
> + * chroot
> + *   chroot <path> -> <profile>
> + *   chroot <path> -> {  }
> + *
> + *----------------------------------------------------------------------
> + * AppArmor mount rule encoding
> + *
> + * TODO:
> + *   add semantic checking of options against specified filesytem types
> + *   to catch mount options, that can't be covered.
> + *
> + *
> + */
> +
> +
> +#include <stdlib.h>
> +#include <string.h>
> +
> +#include "parser.h"
> +#include "mount.h"
> +
> +struct mnt_keyword_table {
> +	char *keyword;
> +	unsigned int set;
> +	unsigned int clear;
> +};
> +
> +static struct mnt_keyword_table mnt_opts_table[] = {
> +	{"ro",			MS_RDONLY, 0},
> +	{"r",			MS_RDONLY, 0},
> +	{"read-only",		MS_RDONLY, 0},
> +	{"rw",			0, MS_RDONLY},
> +	{"w",			0, MS_RDONLY},
> +	{"suid",		0, MS_NOSUID},
> +	{"nosuid",		MS_NOSUID, 0},
> +	{"dev",			0, MS_NODEV},
> +	{"nodev",		MS_NODEV, 0},
> +	{"exec",		0, MS_NOEXEC},
> +	{"noexec",		MS_NOEXEC, 0},
> +	{"sync",		MS_SYNC, 0},
> +	{"async",		0, MS_SYNC},
> +	{"remount",		MS_REMOUNT, 0},
> +	{"mand",		MS_MAND, 0},
> +	{"nomand",		0, MS_MAND},
> +	{"dirsync",		MS_DIRSYNC, 0},
> +	{"atime",		0, MS_NOATIME},
> +	{"noatime",		MS_NOATIME, 0},
> +	{"diratime",		0, MS_NODIRATIME},
> +	{"nodiratime",		MS_NODIRATIME, 0},
> +	{"bind",		MS_BIND, 0},
> +	{"B",			MS_BIND, 0},
> +	{"move",		MS_MOVE, 0},
> +	{"M",			MS_MOVE, 0},
> +	{"rbind",		MS_RBIND, 0},
> +	{"R",			MS_RBIND, 0},
> +	{"verbose",		MS_VERBOSE, 0},
> +	{"silent",		MS_SILENT, 0},
> +	{"load",		0, MS_SILENT},
> +	{"acl",			MS_ACL, 0},
> +	{"noacl",		0, MS_ACL},
> +	{"make-unbindable",	MS_UNBINDABLE, 0},
> +	{"make-runbindable",	MS_RUNBINDABLE, 0},
> +	{"make-private",	MS_PRIVATE, 0},
> +	{"make-rprivate",	MS_RPRIVATE, 0},
> +	{"make-slave",		MS_SLAVE, 0},
> +	{"make-rslave",		MS_RSLAVE, 0},
> +	{"make-shared",		MS_SHARED, 0},
> +	{"make-rshared",	MS_RSHARED, 0},
> +
> +	{"relatime",		MS_RELATIME, 0},
> +	{"norelatime",		0, MS_NORELATIME},
> +	{"iversion",		MS_IVERSION, 0},
> +	{"noiversion",		0, MS_IVERSION},
> +	{"strictatime",		MS_STRICTATIME, 0},
> +	{"user",		0, MS_NOUSER},
> +	{"nouser",		MS_NOUSER, 0},
> +
> +	{NULL, 0, 0}

This and the next could just be "{ }".

> +};
> +
> +static struct mnt_keyword_table mnt_conds_table[] = {
> +	{"options", MNT_SRC_OPT, MNT_COND_OPTIONS},
> +	{"option", MNT_SRC_OPT, MNT_COND_OPTIONS},
> +	{"fstype", MNT_SRC_OPT | MNT_DST_OPT, MNT_COND_FSTYPE},
> +	{"vfstype", MNT_SRC_OPT | MNT_DST_OPT, MNT_COND_FSTYPE},
> +
> +	{NULL, 0, 0}
> +};
> +
> +static int find_mnt_keyword(struct mnt_keyword_table *table, const char *name)
> +{
> +	int i;
> +	for (i = 0; table[i].keyword; i++) {
> +		if (strcmp(name, table[i].keyword) == 0)
> +			return i;
> +	}
> +
> +	return -1;
> +}
> +
> +int is_valid_mnt_cond(const char *name, int src)
> +{
> +	int i;
> +	i = find_mnt_keyword(mnt_conds_table, name);
> +	if (i != -1)
> +		return (mnt_conds_table[i].set & src);
> +	return -1;
> +}
> +
> +static unsigned int extract_flags(struct value_list **list, unsigned int *inv)
> +{
> +	unsigned int flags = 0;
> +	*inv = 0;
> +
> +	struct value_list *entry, *tmp, *prev = NULL;
> +	list_for_each_safe(*list, entry, tmp) {
> +		int i;
> +		i = find_mnt_keyword(mnt_opts_table, entry->value);
> +		if (i != -1) {
> +			flags |= mnt_opts_table[i].set;
> +			*inv |= mnt_opts_table[i].clear;
> +			PDEBUG(" extracting mount flag %s req: 0x%x inv: 0x%x"
> +			       " => req: 0x%x inv: 0x%x\n",
> +			       entry->value, mnt_opts_table[i].set,
> +			       mnt_opts_table[i].clear, flags, *inv);
> +			if (prev)
> +				prev->next = tmp;
> +			if (entry == *list)
> +				*list = tmp;
> +			entry->next = NULL;
> +			free_value_list(entry);
> +		} else
> +			prev = entry;
> +	}
> +
> +	return flags;
> +}
> +
> +static struct value_list *extract_fstype(struct cond_entry **conds)
> +{
> +	struct value_list *list = NULL;
> +
> +	struct cond_entry *entry, *tmp, *prev = NULL;
> +
> +	list_for_each_safe(*conds, entry, tmp) {
> +		if (strcmp(entry->name, "fstype") == 0 ||
> +		    strcmp(entry->name, "vfstype") == 0) {
> +			PDEBUG("  extracting fstype ");
> +			if (prev)
> +				prev->next = tmp;
> +			if (entry == *conds)
> +				*conds = tmp;
> +			entry->next = NULL;
> +			list_append(entry->vals, list);
> +			list = entry->vals;
> +			entry->vals = NULL;
> +			free_cond_entry(entry);
> +		} else
> +			prev = entry;
> +	}
> +
> +	return list;
> +}
> +
> +static struct value_list *extract_options(struct cond_entry **conds)
> +{
> +	struct value_list *list = NULL;
> +
> +	struct cond_entry *entry, *tmp, *prev = NULL;
> +
> +	list_for_each_safe(*conds, entry, tmp) {
> +		if (strcmp(entry->name, "options") == 0 ||
> +		    strcmp(entry->name, "option") == 0) {
> +			if (prev)
> +				prev->next = tmp;
> +			if (entry == *conds)
> +				*conds = tmp;
> +			entry->next = NULL;
> +			PDEBUG("  extracting option %s\n", entry->name);
> +			list_append(entry->vals, list);
> +			list = entry->vals;
> +			entry->vals = NULL;
> +			free_cond_entry(entry);
> +		} else
> +			prev = entry;
> +	}
> +
> +	return list;
> +}
> +
> +struct mnt_entry *new_mnt_entry(struct cond_entry *sconds, char *device,
> +				struct cond_entry *dconds, char *mnt_point,
> +				int allow)
> +{
> +	/* FIXME: dconds are ignored atm */
> +
> +	struct mnt_entry *ent;
> +	ent = (struct mnt_entry *) calloc(1, sizeof(struct mnt_entry));
> +	if (ent) {
> +		unsigned int rclear, aclear;
> +
> +		ent->mnt_point = mnt_point;
> +		ent->device = device;
> +		ent->dev_type = extract_fstype(&sconds);
> +
> +		ent->flags = 0;
> +
> +		if (sconds) {
> +			ent->opts = extract_options(&sconds);
> +			if (ent->opts)
> +				ent->flags = extract_flags(&ent->opts,
> +							   &ent->inv_flags);
> +		}
> +
> +		if (allow & AA_MAY_REMOUNT) {
> +			allow = AA_MAY_MOUNT;
> +			ent->flags |= MS_REMOUNT;
> +			ent->inv_flags = 0;
> +		} else if (!(ent->flags | ent->inv_flags)) {
> +			/* no flag options, and not remount, allow everything */
> +			ent->flags = 0xffffffff;
> +			ent->inv_flags = 0xffffffff;
> +		}
> +
> +		ent->allow = allow;
> +
> +		if (sconds) {
> +			PERROR("  unsupported mount conditions\n");
> +			exit(1);
> +		}
> +	}
> +
> +	return ent;
> +}
> +
> +void free_mnt_entry(struct mnt_entry *ent)
> +{
> +	if (!ent)
> +		return;
> +
> +	free_mnt_entry(ent->next);
> +	free_value_list(ent->opts);
> +	free_value_list(ent->dev_type);
> +	free(ent->device);
> +	free(ent->mnt_point);
> +	free(ent->trans);
> +
> +	free(ent);
> +}
> +
> +
> +struct mnt_entry *dup_mnt_entry(struct mnt_entry *orig)
> +{
> +	struct mnt_entry *entry = NULL;
> +
> +	entry = (struct mnt_entry *) calloc(1, sizeof(struct mnt_entry));
> +	if (!entry)
> +		return NULL;
> +
> +	entry->mnt_point = orig->mnt_point ? strdup(orig->mnt_point) : NULL;
> +	entry->device = orig->device ? strdup(orig->device) : NULL;
> +	entry->trans = orig->trans ? strdup(orig->trans) : NULL;
> +
> +	entry->dev_type = dup_value_list(orig->dev_type);
> +	entry->opts = dup_value_list(orig->opts);
> +
> +	entry->flags = orig->flags;
> +	entry->inv_flags = orig->inv_flags;
> +
> +	entry->allow = orig->allow;
> +	entry->audit = orig->audit;
> +	entry->deny = orig->deny;
> +
> +	entry->next = orig->next;
> +
> +	return entry;
> +}
> +
> +void print_mnt_entry(struct mnt_entry *entry)
> +{
> +	if (entry->allow & AA_MAY_MOUNT)
> +		fprintf(stderr, "mount");
> +	else if (entry->allow & AA_MAY_UMOUNT)
> +		fprintf(stderr, "umount");
> +	else if (entry->allow & AA_MAY_PIVOTROOT)
> +		fprintf(stderr, "pivotroot");
> +	else
> +		fprintf(stderr, "error: unknonw mount perm");
> +
> +	fprintf(stderr, " (0x%x - 0x%x) ", entry->flags, entry->inv_flags);
> +	if (entry->dev_type) {
> +		fprintf(stderr, " type=");
> +		print_value_list(entry->dev_type);
> +	}
> +	if (entry->opts) {
> +		fprintf(stderr, " options=");
> +		print_value_list(entry->opts);
> +	}
> +	if (entry->device)
> +		fprintf(stderr, " %s", entry->device);
> +	if (entry->mnt_point)
> +		fprintf(stderr, " -> %s", entry->mnt_point);
> +	if (entry->trans)
> +		fprintf(stderr, " -> %s", entry->trans);
> +
> +	fprintf(stderr, " %s (0x%x/0x%x)", entry->deny ? "deny" : "", entry->allow, entry->audit); 
> +	fprintf(stderr, ",\n");
> +}
> diff --git a/parser/mount.h b/parser/mount.h
> new file mode 100644
> index 0000000..e65f606
> --- /dev/null
> +++ b/parser/mount.h
> @@ -0,0 +1,119 @@
> +/*
> + *   Copyright (c) 2010

2012.

> + *   Canonical, Ltd. (All rights reserved)
> + *
> + *   This program is free software; you can redistribute it and/or
> + *   modify it under the terms of version 2 of the GNU General Public
> + *   License published by the Free Software Foundation.
> + *
> + *   This program is distributed in the hope that it will be useful,
> + *   but WITHOUT ANY WARRANTY; without even the implied warranty of
> + *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + *   GNU General Public License for more details.
> + *
> + *   You should have received a copy of the GNU General Public License
> + *   along with this program; if not, contact Novell, Inc. or Canonical
> + *   Ltd.
> + */
> +
> +#ifndef __AA_MOUNT_H
> +#define __AA_MOUNT_H
> +
> +#include "parser.h"
> +
> +#define MS_RDONLY	(1 << 0)
> +#define MS_RW		0
> +#define MS_NOSUID	(1 << 1)
> +#define MS_SUID		0
> +#define MS_NODEV	(1 << 2)
> +#define MS_DEV		0
> +#define MS_NOEXEC	(1 << 3)
> +#define MS_EXEC		0
> +#define MS_SYNC		(1 << 4)
> +#define MS_ASYNC	0
> +#define MS_REMOUNT	(1 << 5)
> +#define MS_MAND		(1 << 6)
> +#define MS_NOMAND	0
> +#define MS_DIRSYNC	(1 << 7)
> +#define MS_NODIRSYNC	0
> +#define MS_NOATIME	(1 << 10)
> +#define MS_ATIME	0
> +#define MS_NODIRATIME	(1 << 11)
> +#define MS_DIRATIME	0
> +#define MS_BIND		(1 << 12)
> +#define MS_MOVE		(1 << 13)
> +#define MS_REC		(1 << 14)
> +#define MS_VERBOSE	(1 << 15)
> +#define MS_SILENT	(1 << 15)
> +#define MS_LOAD		0
> +#define MS_ACL		(1 << 16)
> +#define MS_NOACL	0
> +#define MS_UNBINDABLE	(1 << 17)
> +#define MS_PRIVATE	(1 << 18)
> +#define MS_SLAVE	(1 << 19)
> +#define MS_SHARED	(1 << 20)
> +#define MS_RELATIME	(1 << 21)
> +#define MS_NORELATIME	0
> +#define MS_IVERSION	(1 << 23)
> +#define MS_NOIVERSION	0
> +#define MS_STRICTATIME	(1 << 24)
> +#define MS_NOUSER	(1 << 31)
> +#define MS_USER		0
> +
> +#define MS_ALL_FLAGS	0x71bffcff

Should this be a massive or-list instead?

> +
> +#define MS_RBIND	(MS_BIND | MS_REC)
> +#define MS_RUNBINDABLE	(MS_UNBINDABLE | MS_REC)
> +#define MS_RPRIVATE	(MS_PRIVATE | MS_REC)
> +#define MS_RSLAVE	(MS_SLAVE | MS_REC)
> +#define MS_RSHARED	(MS_SHARED | MS_REC)
> +
> +#define MNT_FLAGS	(MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_NOATIME | \
> +			 MS_NODIRATIME | MS_RELATIME | MS_STRICTATIME)
> +
> +#define MS_REMOUNT_FLAGS (MS_REMOUNT | MNT_FLAGS)
> +#define MS_BIND_FLAGS (MS_BIND | MS_REC)
> +#define MS_MAKE_FLAGS ((MS_UNBINDABLE | MS_PRIVATE | MS_SLAVE | MS_SHARED | \
> +			MS_REC) | (MS_ALL_FLAGS & ~(MNT_FLAGS)))
> +#define MS_MOVE_FLAGS (MS_MOVE)
> +
> +#define MS_CMDS (MS_MOVE | MS_REMOUNT | MS_BIND | MS_PRIVATE | MS_SLAVE | \
> +		 MS_SHARED | MS_UNBINDABLE)
> +
> +#define MNT_SRC_OPT 1
> +#define MNT_DST_OPT 2
> +
> +#define MNT_COND_FSTYPE 1
> +#define MNT_COND_OPTIONS 2
> +
> +#define AA_MAY_PIVOTROOT 1
> +#define AA_MAY_MOUNT 2
> +#define AA_MAY_UMOUNT 4
> +#define AA_MAY_REMOUNT 8	/* dump perm for remount rule */
> +
> +
> +struct mnt_entry {
> +	char *mnt_point;
> +	char *device;
> +	char *trans;
> +	struct value_list *dev_type;
> +	struct value_list *opts;
> +
> +	unsigned int flags, inv_flags;
> +
> +	int allow, audit;
> +	int deny;
> +	struct mnt_entry *next;
> +};
> +
> +void print_mnt_entry(struct mnt_entry *entry);
> +
> +int is_valid_mnt_cond(const char *name, int src);
> +struct mnt_entry *new_mnt_entry(struct cond_entry *sconds, char *device,
> +				struct cond_entry *dconds, char *mnt_point,
> +				int mode);
> +struct mnt_entry *dup_mnt_entry(struct mnt_entry *orig);
> +void free_mnt_entry(struct mnt_entry *ent);
> +
> +
> +#endif /* __AA_MOUNT_H */
> diff --git a/parser/parser.h b/parser/parser.h
> index 7be0f83..899bfb8 100644
> --- a/parser/parser.h
> +++ b/parser/parser.h
> @@ -28,6 +28,8 @@
>  #include "libapparmor_re/apparmor_re.h"
>  #include "libapparmor_re/aare_rules.h"
>  
> +struct mnt_ent;
> +
>  /* Global variable to pass token to lexer.  Will be replaced by parameter
>   * when lexer and parser are made reentrant
>   */
> @@ -58,6 +60,13 @@ struct value_list {
>  	struct value_list *next;
>  };
>  
> +struct cond_entry {
> +	char *name;
> +	struct value_list *vals;
> +
> +	struct cond_entry *next;
> +};
> +
>  struct cod_entry {
>  	char *namespace;
>  	char *name;
> @@ -132,6 +141,8 @@ struct codomain {
>  
>  	char *exec_table[AA_EXEC_COUNT];
>  	struct cod_entry *entries;
> +	struct mnt_entry *mnt_ents;
> +
>  	void *hat_table;
>  	//struct codomain *next;
>  
> @@ -234,12 +245,19 @@ extern int preprocess_only;
>  #define __unused __attribute__ ((unused))
>  #endif
>  
> +
>  #define list_for_each(LIST, ENTRY) \
>  	for ((ENTRY) = (LIST); (ENTRY); (ENTRY) = (ENTRY)->next)
>  #define list_for_each_safe(LIST, ENTRY, TMP) \
>  	for ((ENTRY) = (LIST), (TMP) = (LIST) ? (LIST)->next : NULL; (ENTRY); (ENTRY) = (TMP), (TMP) = (TMP) ? (TMP)->next : NULL)
>  #define list_last_entry(LIST, ENTRY) \
>  	for ((ENTRY) = (LIST); (ENTRY) && (ENTRY)->next; (ENTRY) = (ENTRY)->next)
> +#define list_append(LISTA, LISTB)		\
> +	do {					\
> +		typeof(LISTA) ___tmp;		\
> +		list_last_entry((LISTA), ___tmp);\
> +		___tmp->next = (LISTB);		\
> +	} while (0)
>  
>  /* from parser_common.c */
>  extern int regex_type;
> @@ -286,6 +304,8 @@ extern void reset_regex(void);
>  
>  extern int process_policydb(struct codomain *cod);
>  
> +extern int process_policy_ents(struct codomain *cod);
> +
>  /* parser_variable.c */
>  extern int process_variables(struct codomain *cod);
>  extern struct var_string *split_out_var(char *string);
> @@ -293,8 +313,12 @@ extern void free_var_string(struct var_string *var);
>  
>  /* parser_misc.c */
>  extern struct value_list *new_value_list(char *value);
> +extern struct value_list *dup_value_list(struct value_list *list);
>  extern void free_value_list(struct value_list *list);
>  extern void print_value_list(struct value_list *list);
> +extern struct cond_entry *new_cond_entry(char *name, struct value_list *list);
> +extern void free_cond_entry(struct cond_entry *ent);
> +extern void print_cond_entry(struct cond_entry *ent);
>  extern char *processid(char *string, int len);
>  extern char *processquoted(char *string, int len);
>  extern char *processunquoted(char *string, int len);
> @@ -318,6 +342,7 @@ extern void debug_cod_list(struct codomain *list);
>  extern int str_to_boolean(const char* str);
>  extern struct cod_entry *copy_cod_entry(struct cod_entry *cod);
>  extern void free_cod_entries(struct cod_entry *list);
> +extern void free_mnt_entries(struct mnt_entry *list);
>  
>  /* parser_symtab.c */
>  struct set_value {;
> @@ -356,6 +381,7 @@ extern void add_to_list(struct codomain *codomain);
>  extern void add_hat_to_policy(struct codomain *policy, struct codomain *hat);
>  extern void add_entry_to_policy(struct codomain *policy, struct cod_entry *entry);
>  extern void post_process_nt_entries(struct codomain *cod);
> +extern void post_process_mnt_entries(struct codomain *cod);
>  extern int post_process_policy(int debug_only);
>  extern int process_hat_regex(struct codomain *cod);
>  extern int process_hat_variables(struct codomain *cod);
> diff --git a/parser/parser_lex.l b/parser/parser_lex.l
> index 0c9323b..dd1ff5b 100644
> --- a/parser/parser_lex.l
> +++ b/parser/parser_lex.l
> @@ -45,7 +45,7 @@
>  #endif
>  /* #define DEBUG */
>  #ifdef DEBUG
> -#define PDEBUG(fmt, args...) printf("Lexer: " fmt, ## args)
> +#define PDEBUG(fmt, args...) printf("Lexer (state %d): " fmt, YY_START, ## args)
>  #else
>  #define PDEBUG(fmt, args...)	/* Do nothing */
>  #endif
> @@ -187,6 +187,9 @@ POST_VAR_ID_CHARS	[^ \t\n"!,]{-}[=\+]
>  POST_VAR_ID 	{POST_VAR_ID_CHARS}|(,{POST_VAR_ID_CHARS})
>  LIST_VALUE_ID_CHARS	[^ \t\n"!,]{-}[()]
>  LIST_VALUE_ID	{LIST_VALUE_ID_CHARS}+
> +ID_CHARS_NOEQ	[^ \t\n"!,]{-}[=]
> +ID_NOEQ		{ID_CHARS_NOEQ}|(,{ID_CHARS_NOEQ})
> +IDS_NOEQ	{ID_NOEQ}+
>  ALLOWED_QUOTED_ID 	[^\0"]|\\\"
>  QUOTED_ID	\"{ALLOWED_QUOTED_ID}*\"
>  
> @@ -211,12 +214,14 @@ ADD_ASSIGN	\+=
>  ARROW		->
>  LT_EQUAL	<=
>  
> -%x SUB_NAME
> +%x SUB_ID
> +%x SUB_VALUE
>  %x EXTCOND_MODE
>  %x NETWORK_MODE
>  %x LIST_VAL_MODE
>  %x ASSIGN_MODE
>  %x RLIMIT_MODE
> +%x MOUNT_MODE
>  %x CHANGE_PROFILE_MODE
>  %x INCLUDE
>  
> @@ -262,7 +267,8 @@ LT_EQUAL	<=
>  	if ( !YY_CURRENT_BUFFER ) yyterminate();
>  }
>  
> -{VARIABLE_NAME}/{WS}*={WS}*	{
> +<INITIAL,MOUNT_MODE>{
> +	{VARIABLE_NAME}/{WS}*=	{
>  				/* we match to the = in the lexer so that
>  				 * can switch scanner state.  By the time
>  				 * the parser see the = it may be to late
> @@ -274,8 +280,9 @@ LT_EQUAL	<=
>  				yy_push_state(EXTCOND_MODE);
>  				return TOK_CONDID;
>  			}
> +}
>  
> -<SUB_NAME>{
> +<SUB_ID>{
>  	({IDS}|{QUOTED_ID})	{
>  			  /* Ugh, this is a gross hack. I used to use
>  			   * {IDS} to match all TOK_IDs, but that would
> @@ -298,6 +305,29 @@ LT_EQUAL	<=
>  		}
>  }
>  
> +<SUB_VALUE>{
> +	({IDS}|{QUOTED_ID})	{
> +			  /* Ugh, this is a gross hack. I used to use
> +			   * {IDS} to match all TOK_IDs, but that would
> +			   * also match TOK_MODE + TOK_END_OF_RULE
> +			   * without any spaces in between (because it's
> +			   * a longer match). So now, when I want to
> +			   * match any random string, I go into a
> +			   * separate state. */
> +			DUMP_PREPROCESS;
> +			yylval.id =  processid(yytext, yyleng);
> +			PDEBUG("Found sub value: \"%s\"\n",  yylval.id);
> +			yy_pop_state();
> +			return TOK_VALUE;
> +		}
> +
> +	[^\n]	{
> +			DUMP_PREPROCESS;
> +			/* Something we didn't expect */
> +			yyerror(_("Found unexpected character: '%s'"), yytext);
> +		}
> +}
> +
>  <LIST_VAL_MODE>{
>  	{CLOSE_PAREN}	{
>  			DUMP_PREPROCESS;
> @@ -331,6 +361,12 @@ LT_EQUAL	<=
>  <EXTCOND_MODE>{
>  	{WS}+		{ DUMP_PREPROCESS; /* Eat whitespace */ }
>  
> +	{EQUALS}{WS}*/[^(\n]{-}{WS}	{
> +			DUMP_PREPROCESS;
> +			BEGIN(SUB_VALUE);
> +			return TOK_EQUALS;
> +		}
> +
>  	{EQUALS}	{
>  			DUMP_PREPROCESS;
>  			return TOK_EQUALS;
> @@ -342,7 +378,7 @@ LT_EQUAL	<=
>  			/* Don't push state here as this is a transition
>  			 * start condition and we want to return to the start
>  			 * condition that invoked <EXTCOND_MODE> when
> -			 * SUB_NAME is done
> +			 * LIST_VAL_ID is done
>  			 */
>  			BEGIN(LIST_VAL_MODE);
>  			return TOK_OPENPAREN;
> @@ -351,7 +387,7 @@ LT_EQUAL	<=
>  	[^\n]	{
>  			DUMP_PREPROCESS;
>  			/* Something we didn't expect */
> -			yyerror(_("Found unexpected character: '%s'"), yytext);
> +			yyerror(_("Found unexpected character: '%s' %d"), yytext, *yytext);
>  		}
>  
>  }
> @@ -481,6 +517,41 @@ LT_EQUAL	<=
>  			}
>  }
>  
> +<MOUNT_MODE>{
> +	{WS}+		{  DUMP_PREPROCESS; /* Ignoring whitespace */ }
> +
> +	{ARROW}		{
> +			DUMP_PREPROCESS;
> +			PDEBUG("Matched arrow\n");
> +			return TOK_ARROW;
> +			}
> +
> +	({IDS_NOEQ}|{PATHNAME}|{QUOTED_ID})	{
> +			DUMP_PREPROCESS;
> +			yylval.id = processid(yytext, yyleng);
> +			PDEBUG("Found ID: \"%s\"\n", yylval.id);
> +			return TOK_ID;
> +			}
> +
> +	{END_OF_RULE}	{
> +			DUMP_PREPROCESS;
> +			yy_pop_state();
> +			return TOK_END_OF_RULE;
> +			}
> +
> +	[^\n]		{
> +			DUMP_PREPROCESS;
> +			/* Something we didn't expect */
> +			yyerror(_("Found unexpected character: '%s'"), yytext);
> +			}
> +
> +	\r?\n		{
> +			DUMP_PREPROCESS;
> +			current_lineno++;
> +			yy_pop_state();
> +			}
> +}
> +
>  #include/.*\r?\n	 { /* include */
>  			PDEBUG("Matched #include\n");
>  			yy_push_state(INCLUDE);
> @@ -497,7 +568,7 @@ LT_EQUAL	<=
>  {CARET}			{
>  			DUMP_PREPROCESS;
>  			PDEBUG("Matched hat ^\n");
> -			yy_push_state(SUB_NAME);
> +			yy_push_state(SUB_ID);
>  			return TOK_CARET;
>  			}
>  {ARROW}			{
> @@ -558,13 +629,13 @@ LT_EQUAL	<=
>  
>  {HAT}			{
>  			DUMP_PREPROCESS;
> -			yy_push_state(SUB_NAME);
> +			yy_push_state(SUB_ID);
>  			return TOK_HAT;
>  			}
>  
>  {PROFILE}		{
>  			DUMP_PREPROCESS;
> -			yy_push_state(SUB_NAME);
> +			yy_push_state(SUB_ID);
>  			return TOK_PROFILE;
>  			}
>  
> @@ -602,6 +673,13 @@ LT_EQUAL	<=
>  			case TOK_CHANGE_PROFILE:
>  				yy_push_state(CHANGE_PROFILE_MODE);
>  				break;
> +			case TOK_MOUNT:
> +			case TOK_REMOUNT:
> +			case TOK_UMOUNT:
> +				DUMP_PREPROCESS;
> +				PDEBUG("Entering mount\n");
> +				yy_push_state(MOUNT_MODE);
> +				break;
>  			default: /* nothing */
>  				break;
>  			}
> diff --git a/parser/parser_misc.c b/parser/parser_misc.c
> index f1779c7..1668b31 100644
> --- a/parser/parser_misc.c
> +++ b/parser/parser_misc.c
> @@ -37,6 +37,7 @@
>  
>  #include "parser.h"
>  #include "parser_yacc.h"
> +#include "mount.h"
>  
>  /* #define DEBUG */
>  #ifdef DEBUG
> @@ -78,6 +79,11 @@ static struct keyword_table keyword_table[] = {
>  	{"rewrite",		TOK_ALIAS},
>  	{"ptrace",		TOK_PTRACE},
>  	{"file",		TOK_FILE},
> +	{"mount",               TOK_MOUNT},
> +	{"remount",             TOK_REMOUNT},
> +	{"umount",              TOK_UMOUNT},
> +	{"unmount",             TOK_UMOUNT},
> +	{"pivotroot",		TOK_PIVOTROOT},

Space vs tabs here?

>  	/* terminate */
>  	{NULL, 0}
>  };
> @@ -776,6 +782,20 @@ void free_cod_entries(struct cod_entry *list)
>  	free(list);
>  }
>  
> +void free_mnt_entries(struct mnt_entry *list)
> +{
> +	if (!list)
> +		return;
> +	if (list->next)
> +		free_mnt_entries(list->next);
> +	free(list->mnt_point);
> +	free(list->device);
> +	free_value_list(list->dev_type);
> +	free_value_list(list->opts);
> +
> +	free(list);
> +}
> +
>  static void debug_base_perm_mask(int mask)
>  {
>  	if (HAS_MAY_READ(mask))
> @@ -960,6 +980,37 @@ void free_value_list(struct value_list *list)
>  	}
>  }
>  
> +struct value_list *dup_value_list(struct value_list *list)
> +{
> +	struct value_list *entry, *dup, *head = NULL;
> +	char *value;
> +
> +	list_for_each(list, entry) {
> +		value = NULL;
> +		if (list->value) {
> +			value = strdup(list->value);
> +			if (!value)
> +				goto fail2;
> +		}
> +		dup = new_value_list(value);
> +		if (!dup)
> +			goto fail;
> +		if (head)
> +			list_append(head, dup);
> +		else
> +			head = dup;
> +	}
> +
> +	return head;
> +
> +fail:
> +	free(value);
> +fail2:
> +	free_value_list(head);
> +
> +	return NULL;
> +}
> +
>  void print_value_list(struct value_list *list)
>  {
>  	struct value_list *entry;
> @@ -974,6 +1025,35 @@ void print_value_list(struct value_list *list)
>  	}
>  }
>  
> +struct cond_entry *new_cond_entry(char *name, struct value_list *list)
> +{
> +	struct cond_entry *ent = calloc(1, sizeof(struct cond_entry));
> +	if (ent) {
> +		ent->name = name;
> +		ent->vals = list;
> +	}
> +
> +	return ent;
> +}
> +
> +void free_cond_entry(struct cond_entry *ent)
> +{
> +	if (ent) {
> +		free(ent->name);
> +		free_value_list(ent->vals);
> +		free(ent);
> +	}
> +}
> +
> +void print_cond_entry(struct cond_entry *ent)
> +{
> +	if (ent) {
> +		fprintf(stderr, "%s=(", ent->name);
> +		print_value_list(ent->vals);
> +		fprintf(stderr, ")\n");
> +	}
> +}
> +
>  #ifdef UNIT_TEST
>  int test_str_to_boolean(void)
>  {
> diff --git a/parser/parser_policy.c b/parser/parser_policy.c
> index 0e4a853..ed9b1c9 100644
> --- a/parser/parser_policy.c
> +++ b/parser/parser_policy.c
> @@ -29,6 +29,7 @@
>  #define _(s) gettext(s)
>  
>  #include "parser.h"
> +#include "mount.h"
>  #include "parser_yacc.h"
>  
>  /* #define DEBUG */
> @@ -95,10 +96,26 @@ void add_hat_to_policy(struct codomain *cod, struct codomain *hat)
>  	}
>  }
>  
> +static int add_entry_to_x_table(struct codomain *cod, char *name)
> +{
> +	int i;
> +	for (i = (AA_EXEC_LOCAL >> 10) + 1; i < AA_EXEC_COUNT; i++) {
> +		if (!cod->exec_table[i]) {
> +			cod->exec_table[i] = name;
> +			return i;
> +		} else if (strcmp(cod->exec_table[i], name) == 0) {
> +			/* name already in table */
> +			free(name);
> +			return i;
> +		}
> +	}
> +	free(name);
> +	return 0;
> +}
> +
>  static int add_named_transition(struct codomain *cod, struct cod_entry *entry)
>  {
>  	char *name = NULL;
> -	int i;
>  
>  	/* check to see if it is a local transition */
>  	if (!entry->namespace) {
> @@ -146,18 +163,7 @@ static int add_named_transition(struct codomain *cod, struct cod_entry *entry)
>  		name = entry->nt_name;
>  	}
>  
> -	for (i = (AA_EXEC_LOCAL >> 10) + 1; i < AA_EXEC_COUNT; i++) {
> -		if (!cod->exec_table[i]) {
> -			cod->exec_table[i] = name;
> -			return i;
> -		} else if (strcmp(cod->exec_table[i], name) == 0) {
> -			/* name already in table */
> -			free(name);
> -			return i;
> -		}
> -	}
> -	free(name);
> -	return 0;
> +	return add_entry_to_x_table(cod, name);
>  }
>  
>  void add_entry_to_policy(struct codomain *cod, struct cod_entry *entry)
> @@ -190,6 +196,32 @@ void post_process_nt_entries(struct codomain *cod)
>  	}
>  }
>  
> +void post_process_mnt_entries(struct codomain *cod)
> +{
> +	struct mnt_entry *entry;
> +
> +	list_for_each(cod->mnt_ents, entry) {
> +		if (entry->trans) {
> +			unsigned int mode = 0;
> +			int n = add_entry_to_x_table(cod, entry->trans);
> +			if (!n) {
> +				PERROR("Profile %s has to many specified profile transitions.\n", cod->name);
> +				exit(1);
> +			}
> +
> +			if (entry->allow & AA_USER_EXEC)
> +				mode |= SHIFT_MODE(n << 10, AA_USER_SHIFT);
> +			if (entry->allow & AA_OTHER_EXEC)
> +				mode |= SHIFT_MODE(n << 10, AA_OTHER_SHIFT);
> +			entry->allow = ((entry->allow & ~AA_ALL_EXEC_MODIFIERS) |
> +				       (mode & AA_ALL_EXEC_MODIFIERS));
> +
> +			entry->trans = NULL;
> +		}
> +	}
> +}
> +
> +
>  static void __merge_rules(const void *nodep, const VISIT value,
>  			  const int __unused depth)
>  {
> @@ -763,6 +795,7 @@ void free_policy(struct codomain *cod)
>  		return;
>  	free_hat_table(cod->hat_table);
>  	free_cod_entries(cod->entries);
> +	free_mnt_entries(cod->mnt_ents);
>  	if (cod->dfarules)
>  		aare_delete_ruleset(cod->dfarules);
>  	if (cod->dfa)
> diff --git a/parser/parser_regex.c b/parser/parser_regex.c
> index 50a5836..c27ed0c 100644
> --- a/parser/parser_regex.c
> +++ b/parser/parser_regex.c
> @@ -28,6 +28,8 @@
>  #include "parser.h"
>  #include "libapparmor_re/apparmor_re.h"
>  #include "libapparmor_re/aare_rules.h"
> +#include "mount.h"
> +#include "policydb.h"
>  
>  enum error_type {
>  	e_no_error,
> @@ -611,12 +613,373 @@ out:
>  	return error;
>  }
>  
> +static int build_list_val_expr(char *buffer, int size, struct value_list *list)
> +{
> +	struct value_list *ent;
> +	char tmp[PATH_MAX + 3];
> +	char *p;
> +	int len;
> +	pattern_t ptype;
> +	int pos;
> +
> +	if (!list) {
> +		strncpy(buffer, "[^\\000]*", size);
> +		return TRUE;
> +	}
> +
> +	p = buffer;
> +	strncpy(p, "(", size - (p - buffer));
> +	p++;
> +	if (p > buffer + size)
> +		goto fail;
> +
> +	ptype = convert_aaregex_to_pcre(list->value, 0, tmp, PATH_MAX+3, &pos);
> +	if (ptype == ePatternInvalid)
> +		goto fail;
> +
> +	len = strlen(tmp);
> +	if (len > size - (p - buffer))
> +		goto fail;
> +	strcpy(p, tmp);
> +	p += len;
> +
> +	list_for_each(list->next, ent) {
> +		ptype = convert_aaregex_to_pcre(ent->value, 0, tmp,
> +						PATH_MAX+3, &pos);
> +		if (ptype == ePatternInvalid)
> +			goto fail;
> +
> +		strncpy(p, "|", size - (p - buffer));
> +		p++;
> +		len = strlen(tmp);
> +		if (len > size - (p - buffer))
> +			goto fail;
> +		strcpy(p, tmp);
> +		p += len;
> +	}
> +	strncpy(p, ")", size - (p - buffer));
> +	p++;
> +	if (p > buffer + size)
> +		goto fail;
> +
> +	return TRUE;
> +fail:
> +	return FALSE;
> +}
> +
> +static int convert_entry(char *buffer, int size, char *entry)
> +{
> +	pattern_t ptype;
> +	int pos;
> +
> +	if (entry) {
> +		ptype = convert_aaregex_to_pcre(entry, 0, buffer, size, &pos);
> +		if (ptype == ePatternInvalid)
> +			return FALSE;
> +	} else {
> +		/* match any char except \000 0 or more times */
> +		if (size < 8)
> +			return FALSE;
> +
> +		strcpy(buffer, "[^\\000]*");
> +	}
> +	return TRUE;
> +}
> +
> +static int build_mnt_flags(char *buffer, int size, unsigned int flags,
> +			   unsigned int inv_flags)
> +{
> +	char *p = buffer;
> +	int i, len = 0;
> +
> +	if (flags == 0xffffffff) {
> +		/* all flags are optional */
> +		len = snprintf(p, size, "[^\\000]*");
> +		if (len < 0 || len >= size)
> +			return FALSE;
> +		return TRUE;
> +	}
> +	for (i = 0; i <= 31; ++i) {
> +		if ((flags & inv_flags) & (1 << i))
> +			len = snprintf(p, size, "(\\x%02x|)", i + 1);
> +		else if (flags & (1 << i))
> +			len = snprintf(p, size, "\\x%02x", i + 1);
> +		/* else     no entry = not set */
> +
> +		if (len < 0 || len >= size)
> +			return FALSE;
> +		p += len;
> +		size -= len;
> +	}
> +
> +	if (buffer == p) {
> +		/* match nothing - use impossible 254 as regex parser doesn't
> +		 * like the empty string
> +		 */
> +		if (size < 9)
> +			return FALSE;
> +
> +		strcpy(p, "(\\0xfe|)");
> +	}
> +
> +	return TRUE;
> +}
> +
> +static int build_mnt_opts(char *buffer, int size, struct value_list *opts)
> +{
> +	struct value_list *ent;
> +	char tmp[PATH_MAX + 3];
> +	char *p;
> +	int len;
> +	pattern_t ptype;
> +	int pos;
> +
> +	if (!opts) {
> +		if (size < 8)
> +			return FALSE;
> +		strncpy(buffer, "[^\\000]*", size);
> +		return TRUE;
> +	}
> +
> +	p = buffer;
> +	list_for_each(opts, ent) {
> +		ptype = convert_aaregex_to_pcre(ent->value, 0, tmp,
> +						PATH_MAX+3, &pos);
> +		if (ptype == ePatternInvalid)
> +			goto fail;
> +
> +		len = strlen(tmp);
> +		if (len > size - (p - buffer))
> +			goto fail;
> +		strcpy(p, tmp);
> +		p += len;
> +		if (ent->next && size - (p -buffer) > 1) {
> +			*p++ = ',';
> +			*p = 0;
> +		}
> +	}
> +
> +	return TRUE;
> +
> +fail:
> +	return FALSE;
> +}
> +
> +static int process_mnt_entry(aare_ruleset_t *dfarules, struct mnt_entry *entry)
> +{
> +	char mntbuf[PATH_MAX + 3];
> +	char devbuf[PATH_MAX + 3];
> +	char typebuf[PATH_MAX + 3];
> +	char flagsbuf[PATH_MAX + 3];
> +	char optsbuf[PATH_MAX + 3];
> +	char *p, *vec[5];
> +	int count = 0;
> +
> +	/* a single mount rule may result in multiple matching rules being
> +	 * created in the backend to cover all the possible choices
> +	 */
> +
> +	if ((entry->allow & AA_MAY_MOUNT) && (entry->flags & MS_REMOUNT)
> +	    && !entry->device && !entry->dev_type) {
> +		/* remount is can't be conditional on device and type */
> +		p = mntbuf;
> +		/* rule class single byte header */
> +		p += sprintf(p, "\\x%02x", AA_CLASS_MOUNT);
> +		if (entry->mnt_point) {
> +			/* both device && mnt_point or just mnt_point */
> +			if (!convert_entry(p, PATH_MAX +3, entry->mnt_point))
> +				goto fail;
> +			vec[0] = mntbuf;
> +		} else {
> +			if (!convert_entry(p, PATH_MAX +3, entry->device))
> +				goto fail;
> +			vec[0] = mntbuf;
> +		}
> +		/* skip device */
> +		if (!convert_entry(devbuf, PATH_MAX +3, NULL))
> +			goto fail;
> +		vec[1] = devbuf;
> +		/* skip type */
> +		vec[2] = devbuf;
> +		if (!build_mnt_flags(flagsbuf, PATH_MAX,
> +				     entry->flags & MS_REMOUNT_FLAGS,
> +				     entry->inv_flags & MS_REMOUNT_FLAGS))
> +			goto fail;
> +		vec[3] = flagsbuf;
> +		if (!build_mnt_opts(optsbuf, PATH_MAX, entry->opts))
> +			goto fail;
> +		vec[4] = optsbuf;
> +		if (!aare_add_rule_vec(dfarules, entry->deny, entry->allow,
> +				       entry->audit, 5, vec, dfaflags))
> +			goto fail;
> +		count++;
> +	}
> +	if ((entry->allow & AA_MAY_MOUNT) && (entry->flags & MS_BIND)
> +	    && !entry->dev_type && !entry->opts) {
> +		/* bind mount rules can't be conditional on dev_type or data */
> +		p = mntbuf;
> +		/* rule class single byte header */
> +		p += sprintf(p, "\\x%02x", AA_CLASS_MOUNT);
> +		if (!convert_entry(p, PATH_MAX +3, entry->mnt_point))
> +			goto fail;
> +		vec[0] = mntbuf;
> +		if (!convert_entry(devbuf, PATH_MAX +3, entry->device))
> +			goto fail;
> +		vec[1] = devbuf;
> +		if (!convert_entry(typebuf, PATH_MAX +3, NULL))
> +			goto fail;
> +		vec[2] = typebuf;
> +		if (!build_mnt_flags(flagsbuf, PATH_MAX,
> +				     entry->flags & MS_BIND_FLAGS,
> +				     entry->inv_flags & MS_BIND_FLAGS))
> +			goto fail;
> +		vec[3] = flagsbuf;
> +		if (!aare_add_rule_vec(dfarules, entry->deny, entry->allow,
> +				       entry->audit, 4, vec, dfaflags))
> +			goto fail;
> +		count++;
> +	}
> +	if ((entry->allow & AA_MAY_MOUNT) &&
> +	    (entry->flags & (MS_UNBINDABLE | MS_PRIVATE | MS_SLAVE | MS_SHARED))
> +	    && !entry->device && !entry->dev_type && !entry->opts) {
> +		/* change type base rules can not be conditional on device,
> +		 * device type or data
> +		 */
> +		p = mntbuf;
> +		/* rule class single byte header */
> +		p += sprintf(p, "\\x%02x", AA_CLASS_MOUNT);
> +		if (!convert_entry(p, PATH_MAX +3, entry->mnt_point))
> +			goto fail;
> +		vec[0] = mntbuf;
> +		/* skip device and type */
> +		if (!convert_entry(devbuf, PATH_MAX +3, NULL))
> +			goto fail;
> +		vec[1] = devbuf;
> +		vec[2] = devbuf;
> +		if (!build_mnt_flags(flagsbuf, PATH_MAX,
> +				     entry->flags & MS_MAKE_FLAGS,
> +				     entry->inv_flags & MS_MAKE_FLAGS))
> +			goto fail;
> +		vec[3] = flagsbuf;
> +		if (!aare_add_rule_vec(dfarules, entry->deny, entry->allow,
> +				       entry->audit, 4, vec, dfaflags))
> +			goto fail;
> +		count++;
> +	}
> +	if ((entry->allow & AA_MAY_MOUNT) && (entry->flags & MS_MOVE)
> +	    && !entry->dev_type && !entry->opts) {
> +		/* mount move rules can not be conditional on dev_type,
> +		 * or data
> +		 */
> +		p = mntbuf;
> +		/* rule class single byte header */
> +		p += sprintf(p, "\\x%02x", AA_CLASS_MOUNT);
> +		if (!convert_entry(p, PATH_MAX +3, entry->mnt_point))
> +			goto fail;
> +		vec[0] = mntbuf;
> +		if (!convert_entry(devbuf, PATH_MAX +3, entry->device))
> +			goto fail;
> +		vec[1] = devbuf;
> +		/* skip type */
> +		if (!convert_entry(typebuf, PATH_MAX +3, NULL))
> +			goto fail;
> +		vec[2] = typebuf;
> +		if (!build_mnt_flags(flagsbuf, PATH_MAX,
> +				     entry->flags & MS_MOVE_FLAGS,
> +				     entry->inv_flags & MS_MOVE_FLAGS))
> +			goto fail;
> +		vec[3] = flagsbuf;
> +		if (!aare_add_rule_vec(dfarules, entry->deny, entry->allow,
> +				       entry->audit, 4, vec, dfaflags))
> +			goto fail;
> +		count++;
> +	}
> +	if ((entry->allow & AA_MAY_MOUNT) &&
> +	    (entry->flags | entry->inv_flags) & ~MS_CMDS) {
> +		/* generic mount if flags are set that are not covered by
> +		 * above commands
> +		 */
> +		p = mntbuf;
> +		/* rule class single byte header */
> +		p += sprintf(p, "\\x%02x", AA_CLASS_MOUNT);
> +		if (!convert_entry(p, PATH_MAX +3, entry->mnt_point))
> +			goto fail;
> +		vec[0] = mntbuf;
> +		if (!convert_entry(devbuf, PATH_MAX +3, entry->device))
> +			goto fail;
> +		vec[1] = devbuf;
> +		if (!build_list_val_expr(typebuf, PATH_MAX+2, entry->dev_type))
> +			goto fail;
> +		vec[2] = typebuf;
> +		if (!build_mnt_flags(flagsbuf, PATH_MAX,
> +				     entry->flags & ~MS_CMDS,
> +				     entry->inv_flags & ~MS_CMDS))
> +			goto fail;
> +		vec[3] = flagsbuf;
> +		if (!build_mnt_opts(optsbuf, PATH_MAX, entry->opts))
> +			goto fail;
> +		vec[4] = optsbuf;
> +		if (!aare_add_rule_vec(dfarules, entry->deny, entry->allow,
> +				       entry->audit, 5, vec, dfaflags))
> +			goto fail;
> +		count++;
> +	}
> +	if (entry->allow & AA_MAY_UMOUNT) {
> +		p = mntbuf;
> +		/* rule class single byte header */
> +		p += sprintf(p, "\\x%02x", AA_CLASS_MOUNT);
> +		if (!convert_entry(p, PATH_MAX +3, entry->mnt_point))
> +			goto fail;
> +		vec[0] = mntbuf;
> +		if (!aare_add_rule_vec(dfarules, entry->deny, entry->allow,
> +				       entry->audit, 1, vec, dfaflags))
> +			goto fail;
> +		count++;
> +	}
> +	if (entry->allow & AA_MAY_PIVOTROOT) {
> +		p = mntbuf;
> +		/* rule class single byte header */
> +		p += sprintf(p, "\\x%02x", AA_CLASS_MOUNT);
> +		if (!convert_entry(p, PATH_MAX +3, entry->mnt_point))
> +			goto fail;
> +		vec[0] = mntbuf;
> +		if (!convert_entry(devbuf, PATH_MAX +3, entry->device))
> +			goto fail;
> +		vec[1] = devbuf;
> +		if (!aare_add_rule_vec(dfarules, entry->deny, entry->allow,
> +				       entry->audit, 2, vec, dfaflags))
> +			goto fail;
> +		count++;
> +	}
> +
> +	if (!count)
> +		/* didn't actually encode anything */
> +		goto fail;
> +
> +	return TRUE;
> +
> +fail:
> +	PERROR("Enocoding of mount rule failed\n");
> +	return FALSE;
> +}
> +
> +
>  int post_process_policydb_ents(struct codomain *cod)
>  {
>  	int ret = TRUE;
>  	int count = 0;
>  
>  	/* Add fns for rules that should be added to policydb here */
> +	if (cod->mnt_ents && kernel_supports_mount) {
> +		struct mnt_entry *entry;
> +		list_for_each(cod->mnt_ents, entry) {
> +			if (regex_type == AARE_DFA &&
> +			    !process_mnt_entry(cod->policy_rules, entry))
> +				ret = FALSE;
> +			count++;
> +		}
> +	} else if (cod->mnt_ents && !kernel_supports_mount)
> +		pwarn("profile %s mount rules not enforce\n", cod->name);
>  
>  	cod->policy_rule_count = count;
>  	return ret;
> @@ -631,6 +994,7 @@ int process_policydb(struct codomain *cod)
>  		if (!cod->policy_rules)
>  			goto out;
>  	}
> +
>  	if (!post_process_policydb_ents(cod))
>  		goto out;
>  
> @@ -644,6 +1008,7 @@ int process_policydb(struct codomain *cod)
>  			goto out;
>  	}
>  
> +	aare_reset_matchflags();
>  	if (process_hat_policydb(cod) != 0)
>  		goto out;
>  
> diff --git a/parser/parser_variable.c b/parser/parser_variable.c
> index 1afae18..6b5bd78 100644
> --- a/parser/parser_variable.c
> +++ b/parser/parser_variable.c
> @@ -28,6 +28,7 @@
>  /* #define DEBUG */
>  
>  #include "parser.h"
> +#include "mount.h"
>  
>  static inline char *get_var_end(char *var)
>  {
> @@ -130,17 +131,19 @@ void free_var_string(struct var_string *var)
>  	free(var);
>  }
>  
> -static int expand_entry_variables(struct cod_entry *entry)
> +/* doesn't handle variables in options atm */
> +static int expand_entry_variables(char **name, void *entry, 
> +				  int (dup_and_chain)(void *))
>  {
>  	struct set_value *valuelist;
>  	int ret = TRUE;
>  	char *value;
>  	struct var_string *split_var;
>  
> -	if (!entry) 		/* shouldn't happen */
> +	if (!entry) 		/* can happen when entry is optional */
>  		return ret;
>  
> -	while ((split_var = split_out_var(entry->name))) {
> +	while ((split_var = split_out_var(*name))) {
>  		valuelist = get_set_var(split_var->var);
>  		if (!valuelist) {
>  			int boolean = get_boolean_var(split_var->var);
> @@ -159,24 +162,22 @@ static int expand_entry_variables(struct cod_entry *entry)
>  			       split_var->var);
>  			exit(1);
>  		}
> -		free(entry->name);
> -		if (asprintf(&(entry->name), "%s%s%s",
> +		free(*name);
> +		if (asprintf(name, "%s%s%s",
>  			     split_var->prefix ? split_var->prefix : "",
>  			     value,
>  			     split_var->suffix ? split_var->suffix : "") == -1)
>  			return FALSE;
>  
>  		while ((value = get_next_set_value(&valuelist))) {
> -			struct cod_entry *dupe = copy_cod_entry(entry);
> -			if (!dupe) {
> +			if (!dup_and_chain(entry)) {
>  				PERROR("Memory allocaton error while handling set variable %s\n",
>  				       split_var->var);
>  				exit(1);
>  			}
> -			entry->next = dupe;
>  
> -			free(entry->name);
> -			if (asprintf(&(entry->name), "%s%s%s",
> +			free(*name);
> +			if (asprintf(name, "%s%s%s",
>  			      split_var->prefix ? split_var->prefix : "", value,
>  			      split_var->suffix ? split_var->suffix : "") == -1)
>  				return FALSE;
> @@ -187,15 +188,66 @@ static int expand_entry_variables(struct cod_entry *entry)
>  	return ret;
>  }
>  
> +int clone_and_chain_cod(void *v)
> +{
> +	struct cod_entry *entry = v;
> +	struct cod_entry *dup = copy_cod_entry(entry);
> +	if (!dup)
> +		return 0;
> +
> +	entry->next = dup;
> +
> +	return 1;
> +}
> +
> +int clone_and_chain_mnt(void *v)
> +{
> +	struct mnt_entry *entry = v;
> +
> +	struct mnt_entry *dup = dup_mnt_entry(entry);
> +	if (!dup)
> +		return 0;
> +
> +	entry->next = dup;
> +
> +	return 1;
> +}
> +
>  static int process_variables_in_entries(struct cod_entry *entry_list)
>  {
>  	int ret = TRUE, rc;
>  	struct cod_entry *entry;
>  
>  	list_for_each(entry_list, entry) {
> -		rc = expand_entry_variables(entry);
> +		rc = expand_entry_variables(&entry->name, entry,
> +					    clone_and_chain_cod);
>  		if (!rc)
> -			ret = FALSE;
> +			return FALSE;
> +	}
> +
> +	return ret;
> +}
> +
> +/* does not currently support expansion of vars in options */
> +static int process_variables_in_mnt_entries(struct mnt_entry *entry_list)
> +{
> +	int ret = TRUE, rc;
> +	struct mnt_entry *entry;
> +
> +	list_for_each(entry_list, entry) {
> +		rc = expand_entry_variables(&entry->mnt_point, entry,
> +					    clone_and_chain_mnt);
> +		if (!rc)
> +			return FALSE;
> +		rc = expand_entry_variables(&entry->device, entry,
> +					    clone_and_chain_mnt);
> +		if (!rc)
> +			return FALSE;
> +		rc = expand_entry_variables(&entry->trans, entry,
> +					    clone_and_chain_mnt);
> +		if (!rc)
> +			return FALSE;
> +
>  	}
>  
>  	return ret;
> @@ -209,6 +261,10 @@ int process_variables(struct codomain *cod)
>  		error = -1;
>  	}
>  
> +	if (!process_variables_in_mnt_entries(cod->mnt_ents)) {
> +		error = -1;
> +	}
> +
>  	if (process_hat_variables(cod) != 0) {
>  			error = -1;
>  	}
> diff --git a/parser/parser_yacc.y b/parser/parser_yacc.y
> index 9e05d24..2adf969 100644
> --- a/parser/parser_yacc.y
> +++ b/parser/parser_yacc.y
> @@ -32,6 +32,7 @@
>  /* #define DEBUG */
>  
>  #include "parser.h"
> +#include "mount.h"
>  #include "parser_include.h"
>  #include <unistd.h>
>  #include <netinet/in.h>
> @@ -68,6 +69,11 @@ int parser_token = 0;
>  
>  struct cod_entry *do_file_rule(char *namespace, char *id, int mode,
>  			       char *link_id, char *nt);
> +struct mnt_entry *do_mnt_rule(struct cond_entry *src_conds, char *src,
> +			      struct cond_entry *dst_conds, char *dst,
> +			      int mode);
> +struct mnt_entry *do_pivot_rule(struct cond_entry *old, char *root,
> +				char *transition);
>  
>  void add_local_entry(struct codomain *cod);
>  
> @@ -111,6 +117,10 @@ void add_local_entry(struct codomain *cod);
>  %token TOK_CLOSEPAREN
>  %token TOK_COMMA
>  %token TOK_FILE
> +%token TOK_MOUNT
> +%token TOK_REMOUNT
> +%token TOK_UMOUNT
> +%token TOK_PIVOTROOT
>  
>   /* rlimits */
>  %token TOK_RLIMIT
> @@ -146,6 +156,8 @@ void add_local_entry(struct codomain *cod);
>  	struct codomain *cod;
>  	struct cod_net_entry *net_entry;
>  	struct cod_entry *user_entry;
> +	struct mnt_entry *mnt_entry;
> +
>  	struct flagval flags;
>  	int fmode;
>  	uint64_t cap;
> @@ -154,6 +166,7 @@ void add_local_entry(struct codomain *cod);
>  	char *bool_var;
>  	char *var_val;
>  	struct value_list *val_list;
> +	struct cond_entry *cond_entry;
>  	int boolean;
>  	struct named_transition transition;
>  }
> @@ -175,6 +188,9 @@ void add_local_entry(struct codomain *cod);
>  %type <user_entry> link_rule
>  %type <user_entry> ptrace_rule
>  %type <user_entry> frule
> +%type <mnt_entry> mnt_rule
> +%type <cond_entry> opt_conds
> +%type <cond_entry> cond
>  %type <flags>	flags
>  %type <flags>	flagvals
>  %type <flags>	flagval
> @@ -241,6 +257,7 @@ profile_base: TOK_ID opt_id flags TOK_OPEN rules TOK_CLOSE
>  			cod->flags.complain = 1;
>  
>  		post_process_nt_entries(cod);
> +		post_process_mnt_entries(cod);
>  		PDEBUG("%s: flags='%s%s'\n",
>  		       $2,
>  		       cod->flags.complain ? "complain, " : "",
> @@ -386,8 +403,9 @@ valuelist:	valuelist TOK_VALUE
>  			yyerror(_("Memory allocation error."));
>  		PDEBUG("Matched: value list\n");
>  
> -		val->next = $1;
> -		$$ = val;
> +		list_append($1, val);
> +		//		val->next = $1;
> +		$$ = $1;
>  	}
>  
>  flags:	{ /* nothing */
> @@ -647,6 +665,25 @@ rules: rules opt_audit_flag network_rule
>  		$$ = $1;
>  	}
>  
> +rules:  rules opt_audit_flag TOK_DENY mnt_rule
> +	{
> +		$4->deny = $4->allow;
> +		if ($2)
> +			$4->audit = $4->allow;
> +		$4->next = $1->mnt_ents;
> +		$1->mnt_ents = $4;
> +		$$ = $1;
> +	}
> +
> +rules: rules opt_audit_flag mnt_rule
> +	{
> +		if ($2)
> +			$3->audit = $3->allow;
> +		$3->next = $1->mnt_ents;
> +		$1->mnt_ents = $3;
> +		$$ = $1;
> +	}
> +
>  rules:	rules change_profile
>  	{
>  		PDEBUG("matched: rules change_profile\n");
> @@ -1028,6 +1065,66 @@ network_rule: TOK_NETWORK TOK_ID TOK_ID TOK_END_OF_RULE
>  		$$ = entry;
>  	}
>  
> +cond: TOK_CONDID TOK_EQUALS TOK_VALUE
> +	{
> +		struct cond_entry *ent;
> +		struct value_list *value = new_value_list($3);
> +		if (!value)
> +			yyerror(_("Memory allocation error."));
> +		ent = new_cond_entry($1, value);
> +		if (!ent) {
> +			free_value_list(value);
> +			yyerror(_("Memory allocation error."));
> +		}
> +		$$ = ent;
> +	}
> +
> +cond: TOK_CONDID TOK_EQUALS TOK_OPENPAREN valuelist TOK_CLOSEPAREN
> +	{
> +		struct cond_entry *ent = new_cond_entry($1, $4);
> +
> +		if (!ent)
> +			yyerror(_("Memory allocation error."));
> +		$$ = ent;
> +	}
> +
> +opt_conds: { /* nothing */ $$ = NULL; }
> +	| opt_conds cond
> +	{
> +		$2->next = $1;
> +		$$ = $2;
> +	}
> +
> +mnt_rule: TOK_MOUNT opt_conds opt_id TOK_END_OF_RULE
> +	{
> +		$$ = do_mnt_rule($2, $3, NULL, NULL, AA_MAY_MOUNT);
> +	}
> +
> +mnt_rule: TOK_MOUNT opt_conds opt_id TOK_ARROW opt_conds TOK_ID TOK_END_OF_RULE
> +	{
> +		$$ = do_mnt_rule($2, $3, $5, $6, AA_MAY_MOUNT);
> +	}
> +
> +mnt_rule: TOK_REMOUNT opt_conds opt_id TOK_END_OF_RULE
> +	{
> +		$$ = do_mnt_rule($2, NULL, NULL, $3, AA_MAY_REMOUNT);
> +	}
> +
> +mnt_rule: TOK_UMOUNT opt_conds opt_id TOK_END_OF_RULE
> +	{
> +		$$ = do_mnt_rule($2, NULL, NULL, $3, AA_MAY_UMOUNT);
> +	}
> +
> +mnt_rule: TOK_PIVOTROOT opt_conds opt_id TOK_END_OF_RULE
> +	{
> +		$$ = do_pivot_rule($2, $3, NULL);
> +	}
> +
> +mnt_rule: TOK_PIVOTROOT opt_conds opt_id TOK_ARROW TOK_ID TOK_END_OF_RULE
> +	{
> +		$$ = do_pivot_rule($2, $3, $5);
> +	}
> +
>  hat_start: TOK_CARET {}
>  	| TOK_HAT {}
>  
> @@ -1084,14 +1181,11 @@ caps: { /* nothing */ $$ = 0; }
>  %%
>  #define MAXBUFSIZE 4096
>  
> -void yyerror(const char *msg, ...)
> +void vprintyyerror(const char *msg, va_list argptr)
>  {
> -	va_list arg;
>  	char buf[MAXBUFSIZE];
>  
> -	va_start(arg, msg);
> -	vsnprintf(buf, sizeof(buf), msg, arg);
> -	va_end(arg);
> +	vsnprintf(buf, sizeof(buf), msg, argptr);
>  
>  	if (profilename) {
>  		PERROR(_("AppArmor parser error for %s%s%s at line %d: %s\n"),
> @@ -1105,6 +1199,24 @@ void yyerror(const char *msg, ...)
>  		       current_filename ? current_filename : "",
>  		       current_lineno, buf);
>  	}
> +}
> +
> +void printyyerror(const char *msg, ...)
> +{
> +	va_list arg;
> +
> +	va_start(arg, msg);
> +	vprintyyerror(msg, arg);
> +	va_end(arg);
> +}
> +
> +void yyerror(const char *msg, ...)
> +{
> +	va_list arg;
> +
> +	va_start(arg, msg);
> +	vprintyyerror(msg, arg);
> +	va_end(arg);
>  
>  	exit(1);
>  }
> @@ -1146,3 +1258,74 @@ void add_local_entry(struct codomain *cod)
>  		add_entry_to_policy(cod, entry);
>  	}
>  }
> +
> +static char *mnt_cond_msg[] = {"",
> +			 " not allowed as source conditional",
> +			 " not allowed as target conditional",
> +			 "",
> +			 NULL};
> +
> +int verify_mnt_conds(struct cond_entry *conds, int src)
> +{
> +	struct cond_entry *entry;
> +	int error = 0;
> +
> +	if (!conds)
> +		return 0;
> +
> +	list_for_each(conds, entry) {
> +		int res = is_valid_mnt_cond(entry->name, src);
> +		if (res <= 0) {
> +				printyyerror(_("invalid mount conditional %s%s"),
> +					     entry->name,
> +					     res == -1 ? "" : mnt_cond_msg[src]);
> +				error++;
> +		}
> +	}
> +
> +	return error;
> +}
> +
> +struct mnt_entry *do_mnt_rule(struct cond_entry *src_conds, char *src,
> +			      struct cond_entry *dst_conds, char *dst,
> +			      int mode)
> +{
> +	struct mnt_entry *ent;
> +
> +	if (verify_mnt_conds(src_conds, MNT_SRC_OPT) != 0)
> +		yyerror(_("bad mount rule"));
> +
> +	/* FIXME: atm conditions are not supported on dst
> +	if (verify_conds(dst_conds, DST_OPT) != 0)
> +		yyerror(_("bad mount rule"));
> +	*/
> +	if (dst_conds)
> +		yyerror(_("mount point conditions not currently supported"));
> +
> +	ent = new_mnt_entry(src_conds, src, dst_conds, dst, mode);
> +	if (!ent) {
> +		yyerror(_("Memory allocation error."));
> +	}
> +
> +	return ent;
> +}
> +
> +struct mnt_entry *do_pivot_rule(struct cond_entry *old, char *root,
> +				char *transition)
> +{
> +	struct mnt_entry *ent = NULL;
> +
> +	if (old) {
> +		if (strcmp(old->name, "oldroot") != 0)
> +			yyerror(_("invalid pivotroot conditional '%s'"), old->name);
> +	}
> +
> +	ent = new_mnt_entry(NULL, old->vals->value, NULL, root,
> +			    AA_MAY_PIVOTROOT);
> +	ent->trans = transition;
> +
> +	old->vals->value = NULL;
> +	free_cond_entry(old);
> +
> +	return ent;
> +}
> diff --git a/parser/tst/simple_tests/mount/ok_1.sd b/parser/tst/simple_tests/mount/ok_1.sd
> new file mode 100644
> index 0000000..11eaded
> --- /dev/null
> +++ b/parser/tst/simple_tests/mount/ok_1.sd
> @@ -0,0 +1,7 @@
> +#
> +#=Description basic mount rule
> +#=EXRESULT PASS
> +#
> +/usr/bin/foo {
> +  mount,
> +}
> diff --git a/parser/tst/simple_tests/mount/ok_10.sd b/parser/tst/simple_tests/mount/ok_10.sd
> new file mode 100644
> index 0000000..bec51d7
> --- /dev/null
> +++ b/parser/tst/simple_tests/mount/ok_10.sd
> @@ -0,0 +1,7 @@
> +#
> +#=Description basic mount rule
> +#=EXRESULT PASS
> +#
> +/usr/bin/foo {
> +  mount fstype=(procfs) -> /foo,
> +}
> diff --git a/parser/tst/simple_tests/mount/ok_11.sd b/parser/tst/simple_tests/mount/ok_11.sd
> new file mode 100644
> index 0000000..101dc84
> --- /dev/null
> +++ b/parser/tst/simple_tests/mount/ok_11.sd
> @@ -0,0 +1,7 @@
> +#
> +#=Description basic mount rule
> +#=EXRESULT PASS
> +#
> +/usr/bin/foo {
> +  mount fstype=(procfs, sysfs) -> /foo,
> +}
> diff --git a/parser/tst/simple_tests/mount/ok_12.sd b/parser/tst/simple_tests/mount/ok_12.sd
> new file mode 100644
> index 0000000..e5508d5
> --- /dev/null
> +++ b/parser/tst/simple_tests/mount/ok_12.sd
> @@ -0,0 +1,7 @@
> +#
> +#=Description basic mount rule
> +#=EXRESULT PASS
> +#
> +/usr/bin/foo {
> +  mount fstype={procfs,sysfs} -> /foo,
> +}
> diff --git a/parser/tst/simple_tests/mount/ok_14.sd b/parser/tst/simple_tests/mount/ok_14.sd
> new file mode 100644
> index 0000000..e3b0670
> --- /dev/null
> +++ b/parser/tst/simple_tests/mount/ok_14.sd
> @@ -0,0 +1,7 @@
> +#
> +#=Description basic mount rule
> +#=EXRESULT PASS
> +#
> +/usr/bin/foo {
> +  mount fstype=(procfs sysfs) none -> /foo,
> +}
> diff --git a/parser/tst/simple_tests/mount/ok_15.sd b/parser/tst/simple_tests/mount/ok_15.sd
> new file mode 100644
> index 0000000..3b552f7
> --- /dev/null
> +++ b/parser/tst/simple_tests/mount/ok_15.sd
> @@ -0,0 +1,7 @@
> +#
> +#=Description basic mount rule
> +#=EXRESULT PASS
> +#
> +/usr/bin/foo {
> +  mount options=(rw) -> /foo,
> +}
> diff --git a/parser/tst/simple_tests/mount/ok_16.sd b/parser/tst/simple_tests/mount/ok_16.sd
> new file mode 100644
> index 0000000..12c21aa
> --- /dev/null
> +++ b/parser/tst/simple_tests/mount/ok_16.sd
> @@ -0,0 +1,7 @@
> +#
> +#=Description basic mount rule
> +#=EXRESULT PASS
> +#
> +/usr/bin/foo {
> +  mount options=(rw, ro) -> /foo,
> +}
> diff --git a/parser/tst/simple_tests/mount/ok_17.sd b/parser/tst/simple_tests/mount/ok_17.sd
> new file mode 100644
> index 0000000..08aa1bb
> --- /dev/null
> +++ b/parser/tst/simple_tests/mount/ok_17.sd
> @@ -0,0 +1,7 @@
> +#
> +#=Description basic mount rule
> +#=EXRESULT PASS
> +#
> +/usr/bin/foo {
> +  mount options=(rw ro) -> /foo,
> +}
> diff --git a/parser/tst/simple_tests/mount/ok_18.sd b/parser/tst/simple_tests/mount/ok_18.sd
> new file mode 100644
> index 0000000..96a93a2
> --- /dev/null
> +++ b/parser/tst/simple_tests/mount/ok_18.sd
> @@ -0,0 +1,7 @@
> +#
> +#=Description basic mount rule
> +#=EXRESULT PASS
> +#
> +/usr/bin/foo {
> +  mount options=(rw ro) fstype=procfs -> /foo,
> +}
> diff --git a/parser/tst/simple_tests/mount/ok_19.sd b/parser/tst/simple_tests/mount/ok_19.sd
> new file mode 100644
> index 0000000..c939849
> --- /dev/null
> +++ b/parser/tst/simple_tests/mount/ok_19.sd
> @@ -0,0 +1,7 @@
> +#
> +#=Description basic mount rule
> +#=EXRESULT PASS
> +#
> +/usr/bin/foo {
> +  mount options=(rw ro) fstype=(procfs) none -> /foo,
> +}
> diff --git a/parser/tst/simple_tests/mount/ok_2.sd b/parser/tst/simple_tests/mount/ok_2.sd
> new file mode 100644
> index 0000000..06dbbfb
> --- /dev/null
> +++ b/parser/tst/simple_tests/mount/ok_2.sd
> @@ -0,0 +1,7 @@
> +#
> +#=Description basic mount rule
> +#=EXRESULT PASS
> +#
> +/usr/bin/foo {
> +  mount /foo,
> +}
> diff --git a/parser/tst/simple_tests/mount/ok_20.sd b/parser/tst/simple_tests/mount/ok_20.sd
> new file mode 100644
> index 0000000..f22ac8d
> --- /dev/null
> +++ b/parser/tst/simple_tests/mount/ok_20.sd
> @@ -0,0 +1,7 @@
> +#
> +#=Description basic mount rule
> +#=EXRESULT PASS
> +#
> +/usr/bin/foo {
> +  mount options=(bind) /bar -> /foo,
> +}
> diff --git a/parser/tst/simple_tests/mount/ok_21.sd b/parser/tst/simple_tests/mount/ok_21.sd
> new file mode 100644
> index 0000000..b9634be
> --- /dev/null
> +++ b/parser/tst/simple_tests/mount/ok_21.sd
> @@ -0,0 +1,7 @@
> +#
> +#=Description basic mount rule
> +#=EXRESULT PASS
> +#
> +/usr/bin/foo {
> +  mount /bar -> /foo,
> +}
> diff --git a/parser/tst/simple_tests/mount/ok_22.sd b/parser/tst/simple_tests/mount/ok_22.sd
> new file mode 100644
> index 0000000..799b873
> --- /dev/null
> +++ b/parser/tst/simple_tests/mount/ok_22.sd
> @@ -0,0 +1,7 @@
> +#
> +#=Description basic mount rule
> +#=EXRESULT PASS
> +#
> +/usr/bin/foo {
> +  remount /foo,
> +}
> diff --git a/parser/tst/simple_tests/mount/ok_23.sd b/parser/tst/simple_tests/mount/ok_23.sd
> new file mode 100644
> index 0000000..96c3861
> --- /dev/null
> +++ b/parser/tst/simple_tests/mount/ok_23.sd
> @@ -0,0 +1,7 @@
> +#
> +#=Description basic mount rule
> +#=EXRESULT PASS
> +#
> +/usr/bin/foo {
> +  umount /foo,
> +}
> diff --git a/parser/tst/simple_tests/mount/ok_3.sd b/parser/tst/simple_tests/mount/ok_3.sd
> new file mode 100644
> index 0000000..24605bf
> --- /dev/null
> +++ b/parser/tst/simple_tests/mount/ok_3.sd
> @@ -0,0 +1,7 @@
> +#
> +#=Description basic mount rule
> +#=EXRESULT PASS
> +#
> +/usr/bin/foo {
> +  mount /**,
> +}
> diff --git a/parser/tst/simple_tests/mount/ok_4.sd b/parser/tst/simple_tests/mount/ok_4.sd
> new file mode 100644
> index 0000000..8d92cfb
> --- /dev/null
> +++ b/parser/tst/simple_tests/mount/ok_4.sd
> @@ -0,0 +1,7 @@
> +#
> +#=Description basic mount rule
> +#=EXRESULT PASS
> +#
> +/usr/bin/foo {
> +  mount /{foo,bar},
> +}
> diff --git a/parser/tst/simple_tests/mount/ok_5.sd b/parser/tst/simple_tests/mount/ok_5.sd
> new file mode 100644
> index 0000000..fe5ab12
> --- /dev/null
> +++ b/parser/tst/simple_tests/mount/ok_5.sd
> @@ -0,0 +1,10 @@
> +#
> +#=Description basic mount rule
> +#=EXRESULT PASS
> +#
> +
> +@{mntpnt}=/foo/bar
> +
> +/usr/bin/foo {
> +  mount @{mntpnt},
> +}
> diff --git a/parser/tst/simple_tests/mount/ok_8.sd b/parser/tst/simple_tests/mount/ok_8.sd
> new file mode 100644
> index 0000000..c9f64ca
> --- /dev/null
> +++ b/parser/tst/simple_tests/mount/ok_8.sd
> @@ -0,0 +1,9 @@
> +#
> +#=Description basic mount rule
> +#=EXRESULT PASS
> +#
> +@{dev}=/one /two /three /four
> +
> +/usr/bin/foo {
> +  mount @{dev} -> /foo,
> +}
> diff --git a/parser/tst/simple_tests/mount/ok_9.sd b/parser/tst/simple_tests/mount/ok_9.sd
> new file mode 100644
> index 0000000..7358139
> --- /dev/null
> +++ b/parser/tst/simple_tests/mount/ok_9.sd
> @@ -0,0 +1,7 @@
> +#
> +#=Description basic mount rule
> +#=EXRESULT PASS
> +#
> +/usr/bin/foo {
> +  mount none -> /foo,
> +}
> diff --git a/parser/tst/simple_tests/mount/okay_13.sd b/parser/tst/simple_tests/mount/okay_13.sd
> new file mode 100644
> index 0000000..9255c74
> --- /dev/null
> +++ b/parser/tst/simple_tests/mount/okay_13.sd
> @@ -0,0 +1,7 @@
> +#
> +#=Description basic mount rule
> +#=EXRESULT PASS
> +#
> +/usr/bin/foo {
> +  mount fstype=(procfs sysfs) -> /foo,
> +}
> diff --git a/parser/tst/simple_tests/mount/okay_6.sd b/parser/tst/simple_tests/mount/okay_6.sd
> new file mode 100644
> index 0000000..37d39c1
> --- /dev/null
> +++ b/parser/tst/simple_tests/mount/okay_6.sd
> @@ -0,0 +1,7 @@
> +#
> +#=Description basic mount rule
> +#=EXRESULT PASS
> +#
> +/usr/bin/foo {
> +  mount /dev/bar -> /foo,
> +}
> diff --git a/parser/tst/simple_tests/mount/okay_7.sd b/parser/tst/simple_tests/mount/okay_7.sd
> new file mode 100644
> index 0000000..10ac4b0
> --- /dev/null
> +++ b/parser/tst/simple_tests/mount/okay_7.sd
> @@ -0,0 +1,7 @@
> +#
> +#=Description basic mount rule
> +#=EXRESULT PASS
> +#
> +/usr/bin/foo {
> +  mount /dev/** -> /foo,
> +}
> diff --git a/parser/tst/simple_tests/profile/flags/flags_hats_ok.sd b/parser/tst/simple_tests/profile/flags/flags_hats_ok.sd
> index dee9590..5a1fcfd 100644
> --- a/parser/tst/simple_tests/profile/flags/flags_hats_ok.sd
> +++ b/parser/tst/simple_tests/profile/flags/flags_hats_ok.sd
> @@ -145,11 +145,11 @@
>      #include <includes/base>
>    }
>  
> -  ^BUZ flags=(complain,audit) {
> +  ^BUZ flags=(complain, audit) {
>      /var/log/messages r,
>    }
>  
> -  ^BUZ2 (complain,audit) {
> +  ^BUZ2 (complain, audit) {
>      /var/log/messages r,
>    }
>  }
> @@ -295,11 +295,11 @@
>      #include <includes/base>
>    }
>  
> -  ^BUZ flags=(complain,audit) {
> +  ^BUZ flags=(complain, audit) {
>      /var/log/messages r,
>    }
>  
> -  ^BUZ2 (complain,audit) {
> +  ^BUZ2 (complain, audit) {
>      /var/log/messages r,
>    }
>  }
> diff --git a/parser/tst/simple_tests/profile/flags/flags_ok.sd b/parser/tst/simple_tests/profile/flags/flags_ok.sd
> index 96c882b..212cb5c 100644
> --- a/parser/tst/simple_tests/profile/flags/flags_ok.sd
> +++ b/parser/tst/simple_tests/profile/flags/flags_ok.sd
> @@ -18,21 +18,21 @@
>    /does/not/exist2 r,
>  }
>  
> -/does/not/exist3 flags=(complain,audit) {
> +/does/not/exist3 flags=(complain, audit) {
>    #include <includes/base>
>  
>    /usr/X11R6/lib/lib*so* r,
>    /does/not/exist5 r,
>  }
>  
> -/does/not/exist4 flags=(audit,complain) {
> +/does/not/exist4 flags=(audit, complain) {
>    #include <includes/base>
>  
>    /usr/X11R6/lib/lib*so* r,
>    /does/not/exist7 r,
>  }
>  
> -/does/not/exist5 flags=(audit,complain,audit) {
> +/does/not/exist5 flags=(audit, complain, audit) {
>    #include <includes/base>
>  
>    /usr/X11R6/lib/lib*so* r,
> @@ -53,21 +53,21 @@
>    /does/not/exist2 r,
>  }
>  
> -/does/not/exist8 (complain,audit) {
> +/does/not/exist8 (complain, audit) {
>    #include <includes/base>
>  
>    /usr/X11R6/lib/lib*so* r,
>    /does/not/exist5 r,
>  }
>  
> -/does/not/exist9 (audit,complain) {
> +/does/not/exist9 (audit, complain) {
>    #include <includes/base>
>  
>    /usr/X11R6/lib/lib*so* r,
>    /does/not/exist7 r,
>  }
>  
> -/does/not/exist10 (audit,complain,audit) {
> +/does/not/exist10 (audit, complain, audit) {

So lacking the space is invalid now? Is that a problem for migrating profiles?

Also, can you include some negative test cases, like:

#
#=Description basic mount rule
#=EXRESULT FAIL
#
/usr/bin/foo {
  mount fstype=(procfs sysfs monkeyfs) -> /foo,
}

>    #include <includes/base>
>  
>    /usr/X11R6/lib/lib*so* r,

Otherwise, looks great! Very exciting. :)

-Kees

-- 
Kees Cook



More information about the AppArmor mailing list