2
0
mirror of https://gitlab.com/apparmor/apparmor synced 2025-09-02 07:15:18 +00:00

Compare commits

..

12 Commits

Author SHA1 Message Date
John Johansen
1d36e1f196 Prepare for AppArmor 4.0 beta4 release
- update version file

Signed-off-by: John Johansen <john.johansen@canonical.com>
2024-04-08 03:21:40 -07:00
John Johansen
22ee6c19bc Merge profiles: disable bwrap and unshare profiles by default
The bwrap and unshare profiles are special profiles in the same
vein as the unconfined profiles but they actual enforce restrictions
on the applications that are launched.

As such they have come to late in the 4.0 dev cycle to consider enabling
by default. Disable them but ship them so users or distros can easily
enable them.

Fixes: https://gitlab.com/apparmor/apparmor/-/issues/382
Signed-off-by: John Johansen <john.johansen@canonical.com>

Closes #382
MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1206
Approved-by: John Johansen <john@jjmx.net>
Merged-by: John Johansen <john@jjmx.net>

(cherry picked from commit 41d4664124)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2024-04-08 00:17:10 -07:00
John Johansen
6198edb3d0 Merge profiles: Add profile for unshare utility
This adds an unshare profile to allow it to function on a system
with user namespace restrictions enabled.

The child task of unshare will enter into a profile without capabilities
thus preventing unshare from being able to be used to
arbitrarily by-pass the user namespace restriction.

This profile does prevent applications launch with privilege (eg.
sudo unshare ...) from functioning so it may break some use cases.

Fixes: https://bugs.launchpad.net/ubuntu/+source/pageedit/+bug/2046844
Signed-off-by: John Johansen <john.johansen@canonical.com>

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1204
Approved-by: John Johansen <john@jjmx.net>
Merged-by: John Johansen <john@jjmx.net>
(cherry picked from commit 2d59dc3d9b)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2024-04-03 22:45:10 -07:00
John Johansen
4d2a171466 Merge profiles: Add profile for bwrap utility
This adds a bwrap profile to allow it to function on a system with
user namespace restrictions enabled.

The child task of bwrap will enter into a profile without capabilities
thus preventing bwrap from being able to be used to arbitrarily
by-pass user namespace restrictions.

This profile does prevent applications launch with privilege (eg.
sudo bwrap ...) from functioning so it may break some use cases.

Note: The unpriv_bwrap profile is deliberately stacked against the
bwrap profile due to bwraps uses of no-new-privileges.

Fixes: https://bugs.launchpad.net/ubuntu/+source/pageedit/+bug/2046844
Signed-off-by: John Johansen <john.johansen@canonical.com>

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1205
Approved-by: John Johansen <john@jjmx.net>
Merged-by: John Johansen <john@jjmx.net>
(cherry picked from commit b6f2a3d9d2)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2024-04-03 22:44:49 -07:00
John Johansen
e88cf3cd02 Merge profiles: adjust unconfined firefox profile to support mozilla.org download
The version of tarball version of firefox downloaded from mozilla.org
installs to /opt/firefox/firefox. Support this location so that the
firefox from the tarball works.

Note this does not support running firefox from the users home directory
in this case the user must update the profile accordingly.

Signed-off-by: John Johansen <john.johansen@canonical.com>

Fixes: https://bugs.launchpad.net/bugs/2056297
MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1203
Approved-by: John Johansen <john@jjmx.net>
Merged-by: John Johansen <john@jjmx.net>
(cherry picked from commit 2dfe6aeec2)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2024-04-03 22:39:58 -07:00
John Johansen
6f856dfee3 Merge profiles/samba*: allow /etc/gnutls/config & @{HOMEDIRS}
# abstractions/samba: allow /etc/gnutls/config

Various samba components want to read it. Without it, shares cannot be accessed.

    apparmor="DENIED" operation="open" class="file" profile="nmbd" name="/etc/gnutls/config" pid=23509 comm="nmbd" requested_mask="r" denied_mask="r" fsuid=0 ouid=0
    apparmor="DENIED" operation="open" class="file" profile="smbd" name="/etc/gnutls/config" pid=23508 comm="smbd" requested_mask="r" denied_mask="r" fsuid=0 ouid=0
    apparmor="DENIED" operation="open" class="file" profile="samba-rpcd" name="/etc/gnutls/config" pid=24037 comm="rpcd_fsrvp" requested_mask="r" denied_mask="r" fsuid=0 ouid=0
    apparmor="DENIED" operation="open" class="file" profile="samba-rpcd" name="/etc/gnutls/config" pid=24036 comm="rpcd_epmapper" requested_mask="r" denied_mask="r" fsuid=0 ouid=0
    apparmor="DENIED" operation="open" class="file" profile="samba-rpcd" name="/etc/gnutls/config" pid=24038 comm="rpcd_lsad" requested_mask="r" denied_mask="r" fsuid=0 ouid=0
    apparmor="DENIED" operation="open" class="file" profile="samba-rpcd" name="/etc/gnutls/config" pid=24041 comm="rpcd_winreg" requested_mask="r" denied_mask="r" fsuid=0 ouid=0
    apparmor="DENIED" operation="open" class="file" profile="samba-rpcd" name="/etc/gnutls/config" pid=24039 comm="rpcd_mdssvc" requested_mask="r" denied_mask="r" fsuid=0 ouid=0
    apparmor="DENIED" operation="open" class="file" profile="samba-rpcd-spoolss" name="/etc/gnutls/config" pid=24040 comm="rpcd_spoolss" requested_mask="r" denied_mask="r" fsuid=0 ouid=0
    apparmor="DENIED" operation="open" class="file" profile="samba-rpcd-classic" name="/etc/gnutls/config" pid=24035 comm="rpcd_classic" requested_mask="r" denied_mask="r" fsuid=0 ouid=0

# profiles/apparmor.d/samba-rpcd-classic: allow @{HOMEDIRS}

Give access to @{HOMEDIRS}, just like in usr.sbin.smbd, so that
usershares in /home/ can be accessed.

    apparmor="DENIED" operation="open" class="file" profile="samba-rpcd-classic" name="/home/user/path/to/usershare/" pid=4781 comm="rpcd_classic" requested_mask="r" denied_mask="r" fsuid=0 ouid=1000

Fixes: https://gitlab.com/apparmor/apparmor/-/issues/379
MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1200
Approved-by: John Johansen <john@jjmx.net>
Merged-by: John Johansen <john@jjmx.net>
(cherry picked from commit 5998a0021a)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2024-04-03 14:15:52 -07:00
John Johansen
a6d8171bd6 Merge parser: fix getattr and setattr perm mapping on mqueue rules
Signed-off-by: Georgia Garcia <georgia.garcia@canonical.com>

Fixes: https://gitlab.com/apparmor/apparmor/-/issues/377
Fixes: https://gitlab.com/apparmor/apparmor/-/issues/378
MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1197
Approved-by: John Johansen <john@jjmx.net>
Merged-by: John Johansen <john@jjmx.net>
(cherry picked from commit 88ec709aac)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2024-04-03 02:32:32 -07:00
John Johansen
26e7249f44 Merge tests: fix inet tests
Fixes: https://gitlab.com/apparmor/apparmor/-/issues/376
MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1192
Approved-by: John Johansen <john@jjmx.net>
Merged-by: John Johansen <john@jjmx.net>
(cherry picked from commit 01fcce41dc)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2024-04-03 02:30:59 -07:00
John Johansen
117d0cc444 Merge usr.sbin.sshd: Add new permissions needed on Ubuntu 24.04
Testing on noble turned these up:

`2024-03-27T00:10:28.929314-04:00 image-ubuntu64 kernel: audit: type=1400 audit(1711512628.920:155): apparmor="DENIED" operation="bind" class="net" profile="/usr/sbin/sshd" pid=1290 comm="sshd" family="unix" sock_type="stream" protocol=0 requested_mask="bind" denied_mask="bind" addr="@63cf34db7fbab75f/bus/sshd/system"`

`2024-03-27T00:41:09.791826-04:00 image-ubuntu64 kernel: audit: type=1107 audit(1711514469.771:333907): pid=703 uid=101 auid=4294967295 ses=4294967295 subj=unconfined msg='apparmor="DENIED" operation="dbus_method_call"  bus="system" path="/org/freedesktop/login1" interface="org.freedesktop.login1.Manager" member="CreateSessionWithPIDFD" mask="send" name="org.freedesktop.login1" pid=4528 label="/usr/sbin/sshd" peer_pid=688 peer_label="unconfined"`

Fixes: https://bugs.launchpad.net/ubuntu/+source/apparmor/+bug/2060100
MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1196
Approved-by: John Johansen <john@jjmx.net>
Merged-by: John Johansen <john@jjmx.net>
(cherry picked from commit 3aa40249cf)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2024-04-03 02:29:31 -07:00
Georgia Garcia
1c7127d30d Merge utils: allow mount destination globbing
The abstraction lxc/start-container shipped by the liblxc-common
package uses the following mount rule which was not allowed by our
regexes:

  mount options=(rw, make-slave) -> **,
  mount options=(rw, make-rslave) -> **,

Since in AppArmor regex ** includes '/' but * by itself doesn't, I'm
adding explicit support for **.

Signed-off-by: Georgia Garcia <georgia.garcia@canonical.com>

Fixes: https://gitlab.com/apparmor/apparmor/-/issues/381
MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1195
Approved-by: Christian Boltz <apparmor@cboltz.de>
Merged-by: Georgia Garcia <georgia.garcia@canonical.com>
(cherry picked from commit e96fdc0f5b)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2024-04-03 02:23:43 -07:00
Christian Boltz
d111ddcc21 Merge firefox: allow locking of *.sqlite-shm files in user cache area
Noticed a bunch of these after a Firefox 124.0 upgrade:

`Mar 25 22:08:27 darkstar kernel: [598271.991739] audit: type=1400 audit(1711418907.493:27323): apparmor="DENIED" operation="file_lock" profile="firefox" name="/home/username/.cache/mozilla/firefox/deadbeef.default/suggest.sqlite-shm" pid=2855447 comm=4267494F5468727E6F6C2023333530 requested_mask="k" denied_mask="k" fsuid=1000 ouid=1000`

Fixes: https://gitlab.com/apparmor/apparmor/-/issues/380
MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1193
Approved-by: Christian Boltz <apparmor@cboltz.de>
Merged-by: Christian Boltz <apparmor@cboltz.de>
(cherry picked from commit 14572d9581)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2024-04-03 02:18:24 -07:00
John Johansen
fa26623e6d Merge Move pam-related permissions to abstractions/authentication
... instead of keeping them in the smbd profile.

For details, see c09f58a364 and
https://bugzilla.opensuse.org/show_bug.cgi?id=1220032#c12

Also replace /usr/etc/ with @{etc_ro} to that also /etc/ is covered.

Fixes: https://bugzilla.opensuse.org/show_bug.cgi?id=1220032#c12
MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1191
Approved-by: John Johansen <john@jjmx.net>
Merged-by: John Johansen <john@jjmx.net>
(cherry picked from commit f334884787)
Signed-off-by: John Johansen <john.johansen@canonical.com>
2024-04-03 02:14:16 -07:00
354 changed files with 2564 additions and 9233 deletions

41
.gitignore vendored
View File

