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
|
|
|
*
|
2013-10-14 14:34:12 -07:00
|
|
|
* Copyright (c) 2013
|
|
|
|
* Canonical Ltd. (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>
|
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>
|
2014-01-06 14:46:10 -08:00
|
|
|
#include <sys/apparmor.h>
|
2015-03-25 17:09:27 -05:00
|
|
|
#include <sys/apparmor_private.h>
|
2006-04-11 21:52:54 +00:00
|
|
|
|
2023-07-03 23:52:57 -07:00
|
|
|
#include <algorithm>
|
2024-10-07 17:19:35 -07:00
|
|
|
#include <unordered_map>
|
2023-07-03 23:52:57 -07:00
|
|
|
|
2020-07-03 02:19:41 -07:00
|
|
|
#include "capability.h"
|
2014-04-15 14:59:41 -07:00
|
|
|
#include "lib.h"
|
2006-04-11 21:52:54 +00:00
|
|
|
#include "parser.h"
|
2013-09-27 16:16:37 -07:00
|
|
|
#include "profile.h"
|
2006-04-11 21:52:54 +00:00
|
|
|
#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
|
2015-03-02 09:50:11 -06:00
|
|
|
#define PDEBUG(fmt, args...) fprintf(stderr, "Lexer: " fmt, ## args)
|
2006-04-11 21:52:54 +00:00
|
|
|
#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 */
|
|
|
|
|
2020-09-29 11:42:32 -07:00
|
|
|
#ifndef HAVE_REALLOCARRAY
|
|
|
|
void *reallocarray(void *ptr, size_t nmemb, size_t size)
|
|
|
|
{
|
|
|
|
return realloc(ptr, nmemb * size);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2024-10-07 17:19:35 -07:00
|
|
|
#ifndef NULL
|
|
|
|
#define NULL nullptr
|
|
|
|
#endif
|
|
|
|
|
2013-10-26 00:15:13 -07:00
|
|
|
int is_blacklisted(const char *name, const char *path)
|
|
|
|
{
|
2015-06-16 15:49:51 -05:00
|
|
|
int retval = _aa_is_blacklisted(name);
|
2013-10-26 00:15:13 -07:00
|
|
|
|
2015-03-25 17:09:27 -05:00
|
|
|
if (retval == -1)
|
|
|
|
PERROR("Ignoring: '%s'\n", path ? path : name);
|
2013-10-26 00:15:13 -07:00
|
|
|
|
2015-03-25 17:09:27 -05:00
|
|
|
return !retval ? 0 : 1;
|
2013-10-26 00:15:13 -07:00
|
|
|
}
|
|
|
|
|
2024-10-07 17:19:35 -07:00
|
|
|
static const unordered_map<string, int> keyword_table = {
|
2006-04-11 21:52:54 +00:00
|
|
|
/* network */
|
2007-07-27 20:29:47 +00:00
|
|
|
{"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},
|
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},
|
2013-09-20 06:48:56 -07:00
|
|
|
{"allow", TOK_ALLOW},
|
2020-06-18 04:06:42 -07:00
|
|
|
{"prompt", TOK_PROMPT},
|
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},
|
2014-04-23 11:35:29 -07:00
|
|
|
{"signal", TOK_SIGNAL},
|
2013-07-31 09:05:51 -07:00
|
|
|
{"send", TOK_SEND},
|
|
|
|
{"receive", TOK_RECEIVE},
|
|
|
|
{"bind", TOK_BIND},
|
|
|
|
{"read", TOK_READ},
|
|
|
|
{"write", TOK_WRITE},
|
parser: Add dbus eavesdrop permission support to apparmor_parser
Allows for the policy writer to grant permission to eavesdrop on the
specified bus. Some example rules for granting the eavesdrop permission
are:
# Grant send, receive, bind, and eavesdrop
dbus,
# Grant send, receive, bind, and eavesdrop on the session bus
dbus bus=session,
# Grant send and eavesdrop on the system bus
dbus (send eavesdrop) bus=system,
# Grant eavesdrop on any bus
dbus eavesdrop,
Eavesdropping rules can contain the bus conditional. Any other
conditionals are not compatible with eavesdropping rules and the parser
will return an error.
Signed-off-by: Tyler Hicks <tyhicks@canonical.com>
Acked-by: Seth Arnold <seth.arnold@canonical.com>
2013-12-06 11:17:43 -08:00
|
|
|
{"eavesdrop", TOK_EAVESDROP},
|
2013-07-31 09:05:51 -07:00
|
|
|
{"peer", TOK_PEER},
|
2014-04-23 11:38:04 -07:00
|
|
|
{"trace", TOK_TRACE},
|
|
|
|
{"tracedby", TOK_TRACEDBY},
|
|
|
|
{"readby", TOK_READBY},
|
2018-09-19 01:15:49 -07:00
|
|
|
{"abi", TOK_ABI},
|
2022-09-29 17:40:18 -03:00
|
|
|
{"userns", TOK_USERNS},
|
2022-02-07 19:15:11 -03:00
|
|
|
{"mqueue", TOK_MQUEUE},
|
|
|
|
{"delete", TOK_DELETE},
|
|
|
|
{"open", TOK_OPEN},
|
2023-03-20 12:28:53 -03:00
|
|
|
{"io_uring", TOK_IO_URING},
|
|
|
|
{"override_creds", TOK_OVERRIDE_CREDS},
|
|
|
|
{"sqpoll", TOK_SQPOLL},
|
2023-09-21 20:39:27 -07:00
|
|
|
{"all", TOK_ALL},
|
parser: add the ability to specify a priority prefix to rules
This enables adding a priority to a rules in policy, finishing out the
priority work done to plumb priority support through the internals in
the previous patch.
Rules have a default priority of 0. The priority prefix can be added
before the other currently support rule prefixes, ie.
[priority prefix][audit qualifier][rule mode][owner]
If present a numerical priority can be assigned to the rule, where the
greater the number the higher the priority. Eg.
priority=1 audit file r /etc/passwd,
priority=-1 deny file w /etc/**,
Rule priority allows the rule with the highest priority to completely
override lower priority rules where they overlap. Within a given
priority level rules will accumulate in standard apparmor fashion.
Eg. given
priority=1 w /*c,
priority=0 r /a*,
priority=-1 k /*b*,
/abc, /bc, /ac .. will have permissions of w
/ab, /abb, /aaa, .. will have permissions of r
/b, /bcb, /bab, .. will have permissions of k
User specified rule priorities are currently capped at the arbitrary
values of 1000, and -1000.
Notes:
* not all rule types support the priority prefix. Rukes like
- network
- capability
- rlimits need to be reworked
need to be reworked to properly preserve the policy rule structure.
* this patch does not support priority on rule blocks
* this patch does not support using a variable in the priority value.
Signed-off-by: John Johansen <john.johansen@canonical.com>
2024-05-11 23:33:42 -07:00
|
|
|
{"priority", TOK_PRIORITY},
|
2008-04-06 18:55:46 +00:00
|
|
|
};
|
|
|
|
|
2024-10-07 17:19:35 -07:00
|
|
|
static const unordered_map<string, int> rlimit_table = {
|
2008-04-06 18:55:46 +00:00
|
|
|
{"cpu", RLIMIT_CPU},
|
|
|
|
{"fsize", RLIMIT_FSIZE},
|
|
|
|
{"data", RLIMIT_DATA},
|
|
|
|
{"stack", RLIMIT_STACK},
|
|
|
|
{"core", RLIMIT_CORE},
|
|
|
|
{"rss", RLIMIT_RSS},
|
|
|
|
{"nofile", RLIMIT_NOFILE},
|
2017-09-27 11:31:10 +02:00
|
|
|
#ifdef RLIMIT_OFILE
|
2008-04-06 18:55:46 +00:00
|
|
|
{"ofile", RLIMIT_OFILE},
|
2017-09-27 11:31:10 +02:00
|
|
|
#endif
|
2008-04-06 18:55:46 +00:00
|
|
|
{"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},
|
2014-01-24 11:06:31 -08:00
|
|
|
#endif
|
|
|
|
#ifdef RLIMIT_RTTIME
|
|
|
|
{"rttime", RLIMIT_RTTIME},
|
2008-06-09 21:17:41 +00:00
|
|
|
#endif
|
2006-04-11 21:52:54 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/* for alpha matches, check for keywords */
|
2024-10-07 17:19:35 -07:00
|
|
|
static int get_table_token(const char *name unused, const unordered_map<string, int> &table,
|
|
|
|
const string &keyword)
|
2006-04-11 21:52:54 +00:00
|
|
|
{
|
2024-10-07 17:19:35 -07:00
|
|
|
auto token_entry = table.find(keyword);
|
|
|
|
if (token_entry == table.end()) {
|
2024-10-08 15:37:08 -07:00
|
|
|
PDEBUG("Unable to find %s %s\n", name, keyword.c_str());
|
2024-10-07 17:19:35 -07:00
|
|
|
return -1;
|
|
|
|
} else {
|
|
|
|
PDEBUG("Found %s %s\n", name, keyword.c_str());
|
|
|
|
return token_entry->second;
|
2006-04-11 21:52:54 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-02 06:25:38 -07:00
|
|
|
/* for alpha matches, check for keywords */
|
|
|
|
int get_keyword_token(const char *keyword)
|
|
|
|
{
|
2024-10-07 17:19:35 -07:00
|
|
|
// Can't use string_view because that requires C++17
|
|
|
|
return get_table_token("keyword", keyword_table, string(keyword));
|
2020-07-02 06:25:38 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
int get_rlimit(const char *name)
|
|
|
|
{
|
2024-10-07 17:19:35 -07:00
|
|
|
// Can't use string_view because that requires C++17
|
|
|
|
return get_table_token("rlimit", rlimit_table, string(name));
|
2020-07-02 06:25:38 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-08-27 00:58:55 -07:00
|
|
|
/*
|
|
|
|
* WARNING: if the format of the following table is changed then
|
|
|
|
* the Makefile targets, cap_names.h and generated_cap_names.h
|
|
|
|
* must be updated.
|
|
|
|
*/
|
2020-07-02 03:01:25 -07:00
|
|
|
struct capability_table {
|
2020-07-03 02:19:41 -07:00
|
|
|
const char *name;
|
|
|
|
unsigned int cap;
|
2020-07-02 03:01:25 -07:00
|
|
|
unsigned int backmap;
|
2020-07-02 06:37:39 -07:00
|
|
|
capability_flags flags;
|
2020-07-02 03:01:25 -07:00
|
|
|
};
|
|
|
|
|
2024-10-08 15:37:08 -07:00
|
|
|
/*
|
|
|
|
* Enum for the results of adding a capability, with values assigned to match
|
|
|
|
* the int values returned by the old capable_add_cap function:
|
|
|
|
*
|
|
|
|
* -1: error
|
|
|
|
* 0: no change - capability already in table
|
|
|
|
* 1: added flag to capability in table
|
|
|
|
* 2: added new capability
|
|
|
|
*/
|
|
|
|
enum add_cap_result {
|
|
|
|
ERROR = -1, // Was only used for OOM conditions
|
|
|
|
ALREADY_EXISTS = 0,
|
|
|
|
FLAG_ADDED = 1,
|
|
|
|
CAP_ADDED = 2
|
|
|
|
};
|
|
|
|
|
2020-07-02 03:01:25 -07:00
|
|
|
static struct capability_table base_capability_table[] = {
|
2007-11-16 09:32:38 +00:00
|
|
|
/* capabilities */
|
|
|
|
#include "cap_names.h"
|
|
|
|
};
|
2024-10-08 15:37:08 -07:00
|
|
|
static const size_t BASE_CAP_TABLE_SIZE = sizeof(base_capability_table)/sizeof(struct capability_table);
|
|
|
|
|
|
|
|
class capability_lookup {
|
|
|
|
vector<capability_table> cap_table;
|
|
|
|
// Use unordered_map to avoid pulling in two map implementations
|
|
|
|
// We may want to switch to boost::multiindex to avoid duplication
|
|
|
|
unordered_map<string, capability_table&> name_cap_map;
|
|
|
|
unordered_map<unsigned int, capability_table&> int_cap_map;
|
|
|
|
|
|
|
|
private:
|
|
|
|
void add_capability_table_entry_raw(capability_table entry) {
|
|
|
|
cap_table.push_back(entry);
|
|
|
|
capability_table &entry_ref = cap_table.back();
|
|
|
|
name_cap_map.emplace(string(entry_ref.name), entry_ref);
|
|
|
|
int_cap_map.emplace(entry_ref.cap, entry_ref);
|
|
|
|
}
|
|
|
|
public:
|
|
|
|
capability_lookup() :
|
|
|
|
cap_table(vector<capability_table>()),
|
|
|
|
name_cap_map(unordered_map<string, capability_table&>(BASE_CAP_TABLE_SIZE)),
|
|
|
|
int_cap_map(unordered_map<unsigned int, capability_table&>(BASE_CAP_TABLE_SIZE)) {
|
|
|
|
cap_table.reserve(BASE_CAP_TABLE_SIZE);
|
|
|
|
for (size_t i=0; i<BASE_CAP_TABLE_SIZE; i++) {
|
|
|
|
add_capability_table_entry_raw(base_capability_table[i]);
|
|
|
|
}
|
|
|
|
}
|
2007-11-16 09:32:38 +00:00
|
|
|
|
2024-10-08 15:37:08 -07:00
|
|
|
capability_table* find_cap_entry_by_name(string const & name) const {
|
|
|
|
auto map_entry = this->name_cap_map.find(name);
|
|
|
|
if (map_entry == this->name_cap_map.end()) {
|
|
|
|
return NULL;
|
|
|
|
} else {
|
|
|
|
PDEBUG("Found %s %s\n", name.c_str(), map_entry->second.name);
|
|
|
|
return &map_entry->second;
|
|
|
|
}
|
|
|
|
}
|
2020-07-02 07:17:36 -07:00
|
|
|
|
2024-10-08 15:37:08 -07:00
|
|
|
capability_table* find_cap_entry_by_num(unsigned int cap) const {
|
|
|
|
auto map_entry = this->int_cap_map.find(cap);
|
|
|
|
if (map_entry == this->int_cap_map.end()) {
|
|
|
|
return NULL;
|
|
|
|
} else {
|
|
|
|
PDEBUG("Found %d %d\n", cap, map_entry->second.cap);
|
|
|
|
return &map_entry->second;
|
|
|
|
}
|
|
|
|
}
|
2020-07-02 07:17:36 -07:00
|
|
|
|
2024-10-08 15:37:08 -07:00
|
|
|
int name_to_capability(string const &cap) const {
|
|
|
|
auto map_entry = this->name_cap_map.find(cap);
|
|
|
|
if (map_entry == this->name_cap_map.end()) {
|
|
|
|
PDEBUG("Unable to find %s %s\n", "capability", cap.c_str());
|
|
|
|
return -1;
|
|
|
|
} else {
|
|
|
|
return map_entry->second.cap;
|
|
|
|
}
|
|
|
|
}
|
2020-07-02 03:01:25 -07:00
|
|
|
|
2024-10-08 15:37:08 -07:00
|
|
|
const char *capability_to_name(unsigned int cap) const {
|
|
|
|
auto map_entry = this->int_cap_map.find(cap);
|
|
|
|
if (map_entry == this->int_cap_map.end()) {
|
|
|
|
return "invalid-capability";
|
|
|
|
} else {
|
|
|
|
return map_entry->second.name;
|
2020-07-02 03:01:25 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-10-08 15:37:08 -07:00
|
|
|
int capability_backmap(unsigned int cap) const {
|
|
|
|
auto map_entry = this->int_cap_map.find(cap);
|
|
|
|
if (map_entry == this->int_cap_map.end()) {
|
|
|
|
return NO_BACKMAP_CAP;
|
|
|
|
} else {
|
|
|
|
return map_entry->second.backmap;
|
|
|
|
}
|
|
|
|
}
|
2020-07-02 03:01:25 -07:00
|
|
|
|
2024-10-08 15:37:08 -07:00
|
|
|
bool capability_in_kernel(unsigned int cap) const {
|
|
|
|
auto map_entry = this->int_cap_map.find(cap);
|
|
|
|
if (map_entry == this->int_cap_map.end()) {
|
|
|
|
return false;
|
|
|
|
} else {
|
|
|
|
return map_entry->second.flags & CAPFLAG_KERNEL_FEATURE;
|
|
|
|
}
|
|
|
|
}
|
2020-07-07 07:31:06 -07:00
|
|
|
|
2024-10-08 15:37:08 -07:00
|
|
|
void __debug_capabilities(uint64_t capset, const char *name) const {
|
|
|
|
printf("%s:", name);
|
|
|
|
|
|
|
|
for (auto it = this->cap_table.cbegin(); it != this->cap_table.cend(); it++) {
|
|
|
|
if ((1ull << it->cap) & capset)
|
|
|
|
printf (" %s", it->name);
|
2020-07-07 07:31:06 -07:00
|
|
|
}
|
2024-10-08 15:37:08 -07:00
|
|
|
printf("\n");
|
2020-07-07 07:31:06 -07:00
|
|
|
}
|
|
|
|
|
2024-10-08 15:37:08 -07:00
|
|
|
add_cap_result capable_add_cap(string const & str, unsigned int cap,
|
|
|
|
capability_flags flag) {
|
|
|
|
struct capability_table *ent = this->find_cap_entry_by_name(str);
|
|
|
|
if (ent) {
|
|
|
|
if (ent->cap != cap) {
|
|
|
|
pwarn(WARN_UNEXPECTED, "feature capability '%s:%d' does not equal expected %d. Ignoring ...\n", str.c_str(), cap, ent->cap);
|
|
|
|
/* TODO: make warn to error config */
|
|
|
|
return add_cap_result::ALREADY_EXISTS;
|
|
|
|
}
|
|
|
|
if (ent->flags & flag)
|
|
|
|
return add_cap_result::ALREADY_EXISTS;
|
|
|
|
ent->flags = (capability_flags) (ent->flags | flag);
|
|
|
|
return add_cap_result::FLAG_ADDED;
|
|
|
|
} else {
|
|
|
|
struct capability_table new_entry;
|
|
|
|
new_entry.name = strdup(str.c_str());
|
|
|
|
if (!new_entry.name) {
|
|
|
|
yyerror(_("Out of memory"));
|
|
|
|
return add_cap_result::ERROR;
|
|
|
|
}
|
|
|
|
new_entry.cap = cap;
|
2024-11-14 17:14:16 -08:00
|
|
|
new_entry.backmap = 0;
|
2024-10-08 15:37:08 -07:00
|
|
|
new_entry.flags = flag;
|
|
|
|
try {
|
|
|
|
this->add_capability_table_entry_raw(new_entry);
|
|
|
|
} catch (const std::bad_alloc &_e) {
|
|
|
|
yyerror(_("Out of memory"));
|
|
|
|
return add_cap_result::ERROR;
|
|
|
|
}
|
|
|
|
// TODO: exception catching for causes other than OOM
|
|
|
|
return add_cap_result::CAP_ADDED;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void clear_cap_flag(capability_flags flags)
|
|
|
|
{
|
|
|
|
for (auto it = this->cap_table.begin(); it != this->cap_table.end(); it++) {
|
|
|
|
PDEBUG("Clearing capability flag for capability \"%s\"\n", it->name);
|
|
|
|
it->flags = (capability_flags) (it->flags & ~flags);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
static capability_lookup cap_table;
|
2020-07-07 07:31:06 -07:00
|
|
|
|
2020-07-03 02:19:41 -07:00
|
|
|
/* don't mark up str with \0 */
|
|
|
|
static const char *strn_token(const char *str, size_t &len)
|
2007-11-16 09:32:38 +00:00
|
|
|
{
|
2020-07-03 02:19:41 -07:00
|
|
|
const char *start;
|
|
|
|
|
|
|
|
while (isspace(*str))
|
|
|
|
str++;
|
|
|
|
start = str;
|
|
|
|
while (*str && !isspace(*str))
|
|
|
|
str++;
|
|
|
|
if (start == str)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
len = str - start;
|
|
|
|
return start;
|
|
|
|
}
|
|
|
|
|
2023-07-03 23:52:57 -07:00
|
|
|
int null_strcmp(const char *s1, const char *s2)
|
|
|
|
{
|
|
|
|
if (s1) {
|
|
|
|
if (s2)
|
|
|
|
return strcmp(s1, s2);
|
|
|
|
return 1;
|
|
|
|
} else if (s2) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// both null
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool strcomp (const char *lhs, const char *rhs)
|
|
|
|
{
|
|
|
|
return null_strcmp(lhs, rhs) < 0;
|
|
|
|
}
|
|
|
|
|
2020-07-03 02:19:41 -07:00
|
|
|
bool add_cap_feature_mask(struct aa_features *features, capability_flags flags)
|
|
|
|
{
|
|
|
|
autofree char *value = NULL;
|
|
|
|
const char *capstr;
|
|
|
|
size_t valuelen, len = 0;
|
|
|
|
int n;
|
|
|
|
|
|
|
|
value = aa_features_value(features, "caps/mask", &valuelen);
|
|
|
|
if (!value)
|
2020-12-11 03:56:48 -08:00
|
|
|
/* nothing to add, just use existing set */
|
|
|
|
return true;
|
2020-07-03 02:19:41 -07:00
|
|
|
|
|
|
|
n = 0;
|
|
|
|
for (capstr = strn_token(value, len);
|
|
|
|
capstr;
|
|
|
|
capstr = strn_token(capstr + len, len)) {
|
2024-10-08 15:37:08 -07:00
|
|
|
string capstr_as_str = string(capstr, len);
|
|
|
|
if (cap_table.capable_add_cap(capstr_as_str, n, flags) < 0)
|
2020-07-03 02:19:41 -07:00
|
|
|
return false;
|
|
|
|
n++;
|
|
|
|
if (len > valuelen) {
|
|
|
|
PDEBUG("caplen is > remaining feature string");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
valuelen -= len;
|
|
|
|
PDEBUG("Adding %d capabilities\n", n);
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void clear_cap_flag(capability_flags flags)
|
|
|
|
{
|
2024-10-08 15:37:08 -07:00
|
|
|
cap_table.clear_cap_flag(flags);
|
2020-07-03 02:19:41 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
int name_to_capability(const char *cap)
|
|
|
|
{
|
2024-10-08 15:37:08 -07:00
|
|
|
return cap_table.name_to_capability(string(cap));
|
2007-11-16 09:32:38 +00:00
|
|
|
}
|
|
|
|
|
2020-07-02 06:25:38 -07:00
|
|
|
const char *capability_to_name(unsigned int cap)
|
2007-11-16 09:32:38 +00:00
|
|
|
{
|
2024-10-08 15:37:08 -07:00
|
|
|
return cap_table.capability_to_name(cap);
|
2007-11-16 09:32:38 +00:00
|
|
|
}
|
|
|
|
|
2020-07-07 07:31:06 -07:00
|
|
|
int capability_backmap(unsigned int cap)
|
|
|
|
{
|
2024-10-08 15:37:08 -07:00
|
|
|
return cap_table.capability_backmap(cap);
|
2020-07-07 07:31:06 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
bool capability_in_kernel(unsigned int cap)
|
|
|
|
{
|
2024-10-08 15:37:08 -07:00
|
|
|
return cap_table.capability_in_kernel(cap);
|
2020-07-07 07:31:06 -07:00
|
|
|
}
|
|
|
|
|
2020-07-02 06:25:38 -07:00
|
|
|
void __debug_capabilities(uint64_t capset, const char *name)
|
2008-04-06 18:55:46 +00:00
|
|
|
{
|
2024-10-08 15:37:08 -07:00
|
|
|
cap_table.__debug_capabilities(capset, name);
|
2008-04-06 18:55:46 +00:00
|
|
|
}
|
|
|
|
|
2013-10-01 10:59:04 -07:00
|
|
|
char *processunquoted(const char *string, int len)
|
2006-04-11 21:52:54 +00:00
|
|
|
{
|
2014-04-15 14:59:41 -07:00
|
|
|
char *buffer, *s;
|
2006-04-11 21:52:54 +00:00
|
|
|
|
2014-04-15 14:59:41 -07:00
|
|
|
s = buffer = (char *) malloc(len + 1);
|
|
|
|
if (!buffer)
|
2006-04-11 21:52:54 +00:00
|
|
|
return NULL;
|
|
|
|
|
2014-04-15 14:59:41 -07:00
|
|
|
while (len > 0) {
|
|
|
|
const char *pos = string + 1;
|
|
|
|
long c;
|
|
|
|
if (*string == '\\' && len > 1 &&
|
|
|
|
(c = strn_escseq(&pos, "", len)) != -1) {
|
2014-06-19 13:49:53 -07:00
|
|
|
/* catch \\ or \134 and other aare special chars and
|
|
|
|
* pass it through to be handled by the backend
|
|
|
|
* pcre conversion
|
2014-06-19 13:47:39 -07:00
|
|
|
*/
|
2015-02-12 10:19:16 -08:00
|
|
|
if (c == 0) {
|
|
|
|
strncpy(s, string, pos - string);
|
|
|
|
s += pos - string;
|
|
|
|
} else if (strchr("*?[]{}^,\\", c) != NULL) {
|
2014-06-19 13:47:39 -07:00
|
|
|
*s++ = '\\';
|
2014-06-19 13:49:53 -07:00
|
|
|
*s++ = c;
|
2014-06-19 13:47:39 -07:00
|
|
|
} else
|
|
|
|
*s++ = c;
|
2014-04-15 14:59:41 -07:00
|
|
|
len -= pos - string;
|
|
|
|
string = pos;
|
2006-04-11 21:52:54 +00:00
|
|
|
} else {
|
2014-04-15 14:59:41 -07:00
|
|
|
/* either unescaped char OR
|
|
|
|
* unsupported escape sequence resulting in char being
|
|
|
|
* copied.
|
|
|
|
*/
|
|
|
|
*s++ = *string++;
|
|
|
|
len--;
|
2006-04-11 21:52:54 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
*s = 0;
|
|
|
|
|
2014-04-15 14:59:41 -07:00
|
|
|
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 == '"') {
|
2018-09-20 04:36:24 -07:00
|
|
|
if (string[len -1] != '"')
|
|
|
|
return NULL;
|
2014-04-15 14:59:41 -07:00
|
|
|
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);
|
2012-01-02 16:48:44 -08:00
|
|
|
}
|
|
|
|
|
2013-10-01 10:59:04 -07:00
|
|
|
char *processid(const char *string, int len)
|
2012-01-02 16:48:44 -08:00
|
|
|
{
|
|
|
|
/* 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
|
|
|
}
|
|
|
|
|
|
|
|
/* 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;
|
|
|
|
|
2014-04-07 03:16:50 -07:00
|
|
|
void warn_uppercase(void)
|
[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 (!warned_uppercase) {
|
2020-08-28 08:35:45 -07:00
|
|
|
pwarn(WARN_DEPRECATED, _("Uppercase qualifiers \"RWLIMX\" are deprecated, please convert to lowercase\n"
|
2006-08-04 17:20:16 +00:00
|
|
|
"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
|
|
|
|
2023-08-02 02:07:36 -07:00
|
|
|
static perm32_t parse_sub_perms(const char *str_perms, const char *perms_desc unused)
|
2006-04-11 21:52:54 +00:00
|
|
|
{
|
|
|
|
|
2021-06-09 00:56:59 -07:00
|
|
|
#define IS_DIFF_QUAL(perms, q) (((perms) & AA_MAY_EXEC) && (((perms) & AA_EXEC_TYPE) != ((q) & AA_EXEC_TYPE)))
|
2006-04-11 21:52:54 +00:00
|
|
|
|
2023-08-02 02:07:36 -07:00
|
|
|
perm32_t perms = 0;
|
2006-04-11 21:52:54 +00:00
|
|
|
const char *p;
|
|
|
|
|
2021-06-09 00:56:59 -07:00
|
|
|
PDEBUG("Parsing perms: %s\n", str_perms);
|
2006-04-11 21:52:54 +00:00
|
|
|
|
2021-06-09 00:56:59 -07:00
|
|
|
if (!str_perms)
|
2007-06-26 21:10:28 +00:00
|
|
|
return 0;
|
|
|
|
|
2021-06-09 00:56:59 -07:00
|
|
|
p = str_perms;
|
2006-04-11 21:52:54 +00:00
|
|
|
while (*p) {
|
2013-09-27 16:13:22 -07:00
|
|
|
char thisc = *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 next = *(p + 1);
|
|
|
|
char lower;
|
2023-08-02 02:07:36 -07:00
|
|
|
perm32_t tperms = 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:
|
2013-09-27 16:13:22 -07:00
|
|
|
switch (thisc) {
|
2006-04-11 21:52:54 +00:00
|
|
|
case COD_READ_CHAR:
|
2009-06-10 15:37:27 +00:00
|
|
|
if (read_implies_exec) {
|
2021-06-09 00:56:59 -07:00
|
|
|
PDEBUG("Parsing perms: found %s READ imply X\n", perms_desc);
|
|
|
|
perms |= AA_MAY_READ | AA_OLD_EXEC_MMAP;
|
2009-06-10 15:37:27 +00:00
|
|
|
} else {
|
2021-06-09 00:56:59 -07:00
|
|
|
PDEBUG("Parsing perms: found %s READ\n", perms_desc);
|
|
|
|
perms |= AA_MAY_READ;
|
2009-06-10 15:37:27 +00:00
|
|
|
}
|
2006-04-11 21:52:54 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case COD_WRITE_CHAR:
|
2021-06-09 00:56:59 -07:00
|
|
|
PDEBUG("Parsing perms: found %s WRITE\n", perms_desc);
|
|
|
|
if ((perms & AA_MAY_APPEND) && !(perms & AA_MAY_WRITE))
|
2007-07-27 20:31:38 +00:00
|
|
|
yyerror(_("Conflict 'a' and 'w' perms are mutually exclusive."));
|
2021-06-09 00:56:59 -07:00
|
|
|
perms |= AA_MAY_WRITE | AA_MAY_APPEND;
|
2007-07-27 20:31:38 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case COD_APPEND_CHAR:
|
2021-06-09 00:56:59 -07:00
|
|
|
PDEBUG("Parsing perms: found %s APPEND\n", perms_desc);
|
|
|
|
if (perms & AA_MAY_WRITE)
|
2007-07-27 20:31:38 +00:00
|
|
|
yyerror(_("Conflict 'a' and 'w' perms are mutually exclusive."));
|
2021-06-09 00:56:59 -07:00
|
|
|
perms |= AA_MAY_APPEND;
|
2006-04-11 21:52:54 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case COD_LINK_CHAR:
|
2021-06-09 00:56:59 -07:00
|
|
|
PDEBUG("Parsing perms: found %s LINK\n", perms_desc);
|
|
|
|
perms |= AA_OLD_MAY_LINK;
|
2006-04-11 21:52:54 +00:00
|
|
|
break;
|
|
|
|
|
2007-07-27 20:38:43 +00:00
|
|
|
case COD_LOCK_CHAR:
|
2021-06-09 00:56:59 -07:00
|
|
|
PDEBUG("Parsing perms: found %s LOCK\n", perms_desc);
|
|
|
|
perms |= AA_OLD_MAY_LOCK;
|
2007-07-27 20:38:43 +00:00
|
|
|
break;
|
|
|
|
|
2006-04-11 21:52:54 +00:00
|
|
|
case COD_INHERIT_CHAR:
|
2021-06-09 00:56:59 -07:00
|
|
|
PDEBUG("Parsing perms: found INHERIT\n");
|
|
|
|
if (perms & 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();
|
2021-06-09 00:56:59 -07:00
|
|
|
perms |= (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:
|
2021-06-09 00:56:59 -07:00
|
|
|
tperms = AA_EXEC_UNSAFE;
|
2020-08-28 08:35:45 -07:00
|
|
|
pwarn(WARN_DANGEROUS, _("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:
|
2021-06-09 00:56:59 -07:00
|
|
|
tperms |= AA_EXEC_UNCONFINED | AA_MAY_EXEC;
|
|
|
|
PDEBUG("Parsing perms: found UNCONFINED\n");
|
|
|
|
if (IS_DIFF_QUAL(perms, tperms)) {
|
2006-08-04 17:20:16 +00:00
|
|
|
yyerror(_("Exec qualifier '%c' invalid, conflicting qualifier already specified"),
|
2013-09-27 16:13:22 -07:00
|
|
|
thisc);
|
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();
|
2021-06-09 00:56:59 -07:00
|
|
|
perms |= tperms;
|
2006-04-11 21:52:54 +00:00
|
|
|
p++; /* skip 'x' */
|
|
|
|
}
|
2021-06-09 00:56:59 -07:00
|
|
|
tperms = 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:
|
2021-06-09 00:56:59 -07:00
|
|
|
tperms = 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:
|
2013-09-27 16:13:22 -07:00
|
|
|
if (tolower(thisc) == COD_UNSAFE_PROFILE_CHAR)
|
2021-06-09 00:56:59 -07:00
|
|
|
tperms |= AA_EXEC_PROFILE | AA_MAY_EXEC;
|
2008-04-16 04:44:21 +00:00
|
|
|
else
|
|
|
|
{
|
2021-06-09 00:56:59 -07:00
|
|
|
tperms |= AA_EXEC_LOCAL | AA_MAY_EXEC;
|
2008-04-16 04:44:21 +00:00
|
|
|
}
|
2021-06-09 00:56:59 -07:00
|
|
|
PDEBUG("Parsing perms: found PROFILE\n");
|
2007-11-16 09:27:34 +00:00
|
|
|
if (tolower(next) == COD_INHERIT_CHAR) {
|
2021-06-09 00:56:59 -07:00
|
|
|
tperms |= AA_EXEC_INHERIT;
|
|
|
|
if (IS_DIFF_QUAL(perms, tperms)) {
|
2013-09-27 16:13:22 -07:00
|
|
|
yyerror(_("Exec qualifier '%c%c' invalid, conflicting qualifier already specified"), thisc, next);
|
2007-11-16 09:27:34 +00:00
|
|
|
} else {
|
2021-06-09 00:56:59 -07:00
|
|
|
perms |= tperms;
|
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) {
|
2021-06-09 00:56:59 -07:00
|
|
|
tperms |= AA_EXEC_PUX;
|
|
|
|
if (IS_DIFF_QUAL(perms, tperms)) {
|
2013-09-27 16:13:22 -07:00
|
|
|
yyerror(_("Exec qualifier '%c%c' invalid, conflicting qualifier already specified"), thisc, next);
|
2009-08-20 15:41:10 +00:00
|
|
|
} else {
|
2021-06-09 00:56:59 -07:00
|
|
|
perms |= tperms;
|
2009-08-20 15:41:10 +00:00
|
|
|
p += 2; /* skip x */
|
|
|
|
}
|
2021-06-09 00:56:59 -07:00
|
|
|
} else if (IS_DIFF_QUAL(perms, tperms)) {
|
2013-09-27 16:13:22 -07:00
|
|
|
yyerror(_("Exec qualifier '%c' invalid, conflicting qualifier already specified"), thisc);
|
2008-04-16 04:44:21 +00:00
|
|
|
|
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();
|
2021-06-09 00:56:59 -07:00
|
|
|
perms |= tperms;
|
2006-04-11 21:52:54 +00:00
|
|
|
p++; /* skip 'x' */
|
|
|
|
}
|
2021-06-09 00:56:59 -07:00
|
|
|
tperms = 0;
|
2006-04-11 21:52:54 +00:00
|
|
|
break;
|
|
|
|
|
2006-08-04 17:14:06 +00:00
|
|
|
case COD_MMAP_CHAR:
|
2021-06-09 00:56:59 -07:00
|
|
|
PDEBUG("Parsing perms: found %s MMAP\n", perms_desc);
|
|
|
|
perms |= AA_OLD_EXEC_MMAP;
|
2006-08-04 17:14:06 +00:00
|
|
|
break;
|
|
|
|
|
2006-04-11 21:52:54 +00:00
|
|
|
case COD_EXEC_CHAR:
|
2013-09-27 16:13:22 -07:00
|
|
|
/* 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.
|
|
|
|
*/
|
2021-06-09 00:56:59 -07:00
|
|
|
perms |= 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:
|
2013-09-27 16:13:22 -07:00
|
|
|
lower = tolower(thisc);
|
[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
|
|
|
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:
|
2021-06-09 00:56:59 -07:00
|
|
|
PDEBUG("Parsing perms: found invalid upper case char %c\n", thisc);
|
[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
|
|
|
warn_uppercase();
|
2013-09-27 16:13:22 -07:00
|
|
|
thisc = lower;
|
[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
|
|
|
goto reeval;
|
|
|
|
break;
|
|
|
|
default:
|
2021-06-09 00:56:59 -07:00
|
|
|
yyerror(_("Internal: unexpected perms character '%c' in input"),
|
2013-09-27 16:13:22 -07:00
|
|
|
thisc);
|
[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
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
2006-04-11 21:52:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
p++;
|
|
|
|
}
|
2007-11-29 18:06:53 +00:00
|
|
|
|
2021-06-09 00:56:59 -07:00
|
|
|
PDEBUG("Parsed perms: %s 0x%x\n", str_perms, perms);
|
2006-04-11 21:52:54 +00:00
|
|
|
|
2021-06-09 00:56:59 -07:00
|
|
|
return perms;
|
2006-04-11 21:52:54 +00:00
|
|
|
}
|
|
|
|
|
2023-08-02 02:07:36 -07:00
|
|
|
perm32_t parse_perms(const char *str_perms)
|
2007-11-16 09:35:31 +00:00
|
|
|
{
|
2023-08-02 02:07:36 -07:00
|
|
|
perm32_t tmp, perms = 0;
|
2021-06-09 00:56:59 -07:00
|
|
|
tmp = parse_sub_perms(str_perms, "");
|
|
|
|
perms = SHIFT_PERMS(tmp, AA_USER_SHIFT);
|
|
|
|
perms |= SHIFT_PERMS(tmp, AA_OTHER_SHIFT);
|
|
|
|
if (perms & ~AA_VALID_PERMS)
|
|
|
|
yyerror(_("Internal error generated invalid perm 0x%llx\n"), perms);
|
|
|
|
return perms;
|
2007-11-16 09:35:31 +00:00
|
|
|
}
|
|
|
|
|
2023-08-02 02:07:36 -07:00
|
|
|
static int parse_X_sub_perms(const char *X, const char *str_perms, perm32_t *result, int fail, const char *perms_desc unused)
|
2014-04-07 03:19:19 -07:00
|
|
|
{
|
2023-08-02 02:07:36 -07:00
|
|
|
perm32_t perms = 0;
|
2014-04-07 03:19:19 -07:00
|
|
|
const char *p;
|
|
|
|
|
2021-06-09 00:56:59 -07:00
|
|
|
PDEBUG("Parsing %s perms: %s\n", X, str_perms);
|
2014-04-07 03:19:19 -07:00
|
|
|
|
2021-06-09 00:56:59 -07:00
|
|
|
if (!str_perms)
|
2014-04-07 03:19:19 -07:00
|
|
|
return 0;
|
|
|
|
|
2021-06-09 00:56:59 -07:00
|
|
|
p = str_perms;
|
2014-04-07 03:19:19 -07:00
|
|
|
while (*p) {
|
|
|
|
char current = *p;
|
|
|
|
char lower;
|
|
|
|
|
|
|
|
reeval:
|
|
|
|
switch (current) {
|
|
|
|
case COD_READ_CHAR:
|
2021-06-09 00:56:59 -07:00
|
|
|
PDEBUG("Parsing %s perms: found %s READ\n", X, perms_desc);
|
|
|
|
perms |= AA_DBUS_RECEIVE;
|
2014-04-07 03:19:19 -07:00
|
|
|
break;
|
|
|
|
|
|
|
|
case COD_WRITE_CHAR:
|
2021-06-09 00:56:59 -07:00
|
|
|
PDEBUG("Parsing %s perms: found %s WRITE\n", X,
|
|
|
|
perms_desc);
|
|
|
|
perms |= AA_DBUS_SEND;
|
2014-04-07 03:19:19 -07:00
|
|
|
break;
|
|
|
|
|
|
|
|
/* error cases */
|
|
|
|
|
|
|
|
default:
|
|
|
|
lower = tolower(current);
|
|
|
|
switch (lower) {
|
|
|
|
case COD_READ_CHAR:
|
|
|
|
case COD_WRITE_CHAR:
|
2021-06-09 00:56:59 -07:00
|
|
|
PDEBUG("Parsing %s perms: found invalid upper case char %c\n",
|
2014-04-07 03:19:19 -07:00
|
|
|
X, current);
|
|
|
|
warn_uppercase();
|
|
|
|
current = lower;
|
|
|
|
goto reeval;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
if (fail)
|
2021-06-09 00:56:59 -07:00
|
|
|
yyerror(_("Internal: unexpected %s perms character '%c' in input"),
|
2014-04-07 03:19:19 -07:00
|
|
|
X, current);
|
|
|
|
else
|
|
|
|
return 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
p++;
|
|
|
|
}
|
|
|
|
|
2021-06-09 00:56:59 -07:00
|
|
|
PDEBUG("Parsed %s perms: %s 0x%x\n", X, str_perms, perms);
|
2014-04-07 03:19:19 -07:00
|
|
|
|
2021-06-09 00:56:59 -07:00
|
|
|
*result = perms;
|
2014-04-07 03:19:19 -07:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2023-08-02 02:07:36 -07:00
|
|
|
int parse_X_perms(const char *X, int valid, const char *str_perms, perm32_t *perms, int fail)
|
2014-04-07 03:19:19 -07:00
|
|
|
{
|
2021-06-09 00:56:59 -07:00
|
|
|
*perms = 0;
|
|
|
|
if (!parse_X_sub_perms(X, str_perms, perms, fail, ""))
|
2014-04-07 03:19:19 -07:00
|
|
|
return 0;
|
2021-06-09 00:56:59 -07:00
|
|
|
if (*perms & ~valid) {
|
2014-04-07 03:19:19 -07:00
|
|
|
if (fail)
|
|
|
|
yyerror(_("Internal error generated invalid %s perm 0x%x\n"),
|
2021-06-09 00:56:59 -07:00
|
|
|
X, perms);
|
2014-04-07 03:19:19 -07:00
|
|
|
else
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2016-03-18 17:28:51 -05:00
|
|
|
/**
|
|
|
|
* parse_label - break a label down to the namespace and profile name
|
2016-03-18 17:28:51 -05:00
|
|
|
* @stack: Will be true if the first char in @label is '&' to indicate stacking
|
2016-03-18 17:28:51 -05:00
|
|
|
* @ns: Will point to the first char in the namespace upon return or NULL
|
|
|
|
* if no namespace is present
|
|
|
|
* @ns_len: Number of chars in the namespace string or 0 if no namespace
|
|
|
|
* is present
|
|
|
|
* @name: Will point to the first char in the profile name upon return
|
|
|
|
* @name_len: Number of chars in the name string
|
|
|
|
* @label: The label to parse into namespace and profile name
|
|
|
|
*
|
|
|
|
* The returned pointers will point to locations within the original
|
|
|
|
* @label string. No new strings are allocated.
|
|
|
|
*
|
|
|
|
* Returns 0 upon success or non-zero with @ns, @ns_len, @name, and
|
|
|
|
* @name_len undefined upon error. Error codes are:
|
|
|
|
*
|
|
|
|
* 1) Namespace is not terminated despite @label starting with ':'
|
|
|
|
* 2) Namespace is empty meaning @label starts with "::"
|
|
|
|
* 3) Profile name is empty
|
|
|
|
*/
|
2016-03-18 17:28:51 -05:00
|
|
|
static int _parse_label(bool *stack,
|
|
|
|
char **ns, size_t *ns_len,
|
2016-03-18 17:28:51 -05:00
|
|
|
char **name, size_t *name_len,
|
|
|
|
const char *label)
|
2016-02-18 15:58:06 -06:00
|
|
|
{
|
|
|
|
const char *name_start = NULL;
|
2016-03-18 17:28:51 -05:00
|
|
|
const char *ns_start = NULL;
|
|
|
|
const char *ns_end = NULL;
|
2016-02-18 15:58:06 -06:00
|
|
|
|
2016-03-18 17:28:51 -05:00
|
|
|
if (label[0] == '&') {
|
|
|
|
/**
|
|
|
|
* Leading ampersand means that the current profile should
|
|
|
|
* be stacked with the rest of the label
|
|
|
|
*/
|
|
|
|
*stack = true;
|
|
|
|
label++;
|
|
|
|
} else {
|
|
|
|
*stack = false;
|
|
|
|
}
|
|
|
|
|
2016-02-18 15:58:06 -06:00
|
|
|
if (label[0] != ':') {
|
|
|
|
/* There is no namespace specified in the label */
|
|
|
|
name_start = label;
|
|
|
|
} else {
|
|
|
|
/* A leading ':' indicates that a namespace is specified */
|
2016-03-18 17:28:51 -05:00
|
|
|
ns_start = label + 1;
|
|
|
|
ns_end = strstr(ns_start, ":");
|
2016-02-18 15:58:06 -06:00
|
|
|
|
|
|
|
if (!ns_end)
|
2016-03-18 17:28:51 -05:00
|
|
|
return 1;
|
2016-02-18 15:58:06 -06:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Handle either of the two namespace formats:
|
|
|
|
* 1) :ns:name
|
|
|
|
* 2) :ns://name
|
|
|
|
*/
|
|
|
|
name_start = ns_end + 1;
|
|
|
|
if (!strncmp(name_start, "//", 2))
|
|
|
|
name_start += 2;
|
|
|
|
}
|
|
|
|
|
2016-03-18 17:28:51 -05:00
|
|
|
/**
|
|
|
|
* The casts below are to allow @label to be const, signifying
|
|
|
|
* that this function doesn't modify it, while allowing callers to
|
|
|
|
* decide if they want to pass in pointers to const or non-const
|
|
|
|
* strings.
|
|
|
|
*/
|
|
|
|
*ns = (char *)ns_start;
|
|
|
|
*name = (char *)name_start;
|
|
|
|
*ns_len = ns_end - ns_start;
|
|
|
|
*name_len = strlen(name_start);
|
|
|
|
|
|
|
|
if (*ns && *ns_len == 0)
|
|
|
|
return 2;
|
|
|
|
else if (*name_len == 0)
|
|
|
|
return 3;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-03-18 17:28:51 -05:00
|
|
|
bool label_contains_ns(const char *label)
|
|
|
|
{
|
2016-03-18 17:28:51 -05:00
|
|
|
bool stack = false;
|
2016-03-18 17:28:51 -05:00
|
|
|
char *ns = NULL;
|
|
|
|
char *name = NULL;
|
|
|
|
size_t ns_len = 0;
|
|
|
|
size_t name_len = 0;
|
|
|
|
|
2016-03-18 17:28:51 -05:00
|
|
|
return _parse_label(&stack, &ns, &ns_len, &name, &name_len, label) == 0 && ns;
|
2016-03-18 17:28:51 -05:00
|
|
|
}
|
|
|
|
|
2016-03-18 17:28:51 -05:00
|
|
|
bool parse_label(bool *_stack, char **_ns, char **_name,
|
|
|
|
const char *label, bool yyerr)
|
2016-03-18 17:28:51 -05:00
|
|
|
{
|
2016-03-18 17:28:51 -05:00
|
|
|
const char *err = NULL;
|
2016-03-18 17:28:51 -05:00
|
|
|
char *ns = NULL;
|
|
|
|
char *name = NULL;
|
|
|
|
size_t ns_len = 0;
|
|
|
|
size_t name_len = 0;
|
|
|
|
int res;
|
|
|
|
|
2016-03-18 17:28:51 -05:00
|
|
|
res = _parse_label(_stack, &ns, &ns_len, &name, &name_len, label);
|
2016-03-18 17:28:51 -05:00
|
|
|
if (res == 1) {
|
2016-03-18 17:28:51 -05:00
|
|
|
err = _("Namespace not terminated: %s\n");
|
2016-03-18 17:28:51 -05:00
|
|
|
} else if (res == 2) {
|
2016-03-18 17:28:51 -05:00
|
|
|
err = _("Empty namespace: %s\n");
|
2016-03-18 17:28:51 -05:00
|
|
|
} else if (res == 3) {
|
2016-03-18 17:28:51 -05:00
|
|
|
err = _("Empty named transition profile name: %s\n");
|
2016-03-18 17:28:51 -05:00
|
|
|
} else if (res != 0) {
|
2016-03-18 17:28:51 -05:00
|
|
|
err = _("Unknown error while parsing label: %s\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (err) {
|
|
|
|
if (yyerr)
|
|
|
|
yyerror(err, label);
|
|
|
|
else
|
|
|
|
fprintf(stderr, err, label);
|
|
|
|
|
|
|
|
return false;
|
2016-03-18 17:28:51 -05:00
|
|
|
}
|
2016-02-18 15:58:06 -06:00
|
|
|
|
2016-03-18 17:28:51 -05:00
|
|
|
if (ns) {
|
|
|
|
*_ns = strndup(ns, ns_len);
|
|
|
|
if (!*_ns)
|
2016-03-18 17:28:51 -05:00
|
|
|
goto alloc_fail;
|
2016-03-18 17:28:51 -05:00
|
|
|
} else {
|
|
|
|
*_ns = NULL;
|
2016-02-18 15:58:06 -06:00
|
|
|
}
|
|
|
|
|
2016-03-18 17:28:51 -05:00
|
|
|
*_name = strndup(name, name_len);
|
|
|
|
if (!*_name) {
|
|
|
|
free(*_ns);
|
2016-03-18 17:28:51 -05:00
|
|
|
goto alloc_fail;
|
2016-03-18 17:28:51 -05:00
|
|
|
}
|
2016-03-18 17:28:51 -05:00
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
alloc_fail:
|
|
|
|
err = _("Memory allocation error.");
|
|
|
|
if (yyerr)
|
|
|
|
yyerror(err);
|
|
|
|
else
|
|
|
|
fprintf(stderr, "%s", err);
|
|
|
|
|
|
|
|
return false;
|
2016-02-18 15:58:06 -06:00
|
|
|
}
|
|
|
|
|
2023-08-02 02:07:36 -07:00
|
|
|
struct cod_entry *new_entry(char *id, perm32_t perms, 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;
|
|
|
|
|
2024-05-10 03:06:22 -07:00
|
|
|
entry->priority = 0;
|
2007-11-16 09:18:48 +00:00
|
|
|
entry->name = id;
|
2007-11-16 09:37:31 +00:00
|
|
|
entry->link_name = link_id;
|
2021-06-09 00:56:59 -07:00
|
|
|
entry->perms = perms;
|
2021-08-30 14:31:03 -07:00
|
|
|
entry->audit = AUDIT_UNSPECIFIED;
|
2021-09-09 01:42:51 -07:00
|
|
|
entry->rule_mode = RULE_UNSPECIFIED;
|
2006-04-11 21:52:54 +00:00
|
|
|
|
|
|
|
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;
|
|
|
|
|
2013-09-06 13:39:41 -07:00
|
|
|
DUP_STRING(orig, entry, name, err);
|
|
|
|
DUP_STRING(orig, entry, link_name, err);
|
2016-03-18 17:28:50 -05:00
|
|
|
DUP_STRING(orig, entry, nt_name, err);
|
2024-05-10 03:06:22 -07:00
|
|
|
entry->priority = orig->priority;
|
2021-06-09 00:56:59 -07:00
|
|
|
entry->perms = orig->perms;
|
2021-08-30 14:31:03 -07:00
|
|
|
entry->audit = orig->audit;
|
2021-09-09 01:42:51 -07:00
|
|
|
entry->rule_mode = orig->rule_mode;
|
2006-04-11 21:52:54 +00:00
|
|
|
|
|
|
|
/* 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;
|
2013-09-06 13:39:41 -07:00
|
|
|
|
|
|
|
err:
|
|
|
|
free_cod_entries(entry);
|
|
|
|
return NULL;
|
2006-04-11 21:52:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void free_cod_entries(struct cod_entry *list)
|
|
|
|
{
|
|
|
|
if (!list)
|
|
|
|
return;
|
|
|
|
if (list->next)
|
|
|
|
free_cod_entries(list->next);
|
|
|
|
if (list->name)
|
|
|
|
free(list->name);
|
2007-11-16 09:37:31 +00:00
|
|
|
if (list->link_name)
|
|
|
|
free(list->link_name);
|
2013-10-14 14:34:12 -07:00
|
|
|
if (list->nt_name)
|
|
|
|
free(list->nt_name);
|
2006-04-11 21:52:54 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
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) {
|
2021-06-09 00:56:59 -07:00
|
|
|
printf("Perms:\t");
|
|
|
|
if (HAS_CHANGE_PROFILE(item->perms))
|
2007-07-27 20:25:59 +00:00
|
|
|
printf(" change_profile");
|
2021-06-09 00:56:59 -07:00
|
|
|
if (HAS_EXEC_UNSAFE(item->perms))
|
2007-11-16 09:35:31 +00:00
|
|
|
printf(" unsafe");
|
2021-06-09 00:56:59 -07:00
|
|
|
debug_base_perm_mask(SHIFT_TO_BASE(item->perms, AA_USER_SHIFT));
|
2007-11-16 09:35:31 +00:00
|
|
|
printf(":");
|
2021-06-09 00:56:59 -07:00
|
|
|
debug_base_perm_mask(SHIFT_TO_BASE(item->perms, 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
|
|
|
|
2021-06-09 00:56:59 -07:00
|
|
|
if (AA_LINK_BITS & item->perms)
|
2007-11-16 09:37:31 +00:00
|
|
|
printf("\tlink:\t(%s)\n", item->link_name ? item->link_name : "/**");
|
|
|
|
|
2006-04-11 21:52:54 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-09-21 20:39:27 -07:00
|
|
|
bool check_x_qualifier(struct cod_entry *entry, const char *&error)
|
|
|
|
{
|
|
|
|
if (entry->perms & AA_EXEC_BITS) {
|
|
|
|
if ((entry->rule_mode == RULE_DENY) &&
|
|
|
|
(entry->perms & ALL_AA_EXEC_TYPE)) {
|
|
|
|
error = _("Invalid perms, in deny rules 'x' must not be preceded by exec qualifier 'i', 'p', or 'u'");
|
|
|
|
return false;
|
|
|
|
} else if ((entry->rule_mode != RULE_DENY) &&
|
|
|
|
!(entry->perms & ALL_AA_EXEC_TYPE)) {
|
|
|
|
error = _("Invalid perms, 'x' must be preceded by exec qualifier 'i', 'p', or 'u'");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// cod_entry version of ->add_prefix here just as file rules aren't converted yet
|
|
|
|
bool entry_add_prefix(struct cod_entry *entry, const prefixes &p, const char *&error)
|
|
|
|
{
|
|
|
|
/* modifiers aren't correctly stored for cod_entries yet so
|
|
|
|
* we can't conflict on them easily. Leave that until conversion
|
|
|
|
* to rule_t
|
|
|
|
*/
|
|
|
|
/* apply rule mode */
|
|
|
|
entry->rule_mode = p.rule_mode;
|
|
|
|
|
|
|
|
/* apply owner/other */
|
|
|
|
if (p.owner == 1)
|
|
|
|
entry->perms &= (AA_USER_PERMS | AA_SHARED_PERMS);
|
|
|
|
else if (p.owner == 2)
|
|
|
|
entry->perms &= (AA_OTHER_PERMS | AA_SHARED_PERMS);
|
|
|
|
|
|
|
|
/* implied audit modifier */
|
|
|
|
if (p.audit == AUDIT_FORCE && (entry->rule_mode != RULE_DENY))
|
|
|
|
entry->audit = AUDIT_FORCE;
|
|
|
|
else if (p.audit != AUDIT_FORCE && (entry->rule_mode == RULE_DENY))
|
|
|
|
entry->audit = AUDIT_FORCE;
|
|
|
|
|
|
|
|
return check_x_qualifier(entry, error);
|
|
|
|
}
|
|
|
|
|
2023-07-03 23:52:57 -07:00
|
|
|
// these need to move to stl
|
|
|
|
int ordered_cmp_value_list(value_list *lhs, value_list *rhs)
|
|
|
|
{
|
|
|
|
std::vector<const char *> lhstable;
|
|
|
|
std::vector<const char *> rhstable;
|
|
|
|
|
|
|
|
struct value_list *entry;
|
|
|
|
list_for_each(lhs, entry) {
|
|
|
|
lhstable.push_back(entry->value);
|
|
|
|
}
|
|
|
|
list_for_each(rhs, entry) {
|
|
|
|
rhstable.push_back(entry->value);
|
|
|
|
}
|
|
|
|
|
|
|
|
int res = lhstable.size() - rhstable.size();
|
|
|
|
if (res)
|
|
|
|
return res;
|
|
|
|
|
|
|
|
std::sort(lhstable.begin(), lhstable.end(), strcomp);
|
|
|
|
std::sort(rhstable.begin(), rhstable.end(), strcomp);
|
|
|
|
|
|
|
|
for (unsigned long i = 0; i < lhstable.size(); i++) {
|
|
|
|
res = null_strcmp(lhstable[i], rhstable[i]);
|
|
|
|
if (res)
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int cmp_value_list(value_list *lhs, value_list *rhs)
|
|
|
|
{
|
|
|
|
if (lhs) {
|
|
|
|
if (rhs) {
|
|
|
|
return ordered_cmp_value_list(lhs, rhs);
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
} else if (rhs) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-02-16 08:07:28 -08:00
|
|
|
struct value_list *new_value_list(char *value)
|
|
|
|
{
|
2013-09-27 16:13:22 -07:00
|
|
|
struct value_list *val = (struct value_list *) calloc(1, sizeof(struct value_list));
|
2012-02-16 08:07:28 -08:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-04-23 11:36:26 -07:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
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
|
|
|
{
|
2013-09-27 16:13:22 -07: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;
|
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;
|
|
|
|
|
2019-08-17 05:01:39 -07:00
|
|
|
if (ents) {
|
|
|
|
list_for_each_safe(ents, entry, tmp) {
|
|
|
|
free_cond_entry(entry);
|
|
|
|
}
|
2013-07-31 09:05:51 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-17 05:01:39 -07:00
|
|
|
void free_cond_entry_list(struct cond_entry_list &cond)
|
|
|
|
{
|
|
|
|
free_cond_list(cond.list);
|
|
|
|
free(cond.name);
|
|
|
|
cond.list = NULL;
|
|
|
|
cond.name = NULL;
|
|
|
|
}
|
|
|
|
|
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");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-07-10 18:16:09 -07:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2006-04-11 21:52:54 +00:00
|
|
|
#ifdef UNIT_TEST
|
2013-09-27 16:16:37 -07:00
|
|
|
|
|
|
|
#include "unit_test.h"
|
|
|
|
|
2006-04-11 21:52:54 +00:00
|
|
|
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
|
|
|
|
2016-01-25 12:05:50 -08:00
|
|
|
#define MY_TEST_UNQUOTED(input, expected, description) \
|
|
|
|
do { \
|
|
|
|
char *result_str = NULL; \
|
|
|
|
char *output_str = NULL; \
|
|
|
|
\
|
|
|
|
result_str = processunquoted((input), strlen((input))); \
|
|
|
|
asprintf(&output_str, "processunquoted: %s\tinput = '%s'\texpected = '%s'\tresult = '%s'", \
|
|
|
|
(description), (input), (expected), result_str); \
|
|
|
|
MY_TEST(strcmp((expected), result_str) == 0, output_str); \
|
|
|
|
\
|
|
|
|
free(output_str); \
|
|
|
|
free(result_str); \
|
|
|
|
} \
|
|
|
|
while(0)
|
|
|
|
|
2010-03-08 20:38:54 -08:00
|
|
|
int test_processunquoted(void)
|
|
|
|
{
|
|
|
|
int rc = 0;
|
|
|
|
|
2016-01-25 12:05:50 -08:00
|
|
|
MY_TEST_UNQUOTED("", "", "empty string");
|
|
|
|
MY_TEST_UNQUOTED("\\1", "\001", "one digit octal");
|
|
|
|
MY_TEST_UNQUOTED("\\8", "\\8", "invalid octal digit \\8");
|
|
|
|
MY_TEST_UNQUOTED("\\18", "\0018", "one digit octal followed by invalid octal digit");
|
|
|
|
MY_TEST_UNQUOTED("\\1a", "\001a", "one digit octal followed by hex digit a");
|
|
|
|
MY_TEST_UNQUOTED("\\1z", "\001z", "one digit octal follow by char z");
|
|
|
|
MY_TEST_UNQUOTED("\\11", "\011", "two digit octal");
|
|
|
|
MY_TEST_UNQUOTED("\\118", "\0118", "two digit octal followed by invalid octal digit");
|
|
|
|
MY_TEST_UNQUOTED("\\11a", "\011a", "two digit octal followed by hex digit a");
|
|
|
|
MY_TEST_UNQUOTED("\\11z", "\011z", "two digit octal followed by char z");
|
|
|
|
MY_TEST_UNQUOTED("\\111", "\111", "three digit octal");
|
|
|
|
MY_TEST_UNQUOTED("\\378", "\0378", "three digit octal two large, taken as 2 digit octal plus trailing char");
|
|
|
|
MY_TEST_UNQUOTED("123\\421123", "123\0421123", "two character octal followed by valid octal digit \\421");
|
|
|
|
MY_TEST_UNQUOTED("123\\109123", "123\109123", "octal 109");
|
|
|
|
MY_TEST_UNQUOTED("123\\1089123", "123\1089123", "octal 108");
|
2010-03-08 20:38:54 -08:00
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2011-04-05 20:55:19 -07:00
|
|
|
int test_processquoted(void)
|
|
|
|
{
|
|
|
|
int rc = 0;
|
2013-10-01 10:59:04 -07:00
|
|
|
const 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\"";
|
2014-06-19 13:50:57 -07:00
|
|
|
processedstring = "a\\\\bcdefg";
|
2011-04-05 20:55:19 -07:00
|
|
|
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);
|
|
|
|
|
2014-04-15 14:59:41 -07:00
|
|
|
teststring = "\"abc\\429defg\"";
|
|
|
|
processedstring = "abc\0429defg";
|
2011-04-05 20:55:19 -07:00
|
|
|
out = processquoted(teststring, strlen(teststring));
|
|
|
|
MY_TEST(strcmp(processedstring, out) == 0,
|
2014-04-15 14:59:41 -07:00
|
|
|
"processquoted passthrough quoted invalid octal \\429");
|
2011-04-05 20:55:19 -07:00
|
|
|
free(out);
|
|
|
|
|
2014-04-15 14:59:41 -07:00
|
|
|
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");
|
2016-01-25 12:05:50 -08:00
|
|
|
free(out);
|
2014-04-15 14:59:41 -07:00
|
|
|
|
2011-04-05 20:55:19 -07:00
|
|
|
teststring = "\"abcdefg\\04\"";
|
2014-04-15 14:59:41 -07:00
|
|
|
processedstring = "abcdefg\004";
|
|
|
|
out = processquoted(teststring, strlen(teststring));
|
|
|
|
MY_TEST(strcmp(processedstring, out) == 0,
|
|
|
|
"processquoted passthrough quoted two digit trailing octal \\04");
|
2016-01-25 12:05:50 -08:00
|
|
|
free(out);
|
2014-04-15 14:59:41 -07:00
|
|
|
|
|
|
|
teststring = "\"abcdefg\\004\"";
|
|
|
|
processedstring = "abcdefg\004";
|
2011-04-05 20:55:19 -07:00
|
|
|
out = processquoted(teststring, strlen(teststring));
|
|
|
|
MY_TEST(strcmp(processedstring, out) == 0,
|
2014-04-15 14:59:41 -07:00
|
|
|
"processquoted passthrough quoted three digit trailing octal \\004");
|
2011-04-05 20:55:19 -07:00
|
|
|
free(out);
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2015-07-10 18:16:09 -07:00
|
|
|
#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;
|
|
|
|
}
|
|
|
|
|
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;
|
|
|
|
|
2015-07-10 18:16:09 -07:00
|
|
|
retval = test_convert_time_units();
|
|
|
|
if (retval != 0)
|
|
|
|
rc = retval;
|
|
|
|
|
2006-04-11 21:52:54 +00:00
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
#endif /* UNIT_TEST */
|