2
0
mirror of https://gitlab.com/apparmor/apparmor synced 2025-08-22 01:57:43 +00:00

parser: support enforce, kill and unconfined profile modes

The enforce profile mode is the default but specifying it explicitly
has not been supported. Allow enforce to be specified as a mode. If
no mode is specified the default is still enforce.

The kernel has supported kill and unconfined profile modes for a
long time now. And support to the parser so that profiles can make
use of these modes.

MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/440
Fixes: https://gitlab.com/apparmor/apparmor/-/issues/7
Signed-off-by: John Johansen <john.johansen@canonical.com>
Acked-by: Steve Beattie <sbeattie@ubuntu.com>
This commit is contained in:
John Johansen 2018-07-04 09:26:27 -07:00
parent e0d061d15a
commit 0c9884550c
32 changed files with 398 additions and 28 deletions

View File

@ -115,7 +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' | 'chroot_relative'
B<PROFILE MODE> = 'enforce' | 'complain'
B<PROFILE MODE> = 'enforce' | 'complain' | 'kill' | 'unconfined'
B<AUDIT MODE> = 'audit'
@ -430,10 +430,21 @@ permission the action will be denied, with an I<EACCES> or I<EPERM>
error code returned to userspace, and the violation will be logged
with a tag of the access being B<DENIED>.
=item B<kill> This is a variant of enforce mode where in addition to
returning I<EACCES> or I<EPERM> for a violation, the task is also sent
a signal to kill it.
=item B<complain> For a given action, if the profile rules do not grant
permission the action will be allowed, but the violation will be logged
with a tag of the access being B<ALLOWED>.
=item B<unconfined> This mode allows a task confined by the profile to
behave as though they are I<unconfined>. This mode allow for an
unconfined behavior that can be later changed to confinement by using
profile replacement. This mode is should not be used under regular
deployment but can be useful during debugging and some system
initialization scenarios.
=back
=head4 Audit Mode

View File