@@ -1,4 +1,4 @@
apparmor-
apparmor-*
cscope.*
binutils/aa-enabled
binutils/aa-enabled.1
@@ -16,7 +16,6 @@ parser/af_names.h
parser/cap_names.h
parser/generated_cap_names.h
parser/generated_af_names.h
parser/errnos.h
parser/tst_lib
parser/tst_misc
parser/tst_regex
@@ -28,9 +27,42 @@ parser/parser_version.h
parser/parser_yacc.c
parser/parser_yacc.h
parser/pod2htm*.tmp
parser/libapparmor_re/*.o
parser/af_rule.o
parser/af_unix.o
parser/all_rule.o
parser/common_optarg.o
parser/dbus.o
parser/default_features.o
parser/lib.o
parser/libapparmor_re/aare_rules.o
parser/libapparmor_re/chfa.o
parser/libapparmor_re/expr-tree.o
parser/libapparmor_re/hfa.o
parser/libapparmor_re/libapparmor_re.a
parser/*.o
parser/libapparmor_re/parse.o
parser/mount.o
parser/mqueue.o
parser/network.o
parser/parser_alias.o
parser/parser_common.o
parser/parser_include.o
parser/parser_interface.o
parser/parser_lex.o
parser/parser_main.o
parser/parser_merge.o
parser/parser_misc.o
parser/parser_policy.o
parser/parser_regex.o
parser/parser_symtab.o
parser/parser_variable.o
parser/parser_yacc.o
parser/policy_cache.o
parser/profile.o
parser/ptrace.o
parser/rule.o
parser/signal.o
parser/userns.o
parser/io_uring.o
parser/*.7
parser/*.5
parser/*.8
@@ -180,7 +212,6 @@ utils/apparmor/*.pyc
utils/apparmor/rule/*.pyc
utils/apparmor.egg-info/
utils/build/
!utils/emacs/apparmor-mode.el
utils/htmlcov/
utils/test/common_test.pyc
utils/test/.coverage

View File

@@ -77,8 +77,7 @@ test-utils:
extends:
- .ubuntu-before_script
script:
- apt-get install --no-install-recommends -y libc6-dev libjs-jquery libjs-jquery-throttle-debounce libjs-jquery-isonscreen libjs-jquery-tablesorter flake8 python3-coverage python3-notify2 python3-psutil python3-setuptools python3-tk python3-ttkthemes python3-gi
- apt-get install --no-install-recommends -y libc6-dev libjs-jquery libjs-jquery-throttle-debounce libjs-jquery-isonscreen libjs-jquery-tablesorter pyflakes3 python3-coverage python3-notify2 python3-psutil python3-setuptools
# See apparmor/apparmor#221
- make -C parser/tst gen_dbus
- make -C parser/tst gen_xtrans
@@ -105,7 +104,7 @@ test-profiles:
script:
- make -C profiles check-parser
- make -C profiles check-abstractions.d
- make -C profiles check-local
- make -C profiles check-extras
shellcheck:
stage: test
@@ -113,7 +112,7 @@ shellcheck:
extends:
- .ubuntu-before_script
script:
- apt-get install --no-install-recommends -y python3-minimal file shellcheck xmlstarlet
- apt-get install --no-install-recommends -y file shellcheck xmlstarlet
- shellcheck --version
- './tests/bin/shellcheck-tree --format=checkstyle
| xmlstarlet tr tests/checkstyle2junit.xslt

View File

@@ -354,9 +354,6 @@ The aa-notify tool's Python dependencies can be satisfied by installing the
following packages (Debian package names, other distros may vary):
* python3-notify2
* python3-psutil
* python3-tk
* python3-ttkthemes
* python3-gi
Perl is no longer needed since none of the utilities shipped to end users depend
on it anymore.

View File

@@ -172,8 +172,7 @@ static int load_policy_dir(const char *dir_path)
while ((dir = readdir(d)) != NULL) {
/* Only check regular files for now */
if (dir->d_type == DT_REG) {
/* As per POSIX dir->d_name has at most NAME_MAX characters */
len = strnlen(dir->d_name, NAME_MAX);
len = strnlen(dir->d_name, PATH_MAX);
/* Ignores .features */
if (strncmp(dir->d_name, CACHE_FEATURES_FILE, len) == 0) {
continue;

View File

@@ -1 +1 @@
4.1.0~beta1
4.0.0~beta4

View File

@@ -148,9 +148,6 @@ typedef struct
unsigned long net_local_port;
char *net_foreign_addr;
unsigned long net_foreign_port;
char *execpath;
char *dbus_bus;
char *dbus_path;
char *dbus_interface;
@@ -164,9 +161,6 @@ typedef struct
char *src_name;
char *class;
char *net_addr;
char *peer_addr;
} aa_log_record;
/**

View File

@@ -32,10 +32,10 @@ INCLUDES = $(all_includes)
#
# After changing the AA_LIB_* variables, also update EXPECTED_SO_NAME.
AA_LIB_CURRENT = 20
AA_LIB_REVISION = 0
AA_LIB_AGE = 19
EXPECTED_SO_NAME = libapparmor.so.1.19.0
AA_LIB_CURRENT = 18
AA_LIB_REVISION = 1
AA_LIB_AGE = 17
EXPECTED_SO_NAME = libapparmor.so.1.17.1
SUFFIXES = .pc.in .pc

View File

@@ -114,7 +114,6 @@ aa_record_event_type lookup_aa_event(unsigned int type)
%token TOK_PERIOD
%token TOK_QUESTION_MARK
%token TOK_SINGLE_QUOTE
%token TOK_NONE
%token TOK_TYPE_REJECT
%token TOK_TYPE_AUDIT
@@ -188,8 +187,6 @@ aa_record_event_type lookup_aa_event(unsigned int type)
%token TOK_KEY_FSTYPE
%token TOK_KEY_FLAGS
%token TOK_KEY_SRCNAME
%token TOK_KEY_UNIX_PEER_ADDR
%token TOK_KEY_EXECPATH
%token TOK_KEY_CLASS
%token TOK_SOCKLOGD_KERNEL
@@ -357,13 +354,6 @@ key: TOK_KEY_OPERATION TOK_EQUALS TOK_QUOTED_STRING
{ ret_record->fsuid = $3;}
| TOK_KEY_OUID TOK_EQUALS TOK_DIGITS
{ ret_record->ouid = $3;}
| TOK_KEY_ADDR TOK_EQUALS TOK_QUESTION_MARK
| TOK_KEY_ADDR TOK_EQUALS TOK_NONE
| TOK_KEY_ADDR TOK_EQUALS safe_string
{ ret_record->net_addr = $3; }
| TOK_KEY_UNIX_PEER_ADDR TOK_EQUALS TOK_NONE
| TOK_KEY_UNIX_PEER_ADDR TOK_EQUALS safe_string
{ ret_record->peer_addr = $3; }
| TOK_KEY_FSUID_UPPER TOK_EQUALS TOK_QUOTED_STRING
{ free($3);} /* Ignore - fsuid username */
| TOK_KEY_OUID_UPPER TOK_EQUALS TOK_QUOTED_STRING
@@ -373,7 +363,10 @@ key: TOK_KEY_OPERATION TOK_EQUALS TOK_QUOTED_STRING
| TOK_KEY_HOSTNAME TOK_EQUALS safe_string
{ free($3); /* Ignore - hostname from user AVC messages */ }
| TOK_KEY_HOSTNAME TOK_EQUALS TOK_QUESTION_MARK
| TOK_KEY_ADDR TOK_EQUALS TOK_QUESTION_MARK
| TOK_KEY_TERMINAL TOK_EQUALS TOK_QUESTION_MARK
| TOK_KEY_ADDR TOK_EQUALS safe_string
{ free($3); /* Ignore - IP address from user AVC messages */ }
| TOK_KEY_TERMINAL TOK_EQUALS safe_string
{ free($3); /* Ignore - TTY from user AVC messages */ }
| TOK_KEY_EXE TOK_EQUALS safe_string
@@ -426,14 +419,14 @@ key: TOK_KEY_OPERATION TOK_EQUALS TOK_QUOTED_STRING
{ ret_record->dbus_member = $3; }
| TOK_KEY_SIGNAL TOK_EQUALS TOK_ID
{ ret_record->signal = $3; }
| TOK_KEY_FSTYPE TOK_EQUALS TOK_QUOTED_STRING
{ ret_record->fs_type = $3; }
| TOK_KEY_FLAGS TOK_EQUALS TOK_QUOTED_STRING
{ ret_record->flags = $3; }
| TOK_KEY_SRCNAME TOK_EQUALS TOK_QUOTED_STRING
{ ret_record->src_name = $3; }
| TOK_KEY_EXECPATH TOK_EQUALS TOK_QUOTED_STRING
{ ret_record->execpath = $3; }
| TOK_MSG_REST
{
ret_record->event = AA_RECORD_INVALID;

View File

@@ -103,13 +103,6 @@ void free_record(aa_log_record *record)
free(record->flags);
if (record->src_name != NULL)
free(record->src_name);
if (record->net_addr != NULL)
free(record->net_addr);
if (record->peer_addr != NULL)
free(record->peer_addr);
if (record->execpath != NULL)
free(record->execpath);
if (record->class != NULL)
free(record->class);

View File

@@ -127,7 +127,6 @@ APPARMOR_3.0 {
APPARMOR_3.1 {
global:
aa_features_check;
aa_split_overlay_str;
local:
*;
} APPARMOR_3.0;

View File

@@ -90,7 +90,6 @@ question_mark "?"
single_quote "'"
mode_chars ([RrWwaLlMmkXx])|([Pp][Xx])|([Uu][Xx])|([Ii][Xx])|([Pp][Ii][Xx])
modes ({mode_chars}+)|({mode_chars}+::{mode_chars}*)|(::{mode_chars}*)
none "none"
/* New message types */
aa_reject_type "APPARMOR_DENIED"
@@ -158,13 +157,9 @@ key_capname "capname"
key_offset "offset"
key_target "target"
key_laddr "laddr"
key_saddr "saddr"
key_faddr "faddr"
key_daddr "daddr"
key_lport "lport"
key_srcport "src"
key_fport "fport"
key_destport "dest"
key_bus "bus"
key_dest "dest"
key_path "path"
@@ -178,8 +173,6 @@ key_flags "flags"
key_srcname "srcname"
key_class "class"
key_tcontext "tcontext"
key_unix_peer_addr "peer_addr"
key_execpath "execpath"
audit "audit"
/* network addrs */
@@ -310,8 +303,6 @@ yy_flex_debug = 0;
{period} { return(TOK_PERIOD); }
{question_mark} { return(TOK_QUESTION_MARK); }
{single_quote} { return(TOK_SINGLE_QUOTE); }
{none} { return(TOK_NONE); }
{key_apparmor} { BEGIN(audit_types); return(TOK_KEY_APPARMOR); }
{key_type} { BEGIN(audit_types); return(TOK_KEY_TYPE); }
@@ -351,7 +342,7 @@ yy_flex_debug = 0;
{key_sauid} { return(TOK_KEY_SAUID); }
{key_ses} { return(TOK_KEY_SES); }
{key_hostname} { return(TOK_KEY_HOSTNAME); }
{key_addr} { BEGIN(safe_string); return(TOK_KEY_ADDR); }
{key_addr} { return(TOK_KEY_ADDR); }
{key_terminal} { return(TOK_KEY_TERMINAL); }
{key_exe} { BEGIN(safe_string); return(TOK_KEY_EXE); }
{key_comm} { BEGIN(safe_string); return(TOK_KEY_COMM); }
@@ -360,13 +351,9 @@ yy_flex_debug = 0;
{key_offset} { return(TOK_KEY_OFFSET); }
{key_target} { return(TOK_KEY_TARGET); }
{key_laddr} { yy_push_state(ip_addr, yyscanner); return(TOK_KEY_LADDR); }
{key_saddr} { yy_push_state(ip_addr, yyscanner); return(TOK_KEY_LADDR); }
{key_faddr} { yy_push_state(ip_addr, yyscanner); return(TOK_KEY_FADDR); }
{key_daddr} { yy_push_state(ip_addr, yyscanner); return(TOK_KEY_FADDR); }
{key_lport} { return(TOK_KEY_LPORT); }
{key_srcport} { return(TOK_KEY_LPORT); }
{key_fport} { return(TOK_KEY_FPORT); }
{key_destport} { return(TOK_KEY_FPORT); }
{key_bus} { return(TOK_KEY_BUS); }
{key_path} { return(TOK_KEY_PATH); }
{key_interface} { return(TOK_KEY_INTERFACE); }
@@ -377,8 +364,6 @@ yy_flex_debug = 0;
{key_fstype} { return(TOK_KEY_FSTYPE); }
{key_flags} { BEGIN(safe_string); return(TOK_KEY_FLAGS); }
{key_srcname} { BEGIN(safe_string); return(TOK_KEY_SRCNAME); }
{key_unix_peer_addr} { BEGIN(safe_string); return(TOK_KEY_UNIX_PEER_ADDR); }
{key_execpath} { BEGIN(safe_string); return(TOK_KEY_EXECPATH); }
{key_class} { BEGIN(safe_string); return(TOK_KEY_CLASS); }
{socklogd_kernel} { BEGIN(dmesg_timestamp); return(TOK_SOCKLOGD_KERNEL); }

View File

@@ -135,7 +135,7 @@ static int do_test_walk_one(const char **str, const struct component *component,
static int test_walk_one(void)
{
struct component c = (struct component) { NULL, 0 };
struct component c;
const char *str;
int rc = 0;

View File

@@ -55,7 +55,7 @@ extern int aa_getprocattr_raw(pid_t tid, const char *attr, char *buf, int len,
extern int aa_getprocattr(pid_t tid, const char *attr, char **buf, char **mode);
extern int aa_gettaskcon(pid_t target, char **label, char **mode);
extern int aa_getcon(char **label, char **mode);
extern int aa_getpeercon_raw(int fd, char *buf, socklen_t *len, char **mode);
extern int aa_getpeercon_raw(int fd, char *buf, int *len, char **mode);
extern int aa_getpeercon(int fd, char **label, char **mode);
extern int aa_query_label(uint32_t mask, char *query, size_t size, int *allow,
int *audit);

View File

@@ -14,7 +14,7 @@ MOSTLYCLEANFILES=libapparmor_wrap.c LibAppArmor.py
all-local: libapparmor_wrap.c setup.py
if test ! -f libapparmor_wrap.c; then cp $(srcdir)/libapparmor_wrap.c . ; fi
CC="$(CC)" CFLAGS="$(PYTHON_CPPFLAGS) $(CFLAGS) $(EXTRA_WARNINGS)" LDSHARED="$(CC) -shared" LDFLAGS="$(PYTHON_LDFLAGS) $(LDFLAGS)" $(PYTHON) setup.py build
CC="$(CC)" CFLAGS="$(PYTHON_CPPFLAGS) $(EXTRA_WARNINGS)" LDSHARED="$(CC) -shared" LDFLAGS="$(PYTHON_LDFLAGS) $(LDFLAGS)" $(PYTHON) setup.py build
install-exec-local:
$(PYTHON) setup.py install --root="/$(DESTDIR)" --prefix="$(prefix)"

View File

@@ -2,7 +2,7 @@ from setuptools import setup, Extension
import string
setup(name = 'LibAppArmor',
version = '@VERSION@'.replace('~', '-'),
version = '@VERSION@',
author = 'AppArmor Dev Team',
author_email = 'apparmor@lists.ubuntu.com',
url = 'https://wiki.apparmor.net',

View File

@@ -1,3 +1,5 @@
#define _GNU_SOURCE /* for glibc's basename version */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -5,12 +7,6 @@
#include <aalogparse.h>
static const char *basename(const char *path)
{
const char *p = strrchr(path, '/');
return p ? p + 1 : path;
}
int print_results(aa_log_record *record);
int main(int argc, char **argv)
@@ -119,8 +115,6 @@ int print_results(aa_log_record *record)
print_long("Peer PID", record->peer_pid, 0);
print_string("Active hat", record->active_hat);
print_string("Net Addr", record->net_addr);
print_string("Peer Addr", record->peer_addr);
print_string("Network family", record->net_family);
print_string("Socket type", record->net_sock_type);
print_string("Protocol", record->net_protocol);
@@ -140,8 +134,6 @@ int print_results(aa_log_record *record)
print_string("Flags", record->flags);
print_string("Src name", record->src_name);
print_string("Execpath", record->execpath);
print_string("Class", record->class);
print_long("Epoch", record->epoch, 0);

View File

@@ -1,4 +1,4 @@
/usr/lib/NetworkManager/nm-dhcp-client.action {
network inet6 dgram port=10580,
network inet6 dgram,
}

View File

@@ -1,4 +1,4 @@
/usr/sbin/apache2 {
network inet6 stream ip=::ffff:192.168.236.159 port=80 peer=(ip=::ffff:192.168.103.80 port=61985),
network inet6 stream,
}

View File

@@ -1,7 +1,7 @@
/usr/sbin/apache2 {
^www.xxxxxxxxxx.co.uk {
network (send) inet6 stream ip=::ffff:192.168.1.100 port=80 peer=(ip=::ffff:192.168.1.100 port=45658),
network inet6 stream,
}
}

View File

@@ -1,7 +1,7 @@
/usr/local/apache-tomcat-8.0.33/bin/catalina.sh {
^/usr/local/jdk1.8.0_92/bin/java {
network (receive) inet6 stream ip=::ffff:127.0.0.1 port=8080 peer=(ip=::ffff:127.0.0.1 port=52308),
network inet6 stream,
}
}

View File

@@ -1,4 +1,4 @@
/home/ubuntu/bzr/apparmor/tests/regression/apparmor/mount {
mount fstype=(ext2) options=(mand, rw) /dev/loop0/ -> /tmp/sdtest.19033-29001-MPfz98/mountpoint/,
mount fstype=ext2 options="rw, mand" /dev/loop0/ -> /tmp/sdtest.19033-29001-MPfz98/mountpoint/,
}

View File

@@ -1 +0,0 @@
type=AVC msg=audit(1715045678.914:344186): apparmor="ALLOWED" operation="mount" info="failed flags match" error=-13 profile="steam" name="/newroot/dev/" pid=26487 comm="srt-bwrap" flags="rw, nosuid, nodev, remount, bind, silent, relatime"

View File

@@ -1,14 +0,0 @@
START
File: testcase_mount_02.in
Event type: AA_RECORD_ALLOWED
Audit ID: 1715045678.914:344186
Operation: mount
Profile: steam
Name: /newroot/dev/
Command: srt-bwrap
Info: failed flags match
ErrorCode: 13
PID: 26487
Flags: rw, nosuid, nodev, remount, bind, silent, relatime
Epoch: 1715045678
Audit subid: 344186

View File

@@ -1,4 +0,0 @@
profile steam {
mount options=(bind, nodev, nosuid, relatime, remount, rw, silent) -> /newroot/dev/,
}

View File

@@ -1,4 +1,4 @@
/usr/bin/evince-thumbnailer {
network inet stream ip=192.168.66.150 port=765 peer=(ip=192.168.66.200 port=2049),
network inet stream,
}

View File

@@ -1,4 +1,4 @@
/usr/bin/evince-thumbnailer {
network inet stream port=765 peer=(port=2049),
network inet stream,
}

View File

@@ -1,4 +1,4 @@
/usr/lib/dovecot/imap-login {
network inet6 stream port=143,
network inet6 stream,
}

View File

@@ -1,4 +1,4 @@
/home/ubuntu/tmp/nc {
network inet6 stream ip=::1 port=2048 peer=(ip=::1 port=33986),
network inet6 stream,
}

View File

@@ -1,4 +1,4 @@
/home/ubuntu/tmp/nc {
network inet6 stream ip=::ffff:127.0.0.1 port=2048 peer=(ip=::ffff:127.0.0.1 port=59180),
network inet6 stream,
}

View File

@@ -1 +0,0 @@
[319992.813426] audit: type=1400 audit(1716557137.764:477): apparmor="DENIED" operation="recvmsg" class="net" info="failed remote addr match" error=-13 profile="/home/ubuntu/apparmor/tests/regression/apparmor/net_inet_rcv" pid=22237 comm="net_inet_rcv" laddr=127.0.97.3 lport=3456 saddr=127.0.97.3 src=3456 family="inet" sock_type="dgram" protocol=17 requested="receive" denied="receive"

View File

@@ -1,20 +0,0 @@
START
File: testcase_network_06.in
Event type: AA_RECORD_DENIED
Audit ID: 1716557137.764:477
Operation: recvmsg
Mask: receive
Denied Mask: receive
Profile: /home/ubuntu/apparmor/tests/regression/apparmor/net_inet_rcv
Command: net_inet_rcv
Info: failed remote addr match
ErrorCode: 13
PID: 22237
Network family: inet
Socket type: dgram
Protocol: udp
Local addr: 127.0.97.3
Local port: 3456
Class: net
Epoch: 1716557137
Audit subid: 477

View File

@@ -1,4 +0,0 @@
/home/ubuntu/apparmor/tests/regression/apparmor/net_inet_rcv {
network (receive) inet dgram ip=127.0.97.3 port=3456,
}

View File

@@ -1 +0,0 @@
[321266.557863] audit: type=1400 audit(1716558411.518:583): apparmor="DENIED" operation="bind" class="net" profile="/home/ubuntu/apparmor/tests/regression/apparmor/net_inet_rcv" pid=23602 comm="net_inet_rcv" saddr=127.0.97.3 src=3456 family="inet" sock_type="dgram" protocol=17 requested="bind" denied="bind"

View File

@@ -1,18 +0,0 @@
START
File: testcase_network_07.in
Event type: AA_RECORD_DENIED
Audit ID: 1716558411.518:583
Operation: bind
Mask: bind
Denied Mask: bind
Profile: /home/ubuntu/apparmor/tests/regression/apparmor/net_inet_rcv
Command: net_inet_rcv
PID: 23602
Network family: inet
Socket type: dgram
Protocol: udp
Local addr: 127.0.97.3
Local port: 3456
Class: net
Epoch: 1716558411
Audit subid: 583

View File

@@ -1,4 +0,0 @@
/home/ubuntu/apparmor/tests/regression/apparmor/net_inet_rcv {
network (bind) inet dgram ip=127.0.97.3 port=3456,
}

View File

@@ -1 +0,0 @@
[321557.117710] audit: type=1400 audit(1716558702.097:793): apparmor="DENIED" operation="setsockopt" class="net" info="failed cmd selection match" error=-13 profile="/home/ubuntu/apparmor/tests/regression/apparmor/net_inet_rcv" pid=26135 comm="net_inet_rcv" family="inet" sock_type="dgram" protocol=17 requested="setopt" denied="setopt"

View File

@@ -1,18 +0,0 @@
START
File: testcase_network_08.in
Event type: AA_RECORD_DENIED
Audit ID: 1716558702.097:793
Operation: setsockopt
Mask: setopt
Denied Mask: setopt
Profile: /home/ubuntu/apparmor/tests/regression/apparmor/net_inet_rcv
Command: net_inet_rcv
Info: failed cmd selection match
ErrorCode: 13
PID: 26135
Network family: inet
Socket type: dgram
Protocol: udp
Class: net
Epoch: 1716558702
Audit subid: 793

View File

@@ -1,4 +0,0 @@
/home/ubuntu/apparmor/tests/regression/apparmor/net_inet_rcv {
network (setopt) inet dgram,
}

View File

@@ -1 +0,0 @@
[338728.513756] audit: type=1400 audit(1716575873.613:1160): apparmor="DENIED" operation="sendmsg" class="net" profile="/home/ubuntu/apparmor/tests/regression/apparmor/net_inet_snd" pid=31340 comm="net_inet_snd" laddr=127.187.243.54 lport=3457 saddr=127.187.243.54 src=3457 daddr=127.0.97.3 dest=3456 family="inet" sock_type="dgram" protocol=17 requested="send" denied="send"

View File

@@ -1,20 +0,0 @@
START
File: testcase_network_09.in
Event type: AA_RECORD_DENIED
Audit ID: 1716575873.613:1160
Operation: sendmsg
Mask: send
Denied Mask: send
Profile: /home/ubuntu/apparmor/tests/regression/apparmor/net_inet_snd
Command: net_inet_snd
PID: 31340
Network family: inet
Socket type: dgram
Protocol: udp
Local addr: 127.187.243.54
Foreign addr: 127.0.97.3
Local port: 3457
Foreign port: 3456
Class: net
Epoch: 1716575873
Audit subid: 1160

View File

@@ -1,4 +0,0 @@
/home/ubuntu/apparmor/tests/regression/apparmor/net_inet_snd {
network (send) inet dgram ip=127.187.243.54 port=3457 peer=(ip=127.0.97.3 port=3456),
}

View File

@@ -1 +0,0 @@
[341455.536270] audit: type=1400 audit(1716578600.733:1467): apparmor="DENIED" operation="bind" class="net" profile="/home/ubuntu/apparmor/tests/regression/apparmor/net_inet_rcv" pid=35013 comm="net_inet_rcv" saddr=fd74:1820:b03a:b361::cf32 src=3456 family="inet6" sock_type="dgram" protocol=17 requested="bind" denied="bind"

View File

@@ -1,18 +0,0 @@
START
File: testcase_network_10.in
Event type: AA_RECORD_DENIED
Audit ID: 1716578600.733:1467
Operation: bind
Mask: bind
Denied Mask: bind
Profile: /home/ubuntu/apparmor/tests/regression/apparmor/net_inet_rcv
Command: net_inet_rcv
PID: 35013
Network family: inet6
Socket type: dgram
Protocol: udp
Local addr: fd74:1820:b03a:b361::cf32
Local port: 3456
Class: net
Epoch: 1716578600
Audit subid: 1467

View File

@@ -1,4 +0,0 @@
/home/ubuntu/apparmor/tests/regression/apparmor/net_inet_rcv {
network (bind) inet6 dgram ip=fd74:1820:b03a:b361::cf32 port=3456,
}

View File

@@ -1 +0,0 @@
[342092.040080] audit: type=1400 audit(1716579237.240:2187): apparmor="DENIED" operation="sendmsg" class="net" profile="/home/ubuntu/apparmor/tests/regression/apparmor/net_inet_snd" pid=43431 comm="net_inet_snd" laddr=fd74:1820:b03a:b361::a0f9 lport=3457 saddr=fd74:1820:b03a:b361::a0f9 src=3457 daddr=fd74:1820:b03a:b361::cf32 dest=3456 family="inet6" sock_type="dgram" protocol=17 requested="send" denied="send"

View File

@@ -1,20 +0,0 @@
START
File: testcase_network_11.in
Event type: AA_RECORD_DENIED
Audit ID: 1716579237.240:2187
Operation: sendmsg
Mask: send
Denied Mask: send
Profile: /home/ubuntu/apparmor/tests/regression/apparmor/net_inet_snd
Command: net_inet_snd
PID: 43431
Network family: inet6
Socket type: dgram
Protocol: udp
Local addr: fd74:1820:b03a:b361::a0f9
Foreign addr: fd74:1820:b03a:b361::cf32
Local port: 3457
Foreign port: 3456
Class: net
Epoch: 1716579237
Audit subid: 2187

View File

@@ -1,4 +0,0 @@
/home/ubuntu/apparmor/tests/regression/apparmor/net_inet_snd {
network (send) inet6 dgram ip=fd74:1820:b03a:b361::a0f9 port=3457 peer=(ip=fd74:1820:b03a:b361::cf32 port=3456),
}

View File

@@ -1,7 +1,7 @@
/usr/bin/nginx-amplify-agent.py {
^null-/bin/dash {
network (receive, send) inet stream ip=192.168.10.3 port=50758 peer=(ip=54.153.70.241 port=443),
network inet stream,
}
}

View File

@@ -1 +0,0 @@
type=AVC msg=audit(1711454639.955:322): apparmor="DENIED" operation="connect" class="net" profile="/home/user/test/client.py" pid=80819 comm="client.py" family="unix" sock_type="stream" protocol=0 requested="send receive connect" denied="send receive connect" addr=none peer_addr="@test_abstract_socket" peer="/home/user/test/server.py"

View File

@@ -1,18 +0,0 @@
START
File: testcase_unix_01.in
Event type: AA_RECORD_DENIED
Audit ID: 1711454639.955:322
Operation: connect
Mask: send receive connect
Denied Mask: send receive connect
Profile: /home/user/test/client.py
Peer: /home/user/test/server.py
Command: client.py
PID: 80819
Peer Addr: @test_abstract_socket
Network family: unix
Socket type: stream
Protocol: ip
Class: net
Epoch: 1711454639
Audit subid: 322

View File

@@ -1,4 +0,0 @@
/home/user/test/client.py {
unix (connect, receive, send) type=stream peer=(addr=@test_abstract_socket),
}

View File

@@ -1 +0,0 @@
type=AVC msg=audit(1711214183.107:298): apparmor="DENIED" operation="connect" class="net" profile="/home/user/test/client.py" pid=65262 comm="server.py" family="unix" sock_type="stream" protocol=0 requested="send receive accept" denied="send accept" addr="@test_abstract_socket" peer_addr=none peer="unconfined"

View File

@@ -1,18 +0,0 @@
START
File: testcase_unix_02.in
Event type: AA_RECORD_DENIED
Audit ID: 1711214183.107:298
Operation: connect
Mask: send receive accept
Denied Mask: send accept
Profile: /home/user/test/client.py
Peer: unconfined
Command: server.py
PID: 65262
Net Addr: @test_abstract_socket
Network family: unix
Socket type: stream
Protocol: ip
Class: net
Epoch: 1711214183
Audit subid: 298

View File

@@ -1,4 +0,0 @@
/home/user/test/client.py {
unix (accept, send) type=stream addr=@test_abstract_socket,
}

View File

@@ -1 +0,0 @@
type=AVC msg=audit(1711214069.931:292): apparmor="DENIED" operation="bind" class="net" profile="/home/user/test/client.py" pid=64952 comm="client.py" family="unix" sock_type="stream" protocol=0 requested="bind" denied="bind" addr="@test_abstract_socket"

View File

@@ -1,17 +0,0 @@
START
File: testcase_unix_03.in
Event type: AA_RECORD_DENIED
Audit ID: 1711214069.931:292
Operation: bind
Mask: bind
Denied Mask: bind
Profile: /home/user/test/client.py
Command: client.py
PID: 64952
Net Addr: @test_abstract_socket
Network family: unix
Socket type: stream
Protocol: ip
Class: net
Epoch: 1711214069
Audit subid: 292

View File

@@ -1,4 +0,0 @@
/home/user/test/client.py {
unix (bind) type=stream addr=@test_abstract_socket,
}

View File

@@ -1 +0,0 @@
[ 429.272003] audit: type=1400 audit(1720613712.153:168): apparmor="AUDIT" operation="userns_create" class="namespace" info="Userns create - transitioning profile" profile="unconfined" pid=5630 comm="unshare" requested="userns_create" target="unprivileged_userns" execpath="/usr/bin/unshare"

View File

@@ -1,15 +0,0 @@
START
File: testcase_userns_02.in
Event type: AA_RECORD_AUDIT
Audit ID: 1720613712.153:168
Operation: userns_create
Mask: userns_create
Profile: unconfined
Command: unshare
Name2: unprivileged_userns
Info: Userns create - transitioning profile
PID: 5630
Execpath: /usr/bin/unshare
Class: namespace
Epoch: 1720613712
Audit subid: 168

View File

@@ -1,4 +0,0 @@
/usr/bin/unshare {
audit userns create,
}

View File

@@ -105,16 +105,16 @@ SRCS = parser_common.c parser_include.c parser_interface.c parser_lex.c \
parser_alias.c common_optarg.c lib.c network.cc \
mount.cc dbus.cc profile.cc rule.cc signal.cc ptrace.cc \
af_rule.cc af_unix.cc policy_cache.c default_features.c userns.cc \
mqueue.cc io_uring.cc all_rule.cc cond_expr.cc
mqueue.cc io_uring.cc all_rule.cc
STATIC_HDRS = af_rule.h af_unix.h capability.h common_optarg.h dbus.h \
file_cache.h immunix.h lib.h mount.h network.h parser.h \
parser_include.h parser_version.h policy_cache.h policydb.h \
profile.h ptrace.h rule.h signal.h userns.h mqueue.h io_uring.h \
common_flags.h bignum.h all_rule.h cond_expr.h
common_flags.h bignum.h all_rule.h
SPECIAL_HDRS = parser_yacc.h unit_test.h base_cap_names.h
GENERATED_HDRS = af_names.h generated_af_names.h \
cap_names.h generated_cap_names.h parser_version.h errnos.h
cap_names.h generated_cap_names.h parser_version.h
LIBAA_HDRS = libapparmor_re/apparmor_re.h libapparmor_re/aare_rules.h
TOOLS = apparmor_parser
@@ -328,9 +328,6 @@ io_uring.o: io_uring.cc $(HDRS)
all_rule.o: all_rule.cc $(HDRS)
$(CXX) $(EXTRA_CFLAGS) -c -o $@ $<
cond_expr.o: cond_expr.cc $(HDRS)
$(CXX) $(EXTRA_CFLAGS) -c -o $@ $<
parser_version.h: Makefile
@echo \#define PARSER_VERSION \"$(VERSION)\" > .ver
@mv -f .ver $@
@@ -370,11 +367,6 @@ tst_lib: lib.c parser.h $(filter-out lib.o, ${TEST_OBJECTS})
tst_%: parser_%.c parser.h $(filter-out parser_%.o, ${TEST_OBJECTS})
$(CXX) $(TEST_CFLAGS) -o $@ $< $(filter-out $(<:.c=.o), ${TEST_OBJECTS}) $(TEST_LDFLAGS) $(TEST_LDLIBS)
errnos.h:
echo '#include <errno.h>' > dump.c
$(CC) -E -dD dump.c | awk '/^#define E/ { printf "{ \"%s\", %s },\n", $$2, $$2 }' > errnos.h
rm -f dump.c
.SILENT: check
.PHONY: check
check: check_pod_files tests

View File

@@ -33,7 +33,7 @@
/* See unix(7) for autobind address definition */
#define autobind_address_pattern "\\x00[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]";
int parse_unix_perms(const char *str_perms, perm32_t *perms, int fail)
int parse_unix_perms(const char *str_perms, perms_t *perms, int fail)
{
return parse_X_perms("unix", AA_VALID_NET_PERMS, str_perms, perms, fail);
}
@@ -113,7 +113,7 @@ unix_rule::unix_rule(unsigned int type_p, audit_t audit_p, rule_mode_t rule_mode
downgrade = false;
}
unix_rule::unix_rule(perm32_t perms_p, struct cond_entry *conds,
unix_rule::unix_rule(perms_t perms_p, struct cond_entry *conds,
struct cond_entry *peer_conds):
af_rule(AF_UNIX), addr(NULL), peer_addr(NULL)
{
@@ -191,7 +191,7 @@ static void writeu16(std::ostringstream &o, int v)
#define CMD_OPT 4
void unix_rule::downgrade_rule(Profile &prof) {
perm32_t mask = (perm32_t) -1;
perms_t mask = (perms_t) -1;
if (!prof.net.allow && !prof.net.alloc_net_table())
yyerror(_("Memory allocation error."));
@@ -203,7 +203,7 @@ void unix_rule::downgrade_rule(Profile &prof) {
prof.net.audit[AF_UNIX] |= mask;
const char *error;
network_rule *netv8 = new network_rule(perms, AF_UNIX, sock_type_n);
if(!netv8->add_prefix({0, audit, rule_mode, owner}, error))
if(!netv8->add_prefix({audit, rule_mode, owner}, error))
yyerror(error);
prof.rule_ents.push_back(netv8);
} else {
@@ -318,7 +318,7 @@ int unix_rule::gen_policy_re(Profile &prof)
std::ostringstream buffer;
std::string buf;
perm32_t mask = perms;
perms_t mask = perms;
/* always generate a downgraded rule. This doesn't change generated
* policy size and allows the binary policy to be loaded against
@@ -344,8 +344,7 @@ int unix_rule::gen_policy_re(Profile &prof)
write_to_prot(buffer);
if ((mask & AA_NET_CREATE) && !has_peer_conds()) {
buf = buffer.str();
if (!prof.policy.rules->add_rule(buf.c_str(), priority,
rule_mode,
if (!prof.policy.rules->add_rule(buf.c_str(), rule_mode == RULE_DENY,
map_perms(AA_NET_CREATE),
map_perms(audit == AUDIT_FORCE ? AA_NET_CREATE : 0),
parseopts))
@@ -370,8 +369,7 @@ int unix_rule::gen_policy_re(Profile &prof)
tmp << "\\x00";
buf = tmp.str();
if (!prof.policy.rules->add_rule(buf.c_str(), priority,
rule_mode,
if (!prof.policy.rules->add_rule(buf.c_str(), rule_mode == RULE_DENY,
map_perms(AA_NET_BIND),
map_perms(audit == AUDIT_FORCE ? AA_NET_BIND : 0),
parseopts))
@@ -396,8 +394,7 @@ int unix_rule::gen_policy_re(Profile &prof)
AA_LOCAL_NET_PERMS & ~AA_LOCAL_NET_CMD;
if (mask & local_mask) {
buf = buffer.str();
if (!prof.policy.rules->add_rule(buf.c_str(), priority,
rule_mode,
if (!prof.policy.rules->add_rule(buf.c_str(), rule_mode == RULE_DENY,
map_perms(mask & local_mask),
map_perms(audit == AUDIT_FORCE ? mask & local_mask : 0),
parseopts))
@@ -411,9 +408,7 @@ int unix_rule::gen_policy_re(Profile &prof)
/* TODO: backlog conditional: for now match anything*/
tmp << "..";
buf = tmp.str();
if (!prof.policy.rules->add_rule(buf.c_str(),
priority,
rule_mode,
if (!prof.policy.rules->add_rule(buf.c_str(), rule_mode == RULE_DENY,
map_perms(AA_NET_LISTEN),
map_perms(audit == AUDIT_FORCE ? AA_NET_LISTEN : 0),
parseopts))
@@ -426,13 +421,10 @@ int unix_rule::gen_policy_re(Profile &prof)
/* TODO: sockopt conditional: for now match anything */
tmp << "..";
buf = tmp.str();
if (!prof.policy.rules->add_rule(buf.c_str(),
priority,
rule_mode,
map_perms(mask & AA_NET_OPT),
map_perms(audit == AUDIT_FORCE ?
AA_NET_OPT : 0),
parseopts))
if (!prof.policy.rules->add_rule(buf.c_str(), rule_mode == RULE_DENY,
map_perms(mask & AA_NET_OPT),
map_perms(audit == AUDIT_FORCE ? AA_NET_OPT : 0),
parseopts))
goto fail;
}
mask &= ~AA_LOCAL_NET_PERMS | AA_NET_ACCEPT;
@@ -450,10 +442,7 @@ int unix_rule::gen_policy_re(Profile &prof)
goto fail;
buf = buffer.str();
if (!prof.policy.rules->add_rule(buf.c_str(), priority,
rule_mode, map_perms(perms & AA_PEER_NET_PERMS),
map_perms(audit == AUDIT_FORCE ? perms & AA_PEER_NET_PERMS : 0),
parseopts))
if (!prof.policy.rules->add_rule(buf.c_str(), rule_mode == RULE_DENY, map_perms(perms & AA_PEER_NET_PERMS), map_perms(audit == AUDIT_FORCE ? perms & AA_PEER_NET_PERMS : 0), parseopts))
goto fail;
}

View File

@@ -24,7 +24,7 @@
#include "profile.h"
#include "af_rule.h"
int parse_unix_perms(const char *str_mode, perm32_t *perms, int fail);
int parse_unix_perms(const char *str_mode, perms_t *perms, int fail);
class unix_rule: public af_rule {
void write_to_prot(std::ostringstream &buffer);
@@ -39,7 +39,7 @@ public:
bool downgrade = true;
unix_rule(unsigned int type_p, audit_t audit_p, rule_mode_t rule_mode_p);
unix_rule(perm32_t perms, struct cond_entry *conds,
unix_rule(perms_t perms, struct cond_entry *conds,
struct cond_entry *peer_conds);
virtual ~unix_rule()
{
@@ -48,9 +48,6 @@ public:
};
virtual bool valid_prefix(const prefixes &p, const char *&error) {
// priority is partially supported for unix rules
// rules that get downgraded to just network socket
// won't support them but the fine grained do.
if (p.owner) {
error = "owner prefix not allowed on unix rules";
return false;

View File

@@ -39,7 +39,7 @@ void all_rule::add_implied_rules(Profile &prof)
prefix_rule_t *rule;
const prefixes *prefix = this;
rule = new unix_rule(0xffffffff, audit, rule_mode);
rule = new unix_rule(0, audit, rule_mode);
(void) rule->add_prefix(*prefix);
prof.rule_ents.push_back(rule);
@@ -67,7 +67,7 @@ void all_rule::add_implied_rules(Profile &prof)
(void) rule->add_prefix(*prefix);
prof.rule_ents.push_back(rule);
rule = new mnt_rule(NULL, NULL, NULL, NULL, AA_MAY_MOUNT);
rule = new mnt_rule(NULL, NULL, NULL, NULL, 0);
(void) rule->add_prefix(*prefix);
prof.rule_ents.push_back(rule);
@@ -89,13 +89,15 @@ void all_rule::add_implied_rules(Profile &prof)
/* rules that have not been converted to use rule.h */
//file no x
//file
{
const char *error;
struct cod_entry *entry;
char *path = strdup("/{**,}");
int perms = (AA_BASE_PERMS & ~(AA_EXEC_TYPE | AA_MAY_EXEC));
int perms = ((AA_BASE_PERMS & ~AA_EXEC_TYPE) |
(AA_MAY_EXEC));
if (rule_mode != RULE_DENY)
perms |= AA_EXEC_INHERIT;
/* duplicate to other permission set */
perms |= perms << AA_OTHER_SHIFT;
if (!path)
@@ -106,35 +108,7 @@ void all_rule::add_implied_rules(Profile &prof)
}
add_entry_to_policy(&prof, entry);
}
// lower priority ix
{
const char *error;
struct cod_entry *entry;
char *path = strdup("/{**,}");
int perms = AA_MAY_EXEC;
prefixes ix_prefix;
// TODO:
// need a better way to make sure the prefix is intialized
// without a constructor or copy constructor
ix_prefix.priority = prefix->priority -1;
ix_prefix.audit = prefix->audit;
ix_prefix.rule_mode = prefix->rule_mode;
ix_prefix.owner = prefix->owner;
ix_prefix.priority -= 1;
if (rule_mode != RULE_DENY)
perms |= AA_EXEC_INHERIT;
/* duplicate to other permission set */
perms |= perms << AA_OTHER_SHIFT;
if (!path)
yyerror(_("Memory allocation error."));
entry = new_entry(path, perms, NULL);
if (!entry_add_prefix(entry, ix_prefix, error)) {
yyerror(_("%s"), error);
}
add_entry_to_policy(&prof, entry);
}
// caps
{
if (prefix->owner)

View File

@@ -32,10 +32,6 @@ public:
all_rule(void): prefix_rule_t(RULE_TYPE_ALL) { }
virtual bool valid_prefix(const prefixes &p, const char *&error) {
if (p.priority != 0) {
error = _("priority prefix not allowed on all rules");
return false;
}
if (p.owner) {
error = _("owner prefix not allowed on all rules");
return false;

View File

@@ -115,9 +115,7 @@ B<PROFILE FLAG CONDS> = [ 'flags=' ] '(' comma or white space separated list of
B<PROFILE FLAGS> = I<PROFILE MODE> | I<AUDIT_MODE> | 'mediate_deleted'
| 'attach_disconnected' | 'attach_disconneced.path='I<ABS PATH> | 'chroot_relative'
| 'debug' | 'interruptible' | 'kill.signal='I<SIGNAL> | 'error='I<ERROR CODE>
B<ERROR CODE> = (case insensitive error code name starting with 'E'; see errno(3))
| 'debug' | 'interruptible' | 'kill.signal='I<SIGNAL>
B<PROFILE MODE> = 'enforce' | 'complain' | 'kill' | 'default_allow' | 'unconfined' | 'prompt'
@@ -139,11 +137,9 @@ B<HATNAME> = (must start with alphanumeric character. See aa_change_hat(2) for a
B<QUALIFIER BLOCK> = I<QUALIFIERS> I<BLOCK>
B<INTEGER> = (+ | -)? [[:digit:]]+
B<ACCESS TYPE> = ( 'allow' | 'deny' )
B<QUALIFIERS> = [ 'priority' '=' <INTEGER> ] [ 'audit' ] [ I<ACCESS TYPE> ]
B<QUALIFIERS> = [ 'audit' ] [ I<ACCESS TYPE> ]
B<CAPABILITY RULE> = [ I<QUALIFIERS> ] 'capability' [ I<CAPABILITY LIST> ]
@@ -152,14 +148,7 @@ B<CAPABILITY LIST> = ( I<CAPABILITY> )+
B<CAPABILITY> = (lowercase capability name without 'CAP_' prefix; see
capabilities(7))
B<NETWORK RULE> = [ I<QUALIFIERS> ] 'network' [ I<NETWORK ACCESS EXPR> ] [ I<DOMAIN> ] [ I<TYPE> | I<PROTOCOL> ] [ I<NETWORK LOCAL EXPR> ] [ I<NETWORK PEER EXPR> ]
B<NETWORK ACCESS EXPR> = ( I<NETWORK ACCESS> | I<NETWORK ACCESS LIST> )
B<NETWORK ACCESS> = ( 'create' | 'bind' | 'listen' | 'accept' | 'connect' | 'shutdown' | 'getattr' | 'setattr' | 'getopt' | 'setopt' | 'send' | 'receive' | 'r' | 'w' | 'rw' )
Some access modes are incompatible with some rules.
B<NETWORK ACCESS LIST> = '(' I<NETWORK ACCESS> ( [','] I<NETWORK ACCESS> )* ')'
B<NETWORK RULE> = [ I<QUALIFIERS> ] 'network' [ I<DOMAIN> ] [ I<TYPE> | I<PROTOCOL> ]
B<DOMAIN> = ( 'unix' | 'inet' | 'ax25' | 'ipx' | 'appletalk' | 'netrom' | 'bridge' | 'atmpvc' | 'x25' | 'inet6' | 'rose' | 'netbeui' | 'security' | 'key' | 'netlink' | 'packet' | 'ash' | 'econet' | 'atmsvc' | 'rds' | 'sna' | 'irda' | 'pppox' | 'wanpipe' | 'llc' | 'ib' | 'mpls' | 'can' | 'tipc' | 'bluetooth' | 'iucv' | 'rxrpc' | 'isdn' | 'phonet' | 'ieee802154' | 'caif' | 'alg' | 'nfc' | 'vsock' | 'kcm' | 'qipcrtr' | 'smc' | 'xdp' | 'mctp' ) ','
@@ -167,22 +156,6 @@ B<TYPE> = ( 'stream' | 'dgram' | 'seqpacket' | 'rdm' | 'raw' | 'packet' )
B<PROTOCOL> = ( 'tcp' | 'udp' | 'icmp' )
B<NETWORK LOCAL EXPR> = ( I<NETWORK IP COND> | I<NETWORK PORT COND> )*
Each cond can appear at most once.
B<NETWORK PEER EXPR> = 'peer' '=' '(' ( I<NETWORK IP COND> | I<NETWORK PORT COND> )+ ')'
Each cond can appear at most once.
B<NETWORK IP COND> = 'ip' '=' ( 'none' | I<NETWORK IPV4> | I<NETWORK IPV6> )
B<NETWORK PORT COND> = 'port' '=' ( I<NETWORK PORT> )
B<NETWORK IPV4> = IPv4, represented by four 8-bit decimal numbers separated by '.'
B<NETWORK IPV6> = IPv6, represented by eight groups of four hexadecimal numbers separated by ':'. Shortened representation of contiguous zeros is allowed by using '::'
B<NETWORK PORT> = 16-bit number ranging from 0 to 65535
B<MOUNT RULE> = ( I<MOUNT> | I<REMOUNT> | I<UMOUNT> )
B<MOUNT> = [ I<QUALIFIERS> ] 'mount' [ I<MOUNT CONDITIONS> ] [ I<SOURCE FILEGLOB> ] [ '-E<gt>' [ I<MOUNTPOINT FILEGLOB> ]
@@ -574,9 +547,6 @@ to debug kernel or policy problems.
=item B<kill.signal>=I<SIGNAL> This changes the signal that will be
sent by AppArmor when in kill mode or a kill rule has been violated.
=item B<error>=I<ERROR CODE> This changes the error code returned by
AppArmor when a rule has been violated.
=back
=head2 Access Modes
@@ -942,10 +912,11 @@ and other operations that are typically reserved for the root user.
=head2 Network Rules
AppArmor supports simple coarse grained network mediation. The
network rule restrict all socket(2) based operations. The mediation
done is a coarse-grained check on whether a socket of a given type and
family can be created, read, or written. Network netlink(7) rules may
AppArmor supports simple coarse grained network mediation. The network
rule restrict all socket(2) based operations. The mediation done is
a coarse-grained check on whether a socket of a given type and family
can be created, read, or written. There is no mediation based of port
number or protocol beyond tcp, udp, and raw. Network netlink(7) rules may
only specify type 'dgram' and 'raw'.
AppArmor network rules are accumulated so that the granted network
@@ -962,48 +933,6 @@ eg.
network inet6 tcp, #allow access to tcp only for inet6 addresses
network netlink raw, #allow access to AF_NETLINK SOCK_RAW
=head3 Network permissions
Network 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 create, bind, listen, shutdown, getattr, setattr, getopt, and
setopt permissions are local socket permissions. They are only applied
to the local socket and can't be specified in rules that have a peer
conditional. The accept permission applies to the combination of a
local and peer socket. The connect, send, and receive permissions are
peer socket permissions.
=head3 Mediation of inet/inet6 family
AppArmor supports fine grained mediation of the inet and inet6
families by using the ip and port conditionals. The ip conditional
accepts both IPv4 and IPv6 using the regular representation of four
octets separated by '.' for IPv4 and eight groups of four hexadecimal
numbers separated by ':' for IPv6. Contiguous leading zeros can be
replaced by '::' once. On a connected socket, the sender and receiver
don't need to be specified in the recvfrom and sendto system calls. In
that case, and with unbounded sockets, the IP address is none, or
unknown. Unknown or Unbound IP addresses are represented in policy by the
'none' keyword. When the ip conditional is omitted, then all IP
addresses will be allowed: IPv4, IPv6 and none. If INADDR_ANY or
in6addr_any is used, then the ip conditional can be omitted or they
can be represented by:
network ip=::, #allow in6addr_any
network ip=0.0.0.0; #allow INADDR_ANY
The network rules support the specification of local and remote IP
addresses and ports.
network ip=127.0.0.1 port=8080,
network peer=(ip=10.139.15.23 port=8081),
network ip=fd74:1820:b03a:b361::cf32 peer=(ip=fd74:1820:b03a:b361::a0f9),
network port=8080 peer=(port=8081),
network ip=127.0.0.1 port=8080 peer=(ip=10.139.15.23 port=8081),
=head2 Mount Rules
AppArmor supports mount mediation and allows specifying filesystem types and
@@ -1880,17 +1809,6 @@ Rule qualifiers can modify the rule and/or permissions within the rule.
=over 4
=item B<priority>
Specifies the priority of the rule. Currently the allowed range is
-1000 to 1000 with the default priority of rule is 0. Rules with
higher priority are given preferences and will completely override
permissions of lower priority rules where they overlap. When rules
partially overlap the permissions of the higher priority rule will
completely override lower priority rules within in overlap. Within a
given priority level rules that overlap will accumulate permissions in
the standard apparmor fashion.
=item B<allow>
Specifies that permissions requests that match the rule are allowed. This

View File

@@ -109,7 +109,7 @@ I<Turn off deny audit quieting> if this is a problem).
Complain mode can be used to develop profiles incrementally as an
application is exercised. The logged accesses can be added to the
profile and then can the application further exercised to discover further
profile and then can the application further excercised to discover further
additions that are needed. Because AppArmor allows the accesses the
application will behave as it would if AppArmor was not confining it.
@@ -146,9 +146,9 @@ or to set it on boot add:
apparmor.mode=complain
as a kernel boot parameter.
as a kernel boot paramenter.
B<Warning> Setting complain mode globally disables all apparmor
B<Warning> Setting complain mode gloabally disables all apparmor
security protections. It can be useful during debugging or profile
development, but setting it selectively on a per profile basis is
safer.
@@ -217,7 +217,7 @@ or to set it on boot add:
apparmor.debug=1
as a kernel boot parameter.
as a kernel boot paramenter.
=head2 Turn off deny audit quieting
@@ -232,7 +232,7 @@ or to set it on boot add:
apparmor.audit=noquiet
as a kernel boot parameter.
as a kernel boot paramenter.
=head2 Force audit mode
@@ -254,7 +254,7 @@ or to set it on boot add:
apparmor.audit=all
as a kernel boot parameter.
as a kernel boot paramenter.
B<Audit Rate Limiting>

View File

@@ -17,8 +17,6 @@
APPARMOR_FUNCTIONS=/lib/apparmor/rc.apparmor.functions
# This function is used in rc.apparmor.functions
# shellcheck disable=SC2317
aa_action()
{
echo "$1"
@@ -27,50 +25,36 @@ aa_action()
return $?
}
# This function is used in rc.apparmor.functions
# shellcheck disable=SC2317
aa_log_warning_msg()
{
echo "Warning: $*"
}
# This function is used in rc.apparmor.functions
# shellcheck disable=SC2317
aa_log_failure_msg()
{
echo "Error: $*"
}
# This function is used in rc.apparmor.functions
# shellcheck disable=SC2317
aa_log_action_start()
{
echo "$@"
}
# This function is used in rc.apparmor.functions
# shellcheck disable=SC2317
aa_log_action_end()
{
printf ""
}
# This function is used in rc.apparmor.functions
# shellcheck disable=SC2317
aa_log_daemon_msg()
{
echo "$@"
}
# This function is used in rc.apparmor.functions
# shellcheck disable=SC2317
aa_log_skipped_msg()
{
echo "Skipped: $*"
}
# This function is used in rc.apparmor.functions
# shellcheck disable=SC2317
aa_log_end_msg()
{
printf ""

View File

@@ -397,7 +397,7 @@ failure, instead the parser continues on with processing the remaining
profiles.
=item --estimated-compile-size
Adjust the internal parameter used to estimate how aggressive the parser
Adjust the internal parameter used to estimate how agressive the parser
can be when compiling policy. This may include changes to how or when
caches are dropped or how many compile units (jobs) are launched. The
value should slightly larger than the largest Resident Set Size (RSS)
@@ -451,7 +451,7 @@ Eg.
would result in Optimize=minimize being set.
The Include, Dump, and Optimize options accumulate except for the inversion
The Include, Dump, and Optimize options accululate except for the inversion
option (no-X vs. X), and a couple options that work by setting/clearing
multiple options (compress-small). In that case the option will override
the flags it sets but will may accumulate with others.

View File

@@ -71,10 +71,6 @@ optflag_table_t dumpflag_table[] = {
{ 1, "diff-progress", "Dump progress of differential encoding",
DUMP_DFA_DIFF_PROGRESS | DUMP_DFA_DIFF_STATS },
{ 1, "rule-merge", "dump information about rule merging", DUMP_RULE_MERGE},
{ 1, "state32", "Dump encoding 32 bit states",
DUMP_DFA_STATE32 },
{ 1, "flags_table", "Dump encoding flags table",
DUMP_DFA_FLAGS_TABLE },
{ 0, NULL, NULL, 0 },
};
@@ -82,8 +78,7 @@ optflag_table_t dfaoptflag_table[] = {
{ 2, "0", "no optimizations",
CONTROL_DFA_TREE_NORMAL | CONTROL_DFA_TREE_SIMPLE |
CONTROL_DFA_MINIMIZE | CONTROL_DFA_REMOVE_UNREACHABLE |
CONTROL_DFA_DIFF_ENCODE | CONTROL_DFA_STATE32 |
CONTROL_DFA_FLAGS_TABLE
CONTROL_DFA_DIFF_ENCODE
},
{ 1, "equiv", "use equivalent classes", CONTROL_DFA_EQUIV },
{ 1, "expr-normalize", "expression tree normalization",
@@ -107,10 +102,6 @@ optflag_table_t dfaoptflag_table[] = {
{ 1, "diff-encode", "Differentially encode transitions",
CONTROL_DFA_DIFF_ENCODE },
{ 1, "rule-merge", "turn on rule merging", CONTROL_RULE_MERGE},
{ 1, "state32", "use 32 bit state transitions",
CONTROL_DFA_STATE32 },
{ 1, "flags-table", "use independent flags table",
CONTROL_DFA_FLAGS_TABLE },
{ 0, NULL, NULL, 0 },
};

View File

@@ -1,45 +0,0 @@
/*
* Copyright (c) 2024
* Canonical Ltd. (All rights reserved)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
* License published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, contact Novell, Inc. or Canonical
* Ltd.
*/
#include "cond_expr.h"
#include "parser.h"
cond_expr::cond_expr(bool result):
result(result)
{
}
cond_expr::cond_expr(const char *var, bool defined)
{
char *var_name = process_var(var);
if (!defined) {
int ret = get_boolean_var(var_name);
if (ret < 0) {
/* FIXME check for set var */
free(var_name);
yyerror(_("Unset boolean variable %s used in if-expression"), var);
}
result = ret;
} else {
void *set_value = get_set_var(var_name);
PDEBUG("Matched: defined set expr %s value %lx\n", var_name, (long) set_value);
result = !! (long) set_value;
}
free(var_name);
}

View File

@@ -1,35 +0,0 @@
/*
* Copyright (c) 2024
* Canonical Ltd. (All rights reserved)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
* License published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, contact Novell, Inc. or Canonical
* Ltd.
*/
#ifndef __AA_COND_EXPR_H
#define __AA_COND_EXPR_H
class cond_expr {
private:
bool result;
public:
cond_expr(bool result);
cond_expr(const char *var, bool defined);
virtual ~cond_expr()
{
};
bool eval(void) { return result; }
};
#endif /* __AA_COND_EXPR_H */

View File

@@ -30,7 +30,7 @@
#include "dbus.h"
int parse_dbus_perms(const char *str_perms, perm32_t *perms, int fail)
int parse_dbus_perms(const char *str_perms, perms_t *perms, int fail)
{
return parse_X_perms("DBus", AA_VALID_DBUS_PERMS, str_perms, perms, fail);
}
@@ -66,7 +66,7 @@ void dbus_rule::move_conditionals(struct cond_entry *conds)
}
}
dbus_rule::dbus_rule(perm32_t perms_p, struct cond_entry *conds,
dbus_rule::dbus_rule(perms_t perms_p, struct cond_entry *conds,
struct cond_entry *peer_conds):
perms_rule_t(AA_CLASS_DBUS), bus(NULL), name(NULL), peer_label(NULL), path(NULL), interface(NULL), member(NULL)
{
@@ -274,24 +274,23 @@ int dbus_rule::gen_policy_re(Profile &prof)
}
if (perms & AA_DBUS_BIND) {
if (!prof.policy.rules->add_rule_vec(priority, rule_mode,
perms & AA_DBUS_BIND,
audit == AUDIT_FORCE ? perms & AA_DBUS_BIND : 0,
2, vec, parseopts, false))
if (!prof.policy.rules->add_rule_vec(rule_mode == RULE_DENY, perms & AA_DBUS_BIND,
audit == AUDIT_FORCE ? perms & AA_DBUS_BIND : 0,
2, vec, parseopts, false))
goto fail;
}
if (perms & (AA_DBUS_SEND | AA_DBUS_RECEIVE)) {
if (!prof.policy.rules->add_rule_vec(priority, rule_mode,
perms & (AA_DBUS_SEND | AA_DBUS_RECEIVE),
audit == AUDIT_FORCE ? perms & (AA_DBUS_SEND | AA_DBUS_RECEIVE) : 0,
6, vec, parseopts, false))
if (!prof.policy.rules->add_rule_vec(rule_mode == RULE_DENY,
perms & (AA_DBUS_SEND | AA_DBUS_RECEIVE),
audit == AUDIT_FORCE ? perms & (AA_DBUS_SEND | AA_DBUS_RECEIVE) : 0,
6, vec, parseopts, false))
goto fail;
}
if (perms & AA_DBUS_EAVESDROP) {
if (!prof.policy.rules->add_rule_vec(priority, rule_mode,
perms & AA_DBUS_EAVESDROP,
audit == AUDIT_FORCE ? perms & AA_DBUS_EAVESDROP : 0,
1, vec, parseopts, false))
if (!prof.policy.rules->add_rule_vec(rule_mode == RULE_DENY,
perms & AA_DBUS_EAVESDROP,
audit == AUDIT_FORCE ? perms & AA_DBUS_EAVESDROP : 0,
1, vec, parseopts, false))
goto fail;
}

View File

@@ -23,7 +23,7 @@
#include "rule.h"
#include "profile.h"
extern int parse_dbus_perms(const char *str_mode, perm32_t *mode, int fail);
extern int parse_dbus_perms(const char *str_mode, perms_t *mode, int fail);
class dbus_rule: public perms_rule_t {
void move_conditionals(struct cond_entry *conds);
@@ -40,7 +40,7 @@ public:
char *interface;
char *member;
dbus_rule(perm32_t perms_p, struct cond_entry *conds,
dbus_rule(perms_t perms_p, struct cond_entry *conds,
struct cond_entry *peer_conds);
virtual ~dbus_rule() {
free(bus);
@@ -51,7 +51,7 @@ public:
free(member);
};
virtual bool valid_prefix(const prefixes &p, const char *&error) {
if (p.owner != OWNER_UNSPECIFIED) {
if (p.owner) {
error = "owner prefix not allowed on dbus rules";
return false;
}

View File

@@ -47,7 +47,7 @@ void io_uring_rule::move_conditionals(struct cond_entry *conds)
}
}
io_uring_rule::io_uring_rule(perm32_t perms_p, struct cond_entry *conds, struct cond_entry *ring_conds):
io_uring_rule::io_uring_rule(perms_t perms_p, struct cond_entry *conds, struct cond_entry *ring_conds):
perms_rule_t(AA_CLASS_IO_URING), label(NULL)
{
if (perms_p) {
@@ -122,18 +122,16 @@ int io_uring_rule::gen_policy_re(Profile &prof)
}
if (perms & AA_VALID_IO_URING_PERMS) {
if (!prof.policy.rules->add_rule(buf.c_str(), priority,
rule_mode, perms,
audit == AUDIT_FORCE ? perms : 0,
parseopts))
if (!prof.policy.rules->add_rule(buf.c_str(), rule_mode == RULE_DENY, perms,
audit == AUDIT_FORCE ? perms : 0,
parseopts))
goto fail;
if (perms & AA_IO_URING_OVERRIDE_CREDS) {
buf = buffer.str(); /* update buf to have label */
if (!prof.policy.rules->add_rule(buf.c_str(),
priority, rule_mode,
perms, audit == AUDIT_FORCE ? perms : 0,
parseopts))
if (!prof.policy.rules->add_rule(buf.c_str(), rule_mode == RULE_DENY,
perms, audit == AUDIT_FORCE ? perms : 0,
parseopts))
goto fail;
}

View File

@@ -31,7 +31,7 @@ class io_uring_rule: public perms_rule_t {
public:
char *label;
io_uring_rule(perm32_t perms, struct cond_entry *conds, struct cond_entry *ring_conds);
io_uring_rule(perms_t perms, struct cond_entry *conds, struct cond_entry *ring_conds);
virtual ~io_uring_rule()
{
free(label);

View File

@@ -22,19 +22,17 @@ all : ${TARGET}
UNITTESTS = tst_parse
libapparmor_re.a: parse.o expr-tree.o hfa.o chfa.o aare_rules.o policy_compat.o
libapparmor_re.a: parse.o expr-tree.o hfa.o chfa.o aare_rules.o
${AR} ${ARFLAGS} $@ $^
expr-tree.o: expr-tree.cc expr-tree.h
hfa.o: hfa.cc apparmor_re.h hfa.h ../immunix.h policy_compat.h
hfa.o: hfa.cc apparmor_re.h hfa.h ../immunix.h
aare_rules.o: aare_rules.cc aare_rules.h apparmor_re.h expr-tree.h hfa.h chfa.h parse.h ../immunix.h
chfa.o: chfa.cc chfa.h ../immunix.h
policy_compat.o: policy_compat.cc policy_compat.h ../perms.h ../immunix.h
parse.o : parse.cc apparmor_re.h expr-tree.h
parse.cc : parse.y parse.h flex-tables.h ../immunix.h

View File

@@ -10,70 +10,37 @@ aare_rules.{h,cc} - code to that binds parse -> expr-tree -> hfa generation
-> chfa generation into a basic interface for converting
rules to a runtime ready state machine.
Notes on the compress hfa file format (chfa)
==============================================
Regular Expression Scanner Generator
====================================
Notes in the scanner File Format
--------------------------------
The file format used is based on the GNU flex table file format
(--tables-file option; see Table File Format in the flex info pages and
the flex sources for documentation). The magic number used in the header
is set to 0x1B5E783D instead of 0xF13C57B1 though, which is meant to
indicate that the file format logically is not the same: the YY_ID_CHK
(check) and YY_ID_DEF (default), YY_ID_BASE tables are used differently.
(check) and YY_ID_DEF (default) tables are used differently.
The YY_ID_ACCEPTX tables either encode permissions directly, or are an
index, into an external tables.
There are two DFA table formats to support different size state machines
DFA16
default/next/check - are 16 bit tables
DFA32
default/next/check - are 32 bit tables
DFA32 is limited to 2^24 states, due to the upper 8 bits being used
as flags in the base table, unless the flags table is defined. When
the flags table is defined, DFA32 can have a full 2^32 states.
In both DFA16 and DFA32
base and accept are 32 bit tables.
State 0 is always used as the trap state. Its accept, base and default
fields should be 0.
State 1 is the default start state. Alternate start states are stored
external to the state machine.
If the flags table is not defined, the base table uses the lower 24
bits as index into the next/check tables, and the upper 8 bits are used
as flags.
The currently defined flags are
#define MATCH_FLAG_DIFF_ENCODE 0x80000000
#define MARK_DIFF_ENCODE 0x40000000
#define MATCH_FLAG_OOB_TRANSITION 0x20000000
Note the default[state] is used in two different ways.
1. When diff_encode is set, the state stores the difference to another
state defined by default. The next field will only store the
transitions that are unique to this state. Those transition may mask
transitions in the state that the current state is relative to, also
note the state that this state is relative might also be relative to
another state. Cycles are forbidden and checked for by the verifier.
The exact algorithm used to build these state difference will be
discussed in another section.
Flex uses state compression to store only the differences between states
for states that are similar. The amount of compression influences the parse
speed.
The following two states could be stored as in the tables outlined
below:
States and transitions on specific characters to next states
------------------------------------------------------------
1: ('a' => 2, 'b' => 3, 'c' => 4)
2: ('a' => 2, 'b' => 3, 'd' => 5)
Table format - where D in base represnts Diff encode flag
Flex-like table format
----------------------
index: (default, base)
0: ( 0, 0) <== dummy state (nonmatching)
1: ( 0, 0)
2: ( 1, D 256)
2: ( 1, 256)
index: (next, check)
0: ( 0, 0) <== unused entry
@@ -88,74 +55,66 @@ index: (default, base)
Here, state 2 is described as ('c' => 0, 'd' => 5), and everything else
as in state 1. The matching algorithm is as follows.
Scanner algorithm
Flex-like scanner algorithm
---------------------------
/* current state is in <state>, input character <c> */
while (check[base[state] + c] != state) {
diff = (FLAGS(base) & diff_encode);
state = default[state];
if (!diff)
goto done;
}
state = next[base[state] + c];
done:
while (check[base[state] + c] != state)
state = default[state];
state = next[state];
/* continue with the next input character */
2. When diff_encode is NOT set, the default state is used to represent
all none matching transitions (ie. check[base[state] + c] != state).
The dfa build will compute the transition with the most transitions
and use that for the default state. ie.
if we have
1: ('a' => 2)
("[^a]" => 0)
then 0 will be used as the default state
if we have
1: ("[^a]" => 2)
('a' => 0)
then 2 will be used as the default state, and the only state encoded
in the next/check tables will be for 'a'
The combination of the diff-encoded and non-diff encoded states performs
well even when there are many inverted or wildcard matches ("[^x]", ".").
This state compression algorithm performs well, except when there are
many inverted or wildcard matches ("[^x]", "."). Each input character
may cause several iterations in the while loop.
Simplified Regexp scanner algorithm for non-diff encoded state (note
diff encode algorithm above works as well)
We will have many inverted character classes ("[^/]") that wouldn't
compress very well. Therefore, the regexp matcher uses no state
compression, and uses the check and default tables differently. The
above states could be stored as follows:
Regexp table format
-------------------
index: (default, base)
0: ( 0, 0) <== dummy state (nonmatching)
1: ( 0, 0)
2: ( 1, 3)
index: (next, check)
0: ( 0, 0) <== unused entry
( 0, 0) <== ord('a') identical, unused entries
0+'a': ( 2, 1)
0+'b': ( 3, 1)
0+'c': ( 4, 1)
3+'a': ( 2, 2)
3+'b': ( 3, 2)
3+'c': ( 0, 0) <== entry is unused
3+'d': ( 5, 2)
( 0, 0) <== (255 - ord('d')) identical, unused entries
All the entries with 0 in check (except the first entry, which is
deliberately reserved) are still available for other states that
fit in there.
Regexp scanner algorithm
------------------------
/* current state is in <state>, matching character <c> */
if (check[base[state] + c] == state)
state = next[base[state] + c];
state = next[state];
else
state = default[state];
/* continue with the next input character */
This representation and algorithm allows states which match more
characters than they do not match to be represented as their inverse.
For example, a third state that accepts everything other than 'a' can
be added to the tables as one entry in (default, base) and one entry in
(next, check):
Each input character may cause several iterations in the while loop,
but due to guarantees in the build at most 2n states will be
transitioned for n input characters. The expected number of states
walked is much closer to n and in practice due to cache locality the
diff encoded state machine is usually faster than a non-diff encoded
state machine with a strict n state for n input walk.
Comb Compression
-----------------
The next/check tables of states are only used to encode transitions
not covered by the default transition. The input byte is indexed off
the base value, covering 256 positions within the next/check
tables. However a state may only encode a few transitions within that
range, leaving holes. These holes are filled by other states
transitions whose range will overlap.
1: ('a' => 2, 'b' => 3, 'c' => 4)
2: ('a' => 2, 'b' => 3, 'd' => 5)
3: ('a' => 0, everything else => 5)
State
-----
3: ('a' => 0, everything else => 5)
Regexp tables
-------------
@@ -173,65 +132,12 @@ index: (default, base)
0+'c': ( 4, 1)
3+'a': ( 2, 2)
3+'b': ( 3, 2)
3+'c': ( 0, 0) <== entry is unused, hole that could be filled
3+'c': ( 0, 0) <== entry is unused
3+'d': ( 5, 2)
7+'a': ( 0, 3)
( 0, 0) <== (255 - ord('a')) identical, unused entries
Regexp tables comb compressed
-------------
index: (default, base)
0: ( 0, 0)
1: ( 0, 0)
2: ( 1, 3)
3: ( 5, 5)
index: (next, check)
0: ( 0, 0)
( 0, 0)
0+'a': ( 2, 1)
0+'b': ( 3, 1)
0+'c': ( 4, 1)
3+'a': ( 2, 2)
3+'b': ( 3, 2)
5+'a': ( 0, 3) <== entry was previously at 7+'a'
3+'d': ( 5, 2)
( 0, 0) <== (255 - ord('a')) identical, unused entries
Out of Band Transitions (oobs)
---------------------------------
Out of band transitions (oobs) allow for a state to have transitions
that can not be triggered by input. Any state that has oobs must have
the OOB flag set on the state. An oob is triggered by subtracting the
oob number from the the base index value, to find the next and check
value. Current only single oob is supported. And all states using
an oob must have the oob flag set.
if ((FLAG(base) & OOB) && check[base[state] - oob] == state)
state = next[base[state]] - oob]
oobs might be expressed as a negative number eg. -1 for the first
oob. In which case the oob transition above uses a + oob instead.
If more oobs are needed a second oob flag can be allocated, and if
used in combination with the original, would allow a state to have
up to 3 oobs
00 - none
01 - 1
10 - 2
11 - 3
Diff Encode Spanning Tree
============================================
To build the state machine with diff encoded states and to still meet
run time guaratees about traversing no more than 2n states for n input
a spanning tree is use.
* TODO *
While the current code does not implement any form of state compression,
the flex state compression representation could be combined by
remembering (in a bit per state, for example) which default entries
refer to inverted matches, and which refer to parent states.

View File

@@ -44,11 +44,10 @@ aare_rules::~aare_rules(void)
expr_map.clear();
}
bool aare_rules::add_rule(const char *rule, int priority, rule_mode_t mode,
perm32_t perms, perm32_t audit, optflags const &opts)
bool aare_rules::add_rule(const char *rule, int deny, uint32_t perms,
uint32_t audit, optflags const &opts)
{
return add_rule_vec(priority, mode, perms, audit, 1, &rule, opts,
false);
return add_rule_vec(deny, perms, audit, 1, &rule, opts, false);
}
void aare_rules::add_to_rules(Node *tree, Node *perms)
@@ -72,9 +71,9 @@ static Node *cat_with_oob_separator(Node *l, Node *r)
return new CatNode(new CatNode(l, new CharNode(transchar(-1, true))), r);
}
bool aare_rules::add_rule_vec(int priority, rule_mode_t mode, perm32_t perms,
perm32_t audit, int count, const char **rulev,
optflags const &opts, bool oob)
bool aare_rules::add_rule_vec(int deny, uint32_t perms, uint32_t audit,
int count, const char **rulev, optflags const &opts,
bool oob)
{
Node *tree = NULL, *accept;
int exact_match;
@@ -108,7 +107,7 @@ bool aare_rules::add_rule_vec(int priority, rule_mode_t mode, perm32_t perms,
if (reverse)
flip_tree(tree);
accept = unique_perms.insert(priority, mode, perms, audit, exact_match);
accept = unique_perms.insert(deny, perms, audit, exact_match);
if (opts.dump & DUMP_DFA_RULE_EXPR) {
const char *separator;
@@ -124,12 +123,8 @@ bool aare_rules::add_rule_vec(int priority, rule_mode_t mode, perm32_t perms,
}
cerr << " -> ";
tree->dump(cerr);
// TODO: split out from prefixes class
cerr << " priority=" << priority;
if (mode == RULE_DENY)
if (deny)
cerr << " deny";
else if (mode == RULE_PROMPT)
cerr << " prompt";
cerr << " (0x" << hex << perms <<"/" << audit << dec << ")";
accept->dump(cerr);
cerr << "\n\n";
@@ -194,16 +189,16 @@ bool aare_rules::append_rule(const char *rule, bool oob, bool with_perm,
return true;
}
/* create a chfa from the ruleset
/* create a dfa from the ruleset
* returns: buffer contain dfa tables, @size set to the size of the tables
* else NULL on failure, @min_match_len set to the shortest string
* that can match the dfa for determining xmatch priority.
*/
CHFA *aare_rules::create_chfa(int *min_match_len,
vector <aa_perms> &perms_table,
optflags const &opts, bool filedfa,
bool extended_perms, bool prompt)
void *aare_rules::create_dfa(size_t *size, int *min_match_len, optflags const &opts,
bool filedfa)
{
char *buffer = NULL;
/* finish constructing the expr tree from the different permission
* set nodes */
PermExprMap::iterator i = expr_map.begin();
@@ -252,26 +247,12 @@ CHFA *aare_rules::create_chfa(int *min_match_len,
}
}
CHFA *chfa = NULL;
stringstream stream;
try {
DFA dfa(root, opts, filedfa);
if (opts.dump & DUMP_DFA_UNIQ_PERMS)
dfa.dump_uniq_perms("dfa");
/* since we are building a chfa, use the info about
* whether the chfa supports extended perms to help
* determine whether we clear the deny info.
* This will let us build the minimal dfa for the
* information supported by the backed
*/
if (!extended_perms ||
// TODO: we should drop DFA_MINIMIZE check here but doing
// so changes behavior. Do as a separate patch and fixup
// tests, etc.
((opts.control & CONTROL_DFA_FILTER_DENY) &&
(opts.control & CONTROL_DFA_MINIMIZE)))
dfa.apply_and_clear_deny();
if (opts.control & CONTROL_DFA_MINIMIZE) {
dfa.minimize(opts);
@@ -279,6 +260,22 @@ CHFA *aare_rules::create_chfa(int *min_match_len,
dfa.dump_uniq_perms("minimized dfa");
}
if (opts.control & CONTROL_DFA_FILTER_DENY &&
opts.control & CONTROL_DFA_MINIMIZE &&
dfa.apply_and_clear_deny()) {
/* Do a second minimization pass as removal of deny
* information has moved some states from accepting
* to none accepting partitions
*
* TODO: add this as a tail pass to minimization
* so we don't need to do a full second pass
*/
dfa.minimize(opts);
if (opts.dump & DUMP_DFA_MIN_UNIQ_PERMS)
dfa.dump_uniq_perms("minimized dfa");
}
if (opts.control & CONTROL_DFA_REMOVE_UNREACHABLE)
dfa.remove_unreachable(opts);
@@ -307,45 +304,10 @@ CHFA *aare_rules::create_chfa(int *min_match_len,
dfa.dump_diff_encode(cerr);
}
//cerr << "Checking extended perms " << extended_perms << "\n";
if (extended_perms) {
//cerr << "creating permstable\n";
dfa.compute_perms_table(perms_table, prompt);
}
chfa = new CHFA(dfa, eq, opts, extended_perms, prompt);
CHFA chfa(dfa, eq, opts);
if (opts.dump & DUMP_DFA_TRANS_TABLE)
chfa->dump(cerr);
}
catch(int error) {
return NULL;
}
return chfa;
}
/* create a dfa from the ruleset
* returns: buffer contain dfa tables, @size set to the size of the tables
* else NULL on failure, @min_match_len set to the shortest string
* that can match the dfa for determining xmatch priority.
*/
void *aare_rules::create_dfablob(size_t *size, int *min_match_len,
vector <aa_perms> &perms_table,
optflags const &opts, bool filedfa,
bool extended_perms, bool prompt)
{
char *buffer = NULL;
stringstream stream;
try {
CHFA *chfa = create_chfa(min_match_len, perms_table,
opts, filedfa, extended_perms,
prompt);
if (!chfa) {
*size = 0;
return NULL;
}
chfa->flex_table(stream, opts);
delete (chfa);
chfa.dump(cerr);
chfa.flex_table(stream, "");
}
catch(int error) {
*size = 0;
@@ -361,85 +323,5 @@ void *aare_rules::create_dfablob(size_t *size, int *min_match_len,
if (!buffer)
return NULL;
buf->sgetn(buffer, *size);
return buffer;
}
/* create a dfa from the ruleset
* returns: buffer contain dfa tables, @size set to the size of the tables
* else NULL on failure, @min_match_len set to the shortest string
* that can match the dfa for determining xmatch priority.
*/
void *aare_rules::create_welded_dfablob(aare_rules *file_rules,
size_t *size, int *min_match_len,
size_t *new_start,
vector <aa_perms> &perms_table,
optflags const &opts,
bool extended_perms, bool prompt)
{
int file_min_len;
vector <aa_perms> file_perms;
CHFA *file_chfa;
try {
file_chfa = file_rules->create_chfa(&file_min_len,
file_perms, opts,
true, extended_perms, prompt);
if (!file_chfa) {
*size = 0;
return NULL;
}
}
catch(int error) {
*size = 0;
return NULL;
}
CHFA *policy_chfa;
try {
policy_chfa = create_chfa(min_match_len,
perms_table, opts,
false, extended_perms, prompt);
if (!policy_chfa) {
delete file_chfa;
*size = 0;
return NULL;
}
}
catch(int error) {
delete file_chfa;
*size = 0;
return NULL;
}
stringstream stream;
try {
policy_chfa->weld_file_to_policy(*file_chfa, *new_start,
extended_perms, prompt,
perms_table, file_perms);
policy_chfa->flex_table(stream, opts);
}
catch(int error) {
delete (file_chfa);
delete (policy_chfa);
*size = 0;
return NULL;
}
delete file_chfa;
delete policy_chfa;
/* write blob to buffer */
stringbuf *buf = stream.rdbuf();
buf->pubseekpos(0);
*size = buf->in_avail();
if (file_min_len < *min_match_len)
*min_match_len = file_min_len;
char *buffer = (char *)malloc(*size);
if (!buffer)
return NULL;
buf->sgetn(buffer, *size);
return buffer;
}

View File

@@ -21,31 +21,22 @@
#ifndef __LIBAA_RE_RULES_H
#define __LIBAA_RE_RULES_H
#include <vector>
#include <stdint.h>
#include "../common_optarg.h"
#include "apparmor_re.h"
#include "chfa.h"
#include "expr-tree.h"
#include "../immunix.h"
#include "../perms.h"
#include "../rule.h"
class UniquePerm {
public:
int priority;
rule_mode_t mode;
bool deny;
bool exact_match;
uint32_t perms;
uint32_t audit;
bool operator<(UniquePerm const &rhs)const
{
if (priority < rhs.priority)
return priority < rhs.priority;
if (mode >= rhs.mode) {
if (deny == rhs.deny) {
if (exact_match == rhs.exact_match) {
if (perms == rhs.perms)
return audit < rhs.audit;
@@ -53,7 +44,7 @@ public:
}
return exact_match;
}
return true; // mode < rhs.mode
return deny;
}
};
@@ -74,21 +65,19 @@ public:
nodes.clear();
}
Node *insert(int priority, rule_mode_t mode, uint32_t perms,
uint32_t audit, bool exact_match)
Node *insert(bool deny, uint32_t perms, uint32_t audit,
bool exact_match)
{
UniquePerm tmp = { priority, mode, exact_match, perms, audit };
UniquePerm tmp = { deny, exact_match, perms, audit };
iterator res = nodes.find(tmp);
if (res == nodes.end()) {
Node *node;
if (mode == RULE_DENY)
node = new DenyMatchFlag(priority, perms, audit);
else if (mode == RULE_PROMPT)
node = new PromptMatchFlag(priority, perms, audit);
if (deny)
node = new DenyMatchFlag(perms, audit);
else if (exact_match)
node = new ExactMatchFlag(priority, perms, audit);
node = new ExactMatchFlag(perms, audit);
else
node = new MatchFlag(priority, perms, audit);
node = new MatchFlag(perms, audit);
pair<iterator, bool> val = nodes.insert(make_pair(tmp, node));
if (val.second == false)
return val.first->second;
@@ -112,26 +101,13 @@ class aare_rules {
aare_rules(int reverse): root(NULL), unique_perms(), expr_map(), reverse(reverse), rule_count(0) { };
~aare_rules();
bool add_rule(const char *rule, int priority, rule_mode_t mode,
perm32_t perms, perm32_t audit, optflags const &opts);
bool add_rule_vec(int priority, rule_mode_t mode, perm32_t perms,
perm32_t audit, int count, const char **rulev,
optflags const &opts, bool oob);
bool add_rule(const char *rule, int deny, uint32_t perms,
uint32_t audit, optflags const &opts);
bool add_rule_vec(int deny, uint32_t perms, uint32_t audit, int count,
const char **rulev, optflags const &opts, bool oob);
bool append_rule(const char *rule, bool oob, bool with_perm, optflags const &opts);
CHFA *create_chfa(int *min_match_len,
vector <aa_perms> &perms_table,
optflags const &opts, bool filedfa,
bool extended_perms, bool prompt);
void *create_dfablob(size_t *size, int *min_match_len,
vector <aa_perms> &perms_table,
optflags const &opts,
bool filedfa, bool extended_perms, bool prompt);
void *create_welded_dfablob(aare_rules *file_rules,
size_t *size, int *min_match_len,
size_t *new_start,
vector <aa_perms> &perms_table,
optflags const &opts,
bool extended_perms, bool prompt);
void *create_dfa(size_t *size, int *min_match_len, optflags const &opts,
bool filedfa);
};
#endif /* __LIBAA_RE_RULES_H */

View File

@@ -31,8 +31,6 @@
#define CONTROL_DFA_TRANS_HIGH (1 << 8)
#define CONTROL_DFA_DIFF_ENCODE (1 << 9)
#define CONTROL_RULE_MERGE (1 << 10)
#define CONTROL_DFA_STATE32 (1 << 11)
#define CONTROL_DFA_FLAGS_TABLE (1 << 12)
#define DUMP_DFA_DIFF_PROGRESS (1 << 0)
@@ -58,7 +56,5 @@
#define DUMP_DFA_RULE_EXPR (1 << 20)
#define DUMP_DFA_NODE_TO_DFA (1 << 21)
#define DUMP_RULE_MERGE (1 << 22)
#define DUMP_DFA_STATE32 (1 << 23)
#define DUMP_DFA_FLAGS_TABLE (1 << 24)
#endif /* APPARMOR_RE_H */

View File

@@ -32,7 +32,6 @@
#include "hfa.h"
#include "chfa.h"
#include "../immunix.h"
#include "../policydb.h"
#include "flex-tables.h"
void CHFA::init_free_list(vector<pair<size_t, size_t> > &free_list,
@@ -47,15 +46,11 @@ void CHFA::init_free_list(vector<pair<size_t, size_t> > &free_list,
free_list[free_list.size() - 1].second = 0;
}
/**
* new Construct the transition table.
*
* TODO: split dfaflags into separate control and dump so we can fold in
* permtable index flag
*/
CHFA::CHFA(DFA &dfa, map<transchar, transchar> &eq, optflags const &opts,
bool permindex, bool prompt): eq(eq)
CHFA::CHFA(DFA &dfa, map<transchar, transchar> &eq, optflags const &opts):
eq(eq)
{
if (opts.dump & DUMP_DFA_TRANS_PROGRESS)
fprintf(stderr, "Compressing HFA:\r");
@@ -106,29 +101,18 @@ CHFA::CHFA(DFA &dfa, map<transchar, transchar> &eq, optflags const &opts,
num.insert(make_pair(dfa.nonmatching, num.size()));
accept.resize(max(dfa.states.size(), (size_t) 2));
if (permindex) {
accept[0] = dfa.nonmatching->idx;
accept[1] = dfa.start->idx;
} else {
uint32_t accept3;
accept2.resize(max(dfa.states.size(), (size_t) 2));
dfa.nonmatching->map_perms_to_accept(accept[0],
accept2[0],
accept3,
prompt);
dfa.start->map_perms_to_accept(accept[1],
accept2[1],
accept3,
prompt);
}
accept2.resize(max(dfa.states.size(), (size_t) 2));
next_check.resize(max(optimal, (size_t) dfa.max_range));
free_list.resize(next_check.size());
accept[0] = 0;
accept2[0] = 0;
first_free = 1;
init_free_list(free_list, 0, 1);
start = dfa.start;
insert_state(free_list, dfa.start, dfa);
accept[1] = 0;
accept2[1] = 0;
num.insert(make_pair(dfa.start, num.size()));
int count = 2;
@@ -136,15 +120,9 @@ CHFA::CHFA(DFA &dfa, map<transchar, transchar> &eq, optflags const &opts,
if (!(opts.control & CONTROL_DFA_TRANS_HIGH)) {
for (Partition::iterator i = dfa.states.begin(); i != dfa.states.end(); i++) {
if (*i != dfa.nonmatching && *i != dfa.start) {
uint32_t accept3;
insert_state(free_list, *i, dfa);
if (permindex)
accept[num.size()] = (*i)->idx;
else
(*i)->map_perms_to_accept(accept[num.size()],
accept2[num.size()],
accept3,
prompt);
accept[num.size()] = (*i)->perms.allow;
accept2[num.size()] = PACK_AUDIT_CTL((*i)->perms.audit, (*i)->perms.quiet & (*i)->perms.deny);
num.insert(make_pair(*i, num.size()));
}
if (opts.dump & (DUMP_DFA_TRANS_PROGRESS)) {
@@ -159,15 +137,9 @@ CHFA::CHFA(DFA &dfa, map<transchar, transchar> &eq, optflags const &opts,
i != order.end(); i++) {
if (i->second != dfa.nonmatching &&
i->second != dfa.start) {
uint32_t accept3;
insert_state(free_list, i->second, dfa);
if (permindex)
accept[num.size()] = i->second->idx;
else
i->second->map_perms_to_accept(accept[num.size()],
accept2[num.size()],
accept3,
prompt);
accept[num.size()] = i->second->perms.allow;
accept2[num.size()] = PACK_AUDIT_CTL(i->second->perms.audit, i->second->perms.quiet & i->second->perms.deny);
num.insert(make_pair(i->second, num.size()));
}
if (opts.dump & (DUMP_DFA_TRANS_PROGRESS)) {
@@ -396,9 +368,7 @@ template<class Iter>
os << fill64(sizeof(td) + sizeof(*pos) * size);
}
template<class STATE_TYPE>
void flex_table_serialize(CHFA &chfa, ostream &os,
uint32_t max_size)
void CHFA::flex_table(ostream &os, const char *name)
{
const char th_version[] = "notflex";
struct table_set_header th = { 0, 0, 0, 0 };
@@ -407,15 +377,16 @@ void flex_table_serialize(CHFA &chfa, ostream &os,
* Change the following two data types to adjust the maximum flex
* table size.
*/
typedef uint16_t state_t;
typedef uint32_t trans_t;
if (chfa.default_base.size() >= (max_size)) {
cerr << "Too many states (" << chfa.default_base.size() << ") for "
if (default_base.size() >= (state_t) - 1) {
cerr << "Too many states (" << default_base.size() << ") for "
"type state_t\n";
exit(1);
}
if (chfa.next_check.size() >= (trans_t) - 1) {
cerr << "Too many transitions (" << chfa.next_check.size()
if (next_check.size() >= (trans_t) - 1) {
cerr << "Too many transitions (" << next_check.size()
<< ") for " "type trans_t\n";
exit(1);
}
@@ -425,60 +396,48 @@ void flex_table_serialize(CHFA &chfa, ostream &os,
* using the generic write_flex_table() routine.
*/
vector<uint8_t> equiv_vec;
if (chfa.eq.size()) {
if (eq.size()) {
equiv_vec.resize(256);
for (map<transchar, transchar>::iterator i = chfa.eq.begin(); i != chfa.eq.end(); i++) {
for (map<transchar, transchar>::iterator i = eq.begin(); i != eq.end(); i++) {
equiv_vec[i->first.c] = i->second.c;
}
}
vector<STATE_TYPE> default_vec;
vector<state_t> default_vec;
vector<trans_t> base_vec;
for (DefaultBase::iterator i = chfa.default_base.begin(); i != chfa.default_base.end(); i++) {
default_vec.push_back(chfa.num[i->first]);
for (DefaultBase::iterator i = default_base.begin(); i != default_base.end(); i++) {
default_vec.push_back(num[i->first]);
base_vec.push_back(i->second);
}
vector<STATE_TYPE> next_vec;
vector<STATE_TYPE> check_vec;
for (NextCheck::iterator i = chfa.next_check.begin(); i != chfa.next_check.end(); i++) {
next_vec.push_back(chfa.num[i->first]);
check_vec.push_back(chfa.num[i->second]);
vector<state_t> next_vec;
vector<state_t> check_vec;
for (NextCheck::iterator i = next_check.begin(); i != next_check.end(); i++) {
next_vec.push_back(num[i->first]);
check_vec.push_back(num[i->second]);
}
/* Write the actual flex parser table. */
/* TODO: add max_oob */
// sizeof(th_version) includes trailing \0
size_t hsize = pad64(sizeof(th) + sizeof(th_version));
size_t hsize = pad64(sizeof(th) + sizeof(th_version) + strlen(name) + 1);
th.th_magic = htonl(YYTH_REGEX_MAGIC);
th.th_flags = htons(chfa.chfaflags);
th.th_flags = htons(chfaflags);
th.th_hsize = htonl(hsize);
th.th_ssize = htonl(hsize +
flex_table_size(chfa.accept.begin(),
chfa.accept.end()) +
(chfa.accept2.size() ?
flex_table_size(chfa.accept2.begin(),
chfa.accept2.end()) : 0) +
(chfa.eq.size() ?
flex_table_size(equiv_vec.begin(),
equiv_vec.end()) : 0) +
flex_table_size(base_vec.begin(),
base_vec.end()) +
flex_table_size(default_vec.begin(),
default_vec.end()) +
flex_table_size(accept.begin(), accept.end()) +
flex_table_size(accept2.begin(), accept2.end()) +
(eq.size() ? flex_table_size(equiv_vec.begin(), equiv_vec.end()) : 0) +
flex_table_size(base_vec.begin(), base_vec.end()) +
flex_table_size(default_vec.begin(), default_vec.end()) +
flex_table_size(next_vec.begin(), next_vec.end()) +
flex_table_size(check_vec.begin(),
check_vec.end()));
flex_table_size(check_vec.begin(), check_vec.end()));
os.write((char *)&th, sizeof(th));
os.write(th_version, sizeof(th_version));
os << fill64(sizeof(th) + sizeof(th_version));
os << th_version << (char)0 << name << (char)0;
os << fill64(sizeof(th) + sizeof(th_version) + strlen(name) + 1);
write_flex_table(os, YYTD_ID_ACCEPT, chfa.accept.begin(),
chfa.accept.end());
if (chfa.accept2.size())
write_flex_table(os, YYTD_ID_ACCEPT2, chfa.accept2.begin(),
chfa.accept2.end());
if (chfa.eq.size())
write_flex_table(os, YYTD_ID_ACCEPT, accept.begin(), accept.end());
write_flex_table(os, YYTD_ID_ACCEPT2, accept2.begin(), accept2.end());
if (eq.size())
write_flex_table(os, YYTD_ID_EC, equiv_vec.begin(),
equiv_vec.end());
write_flex_table(os, YYTD_ID_BASE, base_vec.begin(), base_vec.end());
@@ -486,139 +445,3 @@ void flex_table_serialize(CHFA &chfa, ostream &os,
write_flex_table(os, YYTD_ID_NXT, next_vec.begin(), next_vec.end());
write_flex_table(os, YYTD_ID_CHK, check_vec.begin(), check_vec.end());
}
void CHFA::flex_table(ostream &os, optflags const &opts) {
if (opts.control & CONTROL_DFA_STATE32 &&
default_base.size() > (1 << 16) - 1) {
// TODO: implement support for flags in separate table
// if (opts.control & CONTROL_DFA_FLAGS_TABLE) {
// if (opts.dump & DUMP_FLAGS_TABLE)
// cerr << "using flags table\n";
// flex_table_serialize(os, uint32_t, (1 << 32) - 1);
// } else { /* only 24 bits available */
if (opts.dump & DUMP_DFA_STATE32)
cerr << "using 32 bit state tables, embedded flags\n";
flex_table_serialize<uint32_t>(*this, os, (1 << 24) - 1);
} else {
if (opts.control & CONTROL_DFA_FLAGS_TABLE) {
cerr << "Flags table specified when using 16 bit state\n";
exit(1);
}
if (opts.dump & DUMP_DFA_STATE32)
cerr << "using 16 bit state tables, embedded flags\n";
flex_table_serialize<uint16_t>(*this, os, (1 << 16) - 1);
}
}
/*
* @file_chfa: chfa to add on to the policy chfa
* @new_start: new start state for where the @file_dfa is in the new chfa
*
* Make a new chfa that is a combination of policy and file chfas. It
* assumes policy is built with AA_CLASS_FILE support transition. The
* resultant chfa will have file states and indexes offset except for
* start and null states.
*
* NOTE:
* - modifies chfa
* requires:
* - no ec
* - policy chfa has transitions state[start].next[AA_CLASS_FILE]
* - policy perms table is build if using permstable
*/
void CHFA::weld_file_to_policy(CHFA &file_chfa, size_t &new_start,
bool accept_idx, bool prompt,
vector <aa_perms> &policy_perms,
vector <aa_perms> &file_perms)
{
// doesn't support remapping eq classes yet
if (eq.size() > 0 || file_chfa.eq.size() > 0)
throw 1;
size_t old_base_size = default_base.size();
size_t old_next_size = next_check.size();
const State *nonmatching = default_base[0].first;
//const State *start = default_base[1].first;
const State *file_nonmatching = file_chfa.default_base[0].first;
// renumber states from file_dfa by appending to policy dfa
num.insert(make_pair(file_nonmatching, 0)); // remap to policy nonmatching
for (map<const State *, size_t>::iterator i = file_chfa.num.begin(); i != file_chfa.num.end() ; i++) {
if (i->first == file_nonmatching)
continue;
num.insert(make_pair(i->first, i->second + old_base_size));
}
// handle default and base table expansion, and setup renumbering
// while we remap file_nonmatch within the table, we still keep its
// slot.
bool first = true;
for (DefaultBase::iterator i = file_chfa.default_base.begin(); i != file_chfa.default_base.end(); i++) {
const State *def;
size_t base;
if (first) {
first = false;
// remap file_nonmatch to nonmatch
def = nonmatching;
base = 0;
} else {
def = i->first;
base = i->second + old_next_size;
}
default_base.push_back(make_pair(def, base));
}
// mapping for these are handled by num[]
for (NextCheck::iterator i = file_chfa.next_check.begin(); i != file_chfa.next_check.end(); i++) {
next_check.push_back(*i);
}
// append file perms to policy perms, and rework permsidx if needed
if (accept_idx) {
// policy idx double
// file + doubled offset
// Requires: policy perms table, so we can double and
// update indexes
// * file perm idx to start on even idx
// * policy perms table size to double and entries
// to repeat
assert(accept.size() == old_base_size);
accept.resize(accept.size() + file_chfa.accept.size());
size_t size = policy_perms.size();
policy_perms.resize(size*2 + file_perms.size());
// shift and double the policy perms
for (size_t i = size - 1; size >= 0; i--) {
policy_perms[i*2] = policy_perms[i];
policy_perms[i*2 + 1] = policy_perms[i];
}
// update policy accept idx for the new shifted perms table
for (size_t i = 0; i < old_base_size; i++) {
accept[i] = accept[i]*2;
}
// copy over file perms
for (size_t i = 0; i < file_perms.size(); i++) {
policy_perms[size*2 + i] = file_perms[i];
}
// shift file accept indexs
for (size_t i = 0; i < file_chfa.accept.size(); i++) {
accept[old_base_size + i] = file_chfa.accept[i] + size*2;
}
} else {
// perms are stored in accept just append the perms
size_t size = accept.size();
accept.resize(size + file_chfa.accept.size());
accept2.resize(size + file_chfa.accept.size());
for (size_t i = 0; i < file_chfa.accept.size(); i++) {
accept[size + i] = file_chfa.accept[i];
accept2[size + i] = file_chfa.accept2[i];
}
}
// Rework transition state[start].next[AA_CLASS_FILE]
next_check[default_base[1].second + AA_CLASS_FILE].first = file_chfa.start;
new_start = num[file_chfa.start];
}

View File

@@ -16,7 +16,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
* Create a compressed hfa (chfa) from an hfa
* Create a compressed hfa (chfa) from and hfa
*/
#ifndef __LIBAA_RE_CHFA_H
#define __LIBAA_RE_CHFA_H
@@ -25,7 +25,6 @@
#include <vector>
#include "hfa.h"
#include "../perms.h"
#define BASE32_FLAGS 0xff000000
#define DiffEncodeBit32 0x80000000
@@ -34,41 +33,30 @@
using namespace std;
typedef vector<pair<const State *, size_t> > DefaultBase;
typedef vector<pair<const State *, const State *> > NextCheck;
class CHFA {
typedef vector<pair<const State *, size_t> > DefaultBase;
typedef vector<pair<const State *, const State *> > NextCheck;
public:
CHFA(void);
CHFA(DFA &dfa, map<transchar, transchar> &eq, optflags const &opts,
bool permindex, bool prompt);
CHFA(DFA &dfa, map<transchar, transchar> &eq, optflags const &opts);
void dump(ostream & os);
void flex_table(ostream &os, optflags const &opts);
void flex_table(ostream &os, const char *name);
void init_free_list(vector<pair<size_t, size_t> > &free_list,
size_t prev, size_t start);
bool fits_in(vector<pair<size_t, size_t> > &free_list, size_t base,
StateTrans &cases);
void insert_state(vector<pair<size_t, size_t> > &free_list,
State *state, DFA &dfa);
void weld_file_to_policy(CHFA &file_chfa, size_t &new_start,
bool accept_idx, bool prompt,
vector <aa_perms> &policy_perms,
vector <aa_perms> &file_perms);
// private:
// sigh templates suck, friend declaration does not work so for now
// make these public
private:
vector<uint32_t> accept;
vector<uint32_t> accept2;
DefaultBase default_base;
NextCheck next_check;
const State *start;
map<const State *, size_t> num;
map<transchar, transchar> eq;
unsigned int chfaflags;
private:
transchar max_eq;
ssize_t first_free;
unsigned int chfaflags;
};
#endif /* __LIBAA_RE_CHFA_H */

View File

@@ -189,19 +189,6 @@ void Node::dump_syntax_tree(ostream &os)
* a b c T
*
*/
static Node *simplify_eps_pair(Node *t)
{
if (t->is_type(NODE_TYPE_TWOCHILD) &&
t->child[0] == &epsnode &&
t->child[1] == &epsnode) {
t->release();
return &epsnode;
}
return t;
}
static void rotate_node(Node *t, int dir)
{
// (a | b) | c -> a | (b | c)
@@ -210,9 +197,7 @@ static void rotate_node(Node *t, int dir)
t->child[dir] = left->child[dir];
left->child[dir] = left->child[!dir];
left->child[!dir] = t->child[!dir];
// check that rotation didn't create (E | E)
t->child[!dir] = simplify_eps_pair(left);
t->child[!dir] = left;
}
/* return False if no work done */
@@ -224,7 +209,13 @@ int TwoChildNode::normalize_eps(int dir)
// Ea -> aE
// Test for E | (E | E) and E . (E . E) which will
// result in an infinite loop
Node *c = simplify_eps_pair(child[!dir]);
Node *c = child[!dir];
if (c->is_type(NODE_TYPE_TWOCHILD) &&
&epsnode == c->child[dir] &&
&epsnode == c->child[!dir]) {
c->release();
c = &epsnode;
}
child[!dir] = child[dir];
child[dir] = c;
return 1;

View File

@@ -41,7 +41,6 @@
#include <stdint.h>
#include "../perms.h"
#include "apparmor_re.h"
using namespace std;
@@ -886,20 +885,19 @@ public:
class MatchFlag: public AcceptNode {
public:
MatchFlag(int priority, perm32_t perms, perm32_t audit): priority(priority), perms(perms), audit(audit)
MatchFlag(uint32_t flag, uint32_t audit): flag(flag), audit(audit)
{
type_flags |= NODE_TYPE_MATCHFLAG;
}
ostream &dump(ostream &os) { return os << "< 0x" << hex << perms << '>'; }
ostream &dump(ostream &os) { return os << "< 0x" << hex << flag << '>'; }
int priority;
perm32_t perms;
perm32_t audit;
uint32_t flag;
uint32_t audit;
};
class ExactMatchFlag: public MatchFlag {
public:
ExactMatchFlag(int priority, perm32_t perms, perm32_t audit): MatchFlag(priority, perms, audit)
ExactMatchFlag(uint32_t flag, uint32_t audit): MatchFlag(flag, audit)
{
type_flags |= NODE_TYPE_EXACTMATCHFLAG;
}
@@ -907,18 +905,12 @@ public:
class DenyMatchFlag: public MatchFlag {
public:
DenyMatchFlag(int priority, perm32_t perms, perm32_t quiet): MatchFlag(priority, perms, quiet)
DenyMatchFlag(uint32_t flag, uint32_t quiet): MatchFlag(flag, quiet)
{
type_flags |= NODE_TYPE_DENYMATCHFLAG;
}
};
class PromptMatchFlag: public MatchFlag {
public:
PromptMatchFlag(int priority, perm32_t prompt, perm32_t audit): MatchFlag(priority, prompt, audit) {}
};
/* Traverse the syntax tree depth-first in an iterator-like manner. */
class depth_first_traversal {
stack<Node *>pos;

View File

@@ -31,12 +31,11 @@
#include <iostream>
#include <fstream>
#include <string.h>
#include <stdint.h>
#include "expr-tree.h"
#include "hfa.h"
#include "policy_compat.h"
#include "../immunix.h"
#include "../perms.h"
ostream &operator<<(ostream &os, const CacheStats &cache)
{
@@ -493,11 +492,6 @@ DFA::DFA(Node *root, optflags const &opts, bool buildfiledfa): root(root), filed
*/
nnodes_cache.clear();
node_map.clear();
/* once created the priority information is no longer needed and
* can prevent sets with the same perms and different priorities
* from being merged during minimization
*/
clear_priorities();
}
DFA::~DFA()
@@ -543,7 +537,6 @@ void DFA::dump_uniq_perms(const char *s)
<< i->deny << " audit:" << i->audit
<< " quiet:" << i->quiet << dec << "\n";
}
//TODO: add prompt
}
/* Remove dead or unreachable states */
@@ -651,34 +644,36 @@ int DFA::apply_and_clear_deny(void)
return c;
}
void DFA::clear_priorities(void)
{
for (Partition::iterator i = states.begin(); i != states.end(); i++)
(*i)->perms.priority = 0;
}
/* minimize the number of dfa states */
void DFA::minimize(optflags const &opts)
{
map<perms_t, Partition *> perm_map;
map<pair<uint64_t, size_t>, Partition *> perm_map;
list<Partition *> partitions;
/* Set up the initial partitions
* minimum of - 1 non accepting, and 1 accepting
* if trans hashing is used the accepting and non-accepting partitions
* can be further split based on the number and type of transitions
* a state makes.
* If permission hashing is enabled the accepting partitions can
* be further divided by permissions. This can result in not
* obtaining a truly minimized dfa but comes close, and can speedup
* minimization.
*/
int accept_count = 0;
int final_accept = 0;
for (Partition::iterator i = states.begin(); i != states.end(); i++) {
map<perms_t, Partition *>::iterator p = perm_map.find((*i)->perms);
size_t hash = 0;
uint64_t permtype = ((uint64_t) (PACK_AUDIT_CTL((*i)->perms.audit, (*i)->perms.quiet & (*i)->perms.deny)) << 32) | (uint64_t) (*i)->perms.allow;
pair<uint64_t, size_t> group = make_pair(permtype, hash);
map<pair<uint64_t, size_t>, Partition *>::iterator p = perm_map.find(group);
if (p == perm_map.end()) {
Partition *part = new Partition();
part->push_back(*i);
perm_map.insert(make_pair((*i)->perms, part));
perm_map.insert(make_pair(group, part));
partitions.push_back(part);
(*i)->partition = part;
if ((*i)->perms.is_accept())
if (permtype)
accept_count++;
} else {
(*i)->partition = p->second;
@@ -1080,10 +1075,8 @@ void DFA::dump(ostream & os)
for (Partition::iterator i = states.begin(); i != states.end(); i++) {
if (*i == start || (*i)->perms.is_accept()) {
os << **i;
if (*i == start) {
os << " <== ";
(*i)->perms.dump_header(os);
}
if (*i == start)
os << " <== (allow/deny/audit/quiet)";
if ((*i)->perms.is_accept())
(*i)->perms.dump(os);
os << "\n";
@@ -1307,46 +1300,6 @@ void DFA::apply_equivalence_classes(map<transchar, transchar> &eq)
}
}
void DFA::compute_perms_table_ent(State *state, size_t pos,
vector <aa_perms> &perms_table,
bool prompt)
{
uint32_t accept1, accept2, accept3;
// until front end doesn't map the way it does
state->map_perms_to_accept(accept1, accept2, accept3, prompt);
if (filedfa) {
state->idx = pos * 2;
perms_table[pos*2] = compute_fperms_user(accept1, accept2, accept3);
perms_table[pos*2 + 1] = compute_fperms_other(accept1, accept2, accept3);
} else {
state->idx = pos;
perms_table[pos] = compute_perms_entry(accept1, accept2, accept3);
}
}
void DFA::compute_perms_table(vector <aa_perms> &perms_table, bool prompt)
{
size_t mult = filedfa ? 2 : 1;
size_t pos = 2;
assert(states.size() >= 2);
perms_table.resize(states.size() * mult);
// nonmatching and start need to be 0 and 1 so handle outside of loop
if (filedfa)
compute_perms_table_ent(nonmatching, 0, perms_table, prompt);
compute_perms_table_ent(start, 1, perms_table, prompt);
for (Partition::iterator i = states.begin(); i != states.end(); i++) {
if (*i == nonmatching || *i == start)
continue;
compute_perms_table_ent(*i, pos, perms_table, prompt);
pos++;
}
}
#if 0
typedef set <ImportantNode *>AcceptNodes;
map<ImportantNode *, AcceptNodes> dominance(DFA & dfa)
@@ -1376,7 +1329,7 @@ map<ImportantNode *, AcceptNodes> dominance(DFA & dfa)
}
#endif
static inline int diff_qualifiers(perm32_t perm1, perm32_t perm2)
static inline int diff_qualifiers(uint32_t perm1, uint32_t perm2)
{
return ((perm1 & AA_EXEC_TYPE) && (perm2 & AA_EXEC_TYPE) &&
(perm1 & AA_EXEC_TYPE) != (perm2 & AA_EXEC_TYPE));
@@ -1390,7 +1343,8 @@ static inline int diff_qualifiers(perm32_t perm1, perm32_t perm2)
int accept_perms(NodeVec *state, perms_t &perms, bool filedfa)
{
int error = 0;
perms_t exact;
uint32_t exact_match_allow = 0;
uint32_t exact_audit = 0;
perms.clear();
@@ -1402,51 +1356,38 @@ int accept_perms(NodeVec *state, perms_t &perms, bool filedfa)
continue;
MatchFlag *match = static_cast<MatchFlag *>(*i);
if (perms.priority > match->priority)
continue;
if (perms.priority < match->priority) {
perms.clear(match->priority);
exact.clear(match->priority);
}
if (match->is_type(NODE_TYPE_EXACTMATCHFLAG)) {
/* exact match only ever happens with x */
if (filedfa &&
!is_merged_x_consistent(exact.allow, match->perms))
error = 1;
exact.allow |= match->perms;
exact.audit |= match->audit;
if (filedfa && !is_merged_x_consistent(exact_match_allow,
match->flag))
error = 1;;
exact_match_allow |= match->flag;
exact_audit |= match->audit;
} else if (match->is_type(NODE_TYPE_DENYMATCHFLAG)) {
perms.deny |= match->perms;
perms.deny |= match->flag;
perms.quiet |= match->audit;
} else if (dynamic_cast<PromptMatchFlag *>(match)) {
perms.prompt |= match->perms;
perms.audit |= match->audit;
} else {
if (filedfa &&
!is_merged_x_consistent(perms.allow, match->perms))
if (filedfa && !is_merged_x_consistent(perms.allow, match->flag))
error = 1;
perms.allow |= match->perms;
perms.allow |= match->flag;
perms.audit |= match->audit;
}
}
if (filedfa) {
perms.allow |= exact.allow & ~(ALL_AA_EXEC_TYPE);
perms.prompt |= exact.prompt & ~(ALL_AA_EXEC_TYPE);
perms.audit |= exact.audit & ~(ALL_AA_EXEC_TYPE);
perms.allow |= exact_match_allow & ~(ALL_AA_EXEC_TYPE);
perms.audit |= exact_audit & ~(ALL_AA_EXEC_TYPE);
} else {
perms.allow |= exact.allow;
perms.prompt |= exact.prompt;
perms.audit |= exact.audit;
perms.allow |= exact_match_allow;
perms.audit |= exact_audit;
}
if (exact.allow & AA_USER_EXEC) {
perms.allow = (exact.allow & AA_USER_EXEC_TYPE) |
if (exact_match_allow & AA_USER_EXEC) {
perms.allow = (exact_match_allow & AA_USER_EXEC_TYPE) |
(perms.allow & ~AA_USER_EXEC_TYPE);
perms.exact = AA_USER_EXEC_TYPE;
}
if (exact.allow & AA_OTHER_EXEC) {
perms.allow = (exact.allow & AA_OTHER_EXEC_TYPE) |
if (exact_match_allow & AA_OTHER_EXEC) {
perms.allow = (exact_match_allow & AA_OTHER_EXEC_TYPE) |
(perms.allow & ~AA_OTHER_EXEC_TYPE);
perms.exact |= AA_OTHER_EXEC_TYPE;
}
@@ -1458,8 +1399,7 @@ int accept_perms(NodeVec *state, perms_t &perms, bool filedfa)
perms.allow &= ~perms.deny;
perms.quiet &= perms.deny;
perms.prompt &= ~perms.deny;
perms.prompt &= ~perms.allow;
if (error)
fprintf(stderr, "profile has merged rule with conflicting x modifiers\n");

View File

@@ -27,16 +27,11 @@
#include <list>
#include <map>
#include <vector>
#include <iostream>
#include <assert.h>
#include <limits.h>
#include <stdint.h>
#include "expr-tree.h"
#include "policy_compat.h"
#include "../rule.h"
extern int prompt_compat_mode;
#define DiffEncodeFlag 1
@@ -52,37 +47,20 @@ ostream &operator<<(ostream &os, State &state);
class perms_t {
public:
perms_t(void): priority(INT_MIN), allow(0), deny(0), prompt(0), audit(0), quiet(0), exact(0) { };
perms_t(void): allow(0), deny(0), audit(0), quiet(0), exact(0) { };
bool is_accept(void) { return (allow | deny | prompt | audit | quiet); }
bool is_accept(void) { return (allow | audit | quiet); }
void dump_header(ostream &os)
{
os << "priority (allow/deny/prompt/audit/quiet)";
}
void dump(ostream &os)
{
os << " " << priority << " (0x " << hex
<< allow << "/" << deny << "/" << "/" << prompt << "/" << audit << "/" << quiet
os << " (0x " << hex
<< allow << "/" << deny << "/" << audit << "/" << quiet
<< ')' << dec;
}
void clear(void) {
priority = INT_MIN;
allow = deny = prompt = audit = quiet = exact = 0;
}
void clear(int p) {
priority = p;
allow = deny = prompt = audit = quiet = exact = 0;
}
void clear(void) { allow = deny = audit = quiet = 0; }
void add(perms_t &rhs, bool filedfa)
{
if (priority > rhs.priority)
return;
if (priority < rhs.priority) {
*this = rhs;
return;
} //else if (rhs.priority == priority) {
deny |= rhs.deny;
if (filedfa && !is_merged_x_consistent(allow & ALL_USER_EXEC,
@@ -117,7 +95,6 @@ public:
allow = (allow | (rhs.allow & ~ALL_AA_EXEC_TYPE));
else
allow |= rhs.allow;
prompt |= rhs.prompt;
audit |= rhs.audit;
quiet = (quiet | rhs.quiet);
@@ -135,7 +112,6 @@ public:
{
if (deny) {
allow &= ~deny;
prompt &= ~deny;
quiet &= deny;
deny = 0;
return !is_accept();
@@ -145,21 +121,16 @@ public:
bool operator<(perms_t const &rhs)const
{
if (priority < rhs.priority)
return priority < rhs.priority;
if (allow < rhs.allow)
return allow < rhs.allow;
if (deny < rhs.deny)
return deny < rhs.deny;
if (prompt < rhs.prompt)
return prompt < rhs.prompt;
if (audit < rhs.audit)
return audit < rhs.audit;
return quiet < rhs.quiet;
}
int priority;
perm32_t allow, deny, prompt, audit, quiet, exact;
uint32_t allow, deny, audit, quiet, exact;
};
int accept_perms(NodeVec *state, perms_t &perms, bool filedfa);
@@ -227,7 +198,7 @@ struct DiffDag {
class State {
public:
State(int l, ProtoState &n, State *other, bool filedfa):
label(l), flags(0), idx(0), perms(), trans()
label(l), flags(0), perms(), trans()
{
int error;
@@ -277,20 +248,9 @@ public:
void flatten_relative(State *, int upper_bound);
int apply_and_clear_deny(void) { return perms.apply_and_clear_deny(); }
void map_perms_to_accept(perm32_t &accept1, perm32_t &accept2,
perm32_t &accept3, bool prompt)
{
accept1 = perms.allow;
if (prompt && prompt_compat_mode == PROMPT_COMPAT_DEV)
accept2 = PACK_AUDIT_CTL(perms.prompt, perms.quiet & perms.deny);
else
accept2 = PACK_AUDIT_CTL(perms.audit, perms.quiet & perms.deny);
accept3 = perms.prompt;
}
int label;
int flags;
int idx;
perms_t perms;
StateTrans trans;
State *otherwise;
@@ -338,6 +298,7 @@ public:
}
};
/* Transitions in the DFA. */
class DFA {
void dump_node_to_dfa(void);
@@ -368,7 +329,6 @@ public:
bool same_mappings(State *s1, State *s2);
void minimize(optflags const &flags);
int apply_and_clear_deny(void);
void clear_priorities(void);
void diff_encode(optflags const &flags);
void undiff_encode(void);
@@ -381,12 +341,6 @@ public:
map<transchar, transchar> equivalence_classes(optflags const &flags);
void apply_equivalence_classes(map<transchar, transchar> &eq);
void compute_perms_table_ent(State *state, size_t pos,
vector <aa_perms> &perms_table,
bool prompt);
void compute_perms_table(vector <aa_perms> &perms_table,
bool prompt);
unsigned int diffcount;
int oob_range;
int max_range;

View File

@@ -1,218 +0,0 @@
/*
* Copyright (c) 2022
* Canonical, Ltd. (All rights reserved)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
* License published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, contact Novell, Inc. or Canonical
* Ltd.
*/
/*
* This is a set of functions to provide convertion from old style permission
* mappings, to new style kernel mappings. It is based on the kernel to
* as the kernel needs this for backwards compatibility. This allows the
* userspace to convert to the new permission mapping without reworking
* the internal dfa permission tracking.
*
* In the future this code will be converted to go the reverse direction
* i.e. new mappings into old, which the parser will need for backwards
* compat with old kernels.
*/
#include <stdint.h>
#include <iostream>
#include "policy_compat.h"
#include "../perms.h"
#include "../rule.h"
extern int prompt_compat_mode;
/* remap old accept table embedded permissions to separate permission table */
static uint32_t dfa_map_xindex(uint16_t mask)
{
uint16_t old_index = (mask >> 10) & 0xf;
uint32_t index = 0;
if (mask & 0x100)
index |= AA_X_UNSAFE;
if (mask & 0x200)
index |= AA_X_INHERIT;
if (mask & 0x80)
index |= AA_X_UNCONFINED;
if (old_index == 1) {
index |= AA_X_UNCONFINED;
} else if (old_index == 2) {
index |= AA_X_NAME;
} else if (old_index == 3) {
index |= AA_X_NAME | AA_X_CHILD;
} else if (old_index) {
index |= AA_X_TABLE;
index |= old_index - 4;
}
return index;
}
/*
* map old dfa inline permissions to new format
*/
#define dfa_user_allow(accept1) (((accept1) & 0x7f) | \
((accept1) & 0x80000000))
#define dfa_user_xbits(accept1) (((accept1) >> 7) & 0x7f)
#define dfa_user_audit(accept1, accept2) ((accept2) & 0x7f)
#define dfa_user_quiet(accept1, accept2) (((accept2) >> 7) & 0x7f)
#define dfa_user_xindex(accept1) \
(dfa_map_xindex(accept1 & 0x3fff))
#define dfa_other_allow(accept1) ((((accept1) >> 14) & \
0x7f) | \
((accept1) & 0x80000000))
#define dfa_other_xbits(accept1) \
((((accept1) >> 7) >> 14) & 0x7f)
#define dfa_other_audit(accept1, accept2) (((accept2) >> 14) & 0x7f)
#define dfa_other_quiet(accept1, accept2) \
((((accept2) >> 7) >> 14) & 0x7f)
#define dfa_other_xindex(accept1) \
dfa_map_xindex((accept1 >> 14) & 0x3fff)
/**
* map_old_perms - map old file perms layout to the new layout
* @old: permission set in old mapping
*
* Returns: new permission mapping
*/
static uint32_t map_old_perms(uint32_t old)
{
uint32_t perm = old & 0xf;
if (old & AA_MAY_READ)
perm |= AA_MAY_GETATTR | AA_MAY_OPEN;
if (old & AA_MAY_WRITE)
perm |= AA_MAY_SETATTR | AA_MAY_CREATE | AA_MAY_DELETE |
AA_MAY_CHMOD | AA_MAY_CHOWN | AA_MAY_OPEN;
if (old & 0x10)
perm |= AA_MAY_LINK;
/* the old mapping lock and link_subset flags where overlaid
* and use was determined by part of a pair that they were in
*/
if (old & 0x20)
perm |= AA_MAY_LOCK | AA_LINK_SUBSET;
if (old & 0x40) /* AA_EXEC_MMAP */
perm |= AA_EXEC_MMAP;
return perm;
}
static void compute_fperms_allow(struct aa_perms *perms, uint32_t accept1)
{
perms->allow |= AA_MAY_GETATTR;
/* change_profile wasn't determined by ownership in old mapping */
if (accept1 & 0x80000000)
perms->allow |= AA_MAY_CHANGE_PROFILE;
if (accept1 & 0x40000000)
perms->allow |= AA_MAY_ONEXEC;
}
struct aa_perms compute_fperms_user(uint32_t accept1, uint32_t accept2,
uint32_t accept3)
{
struct aa_perms perms = { };
perms.allow = map_old_perms(dfa_user_allow(accept1));
perms.prompt = map_old_perms(dfa_user_allow(accept3));
perms.audit = map_old_perms(dfa_user_audit(accept1, accept2));
perms.quiet = map_old_perms(dfa_user_quiet(accept1, accept2));
if (prompt_compat_mode != PROMPT_COMPAT_PERMSV1)
perms.xindex = dfa_user_xindex(accept1);
compute_fperms_allow(&perms, accept1);
perms.prompt &= ~(perms.allow | perms.deny);
return perms;
}
struct aa_perms compute_fperms_other(uint32_t accept1, uint32_t accept2,
uint32_t accept3)
{
struct aa_perms perms = { };
perms.allow = map_old_perms(dfa_other_allow(accept1));
perms.prompt = map_old_perms(dfa_other_allow(accept3));
perms.audit = map_old_perms(dfa_other_audit(accept1, accept2));
perms.quiet = map_old_perms(dfa_other_quiet(accept1, accept2));
if (prompt_compat_mode != PROMPT_COMPAT_PERMSV1)
perms.xindex = dfa_other_xindex(accept1);
compute_fperms_allow(&perms, accept1);
perms.prompt &= ~(perms.allow | perms.deny);
return perms;
}
static uint32_t map_other(uint32_t x)
{
return ((x & 0x3) << 8) | /* SETATTR/GETATTR */
((x & 0x1c) << 18) | /* ACCEPT/BIND/LISTEN */
((x & 0x60) << 19); /* SETOPT/GETOPT */
}
static uint32_t map_xbits(uint32_t x)
{
return ((x & 0x1) << 7) |
((x & 0x7e) << 9);
}
struct aa_perms compute_perms_entry(uint32_t accept1, uint32_t accept2,
uint32_t accept3)
// don't need to worry about version internally within the parser
// uint32_t version)
{
struct aa_perms perms = { };
perms.allow = dfa_user_allow(accept1);
perms.prompt = dfa_user_allow(accept3);
perms.audit = dfa_user_audit(accept1, accept2);
perms.quiet = dfa_user_quiet(accept1, accept2);
/*
* This mapping is convulated due to history.
* v1-v4: only file perms, which are handled by compute_fperms
* v5: added policydb which dropped user conditional to gain new
* perm bits, but had to map around the xbits because the
* userspace compiler was still munging them.
* v9: adds using the xbits in policydb because the compiler now
* supports treating policydb permission bits different.
* Unfortunately there is no way to force auditing on the
* perms represented by the xbits
*/
perms.allow |= map_other(dfa_other_allow(accept1));
// v9 encoding never rolled out. AA_MAY_LOCK needed to fix
// non fs unix locking see kernel commit
// 1cf26c3d2c4c apparmor: fix apparmor mediating locking non-fs unix sockets
//if (VERSION_LE(version, v8))
perms.allow |= AA_MAY_LOCK;
//else
// perms.allow |= map_xbits(dfa_user_xbits(dfa, state));
/*
* for v5-v9 perm mapping in the policydb, the other set is used
* to extend the general perm set
*/
perms.prompt |= map_other(dfa_other_allow(accept3));
perms.audit |= map_other(dfa_other_audit(accept1, accept2));
perms.quiet |= map_other(dfa_other_quiet(accept1, accept2));
//if (VERSION_GT(version, v8))
// perms.quiet |= map_xbits(dfa_other_xbits(dfa, state));
return perms;
}

View File

@@ -1,25 +0,0 @@
/*
* Copyright (c) 2022
* Canonical, Ltd. (All rights reserved)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
* License published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, contact Novell, Inc. or Canonical
* Ltd.
*/
#ifndef __AA_POLICY_COMPAT_H
#define __AA_POLICY_COMPAT_H
struct aa_perms compute_fperms_user(uint32_t accept1, uint32_t accept2, uint32_t accept3);
struct aa_perms compute_fperms_other(uint32_t accept1, uint32_t accept2, uint32_t accept3);
struct aa_perms compute_perms_entry(uint32_t accept1, uint32_t accept2, uint32_t accept3);
#endif /* __AA_POLICY_COMPAT_H */

Some files were not shown because too many files have changed in this diff Show More