apparmor/parser/parser_misc.c

1204 lines
28 KiB
C
Raw Normal View History

/*
2007-04-11 08:12:51 +00:00
* Copyright (c) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007
* NOVELL (All rights reserved)
*
* Copyright (c) 2013
* 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.
*/
/* assistance routines */
#include <assert.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
2007-11-16 09:32:38 +00:00
#include <linux/capability.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
parser: Add make variable to build against local or system libapparmor [v3] By default, statically link against the in-tree libapparmor. If the in-tree libapparmor is not yet built, print a helpful error message. To build against the system libapparmor, the USE_SYSTEM make variable can be set on the command line like so: $ make USE_SYSTEM=1 This patch also fixes issues around the inclusion of the apparmor.h header. Previously, the in-tree apparmor.h was always being included even if the parser was being linked against the system libapparmor. It modifies the apparmor.h include path based on the previous patch separating them out in the libapparmor source. This was needed because header file name collisions were already occurring. For source files needing to include apparmor.h, the make targets were also updated to depend on the local apparmor.h when building against the in-tree libapparmor. When building against the system libapparmor, the variable used in the dependency list is empty. Likewise, a libapparmor.a dependency is added to the apparmor_parser target when building against the in-tree apparmor. Patch history: v1: from Tyler Hicks <tyhicks@canonical.com> - initial version v2: revert to altering the include search path rather than including the apparmor.h header directly via cpp arguments, alter the include statements to <sys/apparmor.h> which will work against either in-tree or (default) system paths. v3: convert controlling variable to USE_SYSTEM from SYSTEM_LIBAPPARMOR to unify between the parser and the regression tests. Signed-off-by: Tyler Hicks <tyhicks@canonical.com> Signed-off-by: Steve Beattie <steve@nxnw.org>
2014-01-06 14:46:10 -08:00
#include <sys/apparmor.h>
#include <sys/apparmor_private.h>
#include "lib.h"
#include "parser.h"
#include "profile.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"
#include "dbus.h"
/* #define DEBUG */
#ifdef DEBUG
#undef PDEBUG
#define PDEBUG(fmt, args...) fprintf(stderr, "Lexer: " fmt, ## args)
#else
#undef PDEBUG
#define PDEBUG(fmt, args...) /* Do nothing */
#endif
#define NPDEBUG(fmt, args...) /* Do nothing */
int is_blacklisted(const char *name, const char *path)
{
int retval = _aa_is_blacklisted(name);
if (retval == -1)
PERROR("Ignoring: '%s'\n", path ? path : name);
return !retval ? 0 : 1;
}
struct keyword_table {
const char *keyword;
int token;
};
static struct keyword_table keyword_table[] = {
/* network */
{"network", TOK_NETWORK},
parser: first step implementing fine grained mediation for unix domain sockets This patch implements parsing of fine grained mediation for unix domain sockets, that have abstract and anonymous paths. Sockets with file system paths are handled by regular file access rules. The unix network rules follow the general fine grained network rule pattern of [<qualifiers>] af_name [<access expr>] [<rule conds>] [<local expr>] [<peer expr>] specifically for af_unix this is [<qualifiers>] 'unix' [<access expr>] [<rule conds>] [<local expr>] [<peer expr>] <qualifiers> = [ 'audit' ] [ 'allow' | 'deny' ] <access expr> = ( <access> | <access list> ) <access> = ( 'server' | 'create' | 'bind' | 'listen' | 'accept' | 'connect' | 'shutdown' | 'getattr' | 'setattr' | 'getopt' | 'setopt' | 'send' | 'receive' | 'r' | 'w' | 'rw' ) (some access modes are incompatible with some rules or require additional parameters) <access list> = '(' <access> ( [','] <WS> <access> )* ')' <WS> = white space <rule conds> = ( <type cond> | <protocol cond> )* each cond can appear at most once <type cond> = 'type' '=' ( <AARE> | '(' ( '"' <AARE> '"' | <AARE> )+ ')' ) <protocol cond> = 'protocol' '=' ( <AARE> | '(' ( '"' <AARE> '"' | <AARE> )+ ')' ) <local expr> = ( <path cond> | <attr cond> | <opt cond> )* each cond can appear at most once <peer expr> = 'peer' '=' ( <path cond> | <label cond> )+ each cond can appear at most once <path cond> = 'path' '=' ( <AARE> | '(' '"' <AARE> '"' | <AARE> ')' ) <label cond> = 'label' '=' ( <AARE> | '(' '"' <AARE> '"' | <AARE> ')') <attr cond> = 'attr' '=' ( <AARE> | '(' '"' <AARE> '"' | <AARE> ')' ) <opt cond> = 'opt' '=' ( <AARE> | '(' '"' <AARE> '"' | <AARE> ')' ) <AARE> = ?*[]{}^ ( see man page ) unix domain socket rules are accumulated so that the granted unix socket permissions are the union of all the listed unix rule permissions. unix domain socket rules are broad and general and become more restrictive as further information is specified. Policy may be specified down to the path and label level. The content of the communication is not examined. Some permissions are not compatible with all unix rules. unix socket rule permissions are implied when a rule does not explicitly state an access list. By default if a rule does not have an access list all permissions that are compatible with the specified set of local and peer conditionals are implied. The 'server', 'r', 'w' and 'rw' permissions are aliases for other permissions. server = (create, bind, listen, accept) r = (receive, getattr, getopt) w = (create, connect, send, setattr, setopt) In addition it supports the v7 kernel abi semantics around generic network rules. The v7 abi removes the masking unix and netlink address families from the generic masking and uses fine grained mediation for an address type if supplied. This means that the rules network unix, network netlink, are now enforced instead of ignored. The parser previously could accept these but the kernel would ignore anything written to them. If a network rule is supplied it takes precedence over the finer grained mediation rule. If permission is not granted via a broad network access rule fine grained mediation is applied. Signed-off-by: John Johansen <john.johansen@canonical.com> Acked-by: Seth Arnold <seth.arnold@canonical.com>
2014-09-03 13:22:26 -07:00
{"unix", TOK_UNIX},
/* misc keywords */
2007-11-16 09:32:38 +00:00
{"capability", TOK_CAPABILITY},
{"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},
{"unsafe", TOK_UNSAFE},
{"safe", TOK_SAFE},
{"link", TOK_LINK},
{"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},
{"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},
{"allow", TOK_ALLOW},
{"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},
{"rewrite", TOK_ALIAS},
2008-04-09 09:04:08 +00:00
{"ptrace", TOK_PTRACE},
{"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},
{"pivot_root", TOK_PIVOTROOT},
{"in", TOK_IN},
{"dbus", TOK_DBUS},
{"signal", TOK_SIGNAL},
{"send", TOK_SEND},
{"receive", TOK_RECEIVE},
{"bind", TOK_BIND},
{"read", TOK_READ},
{"write", TOK_WRITE},
{"eavesdrop", TOK_EAVESDROP},
{"peer", TOK_PEER},
{"trace", TOK_TRACE},
{"tracedby", TOK_TRACEDBY},
{"readby", TOK_READBY},
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},
#ifdef RLIMIT_NICE
2008-04-06 18:55:46 +00:00
{"nice", RLIMIT_NICE},
#endif
#ifdef RLIMIT_RTPRIO
2008-04-06 18:55:46 +00:00
{"rtprio", RLIMIT_RTPRIO},
#endif
#ifdef RLIMIT_RTTIME
{"rttime", RLIMIT_RTTIME},
#endif
/* terminate */
{NULL, 0}
};
/* for alpha matches, check for keywords */
static int get_table_token(const char *name unused, struct keyword_table *table,
2007-11-16 09:32:38 +00:00
const char *keyword)
{
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;
}
}
2007-11-16 09:32:38 +00:00
PDEBUG("Unable to find %s %s\n", name, keyword);
return -1;
}
2007-11-16 09:32:38 +00:00
static struct keyword_table capability_table[] = {
/* capabilities */
#include "cap_names.h"
#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);
}
char *processunquoted(const char *string, int len)
{
char *buffer, *s;
s = buffer = (char *) malloc(len + 1);
if (!buffer)
return NULL;
while (len > 0) {
const char *pos = string + 1;
long c;
if (*string == '\\' && len > 1 &&
(c = strn_escseq(&pos, "", len)) != -1) {
/* catch \\ or \134 and other aare special chars and
* pass it through to be handled by the backend
* pcre conversion
*/
if (c == 0) {
strncpy(s, string, pos - string);
s += pos - string;
} else if (strchr("*?[]{}^,\\", c) != NULL) {
*s++ = '\\';
*s++ = c;
} else
*s++ = c;
len -= pos - string;
string = pos;
} else {
/* either unescaped char OR
* unsupported escape sequence resulting in char being
* copied.
*/
*s++ = *string++;
len--;
}
}
*s = 0;
return buffer;
}
/* rewrite a quoted string substituting escaped characters for the
* real thing. Strip the quotes around the string */
char *processquoted(const char *string, int len)
{
/* skip leading " and eat trailing " */
if (*string == '"') {
len -= 2;
if (len < 0) /* start and end point to same quote */
len = 0;
return processunquoted(string + 1, len);
}
/* no quotes? treat as unquoted */
return processunquoted(string, len);
}
char *processid(const 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);
}
/* 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;
}
static int warned_uppercase = 0;
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"));
warned_uppercase = 1;
}
}
2007-11-16 09:35:31 +00:00
static int parse_sub_mode(const char *str_mode, const char *mode_desc unused)
{
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)))
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;
p = str_mode;
while (*p) {
char thisc = *p;
char next = *(p + 1);
char lower;
2007-11-16 09:27:34 +00:00
int tmode = 0;
reeval:
switch (thisc) {
case COD_READ_CHAR:
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_OLD_EXEC_MMAP;
} else {
PDEBUG("Parsing mode: found %s READ\n", mode_desc);
mode |= AA_MAY_READ;
}
break;
case COD_WRITE_CHAR:
2007-11-16 09:35:31 +00:00
PDEBUG("Parsing mode: found %s WRITE\n", mode_desc);
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);
if (mode & AA_MAY_WRITE)
yyerror(_("Conflict 'a' and 'w' perms are mutually exclusive."));
mode |= AA_MAY_APPEND;
break;
case COD_LINK_CHAR:
2007-11-16 09:35:31 +00:00
PDEBUG("Parsing mode: found %s LINK\n", mode_desc);
mode |= AA_OLD_MAY_LINK;
break;
case COD_LOCK_CHAR:
2007-11-16 09:35:31 +00:00
PDEBUG("Parsing mode: found %s LOCK\n", mode_desc);
mode |= AA_OLD_MAY_LOCK;
break;
case COD_INHERIT_CHAR:
PDEBUG("Parsing mode: found INHERIT\n");
2008-04-16 04:44:21 +00:00
if (mode & AA_EXEC_MODIFIERS) {
yyerror(_("Exec qualifier 'i' invalid, conflicting qualifier already specified"));
} else {
if (next != tolower(next))
warn_uppercase();
2007-11-16 09:27:34 +00:00
mode |= (AA_EXEC_INHERIT | AA_MAY_EXEC);
p++; /* skip 'x' */
}
break;
case COD_UNSAFE_UNCONFINED_CHAR:
2007-11-16 09:27:34 +00:00
tmode = AA_EXEC_UNSAFE;
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"),
COD_UNSAFE_UNCONFINED_CHAR, COD_EXEC_CHAR);
/* fall through */
case COD_UNCONFINED_CHAR:
2008-04-16 04:44:21 +00:00
tmode |= AA_EXEC_UNCONFINED | AA_MAY_EXEC;
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"),
thisc);
} else {
if (next != tolower(next))
warn_uppercase();
2008-04-16 04:44:21 +00:00
mode |= tmode;
p++; /* skip 'x' */
}
2007-11-16 09:27:34 +00:00
tmode = 0;
break;
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;
/* fall through */
case COD_PROFILE_CHAR:
2008-04-16 04:44:21 +00:00
case COD_LOCAL_CHAR:
if (tolower(thisc) == COD_UNSAFE_PROFILE_CHAR)
2008-04-16 04:44:21 +00:00
tmode |= AA_EXEC_PROFILE | AA_MAY_EXEC;
else
{
tmode |= AA_EXEC_LOCAL | AA_MAY_EXEC;
}
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)) {
yyerror(_("Exec qualifier '%c%c' invalid, conflicting qualifier already specified"), thisc, next);
2007-11-16 09:27:34 +00:00
} else {
2008-04-16 04:44:21 +00:00
mode |= tmode;
2007-11-16 09:27:34 +00:00
p += 2; /* skip x */
}
} 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"), thisc, 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"), thisc);
2008-04-16 04:44:21 +00:00
} else {
if (next != tolower(next))
warn_uppercase();
2008-04-16 04:44:21 +00:00
mode |= tmode;
p++; /* skip 'x' */
}
2007-11-16 09:27:34 +00:00
tmode = 0;
break;
case COD_MMAP_CHAR:
2007-11-16 09:35:31 +00:00
PDEBUG("Parsing mode: found %s MMAP\n", mode_desc);
mode |= AA_OLD_EXEC_MMAP;
break;
case COD_EXEC_CHAR:
/* thisc is valid for deny rules, and named transitions
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
* but invalid for regular x transitions
* sort it out later.
*/
mode |= AA_MAY_EXEC;
break;
/* error cases */
default:
lower = tolower(thisc);
switch (lower) {
case COD_READ_CHAR:
case COD_WRITE_CHAR:
case COD_APPEND_CHAR:
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", thisc);
warn_uppercase();
thisc = lower;
goto reeval;
break;
default:
yyerror(_("Internal: unexpected mode character '%c' in input"),
thisc);
break;
}
break;
}
p++;
}
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)
{
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;
}
static int parse_X_sub_mode(const char *X, const char *str_mode, int *result, int fail, const char *mode_desc unused)
{
int mode = 0;
const char *p;
PDEBUG("Parsing %s mode: %s\n", X, str_mode);
if (!str_mode)
return 0;
p = str_mode;
while (*p) {
char current = *p;
char lower;
reeval:
switch (current) {
case COD_READ_CHAR:
PDEBUG("Parsing %s mode: found %s READ\n", X, mode_desc);
mode |= AA_DBUS_RECEIVE;
break;
case COD_WRITE_CHAR:
PDEBUG("Parsing %s mode: found %s WRITE\n", X,
mode_desc);
mode |= AA_DBUS_SEND;
break;
/* error cases */
default:
lower = tolower(current);
switch (lower) {
case COD_READ_CHAR:
case COD_WRITE_CHAR:
PDEBUG("Parsing %s mode: found invalid upper case char %c\n",
X, current);
warn_uppercase();
current = lower;
goto reeval;
break;
default:
if (fail)
yyerror(_("Internal: unexpected %s mode character '%c' in input"),
X, current);
else
return 0;
break;
}
break;
}
p++;
}
PDEBUG("Parsed %s mode: %s 0x%x\n", X, str_mode, mode);
*result = mode;
return 1;
}
int parse_X_mode(const char *X, int valid, const char *str_mode, int *mode, int fail)
{
*mode = 0;
if (!parse_X_sub_mode(X, str_mode, mode, fail, ""))
return 0;
if (*mode & ~valid) {
if (fail)
yyerror(_("Internal error generated invalid %s perm 0x%x\n"),
X, mode);
else
return 0;
}
return 1;
}
struct cod_entry *new_entry(char *ns, char *id, int mode, char *link_id)
{
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));
if (!entry)
return NULL;
entry->ns = ns;
entry->name = id;
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;
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));
if (!entry)
return NULL;
DUP_STRING(orig, entry, ns, err);
DUP_STRING(orig, entry, name, err);
DUP_STRING(orig, entry, link_name, err);
entry->mode = orig->mode;
entry->audit = orig->audit;
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;
err:
free_cod_entries(entry);
return NULL;
}
void free_cod_entries(struct cod_entry *list)
{
if (!list)
return;
if (list->next)
free_cod_entries(list->next);
if (list->ns)
free(list->ns);
if (list->name)
free(list->name);
if (list->link_name)
free(list->link_name);
if (list->nt_name)
free(list->nt_name);
if (list->pat.regex)
free(list->pat.regex);
free(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);
}
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) {
if (!item)
printf("Item is NULL!\n");
printf("Mode:\t");
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));
if (item->name)
printf("\tName:\t(%s)\n", item->name);
else
printf("\tName:\tNULL\n");
if (item->ns)
printf("\tNs:\t(%s)\n", item->ns);
if (AA_LINK_BITS & item->mode)
printf("\tlink:\t(%s)\n", item->link_name ? item->link_name : "/**");
}
}
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",
"lease",
"audit_write",
2009-08-20 15:27:12 +00:00
"audit_control",
"setfcap",
"mac_override",
"syslog",
};
const char *capability_to_name(unsigned int cap)
{
const char *capname;
capname = (cap < (sizeof(capnames) / sizeof(char *))
? capnames[cap] : "invalid-capability");
return capname;
}
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");
}
struct value_list *new_value_list(char *value)
{
struct value_list *val = (struct value_list *) 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;
}
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);
}
}
void move_conditional_value(const char *rulename, char **dst_ptr,
struct cond_entry *cond_ent)
{
if (*dst_ptr)
yyerror("%s conditional \"%s\" can only be specified once\n",
rulename, cond_ent->name);
*dst_ptr = cond_ent->vals->value;
cond_ent->vals->value = NULL;
}
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 = (struct cond_entry *) calloc(1, sizeof(struct cond_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
if (ent) {
ent->name = name;
ent->vals = list;
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);
}
}
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");
}
}
struct time_units {
const char *str;
long long value;
};
static struct time_units time_units[] = {
{ "us", 1LL },
{ "microsecond", 1LL },
{ "microseconds", 1LL },
{ "ms", 1000LL },
{ "millisecond", 1000LL },
{ "milliseconds", 1000LL },
{ "s", 1000LL * 1000LL },
{ "sec", SECONDS_P_MS },
{ "second", SECONDS_P_MS },
{ "seconds", SECONDS_P_MS },
{ "min" , 60LL * SECONDS_P_MS },
{ "minute", 60LL * SECONDS_P_MS },
{ "minutes", 60LL * SECONDS_P_MS },
{ "h", 60LL * 60LL * SECONDS_P_MS },
{ "hour", 60LL * 60LL * SECONDS_P_MS },
{ "hours", 60LL * 60LL * SECONDS_P_MS },
{ "d", 24LL * 60LL * 60LL * SECONDS_P_MS },
{ "day", 24LL * 60LL * 60LL * SECONDS_P_MS },
{ "days", 24LL * 60LL * 60LL * SECONDS_P_MS },
{ "week", 7LL * 24LL * 60LL * 60LL * SECONDS_P_MS },
{ "weeks", 7LL * 24LL * 60LL * 60LL * SECONDS_P_MS },
{ NULL, 0 }
};
long long convert_time_units(long long value, long long base, const char *units)
{
struct time_units *ent;
if (!units)
/* default to base if no units */
return value;
for (ent = time_units; ent->str; ent++) {
if (strcmp(ent->str, units) == 0) {
if (value * ent->value < base)
return -1LL;
return value * ent->value / base;
}
}
return -2LL;
}
#ifdef UNIT_TEST
#include "unit_test.h"
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;
}
int test_processunquoted(void)
{
int rc = 0;
const char *teststring;
const char *resultstring;
teststring = "";
MY_TEST(strcmp(teststring, processunquoted(teststring, strlen(teststring))) == 0,
"processunquoted on empty string");
teststring = "\\1";
resultstring = "\001";
MY_TEST(strcmp(resultstring, processunquoted(teststring, strlen(teststring))) == 0,
"processunquoted on one digit octal");
teststring = "\\8";
resultstring = "\\8";
MY_TEST(strcmp(resultstring, processunquoted(teststring, strlen(teststring))) == 0,
"processunquoted on invalid octal digit \\8");
teststring = "\\18";
resultstring = "\0018";
MY_TEST(strcmp(resultstring, processunquoted(teststring, strlen(teststring))) == 0,
"processunquoted on one digit octal followed by invalid octal digit");
teststring = "\\1a";
resultstring = "\001a";
MY_TEST(strcmp(resultstring, processunquoted(teststring, strlen(teststring))) == 0,
"processunquoted on one digit octal followed by hex digit a");
teststring = "\\1z";
resultstring = "\001z";
MY_TEST(strcmp(resultstring, processunquoted(teststring, strlen(teststring))) == 0,
"processunquoted on one digit octal follow by char z");
teststring = "\\11";
resultstring = "\011";
MY_TEST(strcmp(resultstring, processunquoted(teststring, strlen(teststring))) == 0,
"processunquoted on two digit octal");
teststring = "\\118";
resultstring = "\0118";
MY_TEST(strcmp(resultstring, processunquoted(teststring, strlen(teststring))) == 0,
"processunquoted on two digit octal followed by invalid octal digit");
teststring = "\\11a";
resultstring = "\011a";
MY_TEST(strcmp(resultstring, processunquoted(teststring, strlen(teststring))) == 0,
"processunquoted on two digit octal followed by hex digit a");
teststring = "\\11z";
resultstring = "\011z";
MY_TEST(strcmp(resultstring, processunquoted(teststring, strlen(teststring))) == 0,
"processunquoted on two digit octal followed by char z");
teststring = "\\111";
resultstring = "\111";
MY_TEST(strcmp(resultstring, processunquoted(teststring, strlen(teststring))) == 0,
"processunquoted on three digit octal");
teststring = "\\378";
resultstring = "\0378";
MY_TEST(strcmp(resultstring, processunquoted(teststring, strlen(teststring))) == 0,
"processunquoted on three digit octal two large, taken as 2 digit octal plus trailing char");
teststring = "123\\421123";
resultstring = "123\0421123";
MY_TEST(strcmp(resultstring, processunquoted(teststring, strlen(teststring))) == 0,
"processunquoted on two character octal followed by valid octal digit \\421");
teststring = "123\\109123";
resultstring = "123\109123";
MY_TEST(strcmp(resultstring, processunquoted(teststring, strlen(teststring))) == 0,
"processunquoted on octal 109");
teststring = "123\\1089123";
resultstring = "123\1089123";
MY_TEST(strcmp(resultstring, processunquoted(teststring, strlen(teststring))) == 0,
"processunquoted on octal 108");
return rc;
}
int test_processquoted(void)
{
int rc = 0;
const char *teststring, *processedstring;
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);
teststring = "\"abc\\429defg\"";
processedstring = "abc\0429defg";
out = processquoted(teststring, strlen(teststring));
MY_TEST(strcmp(processedstring, out) == 0,
"processquoted passthrough quoted invalid octal \\429");
free(out);
teststring = "\"abcdefg\\4\"";
processedstring = "abcdefg\004";
out = processquoted(teststring, strlen(teststring));
MY_TEST(strcmp(processedstring, out) == 0,
"processquoted passthrough quoted one digit trailing octal \\4");
teststring = "\"abcdefg\\04\"";
processedstring = "abcdefg\004";
out = processquoted(teststring, strlen(teststring));
MY_TEST(strcmp(processedstring, out) == 0,
"processquoted passthrough quoted two digit trailing octal \\04");
teststring = "\"abcdefg\\004\"";
processedstring = "abcdefg\004";
out = processquoted(teststring, strlen(teststring));
MY_TEST(strcmp(processedstring, out) == 0,
"processquoted passthrough quoted three digit trailing octal \\004");
free(out);
return rc;
}
#define TIME_TEST(V, B, U, R) \
MY_TEST(convert_time_units((V), (B), U) == (R), \
"convert " #V " with base of " #B ", " #U " units")
int test_convert_time_units()
{
int rc = 0;
TIME_TEST(1LL, 1LL, NULL, 1LL);
TIME_TEST(12345LL, 1LL, NULL, 12345LL);
TIME_TEST(10LL, 10LL, NULL, 10LL);
TIME_TEST(123450LL, 10LL, NULL, 123450LL);
TIME_TEST(12345LL, 1LL, "us", 12345LL);
TIME_TEST(12345LL, 1LL, "microsecond", 12345LL);
TIME_TEST(12345LL, 1LL, "microseconds", 12345LL);
TIME_TEST(12345LL, 1LL, "ms", 12345LL*1000LL);
TIME_TEST(12345LL, 1LL, "millisecond", 12345LL*1000LL);
TIME_TEST(12345LL, 1LL, "milliseconds", 12345LL*1000LL);
TIME_TEST(12345LL, 1LL, "s", 12345LL*1000LL*1000LL);
TIME_TEST(12345LL, 1LL, "sec", 12345LL*1000LL*1000LL);
TIME_TEST(12345LL, 1LL, "second", 12345LL*1000LL*1000LL);
TIME_TEST(12345LL, 1LL, "seconds", 12345LL*1000LL*1000LL);
TIME_TEST(12345LL, 1LL, "min", 12345LL*1000LL*1000LL*60LL);
TIME_TEST(12345LL, 1LL, "minute", 12345LL*1000LL*1000LL*60LL);
TIME_TEST(12345LL, 1LL, "minutes", 12345LL*1000LL*1000LL*60LL);
TIME_TEST(12345LL, 1LL, "h", 12345LL*1000LL*1000LL*60LL*60LL);
TIME_TEST(12345LL, 1LL, "hour", 12345LL*1000LL*1000LL*60LL*60LL);
TIME_TEST(12345LL, 1LL, "hours", 12345LL*1000LL*1000LL*60LL*60LL);
TIME_TEST(12345LL, 1LL, "d", 12345LL*1000LL*1000LL*60LL*60LL*24LL);
TIME_TEST(12345LL, 1LL, "day", 12345LL*1000LL*1000LL*60LL*60LL*24LL);
TIME_TEST(12345LL, 1LL, "days", 12345LL*1000LL*1000LL*60LL*60LL*24LL);
TIME_TEST(12345LL, 1LL, "week", 12345LL*1000LL*1000LL*60LL*60LL*24LL*7LL);
TIME_TEST(12345LL, 1LL, "weeks", 12345LL*1000LL*1000LL*60LL*60LL*24LL*7LL);
return rc;
}
int main(void)
{
int rc = 0;
int retval;
retval = test_str_to_boolean();
if (retval != 0)
rc = retval;
retval = test_processunquoted();
if (retval != 0)
rc = retval;
retval = test_processquoted();
if (retval != 0)
rc = retval;
retval = test_convert_time_units();
if (retval != 0)
rc = retval;
return rc;
}
#endif /* UNIT_TEST */