[apparmor] [PATCH 3/3] Add mount rules
John Johansen
john.johansen at canonical.com
Wed Feb 22 23:45:51 UTC 2012
On 02/22/2012 03:29 PM, Kees Cook wrote:
> 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?
>
yeah
>> +
>> +#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?
>
err, no I missed pulling that one. Originally I had made the change but after
talking to sbeattie about it, I reverted the change. So to be consistent
comma and space are treated as delimeters. It is a bit of a pain for mount
rules but its better to be consistent.
> 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
>
More information about the AppArmor
mailing list