@ -421,7 +421,7 @@ void sd_serialize_profile(std::ostringstream &buf, Profile *profile,
sd_write_struct(buf, "flags");
/* used to be flags.debug, but that's no longer supported */
sd_write_uint32(buf, profile->flags.hat);
sd_write_uint32(buf, profile->flags.complain);
sd_write_uint32(buf, profile_mode_packed(profile->flags.mode));
sd_write_uint32(buf, profile->flags.audit);
sd_write_structend(buf);
if (profile->flags.path) {

View File

@ -303,7 +303,12 @@ Profile *merge_policy(Profile *a, Profile *b)
}
b->entries = NULL;
a->flags.complain = a->flags.complain || b->flags.complain;
if (merge_profile_mode(a->flags.mode, b->flags.mode) == MODE_CONFLICT) {
PERROR("ASSERT: policy merge with different modes 0x%x != 0x%x\n",
a->flags.mode, b->flags.mode);
exit(1);
}
a->flags.audit = a->flags.audit || b->flags.audit;
a->caps.allow |= b->caps.allow;

View File

@ -370,15 +370,11 @@ profile_base: TOK_ID opt_id_or_var opt_cond_list flags TOK_OPEN rules TOK_CLOSE
/* newer abis encode force complain as part of the
* header
*/
prof->flags.complain = 1;
prof->flags.mode = MODE_COMPLAIN;
post_process_file_entries(prof);
post_process_rule_entries(prof);
PDEBUG("%s: flags='%s%s'\n",
$2,
prof->flags.complain ? "complain, " : "",
prof->flags.audit ? "audit" : "");
prof->flags.debug(cerr);
$$ = prof;
};
@ -540,7 +536,7 @@ valuelist: valuelist TOK_VALUE
}
flags: { /* nothing */
flagvals fv = { 0, 0, 0, 0 };
flagvals fv = { 0, MODE_UNSPECIFIED, 0, 0 };
$$ = fv;
};
@ -561,7 +557,11 @@ flags: opt_flags TOK_OPENPAREN flagvals TOK_CLOSEPAREN
flagvals: flagvals flagval
{
$1.complain = $1.complain || $2.complain;
if (merge_profile_mode($1.mode, $2.mode) == MODE_CONFLICT)
yyerror(_("Profile flag '%s' conflicts with '%s'"),
profile_mode_table[$1.mode],
profile_mode_table[$2.mode]);
$1.mode = merge_profile_mode($1.mode, $2.mode);
$1.audit = $1.audit || $2.audit;
$1.path = $1.path | $2.path;
if (($1.path & (PATH_CHROOT_REL | PATH_NS_REL)) ==
@ -588,11 +588,13 @@ flagvals: flagval
flagval: TOK_VALUE
{
flagvals fv = { 0, 0, 0, 0 };
flagvals fv = { 0, MODE_UNSPECIFIED, 0, 0 };
enum profile_mode mode;
if (strcmp($1, "debug") == 0) {
yyerror(_("Profile flag 'debug' is no longer valid."));
} else if (strcmp($1, "complain") == 0) {
fv.complain = 1;
} if ((mode = str_to_mode($1))) {
fv.mode = mode;
} else if (strcmp($1, "audit") == 0) {
fv.audit = 1;
} else if (strcmp($1, "chroot_relative") == 0) {

View File

@ -18,6 +18,14 @@
#include <stdio.h>
#include <stdlib.h>
const char *profile_mode_table[] = {
"",
"enforce",
"complain",
"kill",
"unconfined",
};
bool deref_profileptr_lt::operator()(Profile * const &lhs, Profile * const &rhs) const
{
return *lhs < *rhs;

View File

@ -53,32 +53,92 @@ public:
void dump_profile_names(bool children);
};
extern const char*profile_mode_table[];
/* use profile_mode_packed to convert to the packed representation */
enum profile_mode {
MODE_UNSPECIFIED = 0,
MODE_ENFORCE = 1,
MODE_COMPLAIN = 2,
MODE_KILL = 3,
MODE_UNCONFINED = 4,
MODE_CONFLICT = 5 /* greater than MODE_LAST */
};
#define MODE_LAST MODE_UNCONFINED
static inline enum profile_mode operator++(enum profile_mode &mode)
{
mode = (enum profile_mode)((int) mode + 1);
return mode;
}
static inline enum profile_mode merge_profile_mode(enum profile_mode l, enum profile_mode r)
{
if (l == r || r == MODE_UNSPECIFIED)
return l;
else if (l == MODE_UNSPECIFIED)
return r;
return MODE_CONFLICT;
}
static inline uint32_t profile_mode_packed(enum profile_mode mode)
{
/* kernel doesn't have an unspecified mode everything
* shifts down by 1
*/
if ((uint32_t) mode)
return (uint32_t) mode - 1;
/* unspecified defaults to same as enforce */
return 0;
}
static inline void mode_dump(ostream &os, enum profile_mode mode)
{
if (mode <= MODE_LAST)
os << profile_mode_table[(int) mode];
else
os << "unknown";
}
static inline enum profile_mode str_to_mode(const char *str)
{
for (enum profile_mode i = MODE_ENFORCE; i <= MODE_LAST; ++i) {
if (strcmp(profile_mode_table[i], str) == 0)
return i;
}
return MODE_UNSPECIFIED;
};
class flagvals {
public:
int hat;
int complain;
enum profile_mode mode;
int audit;
int path;
void dump(void)
ostream &dump(ostream &os)
{
printf("Profile Mode:\t");
if (complain)
printf("Complain");
else
printf("Enforce");
os << "Mode: ";
mode_dump(os, mode);
if (audit)
printf(", Audit");
os << ", Audit";
if (hat)
printf(", Hat");
os << ", Hat";
printf("\n");
os << "\n";
return os;
}
ostream &debug(ostream &os)
{
#ifdef DEBUG
return dump(os);
#else
return os;
#endif
}
};
struct capabilities {
@ -160,7 +220,7 @@ public:
parent = NULL;
flags = { 0, 0, 0, 0};
flags = { 0, MODE_UNSPECIFIED, 0, 0 };
rlimits = {0, {}};
std::fill(exec_table, exec_table + AA_EXEC_COUNT, (char *)NULL);
@ -201,7 +261,7 @@ public:
printf("Local To:\t<NULL>\n");
}
flags.dump();
flags.dump(cerr);
caps.dump();
net.dump();

View File

@ -0,0 +1,10 @@
#
#=DESCRIPTION Ensure conflicting mode flags cause an error
#=EXRESULT FAIL
# vim:syntax=subdomain
# Last Modified: Sun Apr 17 19:44:44 2005
#
/does/not/exist flags=(enforce, complain) {
/usr/X11R6/lib/lib*so* r,
/does/not/exist r,
}

View File

@ -0,0 +1,10 @@
#
#=DESCRIPTION Ensure conflicting mode flags cause an error
#=EXRESULT FAIL
# vim:syntax=subdomain
# Last Modified: Sun Apr 17 19:44:44 2005
#
/does/not/exist flags=(enforce, kill) {
/usr/X11R6/lib/lib*so* r,
/does/not/exist r,
}

View File

@ -0,0 +1,10 @@
#
#=DESCRIPTION Ensure conflicting mode flags cause an error
#=EXRESULT FAIL
# vim:syntax=subdomain
# Last Modified: Sun Apr 17 19:44:44 2005
#
/does/not/exist flags=(enforce, unconfined) {
/usr/X11R6/lib/lib*so* r,
/does/not/exist r,
}

View File

@ -0,0 +1,10 @@
#
#=DESCRIPTION Ensure conflicting mode flags cause an error
#=EXRESULT FAIL
# vim:syntax=subdomain
# Last Modified: Sun Apr 17 19:44:44 2005
#
/does/not/exist flags=(complain, enforce) {
/usr/X11R6/lib/lib*so* r,
/does/not/exist r,
}

View File

@ -0,0 +1,10 @@
#
#=DESCRIPTION Ensure conflicting mode flags cause an error
#=EXRESULT FAIL
# vim:syntax=subdomain
# Last Modified: Sun Apr 17 19:44:44 2005
#
/does/not/exist flags=(complain, kill) {
/usr/X11R6/lib/lib*so* r,
/does/not/exist r,
}

View File

@ -0,0 +1,10 @@
#
#=DESCRIPTION Ensure conflicting mode flags cause an error
#=EXRESULT FAIL
# vim:syntax=subdomain
# Last Modified: Sun Apr 17 19:44:44 2005
#
/does/not/exist flags=(complain, unconfined) {
/usr/X11R6/lib/lib*so* r,
/does/not/exist r,
}

View File

@ -0,0 +1,10 @@
#
#=DESCRIPTION Ensure conflicting mode flags cause an error
#=EXRESULT FAIL
# vim:syntax=subdomain
# Last Modified: Sun Apr 17 19:44:44 2005
#
/does/not/exist flags=(kill, enforce) {
/usr/X11R6/lib/lib*so* r,
/does/not/exist r,
}

View File

@ -0,0 +1,10 @@
#
#=DESCRIPTION Ensure conflicting mode flags cause an error
#=EXRESULT FAIL
# vim:syntax=subdomain
# Last Modified: Sun Apr 17 19:44:44 2005
#
/does/not/exist flags=(kill, complain) {
/usr/X11R6/lib/lib*so* r,
/does/not/exist r,
}

View File

@ -0,0 +1,10 @@
#
#=DESCRIPTION Ensure conflicting mode flags cause an error
#=EXRESULT FAIL
# vim:syntax=subdomain
# Last Modified: Sun Apr 17 19:44:44 2005
#
/does/not/exist flags=(kill, unconfined) {
/usr/X11R6/lib/lib*so* r,
/does/not/exist r,
}

View File

@ -0,0 +1,10 @@
#
#=DESCRIPTION Ensure conflicting mode flags cause an error
#=EXRESULT FAIL
# vim:syntax=subdomain
# Last Modified: Sun Apr 17 19:44:44 2005
#
/does/not/exist flags=(unconfined, enforce) {
/usr/X11R6/lib/lib*so* r,
/does/not/exist r,
}

View File

@ -0,0 +1,10 @@
#
#=DESCRIPTION Ensure conflicting mode flags cause an error
#=EXRESULT FAIL
# vim:syntax=subdomain
# Last Modified: Sun Apr 17 19:44:44 2005
#
/does/not/exist flags=(unconfined, complain) {
/usr/X11R6/lib/lib*so* r,
/does/not/exist r,
}

View File

@ -0,0 +1,10 @@
#
#=DESCRIPTION Ensure conflicting mode flags cause an error
#=EXRESULT FAIL
# vim:syntax=subdomain
# Last Modified: Sun Apr 17 19:44:44 2005
#
/does/not/exist flags=(unconfined, kill) {
/usr/X11R6/lib/lib*so* r,
/does/not/exist r,
}

View File

@ -0,0 +1,10 @@
#
#=DESCRIPTION Ensure conflicting mode flags cause an error
#=EXRESULT FAIL
# vim:syntax=subdomain
# Last Modified: Sun Apr 17 19:44:44 2005
#
/does/not/exist flags=(enforce, complain, kill) {
/usr/X11R6/lib/lib*so* r,
/does/not/exist r,
}

View File

@ -0,0 +1,10 @@
#
#=DESCRIPTION Ensure conflicting mode flags cause an error
#=EXRESULT FAIL
# vim:syntax=subdomain
# Last Modified: Sun Apr 17 19:44:44 2005
#
/does/not/exist flags=(enforce, complain, unconfined) {
/usr/X11R6/lib/lib*so* r,
/does/not/exist r,
}

View File

@ -0,0 +1,10 @@
#
#=DESCRIPTION Ensure conflicting mode flags cause an error
#=EXRESULT FAIL
# vim:syntax=subdomain
# Last Modified: Sun Apr 17 19:44:44 2005
#
/does/not/exist flags=(enforce, kill, unconfined) {
/usr/X11R6/lib/lib*so* r,
/does/not/exist r,
}

View File

@ -0,0 +1,10 @@
#
#=DESCRIPTION Ensure conflicting mode flags cause an error
#=EXRESULT FAIL
# vim:syntax=subdomain
# Last Modified: Sun Apr 17 19:44:44 2005
#
/does/not/exist flags=(complain, kill, unconfined) {
/usr/X11R6/lib/lib*so* r,
/does/not/exist r,
}

View File

@ -0,0 +1,10 @@
#
#=DESCRIPTION Ensure conflicting mode flags cause an error
#=EXRESULT FAIL
# vim:syntax=subdomain
# Last Modified: Sun Apr 17 19:44:44 2005
#
/does/not/exist flags=(enforce, complain, kill, unconfined) {
/usr/X11R6/lib/lib*so* r,
/does/not/exist r,
}

View File

@ -0,0 +1,12 @@
#
#=DESCRIPTION validate some uses of the profile flags.
#=EXRESULT PASS
# vim:syntax=subdomain
# Last Modified: Sun Apr 17 19:44:44 2005
#
/does/not/exist flags=(enforce) {
#include <includes/base>
/usr/X11R6/lib/lib*so* r,
/does/not/exist r,
}

View File

@ -0,0 +1,12 @@
#
#=DESCRIPTION validate some uses of the profile flags.
#=EXRESULT PASS
# vim:syntax=subdomain
# Last Modified: Sun Apr 17 19:44:44 2005
#
/does/not/exist flags=(complain) {
#include <includes/base>
/usr/X11R6/lib/lib*so* r,
/does/not/exist r,
}

View File

@ -0,0 +1,12 @@
#
#=DESCRIPTION validate some uses of the profile flags.
#=EXRESULT PASS
# vim:syntax=subdomain
# Last Modified: Sun Apr 17 19:44:44 2005
#
/does/not/exist flags=(kill) {
#include <includes/base>
/usr/X11R6/lib/lib*so* r,
/does/not/exist r,
}

View File

@ -0,0 +1,12 @@
#
#=DESCRIPTION validate some uses of the profile flags.
#=EXRESULT PASS
# vim:syntax=subdomain
# Last Modified: Sun Apr 17 19:44:44 2005
#
/does/not/exist flags=(unconfined) {
#include <includes/base>
/usr/X11R6/lib/lib*so* r,
/does/not/exist r,
}

View File

@ -0,0 +1,12 @@
#
#=DESCRIPTION validate some uses of the profile flags.
#=EXRESULT PASS
# vim:syntax=subdomain
# Last Modified: Sun Apr 17 19:44:44 2005
#
/does/not/exist flags=(enforce audit) {
#include <includes/base>
/usr/X11R6/lib/lib*so* r,
/does/not/exist r,
}

View File

@ -0,0 +1,12 @@
#
#=DESCRIPTION validate some uses of the profile flags.
#=EXRESULT PASS
# vim:syntax=subdomain
# Last Modified: Sun Apr 17 19:44:44 2005
#
/does/not/exist flags=(complain audit) {
#include <includes/base>
/usr/X11R6/lib/lib*so* r,
/does/not/exist r,
}

View File

@ -0,0 +1,12 @@
#
#=DESCRIPTION validate some uses of the profile flags.
#=EXRESULT PASS
# vim:syntax=subdomain
# Last Modified: Sun Apr 17 19:44:44 2005
#
/does/not/exist flags=(kill audit) {
#include <includes/base>
/usr/X11R6/lib/lib*so* r,
/does/not/exist r,
}

View File

@ -0,0 +1,12 @@
#
#=DESCRIPTION validate some uses of the profile flags.
#=EXRESULT PASS
# vim:syntax=subdomain
# Last Modified: Sun Apr 17 19:44:44 2005
#
/does/not/exist flags=(unconfined audit) {
#include <includes/base>
/usr/X11R6/lib/lib*so* r,
/does/not/exist r,
}

View File

@ -119,6 +119,24 @@ exception_not_raised = [
'profile/flags/flags_bad_debug_2.sd',
'profile/flags/flags_bad_debug_3.sd',
'profile/flags/flags_bad_debug_4.sd',
# detection of conflicting flags not supported
'profile/flags/flags_bad30.sd',
'profile/flags/flags_bad31.sd',
'profile/flags/flags_bad32.sd',
'profile/flags/flags_bad33.sd',
'profile/flags/flags_bad34.sd',
'profile/flags/flags_bad35.sd',
'profile/flags/flags_bad36.sd',
'profile/flags/flags_bad37.sd',
'profile/flags/flags_bad38.sd',
'profile/flags/flags_bad39.sd',
'profile/flags/flags_bad40.sd',
'profile/flags/flags_bad41.sd',
'profile/flags/flags_bad42.sd',
'profile/flags/flags_bad43.sd',
'profile/flags/flags_bad44.sd',
'profile/flags/flags_bad45.sd',
'profile/flags/flags_bad46.sd',
'profile/simple_bad_no_close_brace4.sd',
'profile/profile_ns_bad8.sd', # 'profile :ns/t' without terminating ':'
'ptrace/bad_05.sd', # actually contains a capability rule with invalid (ptrace-related) keyword