mirror of
https://gitlab.com/apparmor/apparmor
synced 2025-09-05 08:45:22 +00:00
Compare commits
67 Commits
v3.0.12
...
apparmor-3
Author | SHA1 | Date | |
---|---|---|---|
|
21093ca008 | ||
|
3877683071 | ||
|
2bb1abe9c4 | ||
|
e583abc6cc | ||
|
ae60878492 | ||
|
dc5e9352f1 | ||
|
e7d5937213 | ||
|
bdb650bf98 | ||
|
a5523e3c87 | ||
|
602e4f6b9f | ||
|
aa986e3de3 | ||
|
06ead7294a | ||
|
cb9a152e2a | ||
|
6ebf1cb0ef | ||
|
e8b45df48a | ||
|
19b3052b42 | ||
|
6e06d2216b | ||
|
1b0f51d0ce | ||
|
feb4e75e47 | ||
|
f686f7c0ff | ||
|
eeca73e675 | ||
|
fa7eb62c62 | ||
|
11f1928938 | ||
|
deca4adfed | ||
|
c712509d41 | ||
|
0e4540ac91 | ||
|
54c6343b0f | ||
|
ac7c791ca3 | ||
|
09402d2123 | ||
|
dcb3493d19 | ||
|
8d6174e300 | ||
|
d18bc59f49 | ||
|
b69add4f29 | ||
|
7e0465593a | ||
|
e5758891e6 | ||
|
ff6489bfdf | ||
|
0b5a270045 | ||
|
70ade00801 | ||
|
7fc875af09 | ||
|
7e45341ccd | ||
|
d8bb0435c2 | ||
|
4eac7dd99c | ||
|
6ac6524beb | ||
|
3955b5a499 | ||
|
b7d0b5e0e4 | ||
|
1fb230f11f | ||
|
68930e61d8 | ||
|
0a26ce3acd | ||
|
abcf4a8756 | ||
|
caccb88a9b | ||
|
ff455062a1 | ||
|
0be90d66be | ||
|
dc614a04cb | ||
|
c509d9e3cc | ||
|
afe0226e67 | ||
|
1ada934819 | ||
|
4d3831d1d5 | ||
|
b8094eb9fa | ||
|
fa60f195a6 | ||
|
a769ed11de | ||
|
f87fb39108 | ||
|
c4f58178ec | ||
|
570e26b720 | ||
|
3b7078ac16 | ||
|
f4f6dd970e | ||
|
22deec81f5 | ||
|
50dff3a507 |
12
.gitignore
vendored
12
.gitignore
vendored
@@ -10,6 +10,17 @@ binutils/aa-status
|
||||
binutils/aa-status.8
|
||||
binutils/cJSON.o
|
||||
binutils/po/*.mo
|
||||
changehat/mod_apparmor/.libs
|
||||
changehat/mod_apparmor/mod_apparmor.8
|
||||
changehat/mod_apparmor/mod_apparmor.8.html
|
||||
changehat/mod_apparmor/mod_apparmor.la
|
||||
changehat/mod_apparmor/mod_apparmor.lo
|
||||
changehat/mod_apparmor/mod_apparmor.slo
|
||||
changehat/mod_apparmor/mod_apparmor.so
|
||||
changehat/mod_apparmor/pod2htmd.tmp
|
||||
changehat/pam_apparmor/get_options.o
|
||||
changehat/pam_apparmor/pam_apparmor.o
|
||||
changehat/pam_apparmor/pam_apparmor.so
|
||||
parser/po/*.mo
|
||||
parser/af_names.h
|
||||
parser/cap_names.h
|
||||
@@ -196,7 +207,6 @@ libraries/libapparmor/testsuite/libaalogparse.test/Makefile
|
||||
libraries/libapparmor/testsuite/libaalogparse.test/Makefile.in
|
||||
libraries/libapparmor/testsuite/test_multi/out
|
||||
libraries/libapparmor/testsuite/test_multi_multi-test_multi.o
|
||||
changehat/mod_apparmor/.libs
|
||||
utils/*.8
|
||||
utils/*.8.html
|
||||
utils/*.5
|
||||
|
@@ -1,7 +1,7 @@
|
||||
---
|
||||
image: ubuntu:latest
|
||||
before_script:
|
||||
- export DEBIAN_FRONTEND=noninteractive && apt-get update -qq && apt-get install --no-install-recommends -y build-essential apache2-dev autoconf automake bison dejagnu flex libpam-dev libtool perl liblocale-gettext-perl pkg-config python-all-dev python3-all-dev pyflakes3 ruby-dev swig lsb-release python3-notify2 python3-psutil python3-setuptools zlib1g-dev
|
||||
- export DEBIAN_FRONTEND=noninteractive && apt-get update -qq && apt-get install --no-install-recommends -y build-essential apache2-dev autoconf autoconf-archive automake bison dejagnu flex libpam-dev libtool perl liblocale-gettext-perl pkg-config python3-all-dev pyflakes3 ruby-dev swig lsb-release python3-notify2 python3-psutil python3-setuptools zlib1g-dev
|
||||
- lsb_release -a
|
||||
- uname -a
|
||||
|
||||
|
@@ -1 +1 @@
|
||||
3.0.12
|
||||
3.0.13
|
||||
|
@@ -92,6 +92,13 @@ if test "$ac_cv_prog_cc_c99" = "no"; then
|
||||
AC_MSG_ERROR([C99 mode is required to build libapparmor])
|
||||
fi
|
||||
|
||||
EXTRA_CFLAGS="-Wall $(EXTRA_WARNINGS) -fPIC"
|
||||
AX_CHECK_COMPILE_FLAG([-flto-partition=none], , , [-Werror])
|
||||
AS_VAR_IF([ax_cv_check_cflags__Werror__flto_partition_none], [yes],
|
||||
[EXTRA_CFLAGS="$EXTRA_CFLAGS -flto-partition=none"]
|
||||
,)
|
||||
AC_SUBST([AM_CFLAGS], ["$EXTRA_CFLAGS"])
|
||||
|
||||
AC_OUTPUT(
|
||||
Makefile
|
||||
doc/Makefile
|
||||
|
@@ -116,6 +116,14 @@ The specified I<file/task> does not exist or is not visible.
|
||||
|
||||
The confinement data is too large to fit in the supplied buffer.
|
||||
|
||||
=item B<ENOPROTOOPT>
|
||||
|
||||
The kernel doesn't support the SO_PEERLABEL option in sockets. This happens
|
||||
mainly when the kernel lacks 'fine grained unix mediation' support. It also
|
||||
can happen on LSM stacking kernels where another LSM has claimed this
|
||||
interface and decides to return this error, although this is really a
|
||||
corner case.
|
||||
|
||||
=back
|
||||
|
||||
=head1 NOTES
|
||||
|
@@ -109,12 +109,12 @@ To immediately stack a profile named "profile_a", as performed with
|
||||
aa_stack_profile("profile_a"), the equivalent of this shell command can be
|
||||
used:
|
||||
|
||||
$ echo -n "stackprofile profile_a" > /proc/self/attr/current
|
||||
$ echo -n "stack profile_a" > /proc/self/attr/current
|
||||
|
||||
To stack a profile named "profile_a" at the next exec, as performed with
|
||||
aa_stack_onexec("profile_a"), the equivalent of this shell command can be used:
|
||||
|
||||
$ echo -n "stackexec profile_a" > /proc/self/attr/exec
|
||||
$ echo -n "stack profile_a" > /proc/self/attr/exec
|
||||
|
||||
These raw AppArmor filesystem operations must only be used when using
|
||||
libapparmor is not a viable option.
|
||||
@@ -184,6 +184,7 @@ with apparmor_parser(8):
|
||||
/etc/passwd r,
|
||||
|
||||
# Needed for aa_stack_profile()
|
||||
change-profile -> &i_cant_be_trusted_anymore,
|
||||
/usr/lib/libapparmor*.so* mr,
|
||||
/proc/[0-9]*/attr/current w,
|
||||
}
|
||||
|
@@ -39,7 +39,6 @@ include $(COMMONDIR)/Make.rules
|
||||
BUILT_SOURCES = grammar.h scanner.h af_protos.h
|
||||
AM_LFLAGS = -v
|
||||
AM_YFLAGS = -d -p aalogparse_
|
||||
AM_CFLAGS = -Wall $(EXTRA_WARNINGS) -fPIC -flto-partition=none
|
||||
AM_CPPFLAGS = -D_GNU_SOURCE -I$(top_srcdir)/include/
|
||||
scanner.h: scanner.l
|
||||
$(LEX) -v $<
|
||||
@@ -47,7 +46,7 @@ scanner.h: scanner.l
|
||||
scanner.c: scanner.l
|
||||
|
||||
af_protos.h:
|
||||
echo '#include <netinet/in.h>' | $(CC) $(CPPFLAGS) -E -dM - | LC_ALL=C sed -n -e "/IPPROTO_MAX/d" -e "s/^\#define[ \\t]\\+IPPROTO_\\([A-Z0-9_]\\+\\)\\(.*\\)$$/AA_GEN_PROTO_ENT(\\UIPPROTO_\\1, \"\\L\\1\")/p" > $@
|
||||
echo '#include <netinet/in.h>' | $(CC) $(CPPFLAGS) -E -dD - | LC_ALL=C sed -n -e "/IPPROTO_MAX/d" -e "s/^\#define[ \\t]\\+IPPROTO_\\([A-Z0-9_]\\+\\)\\(.*\\)$$/AA_GEN_PROTO_ENT(\\UIPPROTO_\\1, \"\\L\\1\")/p" > $@
|
||||
|
||||
lib_LTLIBRARIES = libapparmor.la
|
||||
noinst_HEADERS = grammar.h parser.h scanner.h af_protos.h private.h PMurHash.h
|
||||
|
@@ -398,6 +398,10 @@ static bool walk_one(const char **str, const struct component *component,
|
||||
i = 0;
|
||||
|
||||
cur++;
|
||||
|
||||
/* Partial match, continue to search */
|
||||
if (i == component->len && !isbrace_space_or_nul(*cur))
|
||||
i = 0;
|
||||
}
|
||||
|
||||
/* Return false if a full match was not found */
|
||||
|
@@ -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 c = (struct component) { NULL, 0 };
|
||||
const char *str;
|
||||
int rc = 0;
|
||||
|
||||
|
@@ -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, int *len, char **mode);
|
||||
extern int aa_getpeercon_raw(int fd, char *buf, socklen_t *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);
|
||||
|
@@ -70,7 +70,10 @@ CFLAGS = -g -pg -fprofile-arcs -ftest-coverage
|
||||
endif
|
||||
endif #CFLAGS
|
||||
|
||||
CFLAGS += -flto-partition=none
|
||||
HAVE_FLTO_PARTITION_NONE:=$(shell ${CC} -E -flto-partition=none /dev/null 1>/dev/null 2>&1 && echo true)
|
||||
ifeq ($(HAVE_FLTO_PARTITION_NONE),true)
|
||||
CFLAGS += -flto-partition=none
|
||||
endif
|
||||
|
||||
EXTRA_CXXFLAGS = ${CFLAGS} ${CPPFLAGS} ${CXX_WARNINGS} -std=gnu++0x
|
||||
EXTRA_CFLAGS = ${EXTRA_CXXFLAGS} ${CPP_WARNINGS}
|
||||
|
@@ -1786,7 +1786,7 @@ An example AppArmor profile:
|
||||
/usr/lib/** r,
|
||||
/tmp/foo.pid wr,
|
||||
/tmp/foo.* lrw,
|
||||
/@{HOME}/.foo_file rw,
|
||||
@{HOME}/.foo_file rw,
|
||||
/usr/bin/baz Cx -> baz,
|
||||
|
||||
# a comment about foo's hat (subprofile), bar.
|
||||
|
@@ -299,11 +299,11 @@ Enable various warnings during policy compilation. A single warn flag
|
||||
can be specified per --warn option, but the --warn flag can be passed
|
||||
multiple times.
|
||||
|
||||
apparmor_parser --warn=rules-not-enforced ...
|
||||
apparmor_parser --warn=rule-not-enforced ...
|
||||
|
||||
A specific warning can be disabled by prepending I<no>- to the flag
|
||||
|
||||
apparmor_parser --warn=no-rules-not-enforced ...
|
||||
apparmor_parser --warn=no-rule-not-enforced ...
|
||||
|
||||
Use --help=warn to see a full list of which warn flags are supported.
|
||||
|
||||
|
@@ -189,6 +189,19 @@ void Node::dump_syntax_tree(ostream &os)
|
||||
* a b c T
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
static Node *simplify_eps_pair(Node *t)
|
||||
{
|
||||
if (dynamic_cast<TwoChildNode *>(t) &&
|
||||
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)
|
||||
@@ -197,7 +210,9 @@ 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];
|
||||
t->child[!dir] = left;
|
||||
|
||||
// check that rotation didn't create (E | E)
|
||||
t->child[!dir] = simplify_eps_pair(left);
|
||||
}
|
||||
|
||||
/* return False if no work done */
|
||||
@@ -209,13 +224,7 @@ 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 = child[!dir];
|
||||
if (dynamic_cast<TwoChildNode *>(c) &&
|
||||
&epsnode == c->child[dir] &&
|
||||
&epsnode == c->child[!dir]) {
|
||||
c->release();
|
||||
c = &epsnode;
|
||||
}
|
||||
Node *c = simplify_eps_pair(child[!dir]);
|
||||
child[!dir] = child[dir];
|
||||
child[dir] = c;
|
||||
return 1;
|
||||
|
@@ -274,7 +274,7 @@ static inline void sd_write_aligned_blob(std::ostringstream &buf, void *b, int b
|
||||
buf.write((const char *) b, b_size);
|
||||
}
|
||||
|
||||
static void sd_write_strn(std::ostringstream &buf, char *b, int size, const char *name)
|
||||
static void sd_write_strn(std::ostringstream &buf, const char *b, int size, const char *name)
|
||||
{
|
||||
sd_write_name(buf, name);
|
||||
sd_write8(buf, SD_STRING);
|
||||
@@ -282,7 +282,7 @@ static void sd_write_strn(std::ostringstream &buf, char *b, int size, const char
|
||||
buf.write(b, size);
|
||||
}
|
||||
|
||||
static inline void sd_write_string(std::ostringstream &buf, char *b, const char *name)
|
||||
static inline void sd_write_string(std::ostringstream &buf, const char *b, const char *name)
|
||||
{
|
||||
sd_write_strn(buf, b, strlen(b) + 1, name);
|
||||
}
|
||||
@@ -401,11 +401,7 @@ void sd_serialize_profile(std::ostringstream &buf, Profile *profile,
|
||||
sd_write_struct(buf, "profile");
|
||||
if (flattened) {
|
||||
assert(profile->parent);
|
||||
autofree char *name = (char *) malloc(3 + strlen(profile->name) + strlen(profile->parent->name));
|
||||
if (!name)
|
||||
return;
|
||||
sprintf(name, "%s//%s", profile->parent->name, profile->name);
|
||||
sd_write_string(buf, name, NULL);
|
||||
sd_write_string(buf, profile->get_name(false).c_str(), NULL);
|
||||
} else {
|
||||
sd_write_string(buf, profile->name, NULL);
|
||||
}
|
||||
|
@@ -99,11 +99,12 @@ is_container_with_internal_policy() {
|
||||
return 1
|
||||
fi
|
||||
|
||||
# LXD and LXC set up AppArmor namespaces starting with "lxd-" and
|
||||
# "lxc-", respectively. Return non-zero for all other namespace
|
||||
# identifiers.
|
||||
# LXD, Incus and LXC set up AppArmor namespaces starting with "lxd-",
|
||||
# "incus-" and "lxc-", respectively. Return non-zero for all other
|
||||
# namespace identifiers.
|
||||
read -r ns_name < "$ns_name_path"
|
||||
if [ "${ns_name#lxd-*}" = "$ns_name" ] && \
|
||||
[ "${ns_name#incus-*}" = "$ns_name" ] && \
|
||||
[ "${ns_name#lxc-*}" = "$ns_name" ]; then
|
||||
return 1
|
||||
fi
|
||||
|
25
parser/tst/simple_tests/regressions/ok_normalize.sd
Normal file
25
parser/tst/simple_tests/regressions/ok_normalize.sd
Normal file
@@ -0,0 +1,25 @@
|
||||
#
|
||||
#=Description caused an infinite loop in expr normalization
|
||||
#=EXRESULT PASS
|
||||
|
||||
# This test triggers an infinite loop bug in expr normalization
|
||||
# Note: this test might be able to be reduced more but, each element appears
|
||||
# to be required to trigger the bug.
|
||||
# that is the initial var assignment, += with the "comment" at the end
|
||||
# (which is a separate bug), the expansion in the 2nd variable and then
|
||||
# the use of the 2nd variable.
|
||||
# This seems to be due to difference in consistency check between expansion
|
||||
# at parse time and variable expansion.
|
||||
# eg. expanding @{exec_path} manually will result in a failure to parse
|
||||
# see: https://gitlab.com/apparmor/apparmor/-/issues/398
|
||||
|
||||
@{var}=*-linux-gnu*
|
||||
@{var}+=*-suse-linux* #aa:only opensuse
|
||||
|
||||
@{exec_path} = /{,@{var}/}t
|
||||
|
||||
profile test {
|
||||
|
||||
|
||||
@{exec_path} mr,
|
||||
}
|
@@ -28,6 +28,7 @@
|
||||
owner @{run}/user/*/gdm/Xauthority r,
|
||||
owner @{run}/user/*/X11/Xauthority r,
|
||||
owner @{run}/user/*/xauth_* r,
|
||||
owner /tmp/xauth_?????? r,
|
||||
|
||||
# the unix socket to use to connect to the display
|
||||
/tmp/.X11-unix/* rw,
|
||||
|
@@ -86,7 +86,7 @@ owner @{HOME}/.local/share/openal/hrtf/{,**} r,
|
||||
/etc/wildmidi/wildmidi.cfg r,
|
||||
|
||||
# pipewire
|
||||
/usr/share/pipewire/client.conf r,
|
||||
/usr/share/pipewire/client{,-rt}.conf r,
|
||||
|
||||
# Include additions to the abstraction
|
||||
include if exists <abstractions/audio.d>
|
||||
|
@@ -31,6 +31,17 @@
|
||||
/{usr/,}lib/@{multiarch}/security/pam_*.so mr,
|
||||
/{usr/,}lib/@{multiarch}/security/ r,
|
||||
|
||||
# pam_unix
|
||||
owner /proc/@{pid}/loginuid r,
|
||||
/{,usr/}{,s}bin/unix_chkpwd Px,
|
||||
|
||||
# pam_env
|
||||
@{etc_ro}/environment r,
|
||||
|
||||
# pam_limit
|
||||
@{etc_ro}/security/limits.d/ r,
|
||||
@{etc_ro}/security/limits.d/*.conf r,
|
||||
|
||||
# gssapi
|
||||
@{etc_ro}/gss/mech r,
|
||||
@{etc_ro}/gss/mech.d/ r,
|
||||
|
@@ -96,6 +96,9 @@
|
||||
# best place -- but many profiles require it, and it is quite harmless.
|
||||
@{PROC}/sys/kernel/ngroups_max r,
|
||||
|
||||
# Used to determine if Linux is running in FIPS mode
|
||||
@{PROC}/sys/crypto/fips_enabled r,
|
||||
|
||||
# glibc's sysconf(3) routine to determine free memory, etc
|
||||
@{PROC}/meminfo r,
|
||||
@{PROC}/stat r,
|
||||
|
@@ -13,6 +13,9 @@
|
||||
|
||||
abi <abi/3.0>,
|
||||
|
||||
# Global config of openssl
|
||||
include <abstractions/openssl>
|
||||
|
||||
@{etc_ro}/gcrypt/hwf.deny r,
|
||||
@{etc_ro}/gcrypt/random.conf r,
|
||||
@{PROC}/sys/crypto/fips_enabled r,
|
||||
@@ -24,4 +27,8 @@
|
||||
/etc/crypto-policies/*/*.txt r,
|
||||
/usr/share/crypto-policies/*/*.txt r,
|
||||
|
||||
# Global gnutls config
|
||||
@{etc_ro}/gnutls/config r,
|
||||
@{etc_ro}/gnutls/pkcs11.conf r,
|
||||
|
||||
include if exists <abstractions/crypto.d>
|
||||
|
@@ -47,7 +47,7 @@
|
||||
owner @{HOME}/.local/share/fonts/** r,
|
||||
owner @{HOME}/.fonts.cache-2 mr,
|
||||
owner @{HOME}/.{,cache/}fontconfig/ rw,
|
||||
owner @{HOME}/.{,cache/}fontconfig/** mrl,
|
||||
owner @{HOME}/.{,cache/}fontconfig/** mrwkl,
|
||||
owner @{HOME}/.fonts.conf.d/ r,
|
||||
owner @{HOME}/.fonts.conf.d/** r,
|
||||
owner @{HOME}/.config/fontconfig/ r,
|
||||
|
@@ -27,6 +27,9 @@ include <abstractions/qt5>
|
||||
/etc/kde4rc r,
|
||||
/etc/xdg/kdeglobals r,
|
||||
/etc/xdg/Trolltech.conf r,
|
||||
/usr/share/desktop-base/kf5-settings/baloofilerc r,
|
||||
/usr/share/desktop-base/kf5-settings/kdeglobals r,
|
||||
/usr/share/desktop-base/kf5-settings/kscreenlockerrc r,
|
||||
/usr/share/knotifications5/*.notifyrc r, # KNotification::sendEvent()
|
||||
/usr/share/kubuntu-default-settings/kf5-settings/* r,
|
||||
|
||||
|
@@ -20,6 +20,12 @@
|
||||
owner @{HOME}/.cache/mesa_shader_cache/[a-f0-9][a-f0-9]/[0-9a-f]* rw,
|
||||
owner @{HOME}/.cache/mesa_shader_cache/[a-f0-9][a-f0-9]/[0-9a-f]*.tmp rwk,
|
||||
|
||||
owner @{HOME}/.cache/mesa_shader_cache_db/ rw,
|
||||
owner @{HOME}/.cache/mesa_shader_cache_db/index rwk,
|
||||
owner @{HOME}/.cache/mesa_shader_cache_db/part*/ rw,
|
||||
owner @{HOME}/.cache/mesa_shader_cache_db/part*/mesa_cache.db rwk,
|
||||
owner @{HOME}/.cache/mesa_shader_cache_db/part*/mesa_cache.idx rwk,
|
||||
|
||||
# Fallback location when @{HOME}/.cache is not available
|
||||
owner /tmp/Temp-[a-f0-9]*/mesa_shader_cache/ rw,
|
||||
owner /tmp/Temp-[a-f0-9]*/mesa_shader_cache/index rw,
|
||||
|
@@ -23,6 +23,9 @@
|
||||
@{etc_ro}/passwd r,
|
||||
@{etc_ro}/protocols r,
|
||||
|
||||
# On systems with authselect installed, /etc/nsswitch.conf is a symlink to /etc/authselect/nsswitch.conf
|
||||
@{etc_ro}/authselect/nsswitch.conf r,
|
||||
|
||||
# libtirpc (used for NIS/YP login) needs this
|
||||
@{etc_ro}/netconfig r,
|
||||
|
||||
@@ -59,6 +62,10 @@
|
||||
# have open
|
||||
@{run}/nscd/db* rmix,
|
||||
|
||||
# make libnss-libvirt name resolution work.
|
||||
/var/lib/libvirt/dnsmasq/ r,
|
||||
/var/lib/libvirt/dnsmasq/*.status r,
|
||||
|
||||
# The nss libraries are sometimes used in addition to PAM; make sure
|
||||
# they are available
|
||||
/{usr/,}lib{,32,64}/libnss_*.so* mr,
|
||||
|
@@ -12,8 +12,8 @@
|
||||
|
||||
/etc/ssl/openssl.cnf r,
|
||||
/etc/ssl/openssl-*.cnf r,
|
||||
/etc/ssl/{engdef,engines}.d/ r,
|
||||
/etc/ssl/{engdef,engines}.d/*.cnf r,
|
||||
/etc/ssl/{engdef*,engines*}.d/ r,
|
||||
/etc/ssl/{engdef*,engines*}.d/*.cnf r,
|
||||
/usr/share/ssl/openssl.cnf r,
|
||||
|
||||
# Include additions to the abstraction
|
||||
|
@@ -12,6 +12,7 @@
|
||||
abi <abi/3.0>,
|
||||
|
||||
/etc/samba/* r,
|
||||
/etc/gnutls/config r,
|
||||
/usr/lib*/ldb/*.so mr,
|
||||
/usr/lib*/ldb2/*.so mr,
|
||||
/usr/lib*/ldb2/modules/ldb/*.so mr,
|
||||
|
@@ -7,3 +7,6 @@
|
||||
|
||||
include <abstractions/kde>
|
||||
/usr/bin/kde4-config Cx -> sanitized_helper,
|
||||
|
||||
# https://bugs.kde.org/show_bug.cgi?id=397399
|
||||
/usr/bin/plasma-browser-integration-host Cx -> sanitized_helper,
|
||||
|
@@ -13,10 +13,15 @@
|
||||
|
||||
# some services update wtmp, utmp, and lastlog with per-user
|
||||
# connection information
|
||||
/var/lib/wtmpdb/ r,
|
||||
/var/lib/wtmpdb/wtmp.db{,-journal} rwlk,
|
||||
/var/log/lastlog rwk,
|
||||
/var/log/wtmp rwk,
|
||||
/var/log/btmp rwk,
|
||||
@{run}/utmp rwk,
|
||||
|
||||
# Some read the list of sessions from systemd
|
||||
/run/systemd/sessions/ r,
|
||||
|
||||
# Include additions to the abstraction
|
||||
include if exists <abstractions/wutmp.d>
|
||||
|
@@ -24,6 +24,7 @@ profile ping /{usr/,}bin/{,iputils-}ping {
|
||||
|
||||
/{,usr/}bin/{,iputils-}ping mixr,
|
||||
/etc/modules.conf r,
|
||||
@{PROC}/sys/net/ipv6/conf/all/disable_ipv6 r,
|
||||
|
||||
# Site-specific additions and overrides. See local/README for details.
|
||||
include if exists <local/bin.ping>
|
||||
|
@@ -16,12 +16,14 @@ include <tunables/global>
|
||||
profile samba-dcerpcd /usr/lib*/samba/{,samba/}samba-dcerpcd {
|
||||
include <abstractions/samba-rpcd>
|
||||
|
||||
capability sys_resource,
|
||||
|
||||
@{run}/{,samba/}samba-dcerpcd.pid rwk,
|
||||
|
||||
/usr/lib*/samba/{,samba/}samba-dcerpcd mr,
|
||||
|
||||
/usr/lib*/samba/ r,
|
||||
/usr/lib*/samba/{,samba/}rpcd_{mdssvc,epmapper,rpcecho,fsrvp,lsad,winreg} Px -> samba-rpcd,
|
||||
/usr/lib*/samba/{,samba/}rpcd_{mdssvc,epmapper,rpcecho,fsrvp,lsad,winreg,witness} Px -> samba-rpcd,
|
||||
/usr/lib*/samba/{,samba/}rpcd_classic Px -> samba-rpcd-classic,
|
||||
/usr/lib*/samba/{,samba/}rpcd_spoolss Px -> samba-rpcd-spoolss,
|
||||
|
||||
|
@@ -13,10 +13,15 @@ abi <abi/3.0>,
|
||||
|
||||
include <tunables/global>
|
||||
|
||||
profile samba-rpcd /usr/lib*/samba/{,samba/}rpcd_{mdssvc,epmapper,rpcecho,fsrvp,lsad,winreg} {
|
||||
profile samba-rpcd /usr/lib*/samba/{,samba/}rpcd_{mdssvc,epmapper,rpcecho,fsrvp,lsad,winreg,witness} {
|
||||
include <abstractions/samba-rpcd>
|
||||
/usr/lib*/samba/{,samba/}rpcd_{mdssvc,epmapper,rpcecho,fsrvp,lsad,winreg} mr,
|
||||
|
||||
capability sys_resource,
|
||||
|
||||
/usr/lib*/samba/{,samba/}rpcd_{mdssvc,epmapper,rpcecho,fsrvp,lsad,winreg,witness} mr,
|
||||
|
||||
@{run}/samba/ncalrpc/np/lsarpc wr,
|
||||
@{run}/samba/ncalrpc/np/mdssvc wr,
|
||||
@{run}/samba/ncalrpc/np/winreg wr,
|
||||
|
||||
# Site-specific additions and overrides. See local/README for details.
|
||||
|
@@ -17,8 +17,18 @@ profile samba-rpcd-classic /usr/lib*/samba/{,samba/}rpcd_classic {
|
||||
include <abstractions/samba-rpcd>
|
||||
include <abstractions/wutmp>
|
||||
|
||||
capability sys_resource,
|
||||
|
||||
/usr/lib*/samba/{,samba/}rpcd_classic mr,
|
||||
|
||||
@{run}/samba/ncalrpc/np/srvsvc wr,
|
||||
@{run}/samba/ncalrpc/np/winreg wr,
|
||||
/dev/urandom rw,
|
||||
|
||||
/usr/lib*/samba/{,samba/}samba-dcerpcd Px -> samba-dcerpcd,
|
||||
|
||||
@{HOMEDIRS}/** lrwk,
|
||||
|
||||
# Site-specific additions and overrides. See local/README for details.
|
||||
include if exists <local/samba-rpcd-classic>
|
||||
}
|
||||
|
35
profiles/apparmor.d/unix-chkpwd
Normal file
35
profiles/apparmor.d/unix-chkpwd
Normal file
@@ -0,0 +1,35 @@
|
||||
# apparmor.d - Full set of apparmor profiles
|
||||
# Copyright (C) 2019-2021 Mikhail Morfikov
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
# The apparmor.d project comes with several variables and abstractions
|
||||
# that are not part of upstream AppArmor yet. Therefore this profile was
|
||||
# adopted to use abstractions and variables that are available.
|
||||
# Copyright (C) Christian Boltz 2024
|
||||
|
||||
abi <abi/3.0>,
|
||||
|
||||
include <tunables/global>
|
||||
|
||||
profile unix-chkpwd /{,usr/}{,s}bin/unix_chkpwd {
|
||||
include <abstractions/base>
|
||||
include <abstractions/nameservice>
|
||||
|
||||
# To write records to the kernel auditing log.
|
||||
capability audit_write,
|
||||
|
||||
network netlink raw,
|
||||
|
||||
/{,usr/}{,s}bin/unix_chkpwd mr,
|
||||
|
||||
/etc/shadow r,
|
||||
|
||||
# systemd userdb, used in nspawn
|
||||
/run/host/userdb/*.user r,
|
||||
/run/host/userdb/*.user-privileged r,
|
||||
|
||||
# file_inherit
|
||||
owner /dev/tty[0-9]* rw,
|
||||
|
||||
include if exists <local/unix-chkpwd>
|
||||
}
|
@@ -13,7 +13,7 @@ abi <abi/3.0>,
|
||||
|
||||
include <tunables/global>
|
||||
|
||||
profile dovecot-anvil /usr/lib/dovecot/anvil {
|
||||
profile dovecot-anvil /usr/lib*/dovecot/anvil {
|
||||
include <abstractions/base>
|
||||
include <abstractions/dovecot-common>
|
||||
|
||||
@@ -24,7 +24,7 @@ profile dovecot-anvil /usr/lib/dovecot/anvil {
|
||||
|
||||
@{run}/dovecot/anvil rw,
|
||||
@{run}/dovecot/anvil-auth-penalty rw,
|
||||
/usr/lib/dovecot/anvil mr,
|
||||
/usr/lib*/dovecot/anvil mr,
|
||||
|
||||
# Site-specific additions and overrides. See local/README for details.
|
||||
include if exists <local/usr.lib.dovecot.anvil>
|
||||
|
@@ -14,7 +14,7 @@ abi <abi/3.0>,
|
||||
|
||||
include <tunables/global>
|
||||
|
||||
profile dovecot-auth /usr/lib/dovecot/auth {
|
||||
profile dovecot-auth /usr/lib*/dovecot/auth {
|
||||
include <abstractions/authentication>
|
||||
include <abstractions/base>
|
||||
include <abstractions/mysql>
|
||||
@@ -34,7 +34,7 @@ profile dovecot-auth /usr/lib/dovecot/auth {
|
||||
/etc/my.cnf.d/*.cnf r,
|
||||
|
||||
/etc/dovecot/* r,
|
||||
/usr/lib/dovecot/auth mr,
|
||||
/usr/lib*/dovecot/auth mr,
|
||||
/var/lib/dovecot/auth-chroot/* r,
|
||||
|
||||
# kerberos replay cache
|
||||
|
@@ -13,7 +13,7 @@ abi <abi/3.0>,
|
||||
|
||||
include <tunables/global>
|
||||
|
||||
profile dovecot-config /usr/lib/dovecot/config {
|
||||
profile dovecot-config /usr/lib*/dovecot/config {
|
||||
include <abstractions/base>
|
||||
include <abstractions/nameservice>
|
||||
include <abstractions/dovecot-common>
|
||||
@@ -24,8 +24,8 @@ profile dovecot-config /usr/lib/dovecot/config {
|
||||
|
||||
/etc/dovecot/** r,
|
||||
/usr/bin/doveconf rix,
|
||||
/usr/lib/dovecot/config mr,
|
||||
/usr/lib/dovecot/managesieve Px,
|
||||
/usr/lib*/dovecot/config mr,
|
||||
/usr/lib*/dovecot/managesieve Px,
|
||||
/usr/share/dovecot/** r,
|
||||
/var/lib/dovecot/ssl-parameters.dat r,
|
||||
|
||||
|
@@ -16,7 +16,7 @@ abi <abi/3.0>,
|
||||
include <tunables/global>
|
||||
include <tunables/dovecot>
|
||||
|
||||
profile dovecot-deliver /usr/lib/dovecot/deliver {
|
||||
profile dovecot-deliver /usr/lib*/dovecot/deliver {
|
||||
include <abstractions/base>
|
||||
include <abstractions/nameservice>
|
||||
include <abstractions/dovecot-common>
|
||||
@@ -32,7 +32,7 @@ profile dovecot-deliver /usr/lib/dovecot/deliver {
|
||||
/etc/dovecot/dovecot-postfix.conf r, # ???
|
||||
|
||||
@{HOME} r, # ???
|
||||
/usr/lib/dovecot/deliver mr,
|
||||
/usr/lib*/dovecot/deliver mr,
|
||||
|
||||
# Site-specific additions and overrides. See local/README for details.
|
||||
include if exists <local/usr.lib.dovecot.deliver>
|
||||
|
@@ -13,7 +13,7 @@ abi <abi/3.0>,
|
||||
|
||||
include <tunables/global>
|
||||
|
||||
profile dovecot-dict /usr/lib/dovecot/dict {
|
||||
profile dovecot-dict /usr/lib*/dovecot/dict {
|
||||
include <abstractions/base>
|
||||
include <abstractions/mysql>
|
||||
include <abstractions/nameservice>
|
||||
@@ -27,7 +27,7 @@ profile dovecot-dict /usr/lib/dovecot/dict {
|
||||
/etc/dovecot/dovecot-database.conf.ext r,
|
||||
/etc/dovecot/dovecot-dict-sql.conf.ext r,
|
||||
/etc/my.cnf r,
|
||||
/usr/lib/dovecot/dict mr,
|
||||
/usr/lib*/dovecot/dict mr,
|
||||
|
||||
# Site-specific additions and overrides. See local/README for details.
|
||||
include if exists <local/usr.lib.dovecot.dict>
|
||||
|
@@ -11,7 +11,7 @@
|
||||
|
||||
include <tunables/global>
|
||||
|
||||
profile dovecot-director /usr/lib/dovecot/director flags=(attach_disconnected) {
|
||||
profile dovecot-director /usr/lib*/dovecot/director flags=(attach_disconnected) {
|
||||
include <abstractions/base>
|
||||
include <abstractions/dovecot-common>
|
||||
include <abstractions/nameservice>
|
||||
@@ -20,7 +20,7 @@ profile dovecot-director /usr/lib/dovecot/director flags=(attach_disconnected) {
|
||||
capability sys_chroot,
|
||||
|
||||
/run/dovecot/login/proxy-notify rw,
|
||||
/usr/lib/dovecot/director mr,
|
||||
/usr/lib*/dovecot/director mr,
|
||||
|
||||
# Site-specific additions and overrides. See local/README for details.
|
||||
include if exists <local/usr.lib.dovecot.director>
|
||||
|
@@ -11,11 +11,11 @@
|
||||
|
||||
include <tunables/global>
|
||||
|
||||
profile dovecot-doveadm-server /usr/lib/dovecot/doveadm-server flags=(attach_disconnected) {
|
||||
profile dovecot-doveadm-server /usr/lib*/dovecot/doveadm-server flags=(attach_disconnected) {
|
||||
include <abstractions/base>
|
||||
include <abstractions/dovecot-common>
|
||||
|
||||
/usr/lib/dovecot/doveadm-server mr,
|
||||
/usr/lib*/dovecot/doveadm-server mr,
|
||||
|
||||
# Site-specific additions and overrides. See local/README for details.
|
||||
include if exists <local/usr.lib.dovecot.doveadm-server>
|
||||
|
@@ -14,7 +14,7 @@ abi <abi/3.0>,
|
||||
|
||||
include <tunables/global>
|
||||
|
||||
profile dovecot-dovecot-auth /usr/lib/dovecot/dovecot-auth {
|
||||
profile dovecot-dovecot-auth /usr/lib*/dovecot/dovecot-auth {
|
||||
include <abstractions/authentication>
|
||||
include <abstractions/base>
|
||||
include <abstractions/nameservice>
|
||||
@@ -25,7 +25,7 @@ profile dovecot-dovecot-auth /usr/lib/dovecot/dovecot-auth {
|
||||
capability dac_override,
|
||||
|
||||
@{PROC}/@{pid}/mounts r,
|
||||
/usr/lib/dovecot/dovecot-auth mr,
|
||||
/usr/lib*/dovecot/dovecot-auth mr,
|
||||
@{run}/dovecot/** rw,
|
||||
# required for postfix+dovecot integration
|
||||
/var/spool/postfix/private/dovecot-auth w,
|
||||
|
@@ -14,7 +14,7 @@ abi <abi/3.0>,
|
||||
include <tunables/global>
|
||||
include <tunables/dovecot>
|
||||
|
||||
profile dovecot-dovecot-lda /usr/lib/dovecot/dovecot-lda flags=(attach_disconnected) {
|
||||
profile dovecot-dovecot-lda /usr/lib*/dovecot/dovecot-lda flags=(attach_disconnected) {
|
||||
include <abstractions/base>
|
||||
include <abstractions/nameservice>
|
||||
include <abstractions/dovecot-common>
|
||||
@@ -30,7 +30,7 @@ profile dovecot-dovecot-lda /usr/lib/dovecot/dovecot-lda flags=(attach_disconnec
|
||||
@{run}/dovecot/mounts r,
|
||||
@{run}/dovecot/auth-userdb rw,
|
||||
/usr/bin/doveconf mrix,
|
||||
/usr/lib/dovecot/dovecot-lda mrix,
|
||||
/usr/lib*/dovecot/dovecot-lda mrix,
|
||||
/usr/{bin,sbin}/sendmail Cx -> sendmail,
|
||||
/usr/share/dovecot/protocols.d/ r,
|
||||
/usr/share/dovecot/protocols.d/** r,
|
||||
|
@@ -15,7 +15,7 @@ abi <abi/3.0>,
|
||||
include <tunables/global>
|
||||
include <tunables/dovecot>
|
||||
|
||||
profile dovecot-imap /usr/lib/dovecot/imap {
|
||||
profile dovecot-imap /usr/lib*/dovecot/imap {
|
||||
include <abstractions/base>
|
||||
include <abstractions/nameservice>
|
||||
include <abstractions/dovecot-common>
|
||||
@@ -37,7 +37,7 @@ profile dovecot-imap /usr/lib/dovecot/imap {
|
||||
@{PROC}/@{pid}/attr/{apparmor/,}current rw,
|
||||
@{PROC}/@{pid}/stat r,
|
||||
/usr/bin/doveconf rix,
|
||||
/usr/lib/dovecot/imap mrix,
|
||||
/usr/lib*/dovecot/imap mrix,
|
||||
/usr/share/dovecot/** r,
|
||||
@{run}/dovecot/login/imap rw,
|
||||
@{run}/dovecot/auth-master rw,
|
||||
|
@@ -14,7 +14,7 @@ abi <abi/3.0>,
|
||||
|
||||
include <tunables/global>
|
||||
|
||||
profile dovecot-imap-login /usr/lib/dovecot/imap-login {
|
||||
profile dovecot-imap-login /usr/lib*/dovecot/imap-login {
|
||||
include <abstractions/base>
|
||||
include <abstractions/dovecot-common>
|
||||
include <abstractions/openssl>
|
||||
@@ -26,7 +26,7 @@ profile dovecot-imap-login /usr/lib/dovecot/imap-login {
|
||||
network inet6 stream,
|
||||
network unix stream,
|
||||
|
||||
/usr/lib/dovecot/imap-login mr,
|
||||
/usr/lib*/dovecot/imap-login mr,
|
||||
@{run}/dovecot/anvil rw,
|
||||
@{run}/dovecot/login-master-notify* rw,
|
||||
@{run}/dovecot/login/ r,
|
||||
|
@@ -14,7 +14,7 @@ abi <abi/3.0>,
|
||||
include <tunables/global>
|
||||
include <tunables/dovecot>
|
||||
|
||||
profile dovecot-lmtp /usr/lib/dovecot/lmtp {
|
||||
profile dovecot-lmtp /usr/lib*/dovecot/lmtp {
|
||||
include <abstractions/base>
|
||||
include <abstractions/nameservice>
|
||||
include <abstractions/dovecot-common>
|
||||
@@ -35,7 +35,7 @@ profile dovecot-lmtp /usr/lib/dovecot/lmtp {
|
||||
owner @{PROC}/@{pid}/stat r,
|
||||
@{PROC}/*/mounts r,
|
||||
/tmp/dovecot.lmtp.* rw,
|
||||
/usr/lib/dovecot/lmtp mr,
|
||||
/usr/lib*/dovecot/lmtp mr,
|
||||
@{run}/dovecot/mounts r,
|
||||
|
||||
# Site-specific additions and overrides. See local/README for details.
|
||||
|
@@ -13,11 +13,11 @@ abi <abi/3.0>,
|
||||
|
||||
include <tunables/global>
|
||||
|
||||
profile dovecot-log /usr/lib/dovecot/log flags=(attach_disconnected) {
|
||||
profile dovecot-log /usr/lib*/dovecot/log flags=(attach_disconnected) {
|
||||
include <abstractions/base>
|
||||
include <abstractions/dovecot-common>
|
||||
|
||||
/usr/lib/dovecot/log mr,
|
||||
/usr/lib*/dovecot/log mr,
|
||||
|
||||
# Site-specific additions and overrides. See local/README for details.
|
||||
include if exists <local/usr.lib.dovecot.log>
|
||||
|
@@ -15,7 +15,7 @@ abi <abi/3.0>,
|
||||
include <tunables/global>
|
||||
include <tunables/dovecot>
|
||||
|
||||
profile dovecot-managesieve /usr/lib/dovecot/managesieve {
|
||||
profile dovecot-managesieve /usr/lib*/dovecot/managesieve {
|
||||
include <abstractions/base>
|
||||
include <abstractions/dovecot-common>
|
||||
|
||||
@@ -29,7 +29,7 @@ profile dovecot-managesieve /usr/lib/dovecot/managesieve {
|
||||
|
||||
/etc/dovecot/** r,
|
||||
/usr/bin/doveconf rix,
|
||||
/usr/lib/dovecot/managesieve mrix,
|
||||
/usr/lib*/dovecot/managesieve mrix,
|
||||
|
||||
# Site-specific additions and overrides. See local/README for details.
|
||||
include if exists <local/usr.lib.dovecot.managesieve>
|
||||
|
@@ -16,7 +16,7 @@ abi <abi/3.0>,
|
||||
|
||||
include <tunables/global>
|
||||
|
||||
profile dovecot-managesieve-login /usr/lib/dovecot/managesieve-login {
|
||||
profile dovecot-managesieve-login /usr/lib*/dovecot/managesieve-login {
|
||||
include <abstractions/base>
|
||||
include <abstractions/dovecot-common>
|
||||
include <abstractions/openssl>
|
||||
@@ -28,7 +28,7 @@ profile dovecot-managesieve-login /usr/lib/dovecot/managesieve-login {
|
||||
network inet6 stream,
|
||||
network unix stream,
|
||||
|
||||
/usr/lib/dovecot/managesieve-login mr,
|
||||
/usr/lib*/dovecot/managesieve-login mr,
|
||||
@{run}/dovecot/login-master-notify* rw,
|
||||
@{run}/dovecot/login/ r,
|
||||
@{run}/dovecot/login/* rw,
|
||||
|
@@ -15,7 +15,7 @@ abi <abi/3.0>,
|
||||
include <tunables/global>
|
||||
include <tunables/dovecot>
|
||||
|
||||
profile dovecot-pop3 /usr/lib/dovecot/pop3 {
|
||||
profile dovecot-pop3 /usr/lib*/dovecot/pop3 {
|
||||
include <abstractions/base>
|
||||
include <abstractions/nameservice>
|
||||
include <abstractions/dovecot-common>
|
||||
@@ -27,7 +27,7 @@ profile dovecot-pop3 /usr/lib/dovecot/pop3 {
|
||||
|
||||
@{HOME} r, # ???
|
||||
@{PROC}/@{pid}/stat r,
|
||||
/usr/lib/dovecot/pop3 mr,
|
||||
/usr/lib*/dovecot/pop3 mr,
|
||||
|
||||
# Site-specific additions and overrides. See local/README for details.
|
||||
include if exists <local/usr.lib.dovecot.pop3>
|
||||
|
@@ -14,7 +14,7 @@ abi <abi/3.0>,
|
||||
|
||||
include <tunables/global>
|
||||
|
||||
profile dovecot-pop3-login /usr/lib/dovecot/pop3-login {
|
||||
profile dovecot-pop3-login /usr/lib*/dovecot/pop3-login {
|
||||
include <abstractions/base>
|
||||
include <abstractions/dovecot-common>
|
||||
include <abstractions/openssl>
|
||||
@@ -26,7 +26,7 @@ profile dovecot-pop3-login /usr/lib/dovecot/pop3-login {
|
||||
network inet6 stream,
|
||||
network unix stream,
|
||||
|
||||
/usr/lib/dovecot/pop3-login mr,
|
||||
/usr/lib*/dovecot/pop3-login mr,
|
||||
@{run}/dovecot/anvil rw,
|
||||
@{run}/dovecot/login-master-notify* rw,
|
||||
@{run}/dovecot/login/ r,
|
||||
|
@@ -15,7 +15,7 @@
|
||||
include <tunables/dovecot>
|
||||
include <tunables/global>
|
||||
|
||||
profile dovecot-replicator /usr/lib/dovecot/replicator {
|
||||
profile dovecot-replicator /usr/lib*/dovecot/replicator {
|
||||
include <abstractions/base>
|
||||
include <abstractions/dovecot-common>
|
||||
include <abstractions/nameservice>
|
||||
@@ -25,7 +25,7 @@ profile dovecot-replicator /usr/lib/dovecot/replicator {
|
||||
/etc/dovecot/conf.d/ r,
|
||||
/etc/dovecot/conf.d/** r,
|
||||
/etc/dovecot/dovecot.conf r,
|
||||
/usr/lib/dovecot/replicator mr,
|
||||
/usr/lib*/dovecot/replicator mr,
|
||||
/usr/share/dovecot/** r,
|
||||
/{,var/}run/dovecot/auth-master rw,
|
||||
@{DOVECOT_MAILSTORE}/ rw,
|
||||
|
@@ -14,14 +14,14 @@ abi <abi/3.0>,
|
||||
|
||||
include <tunables/global>
|
||||
|
||||
profile dovecot-script-login /usr/lib/dovecot/script-login {
|
||||
profile dovecot-script-login /usr/lib*/dovecot/script-login {
|
||||
include <abstractions/base>
|
||||
include <abstractions/dovecot-common>
|
||||
include <abstractions/nameservice>
|
||||
|
||||
capability setuid,
|
||||
|
||||
/usr/lib/dovecot/script-login mrPx,
|
||||
/usr/lib*/dovecot/script-login mrPx,
|
||||
|
||||
# NOTE: You'll need to allow execution of your actual login script.
|
||||
# The recommended way is to add a rule for it in local/usr.lib.dovecot.script-login
|
||||
|
@@ -13,13 +13,13 @@ abi <abi/3.0>,
|
||||
|
||||
include <tunables/global>
|
||||
|
||||
profile dovecot-ssl-params /usr/lib/dovecot/ssl-params {
|
||||
profile dovecot-ssl-params /usr/lib*/dovecot/ssl-params {
|
||||
include <abstractions/base>
|
||||
include <abstractions/dovecot-common>
|
||||
|
||||
@{run}/dovecot/ssl-params rw,
|
||||
@{run}/dovecot/login/ssl-params rw,
|
||||
/usr/lib/dovecot/ssl-params mr,
|
||||
/usr/lib*/dovecot/ssl-params mr,
|
||||
/var/lib/dovecot/ssl-parameters.dat rw,
|
||||
/var/lib/dovecot/ssl-parameters.dat.tmp rwk,
|
||||
|
||||
|
@@ -13,7 +13,7 @@ abi <abi/3.0>,
|
||||
|
||||
include <tunables/global>
|
||||
|
||||
profile dovecot-stats /usr/lib/dovecot/stats {
|
||||
profile dovecot-stats /usr/lib*/dovecot/stats {
|
||||
include <abstractions/base>
|
||||
include <abstractions/dovecot-common>
|
||||
|
||||
@@ -24,7 +24,7 @@ profile dovecot-stats /usr/lib/dovecot/stats {
|
||||
network inet stream,
|
||||
network inet6 stream,
|
||||
|
||||
/usr/lib/dovecot/stats mr,
|
||||
/usr/lib*/dovecot/stats mr,
|
||||
|
||||
# Site-specific additions and overrides. See local/README for details.
|
||||
include if exists <local/usr.lib.dovecot.stats>
|
||||
|
@@ -33,10 +33,10 @@ profile dovecot /usr/{bin,sbin}/dovecot flags=(attach_disconnected) {
|
||||
capability sys_chroot,
|
||||
capability sys_resource,
|
||||
|
||||
signal send peer=/usr/lib/dovecot/*,
|
||||
signal send peer=/usr/lib*/dovecot/*,
|
||||
signal send peer=dovecot-*,
|
||||
|
||||
unix (receive, send) type=stream peer=(label=/usr/lib/dovecot/anvil),
|
||||
unix (receive, send) type=stream peer=(label=/usr/lib*/dovecot/anvil),
|
||||
unix (receive, send) type=stream peer=(label=dovecot-anvil),
|
||||
|
||||
/etc/dovecot/** r,
|
||||
@@ -46,26 +46,26 @@ profile dovecot /usr/{bin,sbin}/dovecot flags=(attach_disconnected) {
|
||||
@{PROC}/@{pid}/mounts r,
|
||||
@{PROC}/sys/fs/suid_dumpable r,
|
||||
/usr/bin/doveconf rix,
|
||||
/usr/lib/dovecot/anvil mrPx,
|
||||
/usr/lib/dovecot/auth mrPx,
|
||||
/usr/lib/dovecot/config mrPx,
|
||||
/usr/lib/dovecot/dict mrPx,
|
||||
/usr/lib/dovecot/director mrPx,
|
||||
/usr/lib/dovecot/doveadm-server mrPx,
|
||||
/usr/lib/dovecot/dovecot-auth Pxmr,
|
||||
/usr/lib/dovecot/imap Pxmr,
|
||||
/usr/lib/dovecot/imap-login Pxmr,
|
||||
/usr/lib/dovecot/lmtp mrPx,
|
||||
/usr/lib/dovecot/log mrPx,
|
||||
/usr/lib/dovecot/managesieve mrPx,
|
||||
/usr/lib/dovecot/managesieve-login Pxmr,
|
||||
/usr/lib/dovecot/pop3 mrPx,
|
||||
/usr/lib/dovecot/pop3-login Pxmr,
|
||||
/usr/lib/dovecot/replicator mrPx,
|
||||
/usr/lib/dovecot/script-login Px,
|
||||
/usr/lib/dovecot/ssl-build-param rix,
|
||||
/usr/lib/dovecot/ssl-params mrPx,
|
||||
/usr/lib/dovecot/stats Px,
|
||||
/usr/lib*/dovecot/anvil mrPx,
|
||||
/usr/lib*/dovecot/auth mrPx,
|
||||
/usr/lib*/dovecot/config mrPx,
|
||||
/usr/lib*/dovecot/dict mrPx,
|
||||
/usr/lib*/dovecot/director mrPx,
|
||||
/usr/lib*/dovecot/doveadm-server mrPx,
|
||||
/usr/lib*/dovecot/dovecot-auth Pxmr,
|
||||
/usr/lib*/dovecot/imap Pxmr,
|
||||
/usr/lib*/dovecot/imap-login Pxmr,
|
||||
/usr/lib*/dovecot/lmtp mrPx,
|
||||
/usr/lib*/dovecot/log mrPx,
|
||||
/usr/lib*/dovecot/managesieve mrPx,
|
||||
/usr/lib*/dovecot/managesieve-login Pxmr,
|
||||
/usr/lib*/dovecot/pop3 mrPx,
|
||||
/usr/lib*/dovecot/pop3-login Pxmr,
|
||||
/usr/lib*/dovecot/replicator mrPx,
|
||||
/usr/lib*/dovecot/script-login Px,
|
||||
/usr/lib*/dovecot/ssl-build-param rix,
|
||||
/usr/lib*/dovecot/ssl-params mrPx,
|
||||
/usr/lib*/dovecot/stats Px,
|
||||
/usr/{bin,sbin}/dovecot mrix,
|
||||
/usr/share/dovecot/dh.pem r,
|
||||
/usr/share/dovecot/protocols.d/ r,
|
||||
|
@@ -8,6 +8,7 @@ profile nmbd /usr/{bin,sbin}/nmbd {
|
||||
include <abstractions/samba>
|
||||
|
||||
capability net_bind_service,
|
||||
capability sys_resource,
|
||||
|
||||
@{PROC}/sys/kernel/core_pattern r,
|
||||
|
||||
|
@@ -14,6 +14,7 @@ profile smbd /usr/{bin,sbin}/smbd {
|
||||
include <abstractions/wutmp>
|
||||
|
||||
capability audit_write,
|
||||
capability chown,
|
||||
capability dac_override,
|
||||
capability dac_read_search,
|
||||
capability fowner,
|
||||
|
@@ -50,6 +50,15 @@ include <tunables/global>
|
||||
# needed when /proc is mounted with hidepid>=1
|
||||
ptrace (read,trace) peer="unconfined",
|
||||
|
||||
unix (bind) type=stream addr="@*/bus/sshd/system",
|
||||
|
||||
dbus (send)
|
||||
bus=system
|
||||
path=/org/freedesktop/login1
|
||||
interface=org.freedesktop.login1.Manager
|
||||
member=CreateSessionWithPIDFD
|
||||
peer=(label=unconfined),
|
||||
|
||||
/dev/ptmx rw,
|
||||
/dev/pts/[0-9]* rw,
|
||||
/dev/urandom r,
|
||||
|
30
tests/regression/apparmor/capabilities.sh
Normal file → Executable file
30
tests/regression/apparmor/capabilities.sh
Normal file → Executable file
@@ -49,14 +49,20 @@ CAPABILITIES="chown dac_override dac_read_search fowner fsetid kill \
|
||||
sys_admin sys_boot sys_nice sys_resource sys_time \
|
||||
sys_tty_config mknod lease audit_write audit_control"
|
||||
|
||||
# lockdown thwarts both ioperm and iopl
|
||||
notlockeddown=TRUE
|
||||
if [ -f /sys/kernel/security/lockdown ] && ! grep -q "\[none\]" /sys/kernel/security/lockdown; then
|
||||
notlockeddown=FALSE
|
||||
fi
|
||||
|
||||
# defines which test+capability pairs should succeed.
|
||||
syscall_reboot_sys_boot=TRUE
|
||||
syscall_sethostname_sys_admin=TRUE
|
||||
syscall_setdomainname_sys_admin=TRUE
|
||||
syscall_setpriority_sys_nice=TRUE
|
||||
syscall_setscheduler_sys_nice=TRUE
|
||||
syscall_ioperm_sys_rawio=TRUE
|
||||
syscall_iopl_sys_rawio=TRUE
|
||||
syscall_ioperm_sys_rawio=$notlockeddown
|
||||
syscall_iopl_sys_rawio=$notlockeddown
|
||||
syscall_chroot_sys_chroot=TRUE
|
||||
syscall_mlockall_ipc_lock=TRUE
|
||||
syscall_sysctl_sys_admin=TRUE
|
||||
@@ -93,7 +99,13 @@ for TEST in ${TESTS} ; do
|
||||
|
||||
settest ${TEST}
|
||||
# base case, unconfined
|
||||
runchecktest "${TEST} -- unconfined" pass ${my_arg}
|
||||
if [ "${TEST}" = "syscall_ioperm" -a "$notlockeddown" = "FALSE" ] ||
|
||||
[ "${TEST}" = "syscall_iopl" -a "$notlockeddown" = "FALSE" ]; then
|
||||
expected=fail
|
||||
else
|
||||
expected=pass
|
||||
fi
|
||||
runchecktest "${TEST} -- unconfined" ${expected} ${my_arg}
|
||||
|
||||
# no capabilities allowed
|
||||
genprofile ${my_entries}
|
||||
@@ -107,11 +119,13 @@ for TEST in ${TESTS} ; do
|
||||
|
||||
# all capabilities allowed
|
||||
genprofile cap:ALL ${my_entries}
|
||||
runchecktest "${TEST} -- all caps" pass ${my_arg}
|
||||
runchecktest "${TEST} -- all caps" ${expected} ${my_arg}
|
||||
|
||||
# iterate through each of the capabilities
|
||||
for cap in ${CAPABILITIES} ; do
|
||||
if [ "X$(eval echo \${${TEST}_${cap}})" = "XTRUE" ] ; then
|
||||
if [ ${expected} = "fail" ]; then
|
||||
expected_result=fail
|
||||
elif [ "X$(eval echo \${${TEST}_${cap}})" = "XTRUE" ] ; then
|
||||
expected_result=pass
|
||||
elif [ "${TEST}" = "syscall_ptrace" -a "$(kernel_features ptrace)" = "true" ]; then
|
||||
expected_result=pass
|
||||
@@ -136,10 +150,12 @@ for TEST in ${TESTS} ; do
|
||||
|
||||
# all capabilities allowed
|
||||
genprofile hat:$bin/${TEST} addimage:${bin}/${TEST} cap:ALL ${my_entries}
|
||||
runchecktest "${TEST} changehat -- all caps" pass $bin/${TEST} ${my_arg}
|
||||
runchecktest "${TEST} changehat -- all caps" ${expected} $bin/${TEST} ${my_arg}
|
||||
|
||||
for cap in ${CAPABILITIES} ; do
|
||||
if [ "X$(eval echo \${${TEST}_${cap}})" = "XTRUE" ] ; then
|
||||
if [ ${expected} = "fail" ]; then
|
||||
expected_result=fail
|
||||
elif [ "X$(eval echo \${${TEST}_${cap}})" = "XTRUE" ] ; then
|
||||
expected_result=pass
|
||||
elif [ "${TEST}" = "syscall_ptrace" -a "$(kernel_features ptrace)" = "true" ]; then
|
||||
expected_result=pass
|
||||
|
@@ -85,6 +85,32 @@ runchecktest "ENVIRON (shell script): confined/complain & sensitive env" pass ${
|
||||
# TEST environment filtering still works on setuid apps
|
||||
removeprofile
|
||||
|
||||
tmpfs_dir=${tmpdir}/tmpfs_dir
|
||||
remove_mnt() {
|
||||
mountpoint -q "$tmpfs_dir"
|
||||
if [ $? -eq 0 ] ; then
|
||||
umount "$tmpfs_dir"
|
||||
fi
|
||||
}
|
||||
do_onexit="remove_mnt"
|
||||
|
||||
# setuid apps mounted in a fs with "nosuid" option do not honor those
|
||||
# bits during execution, so run the test in a mounted tmpdir without nosuid
|
||||
FINDMNT=/bin/findmnt
|
||||
if [ -x "${FINDMNT}" ] && ${FINDMNT} -no TARGET,OPTIONS -T $tmpdir > /dev/null 2>&1 ; then
|
||||
output="$(${FINDMNT} -no TARGET,OPTIONS -T $tmpdir)"
|
||||
target="$(echo $output | cut -d' ' -f1)"
|
||||
options="$(echo $output | cut -d' ' -f2)"
|
||||
case "$options" in
|
||||
*nosuid* )
|
||||
echo " $target is mounted with nosuid, creating a new mountpoint..."
|
||||
setuid_helper=${tmpfs_dir}/env_check
|
||||
mkdir ${tmpfs_dir}
|
||||
mount -t tmpfs tmpfs ${tmpfs_dir}
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
cp $helper ${setuid_helper}
|
||||
chown nobody ${setuid_helper}
|
||||
chmod u+s ${setuid_helper}
|
||||
|
30
tests/regression/apparmor/mount.inc
Normal file
30
tests/regression/apparmor/mount.inc
Normal file
@@ -0,0 +1,30 @@
|
||||
root_was_shared="no"
|
||||
root="/"
|
||||
|
||||
# systemd mounts / and everything under it MS_SHARED. This breaks
|
||||
# pivot_root and mount "move" operations entirely, so attempt to
|
||||
# detect from which mount point the test is running from, and remount
|
||||
# it MS_PRIVATE temporarily.
|
||||
FINDMNT=/bin/findmnt
|
||||
if [ -x "${FINDMNT}" ] && ${FINDMNT} -no TARGET,PROPAGATION -T $tmpdir > /dev/null 2>&1 ; then
|
||||
output="$(${FINDMNT} -no TARGET,PROPAGATION -T $tmpdir)"
|
||||
root="$(echo $output | cut -d' ' -f1)"
|
||||
if [ "$(echo $output | cut -d' ' -f2)" == "shared" ] ; then
|
||||
root_was_shared="yes"
|
||||
fi
|
||||
elif [ "$(ps hp1 -ocomm)" = "systemd" ] ; then
|
||||
# no findmnt or findmnt doesn't know the PROPAGATION column,
|
||||
# but init is systemd so assume rootfs is shared
|
||||
root_was_shared="yes"
|
||||
fi
|
||||
if [ "${root_was_shared}" = "yes" ] ; then
|
||||
[ -n "$VERBOSE" ] && echo "notice: re-mounting $root as private"
|
||||
mount --make-private $root
|
||||
fi
|
||||
|
||||
prop_cleanup() {
|
||||
if [ "${root_was_shared}" = "yes" ] ; then
|
||||
[ -n "$VERBOSE" ] && echo "notice: re-mounting $root as shared"
|
||||
mount --make-shared $root
|
||||
fi
|
||||
}
|
@@ -32,7 +32,8 @@ mount_point2=$tmpdir/mountpoint2
|
||||
mount_bad=$tmpdir/mountbad
|
||||
loop_device="unset"
|
||||
fstype="ext2"
|
||||
root_was_shared="no"
|
||||
|
||||
. $bin/mount.inc
|
||||
|
||||
setup_mnt() {
|
||||
/bin/mount -n -t${fstype} ${loop_device} ${mount_point}
|
||||
@@ -59,9 +60,7 @@ mount_cleanup() {
|
||||
then
|
||||
/sbin/losetup -d ${loop_device} &> /dev/null
|
||||
fi
|
||||
if [ "${root_was_shared}" = "yes" ] ; then
|
||||
mount --make-shared /
|
||||
fi
|
||||
prop_cleanup
|
||||
}
|
||||
do_onexit="mount_cleanup"
|
||||
|
||||
@@ -81,23 +80,6 @@ fi
|
||||
loop_device=$(losetup -f) || fatalerror 'Unable to find a free loop device'
|
||||
/sbin/losetup "$loop_device" ${mount_file} > /dev/null 2> /dev/null
|
||||
|
||||
# systemd mounts / and everything under it MS_SHARED which does
|
||||
# not work with "move", so attempt to detect it, and remount /
|
||||
# MS_PRIVATE temporarily. snippet from pivot_root.sh
|
||||
FINDMNT=/bin/findmnt
|
||||
if [ -x "${FINDMNT}" ] && ${FINDMNT} -no PROPAGATION / > /dev/null 2>&1 ; then
|
||||
if [ "$(${FINDMNT} -no PROPAGATION /)" == "shared" ] ; then
|
||||
root_was_shared="yes"
|
||||
fi
|
||||
elif [ "$(ps hp1 -ocomm)" = "systemd" ] ; then
|
||||
# no findmnt or findmnt doesn't know the PROPAGATION column,
|
||||
# but init is systemd so assume rootfs is shared
|
||||
root_was_shared="yes"
|
||||
fi
|
||||
if [ "${root_was_shared}" = "yes" ] ; then
|
||||
mount --make-private /
|
||||
fi
|
||||
|
||||
options=(
|
||||
# default and non-default options
|
||||
"rw,ro"
|
||||
|
@@ -25,7 +25,8 @@ put_old=${new_root}put_old/
|
||||
bad=$tmpdir/BAD/
|
||||
proc=$new_root/proc
|
||||
fstype="ext2"
|
||||
root_was_shared="no"
|
||||
|
||||
. $bin/mount.inc
|
||||
|
||||
pivot_root_cleanup() {
|
||||
mountpoint -q "$proc"
|
||||
@@ -38,29 +39,13 @@ pivot_root_cleanup() {
|
||||
umount "$new_root"
|
||||
fi
|
||||
|
||||
if [ "${root_was_shared}" = "yes" ] ; then
|
||||
[ -n "$VERBOSE" ] && echo 'notice: re-mounting / as shared'
|
||||
mount --make-shared /
|
||||
fi
|
||||
prop_cleanup
|
||||
}
|
||||
do_onexit="pivot_root_cleanup"
|
||||
|
||||
# systemd mounts / and everything under it MS_SHARED. This breaks
|
||||
# pivot_root entirely, so attempt to detect it, and remount /
|
||||
# MS_PRIVATE temporarily.
|
||||
FINDMNT=/bin/findmnt
|
||||
if [ -x "${FINDMNT}" ] && ${FINDMNT} -no PROPAGATION / > /dev/null 2>&1 ; then
|
||||
if [ "$(${FINDMNT} -no PROPAGATION /)" = "shared" ] ; then
|
||||
root_was_shared="yes"
|
||||
fi
|
||||
elif [ "$(ps hp1 -ocomm)" = "systemd" ] ; then
|
||||
# no findmnt or findmnt doesn't know the PROPAGATION column,
|
||||
# but init is systemd so assume rootfs is shared
|
||||
root_was_shared="yes"
|
||||
fi
|
||||
if [ "${root_was_shared}" = "yes" ] ; then
|
||||
[ -n "$VERBOSE" ] && echo 'notice: re-mounting / as private'
|
||||
mount --make-private /
|
||||
# loopback module must be loaded for mount -o loop to work
|
||||
if [ ! -b /dev/loop0 ] ; then
|
||||
modprobe loop
|
||||
fi
|
||||
|
||||
# Create disk image since pivot_root doesn't allow old root and new root to be
|
||||
|
@@ -27,16 +27,38 @@ bin=$pwd
|
||||
## A. SWAP
|
||||
##
|
||||
|
||||
# check if we can run the test at all
|
||||
swap_file=$tmpdir/swapfile
|
||||
|
||||
# check if we can run the test in tmpdir
|
||||
fstype=$(stat -f --format '%T' "${tmpdir}")
|
||||
if [ "${fstype}" = "tmpfs" ] ; then
|
||||
echo "ERROR: tmpdir '${tmpdir}' is of type tmpfs; can't mount a swapfile on it" 1>&2
|
||||
echo "ERROR: skipping swap tests" 1>&2
|
||||
num_testfailures=1
|
||||
exit
|
||||
# create a mountpoint not tmpfs
|
||||
mount_file=$tmpdir/mountfile
|
||||
mount_point=$tmpdir/mountpoint
|
||||
fstype="ext2"
|
||||
dd if=/dev/zero of=${mount_file} bs=1024 count=900 2> /dev/null
|
||||
/sbin/mkfs -t${fstype} -F ${mount_file} > /dev/null 2> /dev/null
|
||||
/bin/mkdir ${mount_point}
|
||||
|
||||
loop_device=$(losetup -f) || fatalerror 'Unable to find a free loop device'
|
||||
/sbin/losetup "$loop_device" ${mount_file} > /dev/null 2> /dev/null
|
||||
|
||||
/bin/mount -n -t${fstype} ${loop_device} ${mount_point}
|
||||
|
||||
swap_file=$mount_point/swapfile
|
||||
fi
|
||||
|
||||
swap_file=$tmpdir/swapfile
|
||||
remove_mnt() {
|
||||
mountpoint -q "${mount_point}"
|
||||
if [ $? -eq 0 ] ; then
|
||||
/bin/umount -t${fstype} ${mount_point}
|
||||
fi
|
||||
if [ -n "$loop_device" ]
|
||||
then
|
||||
/sbin/losetup -d ${loop_device} &> /dev/null
|
||||
fi
|
||||
}
|
||||
do_onexit="remove_mnt"
|
||||
|
||||
# ppc64el wants this to be larger than 640KiB
|
||||
# arm/small machines want this as small as possible
|
||||
|
@@ -150,13 +150,19 @@ i386 | i486 | i586 | i686 | x86 | x86_64)
|
||||
# But don't run them on xen kernels
|
||||
if [ ! -d /proc/xen ] ; then
|
||||
|
||||
# lockdown thwarts both ioperm and iopl
|
||||
expected=pass
|
||||
if [ -f /sys/kernel/security/lockdown ] && ! grep -q "\[none\]" /sys/kernel/security/lockdown; then
|
||||
expected=fail
|
||||
fi
|
||||
|
||||
##
|
||||
## F. IOPERM
|
||||
##
|
||||
settest syscall_ioperm
|
||||
|
||||
# TEST F1
|
||||
runchecktest "IOPERM (no confinement)" pass 0 0x3ff
|
||||
runchecktest "IOPERM (no confinement)" $expected 0 0x3ff
|
||||
|
||||
# TEST F2. ioperm will fail
|
||||
genprofile
|
||||
@@ -169,7 +175,7 @@ runchecktest "IOPERM (confinement)" fail 0 0x3ff
|
||||
settest syscall_iopl
|
||||
|
||||
# TEST G1
|
||||
runchecktest "IOPL (no confinement)" pass 3
|
||||
runchecktest "IOPL (no confinement)" $expected 3
|
||||
|
||||
# TEST G2. iopl will fail
|
||||
genprofile
|
||||
|
@@ -14,8 +14,14 @@ pwd=`cd $pwd ; /bin/pwd`
|
||||
|
||||
bin=$pwd
|
||||
|
||||
# TODO:
|
||||
# need to update so we can run the test for ech supported
|
||||
# need to be able to modify the compile features to choose the
|
||||
# kernel feature supported
|
||||
# need to be able to query the parser if it supports the
|
||||
# kernel feature
|
||||
. $bin/prologue.inc
|
||||
requires_kernel_features network
|
||||
requires_any_of_kernel_features network network_v8
|
||||
|
||||
port=34567
|
||||
ip="127.0.0.1"
|
||||
|
@@ -104,7 +104,7 @@ def notify_about_new_entries(logfile, wait=0):
|
||||
debug_logger.info(format_event(event, logfile))
|
||||
yield(format_event(event, logfile))
|
||||
except PermissionError:
|
||||
sys.exit(_("ERROR: Cannot read {}. Please check permissions.".format(logfile)))
|
||||
sys.exit(_("ERROR: Cannot read {}. Please check permissions.").format(logfile))
|
||||
|
||||
else:
|
||||
print(_('Notification emitter started in the background'))
|
||||
@@ -255,18 +255,18 @@ def get_apparmor_events(logfile, since=0):
|
||||
def parse_logdata(logsource):
|
||||
'''Traverse any iterable log source and extract relevant AppArmor events'''
|
||||
|
||||
RE_audit_time_id = '(msg=)?audit\([\d\.\:]+\):\s+' # 'audit(1282626827.320:411): '
|
||||
RE_kernel_time = '\[[\d\.\s]+\]' # '[ 1612.746129]'
|
||||
RE_type_num = '1[45][0-9][0-9]' # 1400..1599
|
||||
RE_aa_or_op = '(apparmor=|operation=)'
|
||||
RE_audit_time_id = r'(msg=)?audit\([\d\.\:]+\):\s+' # 'audit(1282626827.320:411): '
|
||||
RE_kernel_time = r'\[[\d\.\s]+\]' # '[ 1612.746129]'
|
||||
RE_type_num = r'1[45][0-9][0-9]' # 1400..1599
|
||||
RE_aa_or_op = r'(apparmor=|operation=)'
|
||||
|
||||
RE_log_parts = [
|
||||
'kernel:\s+(' + RE_kernel_time + '\s+)?(audit:\s+)?type=' + RE_type_num + '\s+' + RE_audit_time_id + RE_aa_or_op, # v2_6 syslog
|
||||
'kernel:\s+(' + RE_kernel_time + '\s+)?' + RE_audit_time_id + 'type=' + RE_type_num + '\s+' + RE_aa_or_op,
|
||||
'type=(AVC|APPARMOR[_A-Z]*|' + RE_type_num + ')\s+' + RE_audit_time_id + '(type=' + RE_type_num + '\s+)?' + RE_aa_or_op, # v2_6 audit and dmesg
|
||||
'type=USER_AVC\s+' + RE_audit_time_id + '.*apparmor=', # dbus
|
||||
'type=UNKNOWN\[' + RE_type_num + '\]\s+' + RE_audit_time_id + RE_aa_or_op,
|
||||
'dbus\[[0-9]+\]:\s+apparmor=', # dbus
|
||||
r'kernel:\s+(' + RE_kernel_time + r'\s+)?(audit:\s+)?type=' + RE_type_num + r'\s+' + RE_audit_time_id + RE_aa_or_op, # v2_6 syslog
|
||||
r'kernel:\s+(' + RE_kernel_time + r'\s+)?' + RE_audit_time_id + r'type=' + RE_type_num + r'\s+' + RE_aa_or_op,
|
||||
r'type=(AVC|APPARMOR[_A-Z]*|' + RE_type_num + r')\s+' + RE_audit_time_id + r'(type=' + RE_type_num + r'\s+)?' + RE_aa_or_op, # v2_6 audit and dmesg
|
||||
r'type=USER_AVC\s+' + RE_audit_time_id + r'.*apparmor=', # dbus
|
||||
r'type=UNKNOWN\[' + RE_type_num + r'\]\s+' + RE_audit_time_id + RE_aa_or_op,
|
||||
r'dbus\[[0-9]+\]:\s+apparmor=', # dbus
|
||||
]
|
||||
|
||||
# Pre-filter log lines so that we hand over only relevant lines to LibAppArmor parsing
|
||||
|
@@ -102,17 +102,31 @@ def get_pids_netstat(netstat='netstat'):
|
||||
def read_proc_current(filename):
|
||||
attr = None
|
||||
|
||||
if os.path.exists(filename):
|
||||
try:
|
||||
# don't bother with if os.path.exists(filename): there is always a race
|
||||
with apparmor.common.open_file_read(filename) as current:
|
||||
for line in current:
|
||||
line = line.strip()
|
||||
if line.endswith(' (complain)', 1) or line.endswith(' (enforce)', 1) or line.endswith(' (kill)', 1): # enforce at least one char as profile name
|
||||
# intentionally not checking for '(unconfined)', because $binary confined by $profile (unconfined) would look very confusing
|
||||
attr = line
|
||||
except OSError:
|
||||
# just ignore errors atm
|
||||
# print("Error trying to open {filename}")
|
||||
return None
|
||||
|
||||
return attr
|
||||
|
||||
|
||||
def escape_special_chars(data):
|
||||
"""escape special characters in program names so that they can't mess up the terminal"""
|
||||
data = repr(data)
|
||||
if len(data) > 1 and data.startswith("'") and data.endswith("'"):
|
||||
return data[1:-1]
|
||||
else:
|
||||
return data
|
||||
|
||||
|
||||
pids = set()
|
||||
if paranoid:
|
||||
pids = get_all_pids()
|
||||
@@ -124,6 +138,7 @@ else:
|
||||
for pid in sorted(map(int, pids)):
|
||||
try:
|
||||
prog = os.readlink("/proc/%s/exe" % pid)
|
||||
prog = escape_special_chars(prog)
|
||||
except OSError:
|
||||
continue
|
||||
|
||||
@@ -140,6 +155,7 @@ for pid in sorted(map(int, pids)):
|
||||
pname = cmdline.split("\0")[0]
|
||||
if '/' in pname and pname != prog:
|
||||
pname = "(%s)" % pname
|
||||
pname = escape_special_chars(pname)
|
||||
else:
|
||||
pname = ""
|
||||
regex_interpreter = re.compile(r"^(/usr)?/bin/(python|perl|bash|dash|sh)$")
|
||||
@@ -147,6 +163,7 @@ for pid in sorted(map(int, pids)):
|
||||
if regex_interpreter.search(prog):
|
||||
cmdline = re.sub(r"\x00", " ", cmdline)
|
||||
cmdline = re.sub(r"\s+$", "", cmdline).strip()
|
||||
cmdline = escape_special_chars(cmdline)
|
||||
|
||||
ui.UI_Info(_("%(pid)s %(program)s (%(commandline)s) not confined") % {'pid': pid, 'program': prog, 'commandline': cmdline})
|
||||
else:
|
||||
@@ -157,6 +174,7 @@ for pid in sorted(map(int, pids)):
|
||||
if regex_interpreter.search(prog):
|
||||
cmdline = re.sub(r"\0", " ", cmdline)
|
||||
cmdline = re.sub(r"\s+$", "", cmdline).strip()
|
||||
cmdline = escape_special_chars(cmdline)
|
||||
ui.UI_Info(_("%(pid)s %(program)s (%(commandline)s) confined by '%(attribute)s'") % {'pid': pid, 'program': prog, 'commandline': cmdline, 'attribute': attr})
|
||||
else:
|
||||
if pname and pname[-1] == ')':
|
||||
|
@@ -812,22 +812,17 @@ def ask_exec(hashlog):
|
||||
'''ask the user about exec events (requests to execute another program) and which exec mode to use'''
|
||||
|
||||
for aamode in hashlog:
|
||||
for profile in hashlog[aamode]:
|
||||
if '//' in hashlog[aamode][profile]['final_name'] and hashlog[aamode][profile]['exec'].keys():
|
||||
# TODO: is this really needed? Or would removing Cx from the options be good enough?
|
||||
aaui.UI_Important('WARNING: Ignoring exec event in %s, nested profiles are not supported yet.' % hashlog[aamode][profile]['final_name'])
|
||||
continue
|
||||
for full_profile in hashlog[aamode]:
|
||||
profile, hat = split_name(full_profile) # XXX temporary solution to avoid breaking the existing code
|
||||
|
||||
hat = profile # XXX temporary solution to avoid breaking the existing code
|
||||
|
||||
for exec_target in hashlog[aamode][profile]['exec']:
|
||||
for target_profile in hashlog[aamode][profile]['exec'][exec_target]:
|
||||
for exec_target in hashlog[aamode][full_profile]['exec']:
|
||||
for target_profile in hashlog[aamode][full_profile]['exec'][exec_target]:
|
||||
to_name = ''
|
||||
|
||||
if os.path.isdir(exec_target):
|
||||
raise AppArmorBug('exec permissions requested for directory %s. This should not happen - please open a bugreport!' % exec_target)
|
||||
|
||||
if not aa[profile][hat]:
|
||||
if not aa[profile].get(hat):
|
||||
continue # ignore log entries for non-existing profiles
|
||||
|
||||
exec_event = FileRule(exec_target, None, FileRule.ANY_EXEC, FileRule.ALL, owner=False, log_event=True)
|
||||
@@ -848,7 +843,9 @@ def ask_exec(hashlog):
|
||||
##options = 'i'
|
||||
|
||||
# Don't allow hats to cx?
|
||||
options.replace('c', '')
|
||||
if '//' in hashlog[aamode][full_profile]['final_name'] and hashlog[aamode][full_profile]['exec'].keys():
|
||||
options = options.replace('c', '')
|
||||
|
||||
# Add deny to options
|
||||
options += 'd'
|
||||
# Define the default option
|
||||
@@ -1661,6 +1658,9 @@ def collapse_log(hashlog, ignore_null_profiles=True):
|
||||
|
||||
ptrace = hashlog[aamode][full_profile]['ptrace']
|
||||
for peer in ptrace.keys():
|
||||
if '//null-' in peer:
|
||||
continue # ignore null-* peers
|
||||
|
||||
for access in ptrace[peer].keys():
|
||||
ptrace_event = PtraceRule(access, peer, log_event=True)
|
||||
if not hat_exists or not is_known_rule(aa[profile][hat], 'ptrace', ptrace_event):
|
||||
@@ -1668,6 +1668,9 @@ def collapse_log(hashlog, ignore_null_profiles=True):
|
||||
|
||||
sig = hashlog[aamode][full_profile]['signal']
|
||||
for peer in sig.keys():
|
||||
if '//null-' in peer:
|
||||
continue # ignore null-* peers
|
||||
|
||||
for access in sig[peer].keys():
|
||||
for signal in sig[peer][access].keys():
|
||||
signal_event = SignalRule(access, signal, peer, log_event=True)
|
||||
|
@@ -17,6 +17,7 @@ import re
|
||||
import sys
|
||||
import time
|
||||
import LibAppArmor
|
||||
import apparmor.ui as aaui
|
||||
from apparmor.common import AppArmorException, AppArmorBug, hasher, open_file_read, split_name, DebugLogger
|
||||
|
||||
# setup module translations
|
||||
@@ -82,7 +83,13 @@ class ReadLog:
|
||||
if sys.version_info < (3, 0):
|
||||
# parse_record fails with u'foo' style strings hence typecasting to string
|
||||
msg = str(msg)
|
||||
event = LibAppArmor.parse_record(msg)
|
||||
|
||||
try:
|
||||
event = LibAppArmor.parse_record(msg)
|
||||
except TypeError:
|
||||
aaui.UI_Important(_("WARNING: Cannot process log message, skipping entry. Make sure log is plaintext."))
|
||||
return None
|
||||
|
||||
ev = dict()
|
||||
ev['resource'] = event.info
|
||||
ev['active_hat'] = event.active_hat
|
||||
|
@@ -34,8 +34,8 @@ class ProfileList:
|
||||
|
||||
def __init__(self):
|
||||
self.profile_names = {} # profile name -> filename
|
||||
self.attachments = {} # attachment -> filename
|
||||
self.attachments_AARE = {} # AARE(attachment) -> filename
|
||||
self.attachments = {} # attachment -> {'f': filename, 'p': profile}
|
||||
self.attachments_AARE = {} # attachment -> AARE(attachment)
|
||||
self.files = {} # filename -> content - see init_file()
|
||||
|
||||
def __repr__(self):
|
||||
@@ -72,7 +72,7 @@ class ProfileList:
|
||||
self.profile_names[profile_name] = filename
|
||||
|
||||
if attachment:
|
||||
self.attachments[attachment] = filename
|
||||
self.attachments[attachment] = {'f': filename, 'p': profile_name or attachment} # if a profile doesn't have a name, the attachment is stored as profile name
|
||||
self.attachments_AARE[attachment] = AARE(attachment, True)
|
||||
|
||||
self.init_file(filename)
|
||||
@@ -164,18 +164,28 @@ class ProfileList:
|
||||
|
||||
def filename_from_attachment(self, attachment):
|
||||
''' Return profile filename for the given attachment/executable path, or None '''
|
||||
return self.thing_from_attachment(attachment, 'f')
|
||||
|
||||
def profile_from_attachment(self, attachment):
|
||||
"""Return profile filename for the given attachment/executable path, or None"""
|
||||
return self.thing_from_attachment(attachment, 'p')
|
||||
|
||||
def thing_from_attachment(self, attachment, thing):
|
||||
"""Return thing for the given attachment/executable path, or None.
|
||||
|
||||
thing can be 'f' for filename or 'p' for profile name"""
|
||||
|
||||
if not attachment.startswith( ('/', '@', '{') ):
|
||||
raise AppArmorBug('Called filename_from_attachment with non-path attachment: %s' % attachment)
|
||||
|
||||
# plain path
|
||||
if self.attachments.get(attachment):
|
||||
return self.attachments[attachment]
|
||||
return self.attachments[attachment][thing]
|
||||
|
||||
# try AARE matches to cover profile names with alternations and wildcards
|
||||
for path in self.attachments.keys():
|
||||
if self.attachments_AARE[path].match(attachment):
|
||||
return self.attachments[path] # XXX this returns the first match, not necessarily the best one
|
||||
return self.attachments[path][thing] # XXX this returns the first match, not necessarily the best one
|
||||
|
||||
return None # nothing found
|
||||
|
||||
|
@@ -26,7 +26,7 @@ allow_exec_transitions = ('ix', 'ux', 'Ux', 'px', 'Px', 'cx', 'Cx') #
|
||||
allow_exec_fallback_transitions = ('pix', 'Pix', 'cix', 'Cix', 'pux', 'PUx', 'cux', 'CUx') # 3 chars - len relevant for split_perms()
|
||||
deny_exec_transitions = ('x')
|
||||
file_permissions = ('m', 'r', 'w', 'a', 'l', 'k', 'link', 'subset') # also defines the write order
|
||||
|
||||
implicit_all_permissions = ('m', 'r', 'w', 'l', 'k')
|
||||
|
||||
|
||||
class FileRule(BaseRule):
|
||||
@@ -234,7 +234,7 @@ class FileRule(BaseRule):
|
||||
if self.all_paths and self.all_perms and not path and not perms and not target:
|
||||
return('%s%s%sfile,%s' % (space, self.modifiers_str(), owner, self.comment)) # plain 'file,' rule
|
||||
elif not self.all_paths and not self.all_perms and path and perms:
|
||||
return('%s%s%s%s%s%s,%s' % (space, self.modifiers_str(), file_keyword, owner, path_and_perms, target, self.comment))
|
||||
return ('%s%s%s%s%s%s,%s' % (space, self.modifiers_str(), owner, file_keyword, path_and_perms, target, self.comment))
|
||||
else:
|
||||
raise AppArmorBug('Invalid combination of path and perms in file rule - either specify path and perms, or none of them')
|
||||
|
||||
@@ -366,8 +366,21 @@ class FileRule(BaseRule):
|
||||
|
||||
old_mode = ''
|
||||
if self.original_perms:
|
||||
original_perms_all = self._join_given_perms(self.original_perms['allow']['all'], None)
|
||||
original_perms_owner = self._join_given_perms(self.original_perms['allow']['owner'] - self.original_perms['allow']['all'], None) # only list owner perms that are not covered by other perms
|
||||
original_perms_set = {}
|
||||
for who in ['all', 'owner']:
|
||||
original_perms_set[who] = {}
|
||||
original_perms_set[who]['perms'] = self.original_perms['allow'][who]
|
||||
original_perms_set[who]['exec_perms'] = None
|
||||
|
||||
if self.original_perms['allow'][who] == FileRule.ALL:
|
||||
original_perms_set[who]['perms'] = set(implicit_all_permissions)
|
||||
original_perms_set[who]['exec_perms'] = 'ix'
|
||||
|
||||
original_perms_all = self._join_given_perms(original_perms_set['all']['perms'],
|
||||
original_perms_set['all']['exec_perms'])
|
||||
original_perms_owner = self._join_given_perms(
|
||||
original_perms_set['owner']['perms'] - original_perms_set['all']['perms'], # only list owner perms that are not covered by other perms
|
||||
original_perms_set['owner']['exec_perms'])
|
||||
|
||||
if original_perms_all and original_perms_owner:
|
||||
old_mode = '%s + owner %s' % (original_perms_all, original_perms_owner)
|
||||
|
@@ -61,15 +61,27 @@ class aa_tools:
|
||||
profile = fq_path
|
||||
else:
|
||||
program = fq_path
|
||||
profile = apparmor.get_profile_filename_from_attachment(fq_path, True)
|
||||
if self.name == 'cleanprof':
|
||||
profile = apparmor.active_profiles.profile_from_attachment(fq_path)
|
||||
else:
|
||||
profile = apparmor.get_profile_filename_from_attachment(fq_path, True)
|
||||
else:
|
||||
which = apparmor.which(p)
|
||||
if which is not None:
|
||||
if self.name == 'cleanprof' and p in apparmor.aa:
|
||||
program = p # not really correct, but works
|
||||
profile = p
|
||||
elif which is not None:
|
||||
program = apparmor.get_full_path(which)
|
||||
profile = apparmor.get_profile_filename_from_attachment(program, True)
|
||||
if self.name == 'cleanprof':
|
||||
profile = program
|
||||
else:
|
||||
profile = apparmor.get_profile_filename_from_attachment(program, True)
|
||||
elif os.path.exists(os.path.join(apparmor.profile_dir, p)):
|
||||
program = None
|
||||
profile = apparmor.get_full_path(os.path.join(apparmor.profile_dir, p)).strip()
|
||||
if self.name == 'cleanprof':
|
||||
profile = p
|
||||
else:
|
||||
profile = apparmor.get_full_path(os.path.join(apparmor.profile_dir, p)).strip()
|
||||
else:
|
||||
if '/' not in p:
|
||||
aaui.UI_Info(_("Can't find %(program)s in the system path list. If the name of the application\nis correct, please run 'which %(program)s' as a user with correct PATH\nenvironment set up in order to find the fully-qualified path and\nuse the full path as parameter.") % { 'program': p })
|
||||
@@ -87,15 +99,15 @@ class aa_tools:
|
||||
if program is None:
|
||||
program = profile
|
||||
|
||||
if not program or not(os.path.exists(program) or apparmor.profile_exists(program)):
|
||||
if not program or not(os.path.exists(program) or profile in apparmor.aa):
|
||||
if program and not program.startswith('/'):
|
||||
program = aaui.UI_GetString(_('The given program cannot be found, please try with the fully qualified path name of the program: '), '')
|
||||
else:
|
||||
aaui.UI_Info(_("%s does not exist, please double-check the path.") % program)
|
||||
sys.exit(1)
|
||||
|
||||
if program and apparmor.profile_exists(program):
|
||||
self.clean_profile(program)
|
||||
if program and profile in apparmor.aa:
|
||||
self.clean_profile(program, profile)
|
||||
|
||||
else:
|
||||
if '/' not in program:
|
||||
@@ -193,14 +205,14 @@ class aa_tools:
|
||||
if self.aa_mountpoint:
|
||||
apparmor.reload(program)
|
||||
|
||||
def clean_profile(self, program):
|
||||
filename = apparmor.get_profile_filename_from_attachment(program, True)
|
||||
def clean_profile(self, program, profile):
|
||||
filename = apparmor.get_profile_filename_from_profile_name(profile)
|
||||
import apparmor.cleanprofile as cleanprofile
|
||||
prof = cleanprofile.Prof(filename)
|
||||
cleanprof = cleanprofile.CleanProf(True, prof, prof)
|
||||
deleted = cleanprof.remove_duplicate_rules(program)
|
||||
deleted = cleanprof.remove_duplicate_rules(profile)
|
||||
aaui.UI_Info(_("\nDeleted %s rules.") % deleted)
|
||||
apparmor.changed[program] = True
|
||||
apparmor.changed[profile] = True
|
||||
|
||||
if filename:
|
||||
if not self.silent:
|
||||
@@ -216,14 +228,14 @@ class aa_tools:
|
||||
while ans != 'CMD_SAVE_CHANGES':
|
||||
ans, arg = q.promptUser()
|
||||
if ans == 'CMD_SAVE_CHANGES':
|
||||
apparmor.write_profile_ui_feedback(program, True)
|
||||
apparmor.write_profile_ui_feedback(profile)
|
||||
self.reload_profile(filename)
|
||||
elif ans == 'CMD_VIEW_CHANGES':
|
||||
#oldprofile = apparmor.serialize_profile(apparmor.original_aa[program], program, {})
|
||||
newprofile = apparmor.serialize_profile(apparmor.aa[program], program, {'is_attachment': True})
|
||||
#oldprofile = apparmor.serialize_profile(apparmor.original_aa[profile], program, {})
|
||||
newprofile = apparmor.serialize_profile(apparmor.aa[profile], profile, {}) # {'is_attachment': True})
|
||||
aaui.UI_Changes(filename, newprofile, comments=True)
|
||||
else:
|
||||
apparmor.write_profile_ui_feedback(program, True)
|
||||
apparmor.write_profile_ui_feedback(profile, True)
|
||||
self.reload_profile(filename)
|
||||
else:
|
||||
raise apparmor.AppArmorException(_('The profile for %s does not exists. Nothing to clean.') % program)
|
||||
|
@@ -21,16 +21,17 @@ COMMONDIR=../../common/
|
||||
include $(COMMONDIR)/Make.rules
|
||||
|
||||
ifdef USE_SYSTEM
|
||||
LD_LIBRARY_PATH=
|
||||
PYTHONPATH=
|
||||
LD_LIBRARY_PATH?=
|
||||
PYTHONPATH?=
|
||||
CONFDIR=
|
||||
BASEDIR=
|
||||
PARSER=
|
||||
else
|
||||
PYTHON_DIST_BUILD_PATH = ../../libraries/libapparmor/swig/python/build/$$($(PYTHON) ../../libraries/libapparmor/swig/python/test/buildpath.py)
|
||||
LIBAPPARMOR_PATH=../../libraries/libapparmor/src/.libs/
|
||||
LD_LIBRARY_PATH=$(LIBAPPARMOR_PATH):$(PYTHON_DIST_BUILD_PATH)
|
||||
PYTHONPATH=..:$(PYTHON_DIST_BUILD_PATH)
|
||||
LIBAPPARMOR_BASEDIR?=../../libraries/libapparmor
|
||||
PYTHON_DIST_BUILD_PATH ?= $(LIBAPPARMOR_BASEDIR)/swig/python/build/$$($(PYTHON) $(LIBAPPARMOR_BASEDIR)/swig/python/test/buildpath.py)
|
||||
LIBAPPARMOR_PATH?=$(LIBAPPARMOR_BASEDIR)/src/.libs/
|
||||
LD_LIBRARY_PATH:=$(LD_LIBRARY_PATH):$(LIBAPPARMOR_PATH):$(PYTHON_DIST_BUILD_PATH)
|
||||
PYTHONPATH:=$(PYTHONPATH):..:$(PYTHON_DIST_BUILD_PATH)
|
||||
CONFDIR=$(CURDIR)
|
||||
BASEDIR=../../profiles/apparmor.d
|
||||
PARSER=../../parser/apparmor_parser
|
||||
|
@@ -11,11 +11,13 @@
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
import os
|
||||
import pwd
|
||||
import signal
|
||||
import subprocess
|
||||
import tempfile
|
||||
import time
|
||||
import unittest
|
||||
from datetime import datetime
|
||||
|
||||
from common_test import AATest, setup_all_loops, setup_aa
|
||||
import apparmor.aa as aa
|
||||
@@ -64,8 +66,8 @@ def cmd(command):
|
||||
|
||||
class AANotifyTest(AATest):
|
||||
|
||||
def AASetup(self):
|
||||
'''Create temporary log file with 30 enties of different age'''
|
||||
def create_logfile_contents(self, _time):
|
||||
'''Create temporary log file with 30 entries of different age'''
|
||||
|
||||
test_logfile_contents_999_days_old = \
|
||||
'''Feb 4 13:40:38 XPS-13-9370 kernel: [128552.834382] audit: type=1400 audit({epoch}:113): apparmor="ALLOWED" operation="exec" profile="libreoffice-soffice" name="/bin/uname" pid=4097 comm="sh" requested_mask="x" denied_mask="x" fsuid=1001 ouid=0 target="libreoffice-soffice//null-/bin/uname"
|
||||
@@ -78,7 +80,7 @@ Feb 4 13:40:38 XPS-13-9370 kernel: [128552.835421] audit: type=1400 audit({epoc
|
||||
Feb 4 13:40:38 XPS-13-9370 kernel: [128552.835696] audit: type=1400 audit({epoch}:120): apparmor="ALLOWED" operation="open" profile="libreoffice-soffice//null-/bin/uname" name="/usr/lib/locale/locale-archive" pid=4097 comm="uname" requested_mask="r" denied_mask="r" fsuid=1001 ouid=0
|
||||
Feb 4 13:40:38 XPS-13-9370 kernel: [128552.875891] audit: type=1400 audit({epoch}:121): apparmor="ALLOWED" operation="exec" profile="libreoffice-soffice" name="/usr/bin/file" pid=4111 comm="soffice.bin" requested_mask="x" denied_mask="x" fsuid=1001 ouid=0 target="libreoffice-soffice//null-/usr/bin/file"
|
||||
Feb 4 13:40:38 XPS-13-9370 kernel: [128552.880347] audit: type=1400 audit({epoch}:122): apparmor="ALLOWED" operation="file_mmap" profile="libreoffice-soffice//null-/usr/bin/file" name="/usr/bin/file" pid=4111 comm="file" requested_mask="rm" denied_mask="rm" fsuid=1001 ouid=0
|
||||
'''.format(epoch=round(time.time(), 3) - 60*60*24*999)
|
||||
'''.format(epoch=round(_time, 3) - 60 * 60 * 24 * 999)
|
||||
|
||||
test_logfile_contents_30_days_old = \
|
||||
'''Feb 4 13:40:38 XPS-13-9370 kernel: [128552.834382] audit: type=1400 audit({epoch}:113): apparmor="ALLOWED" operation="exec" profile="libreoffice-soffice" name="/bin/uname" pid=4097 comm="sh" requested_mask="x" denied_mask="x" fsuid=1001 ouid=0 target="libreoffice-soffice//null-/bin/uname"
|
||||
@@ -91,7 +93,7 @@ Feb 4 13:40:38 XPS-13-9370 kernel: [128552.835421] audit: type=1400 audit({epoc
|
||||
Feb 4 13:40:38 XPS-13-9370 kernel: [128552.835696] audit: type=1400 audit({epoch}:120): apparmor="ALLOWED" operation="open" profile="libreoffice-soffice//null-/bin/uname" name="/usr/lib/locale/locale-archive" pid=4097 comm="uname" requested_mask="r" denied_mask="r" fsuid=1001 ouid=0
|
||||
Feb 4 13:40:38 XPS-13-9370 kernel: [128552.875891] audit: type=1400 audit({epoch}:121): apparmor="ALLOWED" operation="exec" profile="libreoffice-soffice" name="/usr/bin/file" pid=4111 comm="soffice.bin" requested_mask="x" denied_mask="x" fsuid=1001 ouid=0 target="libreoffice-soffice//null-/usr/bin/file"
|
||||
Feb 4 13:40:38 XPS-13-9370 kernel: [128552.880347] audit: type=1400 audit({epoch}:122): apparmor="ALLOWED" operation="file_mmap" profile="libreoffice-soffice//null-/usr/bin/file" name="/usr/bin/file" pid=4111 comm="file" requested_mask="rm" denied_mask="rm" fsuid=1001 ouid=0
|
||||
'''.format(epoch=round(time.time(), 3) - 60*60*24*30)
|
||||
'''.format(epoch=round(_time, 3) - 60 * 60 * 24 * 30)
|
||||
|
||||
test_logfile_contents_unrelevant_entries = \
|
||||
'''Feb 1 19:35:44 XPS-13-9370 kernel: [99848.048761] audit: type=1400 audit(1549042544.968:72): apparmor="STATUS" operation="profile_load" profile="unconfined" name="/snap/core/6350/usr/lib/snapd/snap-confine" pid=12871 comm="apparmor_parser"
|
||||
@@ -110,24 +112,65 @@ Feb 4 13:40:38 XPS-13-9370 kernel: [128552.835421] audit: type=1400 audit({epoc
|
||||
Feb 4 13:40:38 XPS-13-9370 kernel: [128552.835696] audit: type=1400 audit({epoch}:120): apparmor="ALLOWED" operation="open" profile="libreoffice-soffice//null-/bin/uname" name="/usr/lib/locale/locale-archive" pid=4097 comm="uname" requested_mask="r" denied_mask="r" fsuid=1001 ouid=0
|
||||
Feb 4 13:40:38 XPS-13-9370 kernel: [128552.875891] audit: type=1400 audit({epoch}:121): apparmor="ALLOWED" operation="exec" profile="libreoffice-soffice" name="/usr/bin/file" pid=4111 comm="soffice.bin" requested_mask="x" denied_mask="x" fsuid=1001 ouid=0 target="libreoffice-soffice//null-/usr/bin/file"
|
||||
Feb 4 13:40:38 XPS-13-9370 kernel: [128552.880347] audit: type=1400 audit({epoch}:122): apparmor="ALLOWED" operation="file_mmap" profile="libreoffice-soffice//null-/usr/bin/file" name="/usr/bin/file" pid=4111 comm="file" requested_mask="rm" denied_mask="rm" fsuid=1001 ouid=0
|
||||
'''.format(epoch=round(time.time(), 3))
|
||||
'''.format(epoch=round(_time, 3))
|
||||
|
||||
handle, self.test_logfile = tempfile.mkstemp(prefix='test-aa-notify-')
|
||||
os.close(handle)
|
||||
handle = open(self.test_logfile, "w+")
|
||||
handle.write(
|
||||
test_logfile_contents_999_days_old +
|
||||
test_logfile_contents_30_days_old +
|
||||
test_logfile_contents_unrelevant_entries +
|
||||
test_logfile_contents_0_seconds_old
|
||||
)
|
||||
handle.close()
|
||||
return test_logfile_contents_999_days_old \
|
||||
+ test_logfile_contents_30_days_old \
|
||||
+ test_logfile_contents_unrelevant_entries \
|
||||
+ test_logfile_contents_0_seconds_old
|
||||
|
||||
def AASetup(self):
|
||||
file_current, self.test_logfile_current = tempfile.mkstemp(prefix='test-aa-notify-')
|
||||
os.close(file_current)
|
||||
file_current = open(self.test_logfile_current, "w+")
|
||||
|
||||
file_last_login, self.test_logfile_last_login = tempfile.mkstemp(prefix='test-aa-notify-')
|
||||
os.close(file_last_login)
|
||||
file_last_login = open(self.test_logfile_last_login, "w+")
|
||||
|
||||
current_time_contents = self.create_logfile_contents(time.time())
|
||||
file_current.write(current_time_contents)
|
||||
file_current.close()
|
||||
|
||||
if os.path.isfile('/var/log/wtmp'):
|
||||
if os.name == "posix":
|
||||
username = pwd.getpwuid(os.geteuid()).pw_name
|
||||
else:
|
||||
username = os.environ.get('USER')
|
||||
if not username and hasattr(os, 'getlogin'):
|
||||
username = os.getlogin()
|
||||
if 'SUDO_USER' in os.environ:
|
||||
username = os.environ.get('SUDO_USER')
|
||||
|
||||
return_code, output = cmd(['last', username, '--time-format', 'iso'])
|
||||
output = output.split('\n')[0] # the first line is enough
|
||||
# example of output (util-linux last command):
|
||||
# ubuntu tty7 :0 2024-01-05T14:29:11-03:00 gone - no logout
|
||||
# example of output (wtmpdb last command, local login):
|
||||
# ubuntu tty7 2025-01-15T09:32:49-0800 - still logged in
|
||||
# example of output (wtmpdb last command, remote login)
|
||||
# ubuntu tty7 192.168.122.1 2024-01-05T14:29:11-03:00 gone - no logout
|
||||
if output.startswith(username):
|
||||
# Check both possible columns for the date
|
||||
try:
|
||||
last_login = output.split()[3]
|
||||
last_login_epoch = datetime.fromisoformat(last_login).timestamp()
|
||||
except (IndexError, ValueError):
|
||||
last_login = output.split()[2]
|
||||
last_login_epoch = datetime.fromisoformat(last_login).timestamp()
|
||||
|
||||
# add 60 seconds to the epoch so that the time in the logs are AFTER login time
|
||||
last_login_contents = self.create_logfile_contents(last_login_epoch + 60)
|
||||
file_last_login.write(last_login_contents)
|
||||
file_last_login.close()
|
||||
|
||||
def AATeardown(self):
|
||||
'''Remove temporary log file after tests ended'''
|
||||
|
||||
if self.test_logfile and os.path.exists(self.test_logfile):
|
||||
os.remove(self.test_logfile)
|
||||
if self.test_logfile_current and os.path.exists(self.test_logfile_current):
|
||||
os.remove(self.test_logfile_current)
|
||||
if self.test_logfile_last_login and os.path.exists(self.test_logfile_last_login):
|
||||
os.remove(self.test_logfile_last_login)
|
||||
|
||||
# The Perl aa-notify script was written so, that it will checked for kern.log
|
||||
# before printing help when invoked without arguments (sic!).
|
||||
@@ -186,7 +229,7 @@ Display AppArmor notifications or messages for DENIED entries.
|
||||
expected_return_code = 0
|
||||
expected_output_has = 'AppArmor denials: 20 (since'
|
||||
|
||||
return_code, output = cmd([aanotify_bin, '-f', self.test_logfile, '-s', '100'])
|
||||
return_code, output = cmd([aanotify_bin, '-f', self.test_logfile_current, '-s', '100'])
|
||||
result = 'Got return code %d, expected %d\n' % (return_code, expected_return_code)
|
||||
self.assertEqual(expected_return_code, return_code, result + output)
|
||||
result = 'Got output "%s", expected "%s"\n' % (output, expected_output_has)
|
||||
@@ -199,7 +242,7 @@ Display AppArmor notifications or messages for DENIED entries.
|
||||
expected_return_code = 0
|
||||
expected_output_has = 'AppArmor denials: 10 (since'
|
||||
|
||||
return_code, output = cmd([aanotify_bin, '-f', self.test_logfile, '-l'])
|
||||
return_code, output = cmd([aanotify_bin, '-f', self.test_logfile_last_login, '-l'])
|
||||
if "ERROR: Could not find last login" in output:
|
||||
self.skipTest('Could not find last login')
|
||||
result = 'Got return code %d, expected %d\n' % (return_code, expected_return_code)
|
||||
@@ -273,9 +316,9 @@ Name: /usr/bin/file
|
||||
Denied: rm
|
||||
Logfile: {logfile}
|
||||
|
||||
AppArmor denials: 10 (since'''.format(logfile=self.test_logfile)
|
||||
AppArmor denials: 10 (since'''.format(logfile=self.test_logfile_last_login)
|
||||
|
||||
return_code, output = cmd([aanotify_bin, '-f', self.test_logfile, '-l', '-v'])
|
||||
return_code, output = cmd([aanotify_bin, '-f', self.test_logfile_last_login, '-l', '-v'])
|
||||
if "ERROR: Could not find last login" in output:
|
||||
self.skipTest('Could not find last login')
|
||||
result = 'Got return code %d, expected %d\n' % (return_code, expected_return_code)
|
||||
|
@@ -393,6 +393,9 @@ class WriteFileTest(AATest):
|
||||
(' deny file /foo r,' , 'deny file /foo r,'),
|
||||
(' deny file /foo wr,' , 'deny file /foo rw,'),
|
||||
(' allow file /foo Pxrm -> bar,' , 'allow file /foo mrPx -> bar,'),
|
||||
(' deny owner file /foo r,' , 'deny owner file /foo r,'),
|
||||
(' deny owner file /foo wr,' , 'deny owner file /foo rw,'),
|
||||
(' allow owner file /foo Pxrm -> bar,' , 'allow owner file /foo mrPx -> bar,'),
|
||||
(' deny owner /foo r,' , 'deny owner /foo r,'),
|
||||
(' deny owner /foo wr,' , 'deny owner /foo rw,'),
|
||||
(' allow owner /foo Pxrm -> bar,' , 'allow owner /foo mrPx -> bar,'),
|
||||
@@ -407,6 +410,9 @@ class WriteFileTest(AATest):
|
||||
(' deny file r /foo,' , 'deny file r /foo,'),
|
||||
(' deny file wr /foo ,' , 'deny file rw /foo,'),
|
||||
(' allow file Pxmr /foo -> bar,' , 'allow file mrPx /foo -> bar,'),
|
||||
(' deny owner file r /foo ,' , 'deny owner file r /foo,'),
|
||||
(' deny owner file wr /foo ,' , 'deny owner file rw /foo,'),
|
||||
(' allow owner file Pxrm /foo -> bar,' , 'allow owner file mrPx /foo -> bar,'),
|
||||
(' deny owner r /foo ,' , 'deny owner r /foo,'),
|
||||
(' deny owner wr /foo ,' , 'deny owner rw /foo,'),
|
||||
(' allow owner Pxrm /foo -> bar,' , 'allow owner mrPx /foo -> bar,'),
|
||||
@@ -835,6 +841,17 @@ class FileLogprofHeaderTest(AATest):
|
||||
obj.original_perms = {'allow': { 'all': set(), 'owner': set()}}
|
||||
self.assertEqual(obj.logprof_header(), [_('Path'), '/foo', _('New Mode'), _('rw')])
|
||||
|
||||
def test_implicit_original_perms(self):
|
||||
obj = FileRule._parse('/foo rw,')
|
||||
obj.original_perms = {'allow': {'all': FileRule.ALL, 'owner': set()}}
|
||||
self.assertEqual(obj.logprof_header(), [_('Path'), '/foo', _('Old Mode'), _('mrwlkix'), _('New Mode'), _('rw')])
|
||||
|
||||
def test_owner_implicit_original_perms(self):
|
||||
obj = FileRule._parse('/foo rw,')
|
||||
obj.original_perms = {'allow': {'all': set(), 'owner': FileRule.ALL}}
|
||||
self.assertEqual(obj.logprof_header(), [_('Path'), '/foo', _('Old Mode'), _('owner mrwlkix'), _('New Mode'), _('rw')])
|
||||
|
||||
|
||||
class FileEditHeaderTest(AATest):
|
||||
def _run_test(self, params, expected):
|
||||
rule_obj = FileRule.parse(params)
|
||||
|
@@ -35,14 +35,14 @@ class TestAdd_profile(AATest):
|
||||
def testAdd_profile_1(self):
|
||||
self.pl.add_profile('/etc/apparmor.d/bin.foo', 'foo', '/bin/foo')
|
||||
self.assertEqual(self.pl.profile_names, {'foo': '/etc/apparmor.d/bin.foo'})
|
||||
self.assertEqual(self.pl.attachments, {'/bin/foo': '/etc/apparmor.d/bin.foo'})
|
||||
self.assertEqual(self.pl.attachments, {'/bin/foo': {'f': '/etc/apparmor.d/bin.foo', 'p': 'foo'}})
|
||||
self.assertEqual(self.pl.profiles_in_file('/etc/apparmor.d/bin.foo'), ['foo'])
|
||||
self.assertEqual('%s' % self.pl, '\n<ProfileList>\n/etc/apparmor.d/bin.foo\n</ProfileList>\n')
|
||||
|
||||
def testAdd_profile_2(self):
|
||||
self.pl.add_profile('/etc/apparmor.d/bin.foo', None, '/bin/foo')
|
||||
self.assertEqual(self.pl.profile_names, {})
|
||||
self.assertEqual(self.pl.attachments, {'/bin/foo': '/etc/apparmor.d/bin.foo'})
|
||||
self.assertEqual(self.pl.attachments, {'/bin/foo': {'f': '/etc/apparmor.d/bin.foo', 'p': '/bin/foo'}})
|
||||
self.assertEqual(self.pl.profiles_in_file('/etc/apparmor.d/bin.foo'), ['/bin/foo'])
|
||||
self.assertEqual('%s' % self.pl, '\n<ProfileList>\n/etc/apparmor.d/bin.foo\n</ProfileList>\n')
|
||||
|
||||
@@ -124,7 +124,11 @@ class TestFilename_from_attachment(AATest):
|
||||
self.pl.add_profile('/etc/apparmor.d/bin.foo', 'foo', '/bin/foo')
|
||||
self.pl.add_profile('/etc/apparmor.d/bin.baz', 'baz', '/bin/ba*')
|
||||
self.pl.add_profile('/etc/apparmor.d/bin.foobar', 'foobar', '/bin/foo{bar,baz}')
|
||||
self.pl.add_profile('/etc/apparmor.d/usr.bin.wine', '/usr{,{/lib,/lib32,/lib64}/wine}/bin/wine{,-preloader,server}{,-staging-*,-vanilla-*}', '/usr{,{/lib,/lib32,/lib64}/wine}/bin/wine{,-preloader,server}{,-staging-*,-vanilla-*}')
|
||||
self.pl.add_profile('/etc/apparmor.d/bin.asdf', None, '/bin/asdf')
|
||||
self.pl.add_profile(
|
||||
'/etc/apparmor.d/usr.bin.wine',
|
||||
'wine',
|
||||
'/usr{,{/lib,/lib32,/lib64}/wine}/bin/wine{,-preloader,server}{,-staging-*,-vanilla-*}')
|
||||
|
||||
def _run_test(self, params, expected):
|
||||
self.assertEqual(self.pl.filename_from_attachment(params), expected)
|
||||
@@ -133,6 +137,27 @@ class TestFilename_from_attachment(AATest):
|
||||
with self.assertRaises(AppArmorBug):
|
||||
self.pl.filename_from_attachment('foo')
|
||||
|
||||
class TestProfile_from_attachment(TestFilename_from_attachment):
|
||||
# uses AASetup from TestFilename_from_attachment
|
||||
tests = (
|
||||
('/bin/foo', 'foo'),
|
||||
('/bin/baz', 'baz'),
|
||||
('/bin/foobar', 'foobar'),
|
||||
('/bin/asdf', '/bin/asdf'),
|
||||
('@{foo}', None), # XXX variables not supported yet (and @{foo} isn't defined in this test)
|
||||
('/bin/404', None),
|
||||
('/usr{,{/lib,/lib32,/lib64}/wine}/bin/wine{,-preloader,server}{,-staging-*,-vanilla-*}', 'wine'), # XXX should this really match, or should attachment matching only use AARE?
|
||||
('/usr/lib/wine/bin/wine-preloader-staging-foo', 'wine'), # AARE match
|
||||
)
|
||||
|
||||
def _run_test(self, params, expected):
|
||||
self.assertEqual(self.pl.profile_from_attachment(params), expected)
|
||||
|
||||
def test_non_path_attachment(self):
|
||||
with self.assertRaises(AppArmorBug):
|
||||
self.pl.profile_from_attachment('foo')
|
||||
|
||||
|
||||
class TestAdd_inc_ie(AATest):
|
||||
def AASetup(self):
|
||||
self.pl = ProfileList()
|
||||
|
@@ -159,8 +159,8 @@ syn match sdRLimit /\v^\s*set\s+rlimit\s+(nofile|ofile|nproc|rtprio)\s+\<\=\s+[0
|
||||
syn match sdRLimit /\v^\s*set\s+rlimit\s+(locks|sigpending)\s+\<\=\s+[0-9]+@@EOL@@/ contains=sdComment
|
||||
syn match sdRLimit /\v^\s*set\s+rlimit\s+(fsize|data|stack|core|rss|as|memlock|msgqueue)\s+\<\=\s+[0-9]+([KMG]B)?@@EOL@@/ contains=sdComment
|
||||
syn match sdRLimit /\v^\s*set\s+rlimit\s+nice\s+\<\=\s+(-1?[0-9]|-20|1?[0-9])@@EOL@@/ contains=sdComment
|
||||
syn match sdRLimit /\v^\s*set\s+rlimit\s+cpu\s+\<\=\s+[0-9]+(seconds|minutes|hours|days)?@@EOL@@/ contains=sdComment
|
||||
syn match sdRLimit /\v^\s*set\s+rlimit\s+rttime\s+\<\=\s+[0-9]+(ms|seconds|minutes)?@@EOL@@/ contains=sdComment
|
||||
syn match sdRLimit /\v^\s*set\s+rlimit\s+cpu\s+\<\=\s+[0-9]+\s*(s|sec|second|seconds|min|minute|minutes|h|hour|hours|d|day|days|week|weeks)?@@EOL@@/ contains=sdComment
|
||||
syn match sdRLimit /\v^\s*set\s+rlimit\s+rttime\s+\<\=\s+[0-9]+\s*(us|microsecond|microseconds|ms|millisecond|milliseconds|s|sec|second|seconds|min|minute|minutes|h|hour|hours|d|day|days|week|weeks)?@@EOL@@/ contains=sdComment
|
||||
syn match sdRLimit /\v^\s*set\s+rlimit\s+(cpu|rttime|nofile|nproc|rtprio|locks|sigpending|fsize|data|stack|core|rss|as|memlock|msgqueue|nice)\s+\<\=\s+infinity@@EOL@@/ contains=sdComment
|
||||
|
||||
" link rules
|
||||
|
Reference in New Issue
Block a user