2006-04-11 21:52:54 +00:00
|
|
|
/*
|
2007-04-11 08:12:51 +00:00
|
|
|
* Copyright (c) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007
|
|
|
|
* NOVELL (All rights reserved)
|
2006-04-11 21:52:54 +00:00
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* assistance routines */
|
|
|
|
|
2012-01-02 16:48:44 -08:00
|
|
|
#include <assert.h>
|
2010-07-31 16:00:52 -07:00
|
|
|
#include <ctype.h>
|
2006-04-11 21:52:54 +00:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <stdarg.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <libintl.h>
|
|
|
|
#define _(s) gettext(s)
|
|
|
|
#include <netinet/in.h>
|
2009-06-10 17:51:09 +00:00
|
|
|
#include <linux/socket.h>
|
2010-07-31 16:00:52 -07:00
|
|
|
#include <linux/limits.h>
|
2006-04-11 21:52:54 +00:00
|
|
|
#include <arpa/inet.h>
|
2007-11-16 09:32:38 +00:00
|
|
|
#include <linux/capability.h>
|
2011-03-03 15:45:10 -08:00
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <unistd.h>
|
2006-04-11 21:52:54 +00:00
|
|
|
|
|
|
|
#include "parser.h"
|
|
|
|
#include "parser_yacc.h"
|
Add mount rules
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@canonical.com>
2012-02-24 04:19:38 -08:00
|
|
|
#include "mount.h"
|
2013-07-31 09:05:51 -07:00
|
|
|
#include "dbus.h"
|
2006-04-11 21:52:54 +00:00
|
|
|
|
|
|
|
/* #define DEBUG */
|
|
|
|
#ifdef DEBUG
|
2010-07-26 09:22:45 -07:00
|
|
|
#undef PDEBUG
|
2006-04-11 21:52:54 +00:00
|
|
|
#define PDEBUG(fmt, args...) printf("Lexer: " fmt, ## args)
|
|
|
|
#else
|
2010-07-26 09:22:45 -07:00
|
|
|
#undef PDEBUG
|
2006-04-11 21:52:54 +00:00
|
|
|
#define PDEBUG(fmt, args...) /* Do nothing */
|
|
|
|
#endif
|
|
|
|
#define NPDEBUG(fmt, args...) /* Do nothing */
|
|
|
|
|
|
|
|
struct keyword_table {
|
|
|
|
char *keyword;
|
|
|
|
int token;
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct keyword_table keyword_table[] = {
|
|
|
|
/* network */
|
2007-07-27 20:29:47 +00:00
|
|
|
{"network", TOK_NETWORK},
|
2006-04-11 21:52:54 +00:00
|
|
|
/* misc keywords */
|
2007-11-16 09:32:38 +00:00
|
|
|
{"capability", TOK_CAPABILITY},
|
2006-04-11 21:52:54 +00:00
|
|
|
{"if", TOK_IF},
|
|
|
|
{"else", TOK_ELSE},
|
|
|
|
{"not", TOK_NOT},
|
|
|
|
{"defined", TOK_DEFINED},
|
2007-06-26 21:10:28 +00:00
|
|
|
{"change_profile", TOK_CHANGE_PROFILE},
|
2007-07-27 20:55:25 +00:00
|
|
|
{"unsafe", TOK_UNSAFE},
|
2010-12-20 11:58:05 -08:00
|
|
|
{"safe", TOK_SAFE},
|
2007-11-16 09:37:31 +00:00
|
|
|
{"link", TOK_LINK},
|
2007-11-29 18:06:53 +00:00
|
|
|
{"owner", TOK_OWNER},
|
Add Audit control to AppArmor through, the use of audit and deny
key words. Deny is also used to subtract permissions from the
profiles permission set.
the audit key word can be prepended to any file, network, or capability
rule, to force a selective audit when that rule is matched. Audit
permissions accumulate just like standard permissions.
eg.
audit /bin/foo rw,
will force an audit message when the file /bin/foo is opened for
read or write.
audit /etc/shadow w,
/etc/shadow r,
will force an audit message when /etc/shadow is opened for writing.
The audit message is per permission bit so only opening the file
for read access will not, force an audit message.
audit can also be used in block form instead of prepending audit
to every rule.
audit {
/bin/foo rw,
/etc/shadow w,
}
/etc/shadow r, # don't audit r access to /etc/shadow
the deny key word can be prepended to file, network and capability
rules, to result in a denial of permissions when matching that rule.
The deny rule specifically does 3 things
- it gives AppArmor the ability to remember what has been denied
so that the tools don't prompt for what has been denied in
previous profiling sessions.
- it subtracts globally from the allowed permissions. Deny permissions
accumulate in the the deny set just as allow permissions accumulate
then, the deny set is subtracted from the allow set.
- it quiets known rejects. The default audit behavior of deny rules
is to quiet known rejects so that audit logs are not flooded
with already known rejects. To have known rejects logged prepend
the audit keyword to the deny rule. Deny rules do not have a
block form.
eg.
deny /foo/bar rw,
audit deny /etc/shadow w,
audit {
deny owner /blah w,
deny other /foo w,
deny /etc/shadow w,
}
2008-03-13 17:39:03 +00:00
|
|
|
{"user", TOK_OWNER},
|
|
|
|
{"other", TOK_OTHER},
|
2008-03-13 16:49:10 +00:00
|
|
|
{"subset", TOK_SUBSET},
|
Add Audit control to AppArmor through, the use of audit and deny
key words. Deny is also used to subtract permissions from the
profiles permission set.
the audit key word can be prepended to any file, network, or capability
rule, to force a selective audit when that rule is matched. Audit
permissions accumulate just like standard permissions.
eg.
audit /bin/foo rw,
will force an audit message when the file /bin/foo is opened for
read or write.
audit /etc/shadow w,
/etc/shadow r,
will force an audit message when /etc/shadow is opened for writing.
The audit message is per permission bit so only opening the file
for read access will not, force an audit message.
audit can also be used in block form instead of prepending audit
to every rule.
audit {
/bin/foo rw,
/etc/shadow w,
}
/etc/shadow r, # don't audit r access to /etc/shadow
the deny key word can be prepended to file, network and capability
rules, to result in a denial of permissions when matching that rule.
The deny rule specifically does 3 things
- it gives AppArmor the ability to remember what has been denied
so that the tools don't prompt for what has been denied in
previous profiling sessions.
- it subtracts globally from the allowed permissions. Deny permissions
accumulate in the the deny set just as allow permissions accumulate
then, the deny set is subtracted from the allow set.
- it quiets known rejects. The default audit behavior of deny rules
is to quiet known rejects so that audit logs are not flooded
with already known rejects. To have known rejects logged prepend
the audit keyword to the deny rule. Deny rules do not have a
block form.
eg.
deny /foo/bar rw,
audit deny /etc/shadow w,
audit {
deny owner /blah w,
deny other /foo w,
deny /etc/shadow w,
}
2008-03-13 17:39:03 +00:00
|
|
|
{"audit", TOK_AUDIT},
|
|
|
|
{"deny", TOK_DENY},
|
2008-04-06 18:55:27 +00:00
|
|
|
{"set", TOK_SET},
|
2008-04-06 18:55:46 +00:00
|
|
|
{"rlimit", TOK_RLIMIT},
|
2008-04-09 09:03:17 +00:00
|
|
|
{"alias", TOK_ALIAS},
|
2009-06-10 20:26:31 +00:00
|
|
|
{"rewrite", TOK_ALIAS},
|
2008-04-09 09:04:08 +00:00
|
|
|
{"ptrace", TOK_PTRACE},
|
2012-02-16 08:06:04 -08:00
|
|
|
{"file", TOK_FILE},
|
Add mount rules
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@canonical.com>
2012-02-24 04:19:38 -08:00
|
|
|
{"mount", TOK_MOUNT},
|
|
|
|
{"remount", TOK_REMOUNT},
|
|
|
|
{"umount", TOK_UMOUNT},
|
|
|
|
{"unmount", TOK_UMOUNT},
|
2012-03-15 12:14:15 -07:00
|
|
|
{"pivot_root", TOK_PIVOTROOT},
|
2012-03-26 06:17:40 -07:00
|
|
|
{"in", TOK_IN},
|
2013-07-31 09:05:51 -07:00
|
|
|
{"dbus", TOK_DBUS},
|
|
|
|
{"send", TOK_SEND},
|
|
|
|
{"receive", TOK_RECEIVE},
|
|
|
|
{"bind", TOK_BIND},
|
|
|
|
{"read", TOK_READ},
|
|
|
|
{"write", TOK_WRITE},
|
|
|
|
{"peer", TOK_PEER},
|
|
|
|
|
2008-04-06 18:55:46 +00:00
|
|
|
/* terminate */
|
|
|
|
{NULL, 0}
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct keyword_table rlimit_table[] = {
|
|
|
|
{"cpu", RLIMIT_CPU},
|
|
|
|
{"fsize", RLIMIT_FSIZE},
|
|
|
|
{"data", RLIMIT_DATA},
|
|
|
|
{"stack", RLIMIT_STACK},
|
|
|
|
{"core", RLIMIT_CORE},
|
|
|
|
{"rss", RLIMIT_RSS},
|
|
|
|
{"nofile", RLIMIT_NOFILE},
|
|
|
|
{"ofile", RLIMIT_OFILE},
|
|
|
|
{"as", RLIMIT_AS},
|
|
|
|
{"nproc", RLIMIT_NPROC},
|
|
|
|
{"memlock", RLIMIT_MEMLOCK},
|
|
|
|
{"locks", RLIMIT_LOCKS},
|
|
|
|
{"sigpending", RLIMIT_SIGPENDING},
|
|
|
|
{"msgqueue", RLIMIT_MSGQUEUE},
|
2008-06-09 21:17:41 +00:00
|
|
|
#ifdef RLIMIT_NICE
|
2008-04-06 18:55:46 +00:00
|
|
|
{"nice", RLIMIT_NICE},
|
2008-06-09 21:17:41 +00:00
|
|
|
#endif
|
|
|
|
#ifdef RLIMIT_RTPRIO
|
2008-04-06 18:55:46 +00:00
|
|
|
{"rtprio", RLIMIT_RTPRIO},
|
2008-06-09 21:17:41 +00:00
|
|
|
#endif
|
2006-04-11 21:52:54 +00:00
|
|
|
/* terminate */
|
|
|
|
{NULL, 0}
|
|
|
|
};
|
|
|
|
|
|
|
|
/* for alpha matches, check for keywords */
|
2010-07-26 09:22:45 -07:00
|
|
|
static int get_table_token(const char *name __unused, struct keyword_table *table,
|
2007-11-16 09:32:38 +00:00
|
|
|
const char *keyword)
|
2006-04-11 21:52:54 +00:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
2007-11-16 09:32:38 +00:00
|
|
|
for (i = 0; table[i].keyword; i++) {
|
|
|
|
PDEBUG("Checking %s %s\n", name, table[i].keyword);
|
|
|
|
if (strcmp(keyword, table[i].keyword) == 0) {
|
|
|
|
PDEBUG("Found %s %s\n", name, table[i].keyword);
|
|
|
|
return table[i].token;
|
2006-04-11 21:52:54 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-11-16 09:32:38 +00:00
|
|
|
PDEBUG("Unable to find %s %s\n", name, keyword);
|
2006-04-11 21:52:54 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2007-11-16 09:32:38 +00:00
|
|
|
static struct keyword_table capability_table[] = {
|
|
|
|
/* capabilities */
|
|
|
|
#include "cap_names.h"
|
2011-08-18 23:54:24 +02:00
|
|
|
#ifndef CAP_SYSLOG
|
|
|
|
{"syslog", 34},
|
|
|
|
#endif
|
2007-11-16 09:32:38 +00:00
|
|
|
/* terminate */
|
|
|
|
{NULL, 0}
|
|
|
|
};
|
|
|
|
|
|
|
|
/* for alpha matches, check for keywords */
|
|
|
|
int get_keyword_token(const char *keyword)
|
|
|
|
{
|
|
|
|
return get_table_token("keyword", keyword_table, keyword);
|
|
|
|
}
|
|
|
|
|
|
|
|
int name_to_capability(const char *keyword)
|
|
|
|
{
|
|
|
|
return get_table_token("capability", capability_table, keyword);
|
|
|
|
}
|
|
|
|
|
2008-04-06 18:55:46 +00:00
|
|
|
int get_rlimit(const char *name)
|
|
|
|
{
|
|
|
|
return get_table_token("rlimit", rlimit_table, name);
|
|
|
|
}
|
|
|
|
|
2007-07-27 20:29:47 +00:00
|
|
|
struct network_tuple {
|
|
|
|
char *family_name;
|
|
|
|
unsigned int family;
|
|
|
|
char *type_name;
|
|
|
|
unsigned int type;
|
|
|
|
char *protocol_name;
|
|
|
|
unsigned int protocol;
|
|
|
|
};
|
|
|
|
|
|
|
|
/* FIXME: currently just treating as a bit mask this will have to change
|
|
|
|
* set up a table of mappings, there can be several mappings for a
|
|
|
|
* given match.
|
|
|
|
* currently the mapping does not set the protocol for stream/dgram to
|
|
|
|
* anything other than 0.
|
|
|
|
* network inet tcp -> network inet stream 0 instead of
|
|
|
|
* network inet raw tcp.
|
|
|
|
* some entries are just provided for completeness at this time
|
|
|
|
*/
|
|
|
|
/* values stolen from /etc/protocols - needs to change */
|
|
|
|
#define RAW_TCP 6
|
|
|
|
#define RAW_UDP 17
|
|
|
|
#define RAW_ICMP 1
|
|
|
|
#define RAW_ICMPv6 58
|
|
|
|
|
|
|
|
/* used by af_name.h to auto generate table entries for "name", AF_NAME
|
|
|
|
* pair */
|
2012-06-07 00:41:04 -07:00
|
|
|
#define AA_GEN_NET_ENT(name, AF) \
|
|
|
|
{name, AF, "stream", SOCK_STREAM, "", 0xffffff}, \
|
|
|
|
{name, AF, "dgram", SOCK_DGRAM, "", 0xffffff}, \
|
|
|
|
{name, AF, "seqpacket", SOCK_SEQPACKET, "", 0xffffff}, \
|
|
|
|
{name, AF, "rdm", SOCK_RDM, "", 0xffffff}, \
|
|
|
|
{name, AF, "raw", SOCK_RAW, "", 0xffffff}, \
|
|
|
|
{name, AF, "packet", SOCK_PACKET, "", 0xffffff},
|
2007-07-27 20:29:47 +00:00
|
|
|
/*FIXME: missing {name, AF, "dccp", SOCK_DCCP, "", 0xfffffff}, */
|
|
|
|
|
|
|
|
static struct network_tuple network_mappings[] = {
|
|
|
|
/* basic types */
|
|
|
|
#include "af_names.h"
|
|
|
|
/* FIXME: af_names.h is missing AF_LLC, AF_TIPC */
|
|
|
|
/* mapped types */
|
|
|
|
{"inet", AF_INET, "raw", SOCK_RAW,
|
|
|
|
"tcp", 1 << RAW_TCP},
|
|
|
|
{"inet", AF_INET, "raw", SOCK_RAW,
|
|
|
|
"udp", 1 << RAW_UDP},
|
|
|
|
{"inet", AF_INET, "raw", SOCK_RAW,
|
|
|
|
"icmp", 1 << RAW_ICMP},
|
|
|
|
{"inet", AF_INET, "tcp", SOCK_STREAM,
|
|
|
|
"", 0xffffffff}, /* should we give raw tcp too? */
|
|
|
|
{"inet", AF_INET, "udp", SOCK_DGRAM,
|
|
|
|
"", 0xffffffff}, /* should these be open masks? */
|
|
|
|
{"inet", AF_INET, "icmp", SOCK_RAW,
|
|
|
|
"", 1 << RAW_ICMP},
|
|
|
|
{"inet6", AF_INET6, "tcp", SOCK_STREAM,
|
|
|
|
"", 0xffffffff},
|
|
|
|
{"inet6", AF_INET6, "udp", SOCK_DGRAM,
|
|
|
|
"", 0xffffffff},
|
|
|
|
/* what do we do with icmp on inet6?
|
|
|
|
{"inet6", AF_INET, "icmp", SOCK_RAW, 0},
|
|
|
|
{"inet6", AF_INET, "icmpv6", SOCK_RAW, 0},
|
|
|
|
*/
|
|
|
|
/* terminate */
|
|
|
|
{NULL, 0, NULL, 0, NULL, 0}
|
|
|
|
};
|
|
|
|
|
2011-03-03 15:45:10 -08:00
|
|
|
/* The apparmor kernel patches up until 2.6.38 didn't handle networking
|
|
|
|
* tables with sizes > AF_MAX correctly. This could happen when the
|
|
|
|
* parser was built against newer kernel headers and then used to load
|
|
|
|
* policy on an older kernel. This could happen during upgrades or
|
|
|
|
* in multi-kernel boot systems.
|
|
|
|
*
|
|
|
|
* Try to detect the running kernel version and use that to determine
|
|
|
|
* AF_MAX
|
|
|
|
*/
|
|
|
|
#define PROC_VERSION "/proc/sys/kernel/osrelease"
|
|
|
|
static size_t kernel_af_max(void) {
|
|
|
|
char buffer[32];
|
|
|
|
int major;
|
|
|
|
int fd, res;
|
|
|
|
|
|
|
|
if (!net_af_max_override) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
/* the override parameter is specifying the max value */
|
|
|
|
if (net_af_max_override > 0)
|
|
|
|
return net_af_max_override;
|
|
|
|
|
|
|
|
fd = open(PROC_VERSION, O_RDONLY);
|
|
|
|
if (!fd)
|
|
|
|
/* fall back to default provided during build */
|
|
|
|
return 0;
|
|
|
|
res = read(fd, &buffer, sizeof(buffer));
|
|
|
|
close(fd);
|
|
|
|
if (!res)
|
|
|
|
return 0;
|
2011-03-03 15:53:23 -08:00
|
|
|
buffer[sizeof(buffer)-1] = '\0';
|
2011-03-03 15:45:10 -08:00
|
|
|
res = sscanf(buffer, "2.6.%d", &major);
|
|
|
|
if (res != 1)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
switch(major) {
|
|
|
|
case 24:
|
|
|
|
case 25:
|
|
|
|
case 26:
|
|
|
|
return 34;
|
|
|
|
case 27:
|
|
|
|
return 35;
|
|
|
|
case 28:
|
|
|
|
case 29:
|
|
|
|
case 30:
|
|
|
|
return 36;
|
|
|
|
case 31:
|
|
|
|
case 32:
|
|
|
|
case 33:
|
|
|
|
case 34:
|
|
|
|
case 35:
|
|
|
|
return 37;
|
|
|
|
case 36:
|
|
|
|
case 37:
|
|
|
|
return 38;
|
|
|
|
/* kernels .38 and later should handle this correctly so no
|
|
|
|
* static mapping needed
|
|
|
|
*/
|
|
|
|
default:
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-07-24 17:24:41 +00:00
|
|
|
/* Yuck. We grab AF_* values to define above from linux/socket.h because
|
|
|
|
* they are more accurate than sys/socket.h for what the kernel actually
|
|
|
|
* supports. However, we can't just include linux/socket.h directly,
|
|
|
|
* because the AF_* definitions are protected with an ifdef KERNEL
|
|
|
|
* wrapper, but we don't want to define that because that can cause
|
|
|
|
* other redefinitions from glibc. However, because the kernel may have
|
|
|
|
* more definitions than glibc, we need make sure AF_MAX reflects this,
|
|
|
|
* hence the wrapping function.
|
|
|
|
*/
|
|
|
|
size_t get_af_max() {
|
2011-03-03 15:45:10 -08:00
|
|
|
size_t af_max;
|
2009-11-11 10:58:57 -08:00
|
|
|
/* HACK: declare that version without "create" had a static AF_MAX */
|
2011-03-03 15:45:10 -08:00
|
|
|
if (!perms_create && !net_af_max_override)
|
|
|
|
net_af_max_override = -1;
|
|
|
|
|
2009-07-24 17:24:41 +00:00
|
|
|
#if AA_AF_MAX > AF_MAX
|
2011-03-03 15:45:10 -08:00
|
|
|
af_max = AA_AF_MAX;
|
2009-07-24 17:24:41 +00:00
|
|
|
#else
|
2011-03-03 15:45:10 -08:00
|
|
|
af_max = AF_MAX;
|
2009-07-24 17:24:41 +00:00
|
|
|
#endif
|
2011-03-03 15:45:10 -08:00
|
|
|
|
|
|
|
/* HACK: some kernels didn't handle network tables from parsers
|
|
|
|
* compiled against newer kernel headers as they are larger than
|
|
|
|
* the running kernel expected. If net_override is defined check
|
|
|
|
* to see if there is a static max specified for that kernel
|
|
|
|
*/
|
|
|
|
if (net_af_max_override) {
|
|
|
|
size_t max = kernel_af_max();
|
|
|
|
if (max && max < af_max)
|
|
|
|
return max;
|
|
|
|
}
|
|
|
|
|
|
|
|
return af_max;
|
2009-07-24 17:24:41 +00:00
|
|
|
}
|
2007-07-27 20:29:47 +00:00
|
|
|
struct aa_network_entry *new_network_ent(unsigned int family,
|
|
|
|
unsigned int type,
|
|
|
|
unsigned int protocol)
|
|
|
|
{
|
|
|
|
struct aa_network_entry *new_entry;
|
|
|
|
new_entry = calloc(1, sizeof(struct aa_network_entry));
|
|
|
|
if (new_entry) {
|
|
|
|
new_entry->family = family;
|
|
|
|
new_entry->type = type;
|
|
|
|
new_entry->protocol = protocol;
|
|
|
|
new_entry->next = NULL;
|
|
|
|
}
|
|
|
|
return new_entry;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct aa_network_entry *network_entry(const char *family, const char *type,
|
|
|
|
const char *protocol)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
struct aa_network_entry *new_entry, *entry = NULL;
|
|
|
|
|
|
|
|
for (i = 0; network_mappings[i].family_name; i++) {
|
|
|
|
if (family) {
|
|
|
|
PDEBUG("Checking family %s\n", network_mappings[i].family_name);
|
|
|
|
if (strcmp(family, network_mappings[i].family_name) != 0)
|
|
|
|
continue;
|
|
|
|
PDEBUG("Found family %s\n", family);
|
|
|
|
}
|
|
|
|
if (type) {
|
|
|
|
PDEBUG("Checking type %s\n", network_mappings[i].type_name);
|
|
|
|
if (strcmp(type, network_mappings[i].type_name) != 0)
|
|
|
|
continue;
|
|
|
|
PDEBUG("Found type %s\n", type);
|
|
|
|
}
|
|
|
|
if (protocol) {
|
|
|
|
PDEBUG("Checking protocol type %s\n", network_mappings[i].protocol_name);
|
|
|
|
if (strcmp(type, network_mappings[i].protocol_name) != 0)
|
|
|
|
continue;
|
|
|
|
/* fixme should we allow specifying protocol by #
|
|
|
|
* without needing the protocol mapping? */
|
|
|
|
}
|
|
|
|
/* if here we have a match */
|
|
|
|
new_entry = new_network_ent(network_mappings[i].family,
|
|
|
|
network_mappings[i].type,
|
|
|
|
network_mappings[i].protocol);
|
|
|
|
if (!new_entry)
|
|
|
|
yyerror(_("Memory allocation error."));
|
|
|
|
new_entry->next = entry;
|
|
|
|
entry = new_entry;
|
|
|
|
}
|
|
|
|
|
|
|
|
return entry;
|
|
|
|
};
|
|
|
|
|
2006-04-11 21:52:54 +00:00
|
|
|
char *processunquoted(char *string, int len)
|
|
|
|
{
|
|
|
|
char *tmp, *s;
|
|
|
|
int l;
|
|
|
|
|
|
|
|
tmp = (char *)malloc(len + 1);
|
|
|
|
if (!tmp)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
s = tmp;
|
|
|
|
for (l = 0; l < len; l++) {
|
|
|
|
if (string[l] == '\\' && l < len - 3) {
|
|
|
|
if (strchr("0123", string[l + 1]) &&
|
|
|
|
strchr("0123456789", string[l + 2]) &&
|
|
|
|
strchr("0123456789", string[l + 3])) {
|
|
|
|
/* three digit octal */
|
|
|
|
int res = (string[l + 1] - '0') * 64 +
|
|
|
|
(string[l + 2] - '0') * 8 +
|
|
|
|
(string[l + 3] - '0');
|
|
|
|
*s = res;
|
|
|
|
l += 3;
|
|
|
|
} else {
|
|
|
|
*s = string[l];
|
|
|
|
}
|
|
|
|
s++;
|
|
|
|
} else {
|
|
|
|
*s = string[l];
|
|
|
|
s++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
*s = 0;
|
|
|
|
|
|
|
|
return tmp;
|
2012-01-02 16:48:44 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
char *processid(char *string, int len)
|
|
|
|
{
|
|
|
|
/* lexer should never call this fn if len <= 0 */
|
|
|
|
assert(len > 0);
|
|
|
|
|
|
|
|
if (*string == '"')
|
|
|
|
return processquoted(string, len);
|
|
|
|
return processunquoted(string, len);
|
2006-04-11 21:52:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* rewrite a quoted string substituting escaped characters for the
|
|
|
|
* real thing. Strip the quotes around the string */
|
|
|
|
|
|
|
|
char *processquoted(char *string, int len)
|
|
|
|
{
|
|
|
|
char *tmp, *s;
|
|
|
|
int l;
|
|
|
|
/* the result string will be shorter or equal in length */
|
|
|
|
tmp = (char *)malloc(len + 1);
|
|
|
|
if (!tmp)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
s = tmp;
|
|
|
|
for (l = 1; l < len - 1; l++) {
|
|
|
|
if (string[l] == '\\' && l < len - 2) {
|
|
|
|
switch (string[l + 1]) {
|
|
|
|
case 't':
|
|
|
|
*s = '\t';
|
|
|
|
l++;
|
|
|
|
break;
|
|
|
|
case 'n':
|
|
|
|
*s = '\n';
|
|
|
|
l++;
|
|
|
|
break;
|
|
|
|
case 'r':
|
|
|
|
*s = '\r';
|
|
|
|
l++;
|
|
|
|
break;
|
|
|
|
case '"':
|
|
|
|
*s = '"';
|
|
|
|
l++;
|
|
|
|
break;
|
|
|
|
case '\\':
|
|
|
|
*s = '\\';
|
|
|
|
l++;
|
|
|
|
break;
|
2011-04-05 20:55:19 -07:00
|
|
|
case '0': case '1': case '2': case '3':
|
2006-04-11 21:52:54 +00:00
|
|
|
if ((l < len - 4) &&
|
|
|
|
strchr("0123456789", string[l + 2]) &&
|
|
|
|
strchr("0123456789", string[l + 3])) {
|
|
|
|
/* three digit octal */
|
|
|
|
int res = (string[l + 1] - '0') * 64 +
|
|
|
|
(string[l + 2] - '0') * 8 +
|
|
|
|
(string[l + 3] - '0');
|
|
|
|
*s = res;
|
|
|
|
l += 3;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
/* fall through */
|
|
|
|
default:
|
|
|
|
/* any unsupported escape sequence results in all
|
|
|
|
chars being copied. */
|
|
|
|
*s = string[l];
|
|
|
|
}
|
|
|
|
s++;
|
|
|
|
} else {
|
|
|
|
*s = string[l];
|
|
|
|
s++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
*s = 0;
|
|
|
|
|
|
|
|
return tmp;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* strip off surrounding delimiters around variables */
|
|
|
|
char *process_var(const char *var)
|
|
|
|
{
|
|
|
|
const char *orig = var;
|
|
|
|
int len = strlen(var);
|
|
|
|
|
|
|
|
if (*orig == '@' || *orig == '$') {
|
|
|
|
orig++;
|
|
|
|
len--;
|
|
|
|
} else {
|
|
|
|
PERROR("ASSERT: Found var '%s' without variable prefix\n",
|
|
|
|
var);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (*orig == '{') {
|
|
|
|
orig++;
|
|
|
|
len--;
|
|
|
|
if (orig[len - 1] != '}') {
|
|
|
|
PERROR("ASSERT: No matching '}' in variable '%s'\n",
|
|
|
|
var);
|
|
|
|
return NULL;
|
|
|
|
} else
|
|
|
|
len--;
|
|
|
|
}
|
|
|
|
|
|
|
|
return strndup(orig, len);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* returns -1 if value != true or false, otherwise 0 == false, 1 == true */
|
|
|
|
int str_to_boolean(const char *value)
|
|
|
|
{
|
|
|
|
int retval = -1;
|
|
|
|
|
|
|
|
if (strcasecmp("TRUE", value) == 0)
|
|
|
|
retval = 1;
|
|
|
|
if (strcasecmp("FALSE", value) == 0)
|
|
|
|
retval = 0;
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
[https://bugzilla.novell.com/show_bug.cgi?id=172061]
This (updated) patch to trunk adds support for Px and Ux (toggle
bprm_secure on exec) in the parser, As requested, lowercase p and u
corresponds to an unfiltered environmnet on exec, uppercase will filter
the environment. It applies after the 'm' patch.
As a side effect, I tried to reduce the use of hardcoded characters in
the debugging statements -- there are still a few warnings that have
hard coded letters in them; not sure I can fix them all.
This version issues a warning for every unsafe ux and issues a single
warning for the first 'R', 'W', 'X', 'L', and 'I' it encounters,
except when the "-q" or "--quiet" flag , "--remove" profile flag, or
"-N" report names flags are passed. Unfortunately, it made the logic
somewhat more convoluted. Wordsmithing improvements welcome.
2006-08-04 17:14:49 +00:00
|
|
|
static int warned_uppercase = 0;
|
|
|
|
|
|
|
|
static void warn_uppercase(void)
|
|
|
|
{
|
|
|
|
if (!warned_uppercase) {
|
2006-08-04 17:20:16 +00:00
|
|
|
pwarn(_("Uppercase qualifiers \"RWLIMX\" are deprecated, please convert to lowercase\n"
|
|
|
|
"See the apparmor.d(5) manpage for details.\n"));
|
[https://bugzilla.novell.com/show_bug.cgi?id=172061]
This (updated) patch to trunk adds support for Px and Ux (toggle
bprm_secure on exec) in the parser, As requested, lowercase p and u
corresponds to an unfiltered environmnet on exec, uppercase will filter
the environment. It applies after the 'm' patch.
As a side effect, I tried to reduce the use of hardcoded characters in
the debugging statements -- there are still a few warnings that have
hard coded letters in them; not sure I can fix them all.
This version issues a warning for every unsafe ux and issues a single
warning for the first 'R', 'W', 'X', 'L', and 'I' it encounters,
except when the "-q" or "--quiet" flag , "--remove" profile flag, or
"-N" report names flags are passed. Unfortunately, it made the logic
somewhat more convoluted. Wordsmithing improvements welcome.
2006-08-04 17:14:49 +00:00
|
|
|
warned_uppercase = 1;
|
|
|
|
}
|
|
|
|
}
|
2007-11-16 09:35:31 +00:00
|
|
|
|
2010-07-26 09:22:45 -07:00
|
|
|
static int parse_sub_mode(const char *str_mode, const char *mode_desc __unused)
|
2006-04-11 21:52:54 +00:00
|
|
|
{
|
|
|
|
|
2008-04-16 04:44:21 +00:00
|
|
|
#define IS_DIFF_QUAL(mode, q) (((mode) & AA_MAY_EXEC) && (((mode) & AA_EXEC_TYPE) != ((q) & AA_EXEC_TYPE)))
|
2006-04-11 21:52:54 +00:00
|
|
|
|
|
|
|
int mode = 0;
|
|
|
|
const char *p;
|
|
|
|
|
|
|
|
PDEBUG("Parsing mode: %s\n", str_mode);
|
|
|
|
|
2007-06-26 21:10:28 +00:00
|
|
|
if (!str_mode)
|
|
|
|
return 0;
|
|
|
|
|
2006-04-11 21:52:54 +00:00
|
|
|
p = str_mode;
|
|
|
|
while (*p) {
|
[https://bugzilla.novell.com/show_bug.cgi?id=172061]
This (updated) patch to trunk adds support for Px and Ux (toggle
bprm_secure on exec) in the parser, As requested, lowercase p and u
corresponds to an unfiltered environmnet on exec, uppercase will filter
the environment. It applies after the 'm' patch.
As a side effect, I tried to reduce the use of hardcoded characters in
the debugging statements -- there are still a few warnings that have
hard coded letters in them; not sure I can fix them all.
This version issues a warning for every unsafe ux and issues a single
warning for the first 'R', 'W', 'X', 'L', and 'I' it encounters,
except when the "-q" or "--quiet" flag , "--remove" profile flag, or
"-N" report names flags are passed. Unfortunately, it made the logic
somewhat more convoluted. Wordsmithing improvements welcome.
2006-08-04 17:14:49 +00:00
|
|
|
char this = *p;
|
|
|
|
char next = *(p + 1);
|
|
|
|
char lower;
|
2007-11-16 09:27:34 +00:00
|
|
|
int tmode = 0;
|
2006-04-11 21:52:54 +00:00
|
|
|
|
[https://bugzilla.novell.com/show_bug.cgi?id=172061]
This (updated) patch to trunk adds support for Px and Ux (toggle
bprm_secure on exec) in the parser, As requested, lowercase p and u
corresponds to an unfiltered environmnet on exec, uppercase will filter
the environment. It applies after the 'm' patch.
As a side effect, I tried to reduce the use of hardcoded characters in
the debugging statements -- there are still a few warnings that have
hard coded letters in them; not sure I can fix them all.
This version issues a warning for every unsafe ux and issues a single
warning for the first 'R', 'W', 'X', 'L', and 'I' it encounters,
except when the "-q" or "--quiet" flag , "--remove" profile flag, or
"-N" report names flags are passed. Unfortunately, it made the logic
somewhat more convoluted. Wordsmithing improvements welcome.
2006-08-04 17:14:49 +00:00
|
|
|
reeval:
|
2006-04-11 21:52:54 +00:00
|
|
|
switch (this) {
|
|
|
|
case COD_READ_CHAR:
|
2009-06-10 15:37:27 +00:00
|
|
|
if (read_implies_exec) {
|
2009-06-10 17:51:09 +00:00
|
|
|
PDEBUG("Parsing mode: found %s READ imply X\n", mode_desc);
|
|
|
|
mode |= AA_MAY_READ | AA_EXEC_MMAP;
|
2009-06-10 15:37:27 +00:00
|
|
|
} else {
|
|
|
|
PDEBUG("Parsing mode: found %s READ\n", mode_desc);
|
|
|
|
mode |= AA_MAY_READ;
|
|
|
|
}
|
2006-04-11 21:52:54 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case COD_WRITE_CHAR:
|
2007-11-16 09:35:31 +00:00
|
|
|
PDEBUG("Parsing mode: found %s WRITE\n", mode_desc);
|
2007-07-27 20:31:38 +00:00
|
|
|
if ((mode & AA_MAY_APPEND) && !(mode & AA_MAY_WRITE))
|
|
|
|
yyerror(_("Conflict 'a' and 'w' perms are mutually exclusive."));
|
|
|
|
mode |= AA_MAY_WRITE | AA_MAY_APPEND;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case COD_APPEND_CHAR:
|
2007-11-16 09:35:31 +00:00
|
|
|
PDEBUG("Parsing mode: found %s APPEND\n", mode_desc);
|
2007-07-27 20:31:38 +00:00
|
|
|
if (mode & AA_MAY_WRITE)
|
|
|
|
yyerror(_("Conflict 'a' and 'w' perms are mutually exclusive."));
|
|
|
|
mode |= AA_MAY_APPEND;
|
2006-04-11 21:52:54 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case COD_LINK_CHAR:
|
2007-11-16 09:35:31 +00:00
|
|
|
PDEBUG("Parsing mode: found %s LINK\n", mode_desc);
|
2007-02-27 02:29:16 +00:00
|
|
|
mode |= AA_MAY_LINK;
|
2006-04-11 21:52:54 +00:00
|
|
|
break;
|
|
|
|
|
2007-07-27 20:38:43 +00:00
|
|
|
case COD_LOCK_CHAR:
|
2007-11-16 09:35:31 +00:00
|
|
|
PDEBUG("Parsing mode: found %s LOCK\n", mode_desc);
|
2007-07-27 20:38:43 +00:00
|
|
|
mode |= AA_MAY_LOCK;
|
|
|
|
break;
|
|
|
|
|
2006-04-11 21:52:54 +00:00
|
|
|
case COD_INHERIT_CHAR:
|
|
|
|
PDEBUG("Parsing mode: found INHERIT\n");
|
2008-04-16 04:44:21 +00:00
|
|
|
if (mode & AA_EXEC_MODIFIERS) {
|
2006-04-11 21:52:54 +00:00
|
|
|
yyerror(_("Exec qualifier 'i' invalid, conflicting qualifier already specified"));
|
|
|
|
} else {
|
[https://bugzilla.novell.com/show_bug.cgi?id=172061]
This (updated) patch to trunk adds support for Px and Ux (toggle
bprm_secure on exec) in the parser, As requested, lowercase p and u
corresponds to an unfiltered environmnet on exec, uppercase will filter
the environment. It applies after the 'm' patch.
As a side effect, I tried to reduce the use of hardcoded characters in
the debugging statements -- there are still a few warnings that have
hard coded letters in them; not sure I can fix them all.
This version issues a warning for every unsafe ux and issues a single
warning for the first 'R', 'W', 'X', 'L', and 'I' it encounters,
except when the "-q" or "--quiet" flag , "--remove" profile flag, or
"-N" report names flags are passed. Unfortunately, it made the logic
somewhat more convoluted. Wordsmithing improvements welcome.
2006-08-04 17:14:49 +00:00
|
|
|
if (next != tolower(next))
|
|
|
|
warn_uppercase();
|
2007-11-16 09:27:34 +00:00
|
|
|
mode |= (AA_EXEC_INHERIT | AA_MAY_EXEC);
|
2006-04-11 21:52:54 +00:00
|
|
|
p++; /* skip 'x' */
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2007-07-27 20:45:45 +00:00
|
|
|
case COD_UNSAFE_UNCONFINED_CHAR:
|
2007-11-16 09:27:34 +00:00
|
|
|
tmode = AA_EXEC_UNSAFE;
|
2007-07-27 20:45:45 +00:00
|
|
|
pwarn(_("Unconfined exec qualifier (%c%c) allows some dangerous environment variables "
|
2006-08-04 17:20:16 +00:00
|
|
|
"to be passed to the unconfined process; 'man 5 apparmor.d' for details.\n"),
|
2007-07-27 20:45:45 +00:00
|
|
|
COD_UNSAFE_UNCONFINED_CHAR, COD_EXEC_CHAR);
|
[https://bugzilla.novell.com/show_bug.cgi?id=172061]
This (updated) patch to trunk adds support for Px and Ux (toggle
bprm_secure on exec) in the parser, As requested, lowercase p and u
corresponds to an unfiltered environmnet on exec, uppercase will filter
the environment. It applies after the 'm' patch.
As a side effect, I tried to reduce the use of hardcoded characters in
the debugging statements -- there are still a few warnings that have
hard coded letters in them; not sure I can fix them all.
This version issues a warning for every unsafe ux and issues a single
warning for the first 'R', 'W', 'X', 'L', and 'I' it encounters,
except when the "-q" or "--quiet" flag , "--remove" profile flag, or
"-N" report names flags are passed. Unfortunately, it made the logic
somewhat more convoluted. Wordsmithing improvements welcome.
2006-08-04 17:14:49 +00:00
|
|
|
/* fall through */
|
2007-07-27 20:45:45 +00:00
|
|
|
case COD_UNCONFINED_CHAR:
|
2008-04-16 04:44:21 +00:00
|
|
|
tmode |= AA_EXEC_UNCONFINED | AA_MAY_EXEC;
|
2007-07-27 20:45:45 +00:00
|
|
|
PDEBUG("Parsing mode: found UNCONFINED\n");
|
2008-04-16 04:44:21 +00:00
|
|
|
if (IS_DIFF_QUAL(mode, tmode)) {
|
2006-08-04 17:20:16 +00:00
|
|
|
yyerror(_("Exec qualifier '%c' invalid, conflicting qualifier already specified"),
|
|
|
|
this);
|
2006-04-11 21:52:54 +00:00
|
|
|
} else {
|
[https://bugzilla.novell.com/show_bug.cgi?id=172061]
This (updated) patch to trunk adds support for Px and Ux (toggle
bprm_secure on exec) in the parser, As requested, lowercase p and u
corresponds to an unfiltered environmnet on exec, uppercase will filter
the environment. It applies after the 'm' patch.
As a side effect, I tried to reduce the use of hardcoded characters in
the debugging statements -- there are still a few warnings that have
hard coded letters in them; not sure I can fix them all.
This version issues a warning for every unsafe ux and issues a single
warning for the first 'R', 'W', 'X', 'L', and 'I' it encounters,
except when the "-q" or "--quiet" flag , "--remove" profile flag, or
"-N" report names flags are passed. Unfortunately, it made the logic
somewhat more convoluted. Wordsmithing improvements welcome.
2006-08-04 17:14:49 +00:00
|
|
|
if (next != tolower(next))
|
|
|
|
warn_uppercase();
|
2008-04-16 04:44:21 +00:00
|
|
|
mode |= tmode;
|
2006-04-11 21:52:54 +00:00
|
|
|
p++; /* skip 'x' */
|
|
|
|
}
|
2007-11-16 09:27:34 +00:00
|
|
|
tmode = 0;
|
2006-04-11 21:52:54 +00:00
|
|
|
break;
|
|
|
|
|
[https://bugzilla.novell.com/show_bug.cgi?id=172061]
This (updated) patch to trunk adds support for Px and Ux (toggle
bprm_secure on exec) in the parser, As requested, lowercase p and u
corresponds to an unfiltered environmnet on exec, uppercase will filter
the environment. It applies after the 'm' patch.
As a side effect, I tried to reduce the use of hardcoded characters in
the debugging statements -- there are still a few warnings that have
hard coded letters in them; not sure I can fix them all.
This version issues a warning for every unsafe ux and issues a single
warning for the first 'R', 'W', 'X', 'L', and 'I' it encounters,
except when the "-q" or "--quiet" flag , "--remove" profile flag, or
"-N" report names flags are passed. Unfortunately, it made the logic
somewhat more convoluted. Wordsmithing improvements welcome.
2006-08-04 17:14:49 +00:00
|
|
|
case COD_UNSAFE_PROFILE_CHAR:
|
2008-04-16 04:44:21 +00:00
|
|
|
case COD_UNSAFE_LOCAL_CHAR:
|
2007-11-16 09:27:34 +00:00
|
|
|
tmode = AA_EXEC_UNSAFE;
|
[https://bugzilla.novell.com/show_bug.cgi?id=172061]
This (updated) patch to trunk adds support for Px and Ux (toggle
bprm_secure on exec) in the parser, As requested, lowercase p and u
corresponds to an unfiltered environmnet on exec, uppercase will filter
the environment. It applies after the 'm' patch.
As a side effect, I tried to reduce the use of hardcoded characters in
the debugging statements -- there are still a few warnings that have
hard coded letters in them; not sure I can fix them all.
This version issues a warning for every unsafe ux and issues a single
warning for the first 'R', 'W', 'X', 'L', and 'I' it encounters,
except when the "-q" or "--quiet" flag , "--remove" profile flag, or
"-N" report names flags are passed. Unfortunately, it made the logic
somewhat more convoluted. Wordsmithing improvements welcome.
2006-08-04 17:14:49 +00:00
|
|
|
/* fall through */
|
2006-04-11 21:52:54 +00:00
|
|
|
case COD_PROFILE_CHAR:
|
2008-04-16 04:44:21 +00:00
|
|
|
case COD_LOCAL_CHAR:
|
|
|
|
if (tolower(this) == COD_UNSAFE_PROFILE_CHAR)
|
|
|
|
tmode |= AA_EXEC_PROFILE | AA_MAY_EXEC;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
tmode |= AA_EXEC_LOCAL | AA_MAY_EXEC;
|
|
|
|
}
|
2006-04-11 21:52:54 +00:00
|
|
|
PDEBUG("Parsing mode: found PROFILE\n");
|
2007-11-16 09:27:34 +00:00
|
|
|
if (tolower(next) == COD_INHERIT_CHAR) {
|
2008-04-16 04:44:21 +00:00
|
|
|
tmode |= AA_EXEC_INHERIT;
|
|
|
|
if (IS_DIFF_QUAL(mode, tmode)) {
|
2007-11-16 09:27:34 +00:00
|
|
|
yyerror(_("Exec qualifier '%c%c' invalid, conflicting qualifier already specified"), this, next);
|
|
|
|
} else {
|
2008-04-16 04:44:21 +00:00
|
|
|
mode |= tmode;
|
2007-11-16 09:27:34 +00:00
|
|
|
p += 2; /* skip x */
|
|
|
|
}
|
2009-08-20 15:41:10 +00:00
|
|
|
} else if (tolower(next) == COD_UNSAFE_UNCONFINED_CHAR) {
|
|
|
|
tmode |= AA_EXEC_PUX;
|
|
|
|
if (IS_DIFF_QUAL(mode, tmode)) {
|
|
|
|
yyerror(_("Exec qualifier '%c%c' invalid, conflicting qualifier already specified"), this, next);
|
|
|
|
} else {
|
|
|
|
mode |= tmode;
|
|
|
|
p += 2; /* skip x */
|
|
|
|
}
|
2008-04-16 04:44:21 +00:00
|
|
|
} else if (IS_DIFF_QUAL(mode, tmode)) {
|
|
|
|
yyerror(_("Exec qualifier '%c' invalid, conflicting qualifier already specified"), this);
|
|
|
|
|
2006-04-11 21:52:54 +00:00
|
|
|
} else {
|
[https://bugzilla.novell.com/show_bug.cgi?id=172061]
This (updated) patch to trunk adds support for Px and Ux (toggle
bprm_secure on exec) in the parser, As requested, lowercase p and u
corresponds to an unfiltered environmnet on exec, uppercase will filter
the environment. It applies after the 'm' patch.
As a side effect, I tried to reduce the use of hardcoded characters in
the debugging statements -- there are still a few warnings that have
hard coded letters in them; not sure I can fix them all.
This version issues a warning for every unsafe ux and issues a single
warning for the first 'R', 'W', 'X', 'L', and 'I' it encounters,
except when the "-q" or "--quiet" flag , "--remove" profile flag, or
"-N" report names flags are passed. Unfortunately, it made the logic
somewhat more convoluted. Wordsmithing improvements welcome.
2006-08-04 17:14:49 +00:00
|
|
|
if (next != tolower(next))
|
|
|
|
warn_uppercase();
|
2008-04-16 04:44:21 +00:00
|
|
|
mode |= tmode;
|
2006-04-11 21:52:54 +00:00
|
|
|
p++; /* skip 'x' */
|
|
|
|
}
|
2007-11-16 09:27:34 +00:00
|
|
|
tmode = 0;
|
2006-04-11 21:52:54 +00:00
|
|
|
break;
|
|
|
|
|
2006-08-04 17:14:06 +00:00
|
|
|
case COD_MMAP_CHAR:
|
2007-11-16 09:35:31 +00:00
|
|
|
PDEBUG("Parsing mode: found %s MMAP\n", mode_desc);
|
2007-02-27 02:29:16 +00:00
|
|
|
mode |= AA_EXEC_MMAP;
|
2006-08-04 17:14:06 +00:00
|
|
|
break;
|
|
|
|
|
2006-04-11 21:52:54 +00:00
|
|
|
case COD_EXEC_CHAR:
|
Add Audit control to AppArmor through, the use of audit and deny
key words. Deny is also used to subtract permissions from the
profiles permission set.
the audit key word can be prepended to any file, network, or capability
rule, to force a selective audit when that rule is matched. Audit
permissions accumulate just like standard permissions.
eg.
audit /bin/foo rw,
will force an audit message when the file /bin/foo is opened for
read or write.
audit /etc/shadow w,
/etc/shadow r,
will force an audit message when /etc/shadow is opened for writing.
The audit message is per permission bit so only opening the file
for read access will not, force an audit message.
audit can also be used in block form instead of prepending audit
to every rule.
audit {
/bin/foo rw,
/etc/shadow w,
}
/etc/shadow r, # don't audit r access to /etc/shadow
the deny key word can be prepended to file, network and capability
rules, to result in a denial of permissions when matching that rule.
The deny rule specifically does 3 things
- it gives AppArmor the ability to remember what has been denied
so that the tools don't prompt for what has been denied in
previous profiling sessions.
- it subtracts globally from the allowed permissions. Deny permissions
accumulate in the the deny set just as allow permissions accumulate
then, the deny set is subtracted from the allow set.
- it quiets known rejects. The default audit behavior of deny rules
is to quiet known rejects so that audit logs are not flooded
with already known rejects. To have known rejects logged prepend
the audit keyword to the deny rule. Deny rules do not have a
block form.
eg.
deny /foo/bar rw,
audit deny /etc/shadow w,
audit {
deny owner /blah w,
deny other /foo w,
deny /etc/shadow w,
}
2008-03-13 17:39:03 +00:00
|
|
|
/* this is valid for deny rules, and named transitions
|
|
|
|
* but invalid for regular x transitions
|
|
|
|
* sort it out later.
|
|
|
|
*/
|
|
|
|
mode |= AA_MAY_EXEC;
|
2006-04-11 21:52:54 +00:00
|
|
|
break;
|
|
|
|
|
[https://bugzilla.novell.com/show_bug.cgi?id=172061]
This (updated) patch to trunk adds support for Px and Ux (toggle
bprm_secure on exec) in the parser, As requested, lowercase p and u
corresponds to an unfiltered environmnet on exec, uppercase will filter
the environment. It applies after the 'm' patch.
As a side effect, I tried to reduce the use of hardcoded characters in
the debugging statements -- there are still a few warnings that have
hard coded letters in them; not sure I can fix them all.
This version issues a warning for every unsafe ux and issues a single
warning for the first 'R', 'W', 'X', 'L', and 'I' it encounters,
except when the "-q" or "--quiet" flag , "--remove" profile flag, or
"-N" report names flags are passed. Unfortunately, it made the logic
somewhat more convoluted. Wordsmithing improvements welcome.
2006-08-04 17:14:49 +00:00
|
|
|
/* error cases */
|
|
|
|
|
2006-04-11 21:52:54 +00:00
|
|
|
default:
|
[https://bugzilla.novell.com/show_bug.cgi?id=172061]
This (updated) patch to trunk adds support for Px and Ux (toggle
bprm_secure on exec) in the parser, As requested, lowercase p and u
corresponds to an unfiltered environmnet on exec, uppercase will filter
the environment. It applies after the 'm' patch.
As a side effect, I tried to reduce the use of hardcoded characters in
the debugging statements -- there are still a few warnings that have
hard coded letters in them; not sure I can fix them all.
This version issues a warning for every unsafe ux and issues a single
warning for the first 'R', 'W', 'X', 'L', and 'I' it encounters,
except when the "-q" or "--quiet" flag , "--remove" profile flag, or
"-N" report names flags are passed. Unfortunately, it made the logic
somewhat more convoluted. Wordsmithing improvements welcome.
2006-08-04 17:14:49 +00:00
|
|
|
lower = tolower(this);
|
|
|
|
switch (lower) {
|
|
|
|
case COD_READ_CHAR:
|
|
|
|
case COD_WRITE_CHAR:
|
2007-07-27 20:31:38 +00:00
|
|
|
case COD_APPEND_CHAR:
|
[https://bugzilla.novell.com/show_bug.cgi?id=172061]
This (updated) patch to trunk adds support for Px and Ux (toggle
bprm_secure on exec) in the parser, As requested, lowercase p and u
corresponds to an unfiltered environmnet on exec, uppercase will filter
the environment. It applies after the 'm' patch.
As a side effect, I tried to reduce the use of hardcoded characters in
the debugging statements -- there are still a few warnings that have
hard coded letters in them; not sure I can fix them all.
This version issues a warning for every unsafe ux and issues a single
warning for the first 'R', 'W', 'X', 'L', and 'I' it encounters,
except when the "-q" or "--quiet" flag , "--remove" profile flag, or
"-N" report names flags are passed. Unfortunately, it made the logic
somewhat more convoluted. Wordsmithing improvements welcome.
2006-08-04 17:14:49 +00:00
|
|
|
case COD_LINK_CHAR:
|
|
|
|
case COD_INHERIT_CHAR:
|
|
|
|
case COD_MMAP_CHAR:
|
|
|
|
case COD_EXEC_CHAR:
|
|
|
|
PDEBUG("Parsing mode: found invalid upper case char %c\n", this);
|
|
|
|
warn_uppercase();
|
|
|
|
this = lower;
|
|
|
|
goto reeval;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
yyerror(_("Internal: unexpected mode character '%c' in input"),
|
|
|
|
this);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
2006-04-11 21:52:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
p++;
|
|
|
|
}
|
2007-11-29 18:06:53 +00:00
|
|
|
|
2006-04-11 21:52:54 +00:00
|
|
|
PDEBUG("Parsed mode: %s 0x%x\n", str_mode, mode);
|
|
|
|
|
|
|
|
return mode;
|
|
|
|
}
|
|
|
|
|
2007-11-16 09:35:31 +00:00
|
|
|
int parse_mode(const char *str_mode)
|
|
|
|
{
|
2007-11-29 18:06:53 +00:00
|
|
|
int tmp, mode = 0;
|
|
|
|
tmp = parse_sub_mode(str_mode, "");
|
|
|
|
mode = SHIFT_MODE(tmp, AA_USER_SHIFT);
|
|
|
|
mode |= SHIFT_MODE(tmp, AA_OTHER_SHIFT);
|
2007-11-16 09:35:31 +00:00
|
|
|
if (mode & ~AA_VALID_PERMS)
|
|
|
|
yyerror(_("Internal error generated invalid perm 0x%llx\n"), mode);
|
|
|
|
return mode;
|
|
|
|
}
|
|
|
|
|
2013-07-31 09:05:51 -07:00
|
|
|
static int parse_dbus_sub_mode(const char *str_mode, int *result, int fail, const char *mode_desc __unused)
|
|
|
|
{
|
|
|
|
int mode = 0;
|
|
|
|
const char *p;
|
|
|
|
|
|
|
|
PDEBUG("Parsing DBus mode: %s\n", str_mode);
|
|
|
|
|
|
|
|
if (!str_mode)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
p = str_mode;
|
|
|
|
while (*p) {
|
|
|
|
char this = *p;
|
|
|
|
char lower;
|
|
|
|
|
|
|
|
reeval:
|
|
|
|
switch (this) {
|
|
|
|
case COD_READ_CHAR:
|
|
|
|
PDEBUG("Parsing DBus mode: found %s READ\n", mode_desc);
|
|
|
|
mode |= AA_DBUS_RECEIVE;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case COD_WRITE_CHAR:
|
|
|
|
PDEBUG("Parsing DBus mode: found %s WRITE\n",
|
|
|
|
mode_desc);
|
|
|
|
mode |= AA_DBUS_SEND;
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* error cases */
|
|
|
|
|
|
|
|
default:
|
|
|
|
lower = tolower(this);
|
|
|
|
switch (lower) {
|
|
|
|
case COD_READ_CHAR:
|
|
|
|
case COD_WRITE_CHAR:
|
|
|
|
PDEBUG("Parsing DBus mode: found invalid upper case char %c\n",
|
|
|
|
this);
|
|
|
|
warn_uppercase();
|
|
|
|
this = lower;
|
|
|
|
goto reeval;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
if (fail)
|
|
|
|
yyerror(_("Internal: unexpected DBus mode character '%c' in input"),
|
|
|
|
this);
|
|
|
|
else
|
|
|
|
return 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
p++;
|
|
|
|
}
|
|
|
|
|
|
|
|
PDEBUG("Parsed DBus mode: %s 0x%x\n", str_mode, mode);
|
|
|
|
|
|
|
|
*result = mode;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int parse_dbus_mode(const char *str_mode, int *mode, int fail)
|
|
|
|
{
|
|
|
|
*mode = 0;
|
|
|
|
if (!parse_dbus_sub_mode(str_mode, mode, fail, ""))
|
|
|
|
return 0;
|
|
|
|
if (*mode & ~AA_VALID_DBUS_PERMS) {
|
|
|
|
if (fail)
|
|
|
|
yyerror(_("Internal error generated invalid DBus perm 0x%x\n"),
|
|
|
|
mode);
|
|
|
|
else
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2007-11-16 09:37:31 +00:00
|
|
|
struct cod_entry *new_entry(char *namespace, char *id, int mode, char *link_id)
|
2006-04-11 21:52:54 +00:00
|
|
|
{
|
|
|
|
struct cod_entry *entry = NULL;
|
|
|
|
|
Add Audit control to AppArmor through, the use of audit and deny
key words. Deny is also used to subtract permissions from the
profiles permission set.
the audit key word can be prepended to any file, network, or capability
rule, to force a selective audit when that rule is matched. Audit
permissions accumulate just like standard permissions.
eg.
audit /bin/foo rw,
will force an audit message when the file /bin/foo is opened for
read or write.
audit /etc/shadow w,
/etc/shadow r,
will force an audit message when /etc/shadow is opened for writing.
The audit message is per permission bit so only opening the file
for read access will not, force an audit message.
audit can also be used in block form instead of prepending audit
to every rule.
audit {
/bin/foo rw,
/etc/shadow w,
}
/etc/shadow r, # don't audit r access to /etc/shadow
the deny key word can be prepended to file, network and capability
rules, to result in a denial of permissions when matching that rule.
The deny rule specifically does 3 things
- it gives AppArmor the ability to remember what has been denied
so that the tools don't prompt for what has been denied in
previous profiling sessions.
- it subtracts globally from the allowed permissions. Deny permissions
accumulate in the the deny set just as allow permissions accumulate
then, the deny set is subtracted from the allow set.
- it quiets known rejects. The default audit behavior of deny rules
is to quiet known rejects so that audit logs are not flooded
with already known rejects. To have known rejects logged prepend
the audit keyword to the deny rule. Deny rules do not have a
block form.
eg.
deny /foo/bar rw,
audit deny /etc/shadow w,
audit {
deny owner /blah w,
deny other /foo w,
deny /etc/shadow w,
}
2008-03-13 17:39:03 +00:00
|
|
|
entry = (struct cod_entry *)calloc(1, sizeof(struct cod_entry));
|
2006-04-11 21:52:54 +00:00
|
|
|
if (!entry)
|
|
|
|
return NULL;
|
|
|
|
|
2007-11-16 09:18:48 +00:00
|
|
|
entry->namespace = namespace;
|
|
|
|
entry->name = id;
|
2007-11-16 09:37:31 +00:00
|
|
|
entry->link_name = link_id;
|
2007-06-26 21:10:28 +00:00
|
|
|
entry->mode = mode;
|
Add Audit control to AppArmor through, the use of audit and deny
key words. Deny is also used to subtract permissions from the
profiles permission set.
the audit key word can be prepended to any file, network, or capability
rule, to force a selective audit when that rule is matched. Audit
permissions accumulate just like standard permissions.
eg.
audit /bin/foo rw,
will force an audit message when the file /bin/foo is opened for
read or write.
audit /etc/shadow w,
/etc/shadow r,
will force an audit message when /etc/shadow is opened for writing.
The audit message is per permission bit so only opening the file
for read access will not, force an audit message.
audit can also be used in block form instead of prepending audit
to every rule.
audit {
/bin/foo rw,
/etc/shadow w,
}
/etc/shadow r, # don't audit r access to /etc/shadow
the deny key word can be prepended to file, network and capability
rules, to result in a denial of permissions when matching that rule.
The deny rule specifically does 3 things
- it gives AppArmor the ability to remember what has been denied
so that the tools don't prompt for what has been denied in
previous profiling sessions.
- it subtracts globally from the allowed permissions. Deny permissions
accumulate in the the deny set just as allow permissions accumulate
then, the deny set is subtracted from the allow set.
- it quiets known rejects. The default audit behavior of deny rules
is to quiet known rejects so that audit logs are not flooded
with already known rejects. To have known rejects logged prepend
the audit keyword to the deny rule. Deny rules do not have a
block form.
eg.
deny /foo/bar rw,
audit deny /etc/shadow w,
audit {
deny owner /blah w,
deny other /foo w,
deny /etc/shadow w,
}
2008-03-13 17:39:03 +00:00
|
|
|
entry->audit = 0;
|
2006-04-11 21:52:54 +00:00
|
|
|
entry->deny = FALSE;
|
|
|
|
|
|
|
|
entry->pattern_type = ePatternInvalid;
|
|
|
|
entry->pat.regex = NULL;
|
|
|
|
|
|
|
|
entry->next = NULL;
|
|
|
|
|
|
|
|
PDEBUG(" Insertion of: (%s)\n", entry->name);
|
|
|
|
return entry;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct cod_entry *copy_cod_entry(struct cod_entry *orig)
|
|
|
|
{
|
|
|
|
struct cod_entry *entry = NULL;
|
|
|
|
|
Add Audit control to AppArmor through, the use of audit and deny
key words. Deny is also used to subtract permissions from the
profiles permission set.
the audit key word can be prepended to any file, network, or capability
rule, to force a selective audit when that rule is matched. Audit
permissions accumulate just like standard permissions.
eg.
audit /bin/foo rw,
will force an audit message when the file /bin/foo is opened for
read or write.
audit /etc/shadow w,
/etc/shadow r,
will force an audit message when /etc/shadow is opened for writing.
The audit message is per permission bit so only opening the file
for read access will not, force an audit message.
audit can also be used in block form instead of prepending audit
to every rule.
audit {
/bin/foo rw,
/etc/shadow w,
}
/etc/shadow r, # don't audit r access to /etc/shadow
the deny key word can be prepended to file, network and capability
rules, to result in a denial of permissions when matching that rule.
The deny rule specifically does 3 things
- it gives AppArmor the ability to remember what has been denied
so that the tools don't prompt for what has been denied in
previous profiling sessions.
- it subtracts globally from the allowed permissions. Deny permissions
accumulate in the the deny set just as allow permissions accumulate
then, the deny set is subtracted from the allow set.
- it quiets known rejects. The default audit behavior of deny rules
is to quiet known rejects so that audit logs are not flooded
with already known rejects. To have known rejects logged prepend
the audit keyword to the deny rule. Deny rules do not have a
block form.
eg.
deny /foo/bar rw,
audit deny /etc/shadow w,
audit {
deny owner /blah w,
deny other /foo w,
deny /etc/shadow w,
}
2008-03-13 17:39:03 +00:00
|
|
|
entry = (struct cod_entry *)calloc(1, sizeof(struct cod_entry));
|
2006-04-11 21:52:54 +00:00
|
|
|
if (!entry)
|
|
|
|
return NULL;
|
|
|
|
|
2007-11-16 09:18:48 +00:00
|
|
|
entry->namespace = orig->namespace ? strdup(orig->namespace) : NULL;
|
2006-04-11 21:52:54 +00:00
|
|
|
entry->name = strdup(orig->name);
|
2007-11-16 09:37:31 +00:00
|
|
|
entry->link_name = orig->link_name ? strdup(orig->link_name) : NULL;
|
2006-04-11 21:52:54 +00:00
|
|
|
entry->mode = orig->mode;
|
2010-02-12 13:46:55 -08:00
|
|
|
entry->audit = orig->audit;
|
2006-04-11 21:52:54 +00:00
|
|
|
entry->deny = orig->deny;
|
|
|
|
|
|
|
|
/* XXX - need to create copies of the patterns, too */
|
|
|
|
entry->pattern_type = orig->pattern_type;
|
|
|
|
entry->pat.regex = NULL;
|
|
|
|
|
|
|
|
entry->next = orig->next;
|
|
|
|
|
|
|
|
return entry;
|
|
|
|
}
|
|
|
|
|
|
|
|
void free_cod_entries(struct cod_entry *list)
|
|
|
|
{
|
|
|
|
if (!list)
|
|
|
|
return;
|
|
|
|
if (list->next)
|
|
|
|
free_cod_entries(list->next);
|
2007-11-16 09:18:48 +00:00
|
|
|
if (list->namespace)
|
|
|
|
free(list->namespace);
|
2006-04-11 21:52:54 +00:00
|
|
|
if (list->name)
|
|
|
|
free(list->name);
|
2007-11-16 09:37:31 +00:00
|
|
|
if (list->link_name)
|
|
|
|
free(list->link_name);
|
2006-04-11 21:52:54 +00:00
|
|
|
if (list->pat.regex)
|
|
|
|
free(list->pat.regex);
|
|
|
|
free(list);
|
|
|
|
}
|
|
|
|
|
Add mount rules
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@canonical.com>
2012-02-24 04:19:38 -08:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2013-07-31 09:05:51 -07:00
|
|
|
void free_dbus_entries(struct dbus_entry *list)
|
|
|
|
{
|
|
|
|
if (!list)
|
|
|
|
return;
|
|
|
|
if (list->next)
|
|
|
|
free_dbus_entries(list->next);
|
|
|
|
|
|
|
|
free_dbus_entry(list);
|
|
|
|
}
|
|
|
|
|
2007-11-16 09:35:31 +00:00
|
|
|
static void debug_base_perm_mask(int mask)
|
|
|
|
{
|
|
|
|
if (HAS_MAY_READ(mask))
|
|
|
|
printf("%c", COD_READ_CHAR);
|
|
|
|
if (HAS_MAY_WRITE(mask))
|
|
|
|
printf("%c", COD_WRITE_CHAR);
|
|
|
|
if (HAS_MAY_APPEND(mask))
|
|
|
|
printf("%c", COD_APPEND_CHAR);
|
|
|
|
if (HAS_MAY_LINK(mask))
|
|
|
|
printf("%c", COD_LINK_CHAR);
|
|
|
|
if (HAS_MAY_LOCK(mask))
|
|
|
|
printf("%c", COD_LOCK_CHAR);
|
|
|
|
if (HAS_EXEC_MMAP(mask))
|
|
|
|
printf("%c", COD_MMAP_CHAR);
|
|
|
|
if (HAS_MAY_EXEC(mask))
|
|
|
|
printf("%c", COD_EXEC_CHAR);
|
|
|
|
}
|
|
|
|
|
2006-04-11 21:52:54 +00:00
|
|
|
void debug_cod_entries(struct cod_entry *list)
|
|
|
|
{
|
|
|
|
struct cod_entry *item = NULL;
|
|
|
|
|
|
|
|
printf("--- Entries ---\n");
|
|
|
|
|
2007-02-27 02:29:16 +00:00
|
|
|
list_for_each(list, item) {
|
2006-04-11 21:52:54 +00:00
|
|
|
if (!item)
|
|
|
|
printf("Item is NULL!\n");
|
|
|
|
|
|
|
|
printf("Mode:\t");
|
2007-07-27 20:25:59 +00:00
|
|
|
if (HAS_CHANGE_PROFILE(item->mode))
|
|
|
|
printf(" change_profile");
|
2007-11-16 09:35:31 +00:00
|
|
|
if (HAS_EXEC_UNSAFE(item->mode))
|
|
|
|
printf(" unsafe");
|
|
|
|
debug_base_perm_mask(SHIFT_TO_BASE(item->mode, AA_USER_SHIFT));
|
|
|
|
printf(":");
|
|
|
|
debug_base_perm_mask(SHIFT_TO_BASE(item->mode, AA_OTHER_SHIFT));
|
2006-04-11 21:52:54 +00:00
|
|
|
if (item->name)
|
|
|
|
printf("\tName:\t(%s)\n", item->name);
|
|
|
|
else
|
|
|
|
printf("\tName:\tNULL\n");
|
2007-11-16 09:18:48 +00:00
|
|
|
|
|
|
|
if (item->namespace)
|
|
|
|
printf("\tNamespace:\t(%s)\n", item->namespace);
|
|
|
|
|
2007-11-16 09:37:31 +00:00
|
|
|
if (AA_LINK_BITS & item->mode)
|
|
|
|
printf("\tlink:\t(%s)\n", item->link_name ? item->link_name : "/**");
|
|
|
|
|
2006-04-11 21:52:54 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-03-12 19:23:27 +00:00
|
|
|
void debug_flags(struct codomain *cod)
|
|
|
|
{
|
|
|
|
printf("Profile Mode:\t");
|
|
|
|
|
|
|
|
if (cod->flags.complain)
|
|
|
|
printf("Complain");
|
|
|
|
else
|
|
|
|
printf("Enforce");
|
|
|
|
|
|
|
|
if (cod->flags.audit)
|
|
|
|
printf(", Audit");
|
|
|
|
|
|
|
|
if (cod->flags.hat)
|
|
|
|
printf(", Hat");
|
|
|
|
|
|
|
|
printf("\n");
|
|
|
|
}
|
|
|
|
|
2006-04-11 21:52:54 +00:00
|
|
|
static const char *capnames[] = {
|
|
|
|
"chown",
|
|
|
|
"dac_override",
|
|
|
|
"dac_read_search",
|
|
|
|
"fowner",
|
|
|
|
"fsetid",
|
|
|
|
"kill",
|
|
|
|
"setgid",
|
|
|
|
"setuid",
|
|
|
|
"setpcap",
|
|
|
|
"linux_immutable",
|
|
|
|
"net_bind_service",
|
|
|
|
"net_broadcast",
|
|
|
|
"net_admin",
|
|
|
|
"net_raw",
|
|
|
|
"ipc_lock",
|
|
|
|
"ipc_owner",
|
|
|
|
"sys_module",
|
|
|
|
"sys_rawio",
|
|
|
|
"sys_chroot",
|
|
|
|
"sys_ptrace",
|
|
|
|
"sys_pacct",
|
|
|
|
"sys_admin",
|
|
|
|
"sys_boot",
|
|
|
|
"sys_nice",
|
|
|
|
"sys_resource",
|
|
|
|
"sys_time",
|
|
|
|
"sys_tty_config",
|
|
|
|
"mknod",
|
2006-11-08 10:43:11 +00:00
|
|
|
"lease",
|
|
|
|
"audit_write",
|
2009-08-20 15:27:12 +00:00
|
|
|
"audit_control",
|
|
|
|
"setfcap",
|
|
|
|
"mac_override"
|
2011-08-18 23:54:24 +02:00
|
|
|
"syslog",
|
2006-04-11 21:52:54 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
const char *capability_to_name(unsigned int cap)
|
|
|
|
{
|
|
|
|
const char *capname;
|
|
|
|
|
|
|
|
capname = (cap < (sizeof(capnames) / sizeof(char *))
|
|
|
|
? capnames[cap] : "invalid-capability");
|
|
|
|
|
|
|
|
return capname;
|
|
|
|
}
|
|
|
|
|
2010-01-07 14:17:07 -08:00
|
|
|
void __debug_capabilities(uint64_t capset, const char *name)
|
|
|
|
{
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
printf("%s:", name);
|
|
|
|
for (i = 0; i < (sizeof(capnames)/sizeof(char *)); i++) {
|
|
|
|
if (((1ull << i) & capset) != 0) {
|
|
|
|
printf (" %s", capability_to_name(i));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
printf("\n");
|
|
|
|
}
|
|
|
|
void debug_capabilities(struct codomain *cod)
|
|
|
|
{
|
|
|
|
if (cod->capabilities != 0ull)
|
|
|
|
__debug_capabilities(cod->capabilities, "Capabilities");
|
2010-01-07 15:48:14 -08:00
|
|
|
if (cod->audit_caps != 0ull)
|
|
|
|
__debug_capabilities(cod->audit_caps, "Audit Caps");
|
|
|
|
if (cod->deny_caps != 0ull)
|
|
|
|
__debug_capabilities(cod->deny_caps, "Deny Caps");
|
|
|
|
if (cod->quiet_caps != 0ull)
|
|
|
|
__debug_capabilities(cod->quiet_caps, "Quiet Caps");
|
2010-01-07 14:17:07 -08:00
|
|
|
}
|
|
|
|
|
2012-06-07 00:41:04 -07:00
|
|
|
const char *sock_types[] = {
|
|
|
|
[0] = "none",
|
|
|
|
[SOCK_STREAM] = "stream",
|
|
|
|
[SOCK_DGRAM] = "dgram",
|
|
|
|
[SOCK_RAW] = "raw",
|
|
|
|
[SOCK_RDM] = "rdm",
|
|
|
|
[SOCK_SEQPACKET] = "seqpacket",
|
|
|
|
[SOCK_PACKET] = "packet",
|
|
|
|
/*
|
|
|
|
* See comment above
|
|
|
|
[SOCK_DCCP] = "dccp",
|
|
|
|
*/
|
|
|
|
};
|
|
|
|
#define ALL_TYPES 0x43e
|
|
|
|
|
|
|
|
#undef AA_GEN_NET_ENT
|
|
|
|
#define AA_GEN_NET_ENT(name, AF) [AF] = name,
|
|
|
|
|
|
|
|
static const char *network_families[] = {
|
|
|
|
#include "af_names.h"
|
|
|
|
};
|
|
|
|
|
|
|
|
void __debug_network(unsigned int *array, const char *name)
|
|
|
|
{
|
|
|
|
unsigned int count = sizeof(sock_types)/sizeof(sock_types[0]);
|
|
|
|
unsigned int mask = ~((1 << count) -1);
|
|
|
|
unsigned int i, j;
|
|
|
|
int none = 1;
|
|
|
|
size_t af_max = get_af_max();
|
|
|
|
|
|
|
|
for (i = AF_UNSPEC; i < af_max; i++)
|
|
|
|
if (array[i]) {
|
|
|
|
none = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (none)
|
|
|
|
return;
|
|
|
|
|
|
|
|
printf("%s: ", name);
|
|
|
|
|
|
|
|
/* This can only be set by an unqualified network rule */
|
|
|
|
if (array[AF_UNSPEC]) {
|
|
|
|
printf("<all>\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < af_max; i++) {
|
|
|
|
if (array[i]) {
|
|
|
|
const char *fam = network_families[i];
|
|
|
|
if (fam)
|
|
|
|
printf("%s ", fam);
|
|
|
|
else
|
|
|
|
printf("#%u ", i);
|
|
|
|
|
|
|
|
/* All types/protocols */
|
|
|
|
if (array[i] == 0xffffffff || array[i] == ALL_TYPES)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
printf("{ ");
|
|
|
|
|
|
|
|
for (j = 0; j < count; j++) {
|
|
|
|
const char *type;
|
|
|
|
if (array[i] & (1 << j)) {
|
|
|
|
type = sock_types[j];
|
|
|
|
if (type)
|
|
|
|
printf("%s ", type);
|
|
|
|
else
|
|
|
|
printf("#%u ", j);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (array[i] & mask)
|
|
|
|
printf("#%x ", array[i] & mask);
|
|
|
|
|
|
|
|
printf("} ");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
printf("\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
void debug_network(struct codomain *cod)
|
|
|
|
{
|
|
|
|
if (cod->network_allowed)
|
|
|
|
__debug_network(cod->network_allowed, "Network");
|
|
|
|
if (cod->audit_network)
|
|
|
|
__debug_network(cod->audit_network, "Audit Net");
|
|
|
|
if (cod->deny_network)
|
|
|
|
__debug_network(cod->deny_network, "Deny Net");
|
|
|
|
if (cod->quiet_network)
|
|
|
|
__debug_network(cod->quiet_network, "Quiet Net");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2006-04-11 21:52:54 +00:00
|
|
|
void debug_cod_list(struct codomain *cod)
|
|
|
|
{
|
2007-11-16 09:18:48 +00:00
|
|
|
if (cod->namespace)
|
2009-03-12 16:05:19 +00:00
|
|
|
printf("Namespace:\t\t%s\n", cod->namespace);
|
2007-11-16 09:18:48 +00:00
|
|
|
|
2006-04-11 21:52:54 +00:00
|
|
|
if (cod->name)
|
|
|
|
printf("Name:\t\t%s\n", cod->name);
|
|
|
|
else
|
|
|
|
printf("Name:\t\tNULL\n");
|
|
|
|
|
2009-03-12 19:23:27 +00:00
|
|
|
if (cod->local)
|
|
|
|
printf("Local To:\t%s\n", cod->parent->name);
|
|
|
|
|
|
|
|
debug_flags(cod);
|
|
|
|
|
2010-01-07 14:17:07 -08:00
|
|
|
debug_capabilities(cod);
|
2006-04-11 21:52:54 +00:00
|
|
|
|
2012-06-07 00:41:04 -07:00
|
|
|
debug_network(cod);
|
|
|
|
|
2006-04-11 21:52:54 +00:00
|
|
|
if (cod->entries)
|
|
|
|
debug_cod_entries(cod->entries);
|
|
|
|
|
|
|
|
printf("\n");
|
|
|
|
dump_policy_hats(cod);
|
|
|
|
}
|
|
|
|
|
2012-02-16 08:07:28 -08:00
|
|
|
struct value_list *new_value_list(char *value)
|
|
|
|
{
|
|
|
|
struct value_list *val = calloc(1, sizeof(struct value_list));
|
|
|
|
if (val)
|
|
|
|
val->value = value;
|
|
|
|
return val;
|
|
|
|
}
|
|
|
|
|
|
|
|
void free_value_list(struct value_list *list)
|
|
|
|
{
|
|
|
|
struct value_list *next;
|
|
|
|
|
|
|
|
while (list) {
|
|
|
|
next = list->next;
|
|
|
|
if (list->value)
|
|
|
|
free(list->value);
|
|
|
|
free(list);
|
|
|
|
list = next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Add mount rules
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@canonical.com>
2012-02-24 04:19:38 -08:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2012-02-16 08:07:28 -08:00
|
|
|
void print_value_list(struct value_list *list)
|
|
|
|
{
|
|
|
|
struct value_list *entry;
|
|
|
|
|
|
|
|
if (!list)
|
|
|
|
return;
|
|
|
|
|
|
|
|
fprintf(stderr, "%s", list->value);
|
|
|
|
list = list->next;
|
|
|
|
list_for_each(list, entry) {
|
|
|
|
fprintf(stderr, ", %s", entry->value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-03-26 06:17:40 -07:00
|
|
|
struct cond_entry *new_cond_entry(char *name, int eq, struct value_list *list)
|
Add mount rules
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@canonical.com>
2012-02-24 04:19:38 -08:00
|
|
|
{
|
|
|
|
struct cond_entry *ent = calloc(1, sizeof(struct cond_entry));
|
|
|
|
if (ent) {
|
|
|
|
ent->name = name;
|
|
|
|
ent->vals = list;
|
2012-03-26 06:17:40 -07:00
|
|
|
ent->eq = eq;
|
Add mount rules
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@canonical.com>
2012-02-24 04:19:38 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
return ent;
|
|
|
|
}
|
|
|
|
|
|
|
|
void free_cond_entry(struct cond_entry *ent)
|
|
|
|
{
|
|
|
|
if (ent) {
|
|
|
|
free(ent->name);
|
|
|
|
free_value_list(ent->vals);
|
|
|
|
free(ent);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-07-31 09:05:51 -07:00
|
|
|
void free_cond_list(struct cond_entry *ents)
|
|
|
|
{
|
|
|
|
struct cond_entry *entry, *tmp;
|
|
|
|
|
|
|
|
list_for_each_safe(ents, entry, tmp) {
|
|
|
|
free_cond_entry(entry);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Add mount rules
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@canonical.com>
2012-02-24 04:19:38 -08:00
|
|
|
void print_cond_entry(struct cond_entry *ent)
|
|
|
|
{
|
|
|
|
if (ent) {
|
|
|
|
fprintf(stderr, "%s=(", ent->name);
|
|
|
|
print_value_list(ent->vals);
|
|
|
|
fprintf(stderr, ")\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-04-11 21:52:54 +00:00
|
|
|
#ifdef UNIT_TEST
|
|
|
|
int test_str_to_boolean(void)
|
|
|
|
{
|
|
|
|
int rc = 0;
|
|
|
|
int retval;
|
|
|
|
|
|
|
|
retval = str_to_boolean("TRUE");
|
|
|
|
MY_TEST(retval == 1, "str2bool for TRUE");
|
|
|
|
|
|
|
|
retval = str_to_boolean("TrUe");
|
|
|
|
MY_TEST(retval == 1, "str2bool for TrUe");
|
|
|
|
|
|
|
|
retval = str_to_boolean("false");
|
|
|
|
MY_TEST(retval == 0, "str2bool for false");
|
|
|
|
|
|
|
|
retval = str_to_boolean("flase");
|
|
|
|
MY_TEST(retval == -1, "str2bool for flase");
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
2010-03-08 20:38:54 -08:00
|
|
|
|
|
|
|
int test_processunquoted(void)
|
|
|
|
{
|
|
|
|
int rc = 0;
|
[v2: added clean-ups, backed off on some of the build silencing]
This is a rather large rearrangement of how a subset of the parser global
variables are defined. Right now, there are unit tests built without
linking against parser_main.c. As a result, none of the globals defined in
parser_main.c could be used in the code that is built for unit tests
(misc, regex, symtab, variable). To get a clean build, either stubs needed
to be added to "#ifdef UNIT_TEST" blocks in each .c file, or we had to
depend on link-time optimizations that would throw out the unused routines.
First, this is a problem because all the compile-time warnings had to be
explicitly silenced, so reviewing the build logs becomes difficult on
failures, and we can potentially (in really unlucky situations) test
something that isn't actually part of the "real" parser.
Second, not all compilers will allow this kind of linking (e.g. mips gcc),
and the missing symbols at link time will fail the entire build even though
they're technically not needed.
To solve all of this, I've moved all of the global variables used in lex,
yacc, and main to parser_common.c, and adjusted the .h files. On top of
this, I made sure to fully link the tst builds so all symbols are resolved
(including aare lib) and removedonly tst build-log silencing (for now,
deferring to another future patchset to consolidate the build silencing).
Signed-off-by: Kees Cook <kees.cook@canonical.com>
2011-05-13 02:12:49 -07:00
|
|
|
char *teststring, *processedstring;
|
2010-03-08 20:38:54 -08:00
|
|
|
|
|
|
|
teststring = "";
|
|
|
|
MY_TEST(strcmp(teststring, processunquoted(teststring, strlen(teststring))) == 0,
|
|
|
|
"processunquoted on empty string");
|
|
|
|
|
|
|
|
teststring = "123\\421123";
|
|
|
|
MY_TEST(strcmp(teststring, processunquoted(teststring, strlen(teststring))) == 0,
|
|
|
|
"processunquoted on invalid octal");
|
|
|
|
|
|
|
|
/* Oh wow, our octal processing is busticated - FIXME
|
|
|
|
teststring = "123\\109123";
|
|
|
|
processedstring = "123\109123";
|
|
|
|
MY_TEST(strcmp(processedstring, processunquoted(teststring, strlen(teststring))) == 0,
|
|
|
|
"processunquoted on octal 10");
|
|
|
|
|
|
|
|
teststring = "123\\189123";
|
|
|
|
processedstring = "123\189123";
|
|
|
|
MY_TEST(strcmp(processedstring, processunquoted(teststring, strlen(teststring))) == 0,
|
|
|
|
"processunquoted on octal 10");
|
|
|
|
*/
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2011-04-05 20:55:19 -07:00
|
|
|
int test_processquoted(void)
|
|
|
|
{
|
|
|
|
int rc = 0;
|
[v2: added clean-ups, backed off on some of the build silencing]
This is a rather large rearrangement of how a subset of the parser global
variables are defined. Right now, there are unit tests built without
linking against parser_main.c. As a result, none of the globals defined in
parser_main.c could be used in the code that is built for unit tests
(misc, regex, symtab, variable). To get a clean build, either stubs needed
to be added to "#ifdef UNIT_TEST" blocks in each .c file, or we had to
depend on link-time optimizations that would throw out the unused routines.
First, this is a problem because all the compile-time warnings had to be
explicitly silenced, so reviewing the build logs becomes difficult on
failures, and we can potentially (in really unlucky situations) test
something that isn't actually part of the "real" parser.
Second, not all compilers will allow this kind of linking (e.g. mips gcc),
and the missing symbols at link time will fail the entire build even though
they're technically not needed.
To solve all of this, I've moved all of the global variables used in lex,
yacc, and main to parser_common.c, and adjusted the .h files. On top of
this, I made sure to fully link the tst builds so all symbols are resolved
(including aare lib) and removedonly tst build-log silencing (for now,
deferring to another future patchset to consolidate the build silencing).
Signed-off-by: Kees Cook <kees.cook@canonical.com>
2011-05-13 02:12:49 -07:00
|
|
|
char *teststring, *processedstring;
|
2011-04-05 20:55:19 -07:00
|
|
|
char *out;
|
|
|
|
|
|
|
|
teststring = "";
|
|
|
|
out = processquoted(teststring, strlen(teststring));
|
|
|
|
MY_TEST(strcmp(teststring, out) == 0,
|
|
|
|
"processquoted on empty string");
|
|
|
|
free(out);
|
|
|
|
|
|
|
|
teststring = "\"abcdefg\"";
|
|
|
|
processedstring = "abcdefg";
|
|
|
|
out = processquoted(teststring, strlen(teststring));
|
|
|
|
MY_TEST(strcmp(processedstring, out) == 0,
|
|
|
|
"processquoted on simple string");
|
|
|
|
free(out);
|
|
|
|
|
|
|
|
teststring = "\"abcd\\tefg\"";
|
|
|
|
processedstring = "abcd\tefg";
|
|
|
|
out = processquoted(teststring, strlen(teststring));
|
|
|
|
MY_TEST(strcmp(processedstring, out) == 0,
|
|
|
|
"processquoted on string with tab");
|
|
|
|
free(out);
|
|
|
|
|
|
|
|
teststring = "\"abcdefg\\\"";
|
|
|
|
processedstring = "abcdefg\\";
|
|
|
|
out = processquoted(teststring, strlen(teststring));
|
|
|
|
MY_TEST(strcmp(processedstring, out) == 0,
|
|
|
|
"processquoted on trailing slash");
|
|
|
|
free(out);
|
|
|
|
|
|
|
|
teststring = "\"a\\\\bcdefg\"";
|
|
|
|
processedstring = "a\\bcdefg";
|
|
|
|
out = processquoted(teststring, strlen(teststring));
|
|
|
|
MY_TEST(strcmp(processedstring, out) == 0,
|
|
|
|
"processquoted on quoted slash");
|
|
|
|
free(out);
|
|
|
|
|
|
|
|
teststring = "\"a\\\"bcde\\\"fg\"";
|
|
|
|
processedstring = "a\"bcde\"fg";
|
|
|
|
out = processquoted(teststring, strlen(teststring));
|
|
|
|
MY_TEST(strcmp(processedstring, out) == 0,
|
|
|
|
"processquoted on quoted quotes");
|
|
|
|
free(out);
|
|
|
|
|
|
|
|
teststring = "\"\\rabcdefg\"";
|
|
|
|
processedstring = "\rabcdefg";
|
|
|
|
out = processquoted(teststring, strlen(teststring));
|
|
|
|
MY_TEST(strcmp(processedstring, out) == 0,
|
|
|
|
"processquoted on quoted \\r");
|
|
|
|
free(out);
|
|
|
|
|
|
|
|
teststring = "\"abcdefg\\n\"";
|
|
|
|
processedstring = "abcdefg\n";
|
|
|
|
out = processquoted(teststring, strlen(teststring));
|
|
|
|
MY_TEST(strcmp(processedstring, out) == 0,
|
|
|
|
"processquoted on quoted \\n");
|
|
|
|
free(out);
|
|
|
|
|
|
|
|
teststring = "\"\\Uabc\\Ndefg\\x\"";
|
|
|
|
processedstring = "\\Uabc\\Ndefg\\x";
|
|
|
|
out = processquoted(teststring, strlen(teststring));
|
|
|
|
MY_TEST(strcmp(processedstring, out) == 0,
|
|
|
|
"processquoted passthrough on invalid quoted chars");
|
|
|
|
free(out);
|
|
|
|
|
|
|
|
teststring = "\"abc\\042defg\"";
|
|
|
|
processedstring = "abc\"defg";
|
|
|
|
out = processquoted(teststring, strlen(teststring));
|
|
|
|
MY_TEST(strcmp(processedstring, out) == 0,
|
|
|
|
"processquoted on quoted octal \\042");
|
|
|
|
free(out);
|
|
|
|
|
|
|
|
teststring = "\"abcdefg\\176\"";
|
|
|
|
processedstring = "abcdefg~";
|
|
|
|
out = processquoted(teststring, strlen(teststring));
|
|
|
|
MY_TEST(strcmp(processedstring, out) == 0,
|
|
|
|
"processquoted on quoted octal \\176");
|
|
|
|
free(out);
|
|
|
|
|
|
|
|
/* yes, our octal processing is lame; patches accepted */
|
|
|
|
teststring = "\"abc\\42defg\"";
|
|
|
|
processedstring = "abc\\42defg";
|
|
|
|
out = processquoted(teststring, strlen(teststring));
|
|
|
|
MY_TEST(strcmp(processedstring, out) == 0,
|
|
|
|
"processquoted passthrough quoted invalid octal \\42");
|
|
|
|
free(out);
|
|
|
|
|
|
|
|
teststring = "\"abcdefg\\04\"";
|
|
|
|
processedstring = "abcdefg\\04";
|
|
|
|
out = processquoted(teststring, strlen(teststring));
|
|
|
|
MY_TEST(strcmp(processedstring, out) == 0,
|
|
|
|
"processquoted passthrough quoted invalid trailing octal \\04");
|
|
|
|
free(out);
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2006-04-11 21:52:54 +00:00
|
|
|
int main(void)
|
|
|
|
{
|
|
|
|
int rc = 0;
|
|
|
|
int retval;
|
|
|
|
|
|
|
|
retval = test_str_to_boolean();
|
|
|
|
if (retval != 0)
|
|
|
|
rc = retval;
|
|
|
|
|
2010-03-08 20:38:54 -08:00
|
|
|
retval = test_processunquoted();
|
|
|
|
if (retval != 0)
|
|
|
|
rc = retval;
|
2011-04-05 20:55:19 -07:00
|
|
|
|
|
|
|
retval = test_processquoted();
|
|
|
|
if (retval != 0)
|
|
|
|
rc = retval;
|
|
|
|
|
2006-04-11 21:52:54 +00:00
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
#endif /* UNIT_TEST */
|