[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