mirror of
https://gitlab.com/apparmor/apparmor
synced 2025-08-31 14:25:52 +00:00
Compare commits
56 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
fff8d65985 | ||
|
8e2595c634 | ||
|
711ca72c6b | ||
|
af8ccba6d2 | ||
|
fa5d235f28 | ||
|
bdb18c5ccf | ||
|
0cd0743b47 | ||
|
6e1e27a931 | ||
|
150350c42c | ||
|
61ee9623c5 | ||
|
458f696f8e | ||
|
331e54b36e | ||
|
85be9528ec | ||
|
5493e01408 | ||
|
da0daadf40 | ||
|
6d05fa4a6e | ||
|
f5df1bf45e | ||
|
a80c75e308 | ||
|
f5462aa931 | ||
|
91e73d54fe | ||
|
57cdc4257d | ||
|
4c04a05996 | ||
|
a492bcfc80 | ||
|
ec9292bd5e | ||
|
703cc22b52 | ||
|
4fd66468d8 | ||
|
b80aadd624 | ||
|
0dde5efc62 | ||
|
99b59f7169 | ||
|
061c76c5b1 | ||
|
dab2636a27 | ||
|
a41bff515f | ||
|
483f11d06c | ||
|
1140e54442 | ||
|
b54d1f2049 | ||
|
6e846245ab | ||
|
218cb42fbe | ||
|
df12e87fb5 | ||
|
5a2da347d4 | ||
|
cfbc1a2a79 | ||
|
51a0d5d863 | ||
|
3ab596ed83 | ||
|
d5824674d1 | ||
|
3ef80d788a | ||
|
d579fc51d4 | ||
|
8d68618f0b | ||
|
f6dcade84f | ||
|
0f4310d301 | ||
|
84ab95d263 | ||
|
eb2adf119b | ||
|
68041e0d2e | ||
|
4c0e6334b5 | ||
|
73cdd97596 | ||
|
dfe58983bb | ||
|
07b6148fd1 | ||
|
d04a03359c |
2
Makefile
2
Makefile
@@ -18,7 +18,7 @@ DIRS=parser \
|
||||
|
||||
#REPO_URL?=lp:apparmor
|
||||
# --per-file-timestamps is failing over SSH, https://bugs.launchpad.net/bzr/+bug/1257078
|
||||
REPO_URL?=https://code.launchpad.net/~apparmor-dev/apparmor/master
|
||||
REPO_URL?=https://code.launchpad.net/~apparmor-dev/apparmor/2.10
|
||||
# alternate possibilities to export from
|
||||
#REPO_URL=.
|
||||
#REPO_URL="bzr+ssh://bazaar.launchpad.net/~sbeattie/+junk/apparmor-dev/"
|
||||
|
@@ -111,6 +111,7 @@ int pam_sm_open_session(pam_handle_t *pamh, int flags,
|
||||
sizeof(magic_token));
|
||||
if (retval < 0) {
|
||||
pam_syslog(pamh, LOG_ERR, "Can't read from /dev/urandom\n");
|
||||
close(fd);
|
||||
return PAM_PERM_DENIED;
|
||||
}
|
||||
} while ((magic_token == 0) || (retval != sizeof(magic_token)));
|
||||
|
@@ -98,7 +98,7 @@ list_capabilities: /usr/include/linux/capability.h
|
||||
# to mediate. We use PF_ here since that is what is required in
|
||||
# bits/socket.h, but we will rewrite these as AF_.
|
||||
|
||||
FILTER_FAMILIES=PF_UNSPEC PF_UNIX
|
||||
FILTER_FAMILIES=PF_UNIX
|
||||
|
||||
__FILTER=$(shell echo $(strip $(FILTER_FAMILIES)) | sed -e 's/ /\\\|/g')
|
||||
|
||||
|
@@ -1 +1 @@
|
||||
2.10
|
||||
2.10.1
|
||||
|
@@ -40,16 +40,15 @@ An AppArmor profile applies to an executable program; if a portion of
|
||||
the program needs different access permissions than other portions,
|
||||
the program can "change profile" to a different profile. To change into a
|
||||
new profile, it can use the aa_change_profile() function to do so. It passes
|
||||
in a pointer to the I<profile> to transition to. Transitioning to another
|
||||
profile via aa_change_profile() is permanent and the process is not
|
||||
permitted to transition back to the original profile. Confined programs
|
||||
wanting to use aa_change_profile() need to have rules permitting changing
|
||||
to the named profile. See apparmor.d(8) for details.
|
||||
in a pointer to the I<profile> to transition to. Confined programs wanting to
|
||||
use aa_change_profile() need to have rules permitting changing to the named
|
||||
profile. See apparmor.d(8) for details.
|
||||
|
||||
If a program wants to return out of the current profile to the
|
||||
original profile, it should use aa_change_hat(2) instead.
|
||||
original profile, it may use aa_change_hat(2). Otherwise, the two profiles must
|
||||
have rules permitting changing between the two profiles.
|
||||
|
||||
Open file descriptors are not remediated after a call to aa_change_profile()
|
||||
Open file descriptors may not be remediated after a call to aa_change_profile()
|
||||
so the calling program must close(2) open file descriptors to ensure they
|
||||
are not available after calling aa_change_profile(). As aa_change_profile()
|
||||
is typically used just before execve(2), you may want to use open(2) or
|
||||
@@ -84,8 +83,8 @@ Insufficient kernel memory was available.
|
||||
|
||||
=item B<EPERM>
|
||||
|
||||
The calling application is not confined by apparmor, or the no_new_privs
|
||||
bit is set.
|
||||
The calling application is confined by apparmor and the no_new_privs bit is
|
||||
set.
|
||||
|
||||
=item B<EACCES>
|
||||
|
||||
|
@@ -0,0 +1 @@
|
||||
type=AVC msg=audit(1449442292.901:961): apparmor="ALLOWED" operation="change_hat" profile="/usr/sbin/httpd{,2}-prefork" pid=8527 comm="httpd-prefork" target="/usr/sbin/httpd{,2}-prefork//HANDLING_UNTRUSTED_INPUT"
|
@@ -0,0 +1,11 @@
|
||||
START
|
||||
File: testcase_changehat_01.in
|
||||
Event type: AA_RECORD_ALLOWED
|
||||
Audit ID: 1449442292.901:961
|
||||
Operation: change_hat
|
||||
Profile: /usr/sbin/httpd{,2}-prefork
|
||||
Command: httpd-prefork
|
||||
Name2: /usr/sbin/httpd{,2}-prefork//HANDLING_UNTRUSTED_INPUT
|
||||
PID: 8527
|
||||
Epoch: 1449442292
|
||||
Audit subid: 961
|
@@ -161,7 +161,7 @@ B<SIGNAL SET> = 'set' '=' '(' I<SIGNAL LIST> ')'
|
||||
|
||||
B<SIGNAL LIST> = Comma or space separated list of I<SIGNALS>
|
||||
|
||||
B<SIGNALS> = ( 'hup' | 'int' | 'quit' | 'ill' | 'trap' | 'abrt' | 'bus' | 'fpe' | 'kill' | 'usr1' | 'segv' | 'usr2' | 'pipe' | 'alrm' | 'term' | 'stkflt' | 'chld' | 'cont' | 'stop' | 'stp' | 'ttin' | 'ttou' | 'urg' | 'xcpu' | 'xfsz' | 'vtalrm' | 'prof' | 'winch' | 'io' | 'pwr' | 'sys' | 'emt' | 'exists' )
|
||||
B<SIGNALS> = ( 'hup' | 'int' | 'quit' | 'ill' | 'trap' | 'abrt' | 'bus' | 'fpe' | 'kill' | 'usr1' | 'segv' | 'usr2' | 'pipe' | 'alrm' | 'term' | 'stkflt' | 'chld' | 'cont' | 'stop' | 'stp' | 'ttin' | 'ttou' | 'urg' | 'xcpu' | 'xfsz' | 'vtalrm' | 'prof' | 'winch' | 'io' | 'pwr' | 'sys' | 'emt' | 'exists' | 'rtmin+0' ... 'rtmin+32' )
|
||||
|
||||
B<SIGNAL PEER> = 'peer' '=' I<AARE>
|
||||
|
||||
@@ -251,7 +251,7 @@ B<UNQUOTED FILEGLOB> = (must start with '/' (after variable expansion), B<AARE>
|
||||
|
||||
B<ACCESS> = ( 'r' | 'w' | 'a' | 'l' | 'k' | 'm' | I<EXEC TRANSITION> )+ (not all combinations are allowed; see below.)
|
||||
|
||||
B<EXEC TRANSITION> = ( 'ix' | 'ux' | 'Ux' | 'px' | 'Px' | 'cx' | 'Cx' | 'pix' | 'Pix' | 'cix' | 'Cix' | 'pux' | 'PUx' | 'cux' | 'CUx' )
|
||||
B<EXEC TRANSITION> = ( 'ix' | 'ux' | 'Ux' | 'px' | 'Px' | 'cx' | 'Cx' | 'pix' | 'Pix' | 'cix' | 'Cix' | 'pux' | 'PUx' | 'cux' | 'CUx' | 'x' ) ('x' is only allowed in rules with the deny qualifier, everything else only without the deny qualifier)
|
||||
|
||||
B<EXEC TARGET> = name (requires I<EXEC TRANSITION> specified)
|
||||
|
||||
@@ -366,6 +366,10 @@ modes:
|
||||
|
||||
- transition to subprofile on execute with fallback to unconfined -- scrub the environment
|
||||
|
||||
=item B<deny x>
|
||||
|
||||
- disallow execute (in rules with the deny qualifier)
|
||||
|
||||
=item B<m>
|
||||
|
||||
- allow PROT_EXEC with mmap(2) calls
|
||||
@@ -425,7 +429,7 @@ over the callee. Use this mode only if the child absolutely must be
|
||||
run unconfined and LD_PRELOAD must be used. Any profile using this mode
|
||||
provides negligible security. Use at your own risk.
|
||||
|
||||
Incompatible with other exec transition modes.
|
||||
Incompatible with other exec transition modes and the deny qualifier.
|
||||
|
||||
=item B<Ux - unconfined execute -- scrub the environment>
|
||||
|
||||
@@ -439,7 +443,7 @@ designated child processes to be run without any AppArmor protection.
|
||||
Use this mode only if the child absolutely must be run unconfined. Use
|
||||
at your own risk.
|
||||
|
||||
Incompatible with other exec transition modes.
|
||||
Incompatible with other exec transition modes and the deny qualifier.
|
||||
|
||||
=item B<px - Discrete Profile execute mode>
|
||||
|
||||
@@ -451,7 +455,7 @@ B<WARNING> 'px' does not scrub the environment of variables such as
|
||||
LD_PRELOAD; as a result, the calling domain may have an undue amount of
|
||||
influence over the callee.
|
||||
|
||||
Incompatible with other exec transition modes.
|
||||
Incompatible with other exec transition modes and the deny qualifier.
|
||||
|
||||
=item B<Px - Discrete Profile execute mode -- scrub the environment>
|
||||
|
||||
@@ -460,7 +464,7 @@ will invoke the Linux Kernel's B<unsafe_exec> routines to scrub
|
||||
the environment, similar to setuid programs. (See ld.so(8) for some
|
||||
information on setuid/setgid environment scrubbing.)
|
||||
|
||||
Incompatible with other exec transition modes.
|
||||
Incompatible with other exec transition modes and the deny qualifier.
|
||||
|
||||
=item B<cx - Transition to Subprofile execute mode>
|
||||
|
||||
@@ -472,7 +476,7 @@ B<WARNING> 'cx' does not scrub the environment of variables such as
|
||||
LD_PRELOAD; as a result, the calling domain may have an undue amount of
|
||||
influence over the callee.
|
||||
|
||||
Incompatible with other exec transition modes.
|
||||
Incompatible with other exec transition modes and the deny qualifier.
|
||||
|
||||
=item B<Cx - Transition to Subprofile execute mode -- scrub the environment>
|
||||
|
||||
@@ -481,7 +485,7 @@ will invoke the Linux Kernel's B<unsafe_exec> routines to scrub
|
||||
the environment, similar to setuid programs. (See ld.so(8) for some
|
||||
information on setuid/setgid environment scrubbing.)
|
||||
|
||||
Incompatible with other exec transition modes.
|
||||
Incompatible with other exec transition modes and the deny qualifier.
|
||||
|
||||
=item B<ix - Inherit execute mode>
|
||||
|
||||
@@ -495,7 +499,7 @@ profile, or losing the permissions of the current profile. There is no
|
||||
version to scrub the environment because 'ix' executions don't change
|
||||
privileges.
|
||||
|
||||
Incompatible with other exec transition modes.
|
||||
Incompatible with other exec transition modes and the deny qualifier.
|
||||
|
||||
=item B<Profile transition with inheritance fallback execute mode>
|
||||
|
||||
@@ -509,7 +513,7 @@ the 'ix' transition mode.
|
||||
'Cix' == 'Cx' with fallback to 'ix'
|
||||
'cix' == 'cx' with fallback to 'ix'
|
||||
|
||||
Incompatible with other exec transition modes.
|
||||
Incompatible with other exec transition modes and the deny qualifier.
|
||||
|
||||
=item B<Profile transition with unconfined fallback execute mode>
|
||||
|
||||
@@ -524,7 +528,14 @@ if 'PUx', 'CUx' is used.
|
||||
'CUx' == 'Cx' with fallback to 'Ux'
|
||||
'cux' == 'cx' with fallback to 'ux'
|
||||
|
||||
Incompatible with other exec transition modes.
|
||||
Incompatible with other exec transition modes and the deny qualifier.
|
||||
|
||||
=item B<deny x - Deny execute>
|
||||
|
||||
For rules including the deny modifier, only 'x' is allowed to deny execute.
|
||||
|
||||
The 'ix', 'Px', 'px', 'Cx', 'cx' and the fallback modes conflict with the deny
|
||||
modifier.
|
||||
|
||||
=item B<Directed profile transitions>
|
||||
|
||||
@@ -964,6 +975,9 @@ Example AppArmor signal rules:
|
||||
# Allow us to signal ourselves using the built-in @{profile_name} variable
|
||||
signal peer=@{profile_name},
|
||||
|
||||
# Allow two real-time signals
|
||||
signal set=(rtmin+0 rtmin+32),
|
||||
|
||||
=head2 DBus rules
|
||||
|
||||
AppArmor supports DBus mediation. The mediation is performed in conjunction
|
||||
@@ -1229,8 +1243,10 @@ provided AppArmor policy:
|
||||
@{HOMEDIRS}
|
||||
@{multiarch}
|
||||
@{pid}
|
||||
@{pids}
|
||||
@{PROC}
|
||||
@{securityfs}
|
||||
@{apparmorfs}
|
||||
@{sys}
|
||||
@{tid}
|
||||
@{XDG_DESKTOP_DIR}
|
||||
|
@@ -393,6 +393,9 @@ extern int get_rlimit(const char *name);
|
||||
extern char *process_var(const char *var);
|
||||
extern int parse_mode(const char *mode);
|
||||
extern int parse_X_mode(const char *X, int valid, const char *str_mode, int *mode, int fail);
|
||||
void parse_label(char **ns, char **name, const char *label);
|
||||
void parse_named_transition_target(struct named_transition *nt,
|
||||
const char *target);
|
||||
extern struct cod_entry *new_entry(char *ns, char *id, int mode, char *link_id);
|
||||
|
||||
/* returns -1 if value != true or false, otherwise 0 == false, 1 == true */
|
||||
|
@@ -225,8 +225,8 @@ SET_VAR_PREFIX @
|
||||
SET_VARIABLE {SET_VAR_PREFIX}(\{{VARIABLE_NAME}\}|{VARIABLE_NAME})
|
||||
BOOL_VARIABLE $(\{{VARIABLE_NAME}\}|{VARIABLE_NAME})
|
||||
|
||||
PATHNAME (\/|{SET_VARIABLE}{POST_VAR_ID}){ID}*
|
||||
QPATHNAME \"(\/|{SET_VAR_PREFIX})([^\0"]|\\\")*\"
|
||||
LABEL (\/|{SET_VARIABLE}{POST_VAR_ID}|{COLON}){ID}*
|
||||
QUOTED_LABEL \"(\/|{SET_VAR_PREFIX}|{COLON})([^\0"]|\\\")*\"
|
||||
|
||||
OPEN_PAREN \(
|
||||
CLOSE_PAREN \)
|
||||
@@ -510,7 +510,7 @@ LT_EQUAL <=
|
||||
}
|
||||
|
||||
<MOUNT_MODE,DBUS_MODE,SIGNAL_MODE,PTRACE_MODE,UNIX_MODE>{
|
||||
({IDS_NOEQ}|{PATHNAME}|{QUOTED_ID}) {
|
||||
({IDS_NOEQ}|{LABEL}|{QUOTED_ID}) {
|
||||
yylval.id = processid(yytext, yyleng);
|
||||
RETURN_TOKEN(TOK_ID);
|
||||
}
|
||||
@@ -557,7 +557,7 @@ include/{WS} {
|
||||
|
||||
{CLOSE_BRACE} { RETURN_TOKEN(TOK_CLOSE); }
|
||||
|
||||
({PATHNAME}|{QPATHNAME}) {
|
||||
({LABEL}|{QUOTED_LABEL}) {
|
||||
yylval.id = processid(yytext, yyleng);
|
||||
RETURN_TOKEN(TOK_ID);
|
||||
}
|
||||
|
@@ -964,8 +964,10 @@ int main(int argc, char *argv[])
|
||||
void *data);
|
||||
struct dir_cb_data cb_data;
|
||||
|
||||
memset(&cb_data, 0, sizeof(struct dir_cb_data));
|
||||
cb_data.dirname = profilename;
|
||||
cb_data.cachedir = cacheloc;
|
||||
cb_data.kernel_interface = kernel_interface;
|
||||
cb = binary_input ? binary_dir_cb : profile_dir_cb;
|
||||
if ((retval = dirat_for_each(AT_FDCWD, profilename,
|
||||
&cb_data, cb))) {
|
||||
|
@@ -569,6 +569,65 @@ int parse_X_mode(const char *X, int valid, const char *str_mode, int *mode, int
|
||||
return 1;
|
||||
}
|
||||
|
||||
void parse_label(char **ns, char **name, const char *label)
|
||||
{
|
||||
const char *name_start = NULL;
|
||||
char *_ns = NULL;
|
||||
char *_name = NULL;
|
||||
|
||||
if (label[0] != ':') {
|
||||
/* There is no namespace specified in the label */
|
||||
name_start = label;
|
||||
} else {
|
||||
/* A leading ':' indicates that a namespace is specified */
|
||||
const char *ns_start = label + 1;
|
||||
const char *ns_end = strstr(ns_start, ":");
|
||||
|
||||
if (!ns_end)
|
||||
yyerror(_("Namespace not terminated: %s\n"), label);
|
||||
else if (ns_end - ns_start == 0)
|
||||
yyerror(_("Empty namespace: %s\n"), label);
|
||||
|
||||
/**
|
||||
* Handle either of the two namespace formats:
|
||||
* 1) :ns:name
|
||||
* 2) :ns://name
|
||||
*/
|
||||
name_start = ns_end + 1;
|
||||
if (!strncmp(name_start, "//", 2))
|
||||
name_start += 2;
|
||||
|
||||
_ns = strndup(ns_start, ns_end - ns_start);
|
||||
if (!_ns)
|
||||
yyerror(_("Memory allocation error."));
|
||||
}
|
||||
|
||||
if (!strlen(name_start))
|
||||
yyerror(_("Empty named transition profile name: %s\n"), label);
|
||||
|
||||
_name = strdup(name_start);
|
||||
if (!_name) {
|
||||
free(_ns);
|
||||
yyerror(_("Memory allocation error."));
|
||||
}
|
||||
|
||||
*ns = _ns;
|
||||
*name = _name;
|
||||
}
|
||||
|
||||
void parse_named_transition_target(struct named_transition *nt,
|
||||
const char *target)
|
||||
{
|
||||
memset(nt, 0, sizeof(*nt));
|
||||
if (!target) {
|
||||
/* Return with nt->present set to 0 (thanks to the memset) */
|
||||
return;
|
||||
}
|
||||
|
||||
parse_label(&nt->ns, &nt->name, target);
|
||||
nt->present = 1;
|
||||
}
|
||||
|
||||
struct cod_entry *new_entry(char *ns, char *id, int mode, char *link_id)
|
||||
{
|
||||
struct cod_entry *entry = NULL;
|
||||
@@ -724,7 +783,7 @@ static const char *capnames[] = {
|
||||
"audit_write",
|
||||
"audit_control",
|
||||
"setfcap",
|
||||
"mac_override"
|
||||
"mac_override",
|
||||
"syslog",
|
||||
};
|
||||
|
||||
|
@@ -258,8 +258,6 @@ void add_local_entry(Profile *prof);
|
||||
%type <boolean> opt_profile_flag
|
||||
%type <boolean> opt_flags
|
||||
%type <boolean> opt_perm_mode
|
||||
%type <id> opt_ns
|
||||
%type <id> ns_id
|
||||
%type <id> opt_id
|
||||
%type <prefix> opt_prefix
|
||||
%type <fmode> dbus_perm
|
||||
@@ -299,11 +297,6 @@ opt_profile_flag: { /* nothing */ $$ = 0; }
|
||||
| TOK_PROFILE { $$ = 1; }
|
||||
| hat_start { $$ = 2; }
|
||||
|
||||
ns_id: TOK_COLON id_or_var TOK_COLON { $$ = $2; }
|
||||
|
||||
opt_ns: { /* nothing */ $$ = NULL; }
|
||||
| ns_id { $$ = $1; }
|
||||
|
||||
opt_id: { /* nothing */ $$ = NULL; }
|
||||
| TOK_ID { $$ = $1; }
|
||||
|
||||
@@ -318,7 +311,26 @@ profile_base: TOK_ID opt_id_or_var flags TOK_OPEN rules TOK_CLOSE
|
||||
yyerror(_("Memory allocation error."));
|
||||
}
|
||||
|
||||
prof->name = $1;
|
||||
parse_label(&prof->ns, &prof->name, $1);
|
||||
free($1);
|
||||
|
||||
/* Honor the --namespace-string command line option */
|
||||
if (profile_ns) {
|
||||
/**
|
||||
* Print warning if the profile specified a namespace
|
||||
* different than the one specified with the
|
||||
* --namespace-string command line option
|
||||
*/
|
||||
if (prof->ns && strcmp(prof->ns, profile_ns))
|
||||
pwarn("%s: -n %s overriding policy specified namespace :%s:\n",
|
||||
progname, profile_ns, prof->ns);
|
||||
|
||||
free(prof->ns);
|
||||
prof->ns = strdup(profile_ns);
|
||||
if (!prof->ns)
|
||||
yyerror(_("Memory allocation error."));
|
||||
}
|
||||
|
||||
prof->attachment = $2;
|
||||
if ($2 && !($2[0] == '/' || strncmp($2, "@{", 2) == 0))
|
||||
yyerror(_("Profile attachment must begin with a '/' or variable."));
|
||||
@@ -340,25 +352,18 @@ profile_base: TOK_ID opt_id_or_var flags TOK_OPEN rules TOK_CLOSE
|
||||
|
||||
};
|
||||
|
||||
profile: opt_profile_flag opt_ns profile_base
|
||||
profile: opt_profile_flag profile_base
|
||||
{
|
||||
Profile *prof = $3;
|
||||
if ($2)
|
||||
PDEBUG("Matched: %s://%s { ... }\n", $2, $3->name);
|
||||
else
|
||||
PDEBUG("Matched: %s { ... }\n", $3->name);
|
||||
Profile *prof = $2;
|
||||
|
||||
if ($3->name[0] != '/' && !($1 || $2))
|
||||
if ($2->ns)
|
||||
PDEBUG("Matched: :%s://%s { ... }\n", $2->ns, $2->name);
|
||||
else
|
||||
PDEBUG("Matched: %s { ... }\n", $2->name);
|
||||
|
||||
if ($2->name[0] != '/' && !($1 || $2->ns))
|
||||
yyerror(_("Profile names must begin with a '/', namespace or keyword 'profile' or 'hat'."));
|
||||
|
||||
if ($2 && profile_ns) {
|
||||
pwarn("%s: -n %s overriding policy specified namespace :%s:\n", progname, profile_ns, $2);
|
||||
free($2);
|
||||
prof->ns = strdup(profile_ns);
|
||||
if (!prof->ns)
|
||||
yyerror(_("Memory allocation error."));
|
||||
} else
|
||||
prof->ns = $2;
|
||||
if ($1 == 2)
|
||||
prof->flags.hat = 1;
|
||||
$$ = prof;
|
||||
@@ -1035,21 +1040,12 @@ id_or_var: TOK_SET_VAR { $$ = $1; };
|
||||
|
||||
opt_named_transition:
|
||||
{ /* nothing */
|
||||
$$.present = 0;
|
||||
$$.ns = NULL;
|
||||
$$.name = NULL;
|
||||
parse_named_transition_target(&$$, NULL);
|
||||
}
|
||||
| TOK_ARROW id_or_var
|
||||
{
|
||||
$$.present = 1;
|
||||
$$.ns = NULL;
|
||||
$$.name = $2;
|
||||
}
|
||||
| TOK_ARROW ns_id id_or_var
|
||||
{
|
||||
$$.present = 1;
|
||||
$$.ns = $2;
|
||||
$$.name = $3;
|
||||
parse_named_transition_target(&$$, $2);
|
||||
free($2);
|
||||
};
|
||||
|
||||
rule: file_rule { $$ = $1; }
|
||||
@@ -1490,27 +1486,25 @@ change_profile_head: TOK_CHANGE_PROFILE opt_id
|
||||
$$ = $2;
|
||||
}
|
||||
|
||||
change_profile: change_profile_head TOK_END_OF_RULE
|
||||
change_profile: change_profile_head opt_named_transition TOK_END_OF_RULE
|
||||
{
|
||||
struct cod_entry *entry;
|
||||
char *rule = strdup("**");
|
||||
if (!rule)
|
||||
yyerror(_("Memory allocation error."));
|
||||
PDEBUG("Matched change_profile,\n");
|
||||
entry = new_entry(NULL, rule, AA_CHANGE_PROFILE, $1);
|
||||
if (!entry)
|
||||
yyerror(_("Memory allocation error."));
|
||||
PDEBUG("change_profile,\n");
|
||||
$$ = entry;
|
||||
};
|
||||
|
||||
change_profile: change_profile_head TOK_ARROW opt_ns TOK_ID TOK_END_OF_RULE
|
||||
{
|
||||
struct cod_entry *entry;
|
||||
PDEBUG("Matched change_profile: tok_id (:%s://%s)\n", $3 ? $3 : "", $4);
|
||||
entry = new_entry($3, $4, AA_CHANGE_PROFILE, $1);
|
||||
if ($2.present) {
|
||||
PDEBUG("Matched change_profile: tok_id (:%s://%s)\n",
|
||||
$2.ns ? $2.ns : "", $2.name);
|
||||
entry = new_entry($2.ns, $2.name, AA_CHANGE_PROFILE, $1);
|
||||
} else {
|
||||
char *rule = strdup("**");
|
||||
if (!rule)
|
||||
yyerror(_("Memory allocation error."));
|
||||
|
||||
PDEBUG("Matched change_profile,\n");
|
||||
entry = new_entry(NULL, rule, AA_CHANGE_PROFILE, $1);
|
||||
}
|
||||
if (!entry)
|
||||
yyerror(_("Memory allocation error."));
|
||||
|
||||
PDEBUG("change_profile.entry: (%s)\n", entry->name);
|
||||
$$ = entry;
|
||||
};
|
||||
|
@@ -225,7 +225,7 @@ public:
|
||||
std::string fqname(void)
|
||||
{
|
||||
if (parent)
|
||||
return parent->fqname() + "://" + name;
|
||||
return parent->fqname() + "//" + name;
|
||||
else if (!ns)
|
||||
return hname();
|
||||
return ":" + std::string(ns) + "://" + hname();
|
||||
|
@@ -3,6 +3,7 @@
|
||||
#=EXRESULT PASS
|
||||
#
|
||||
/usr/bin/foo {
|
||||
network unspec,
|
||||
network inet,
|
||||
network ax25,
|
||||
network ipx,
|
||||
|
9
parser/tst/simple_tests/network/network_ok_7.sd
Normal file
9
parser/tst/simple_tests/network/network_ok_7.sd
Normal file
@@ -0,0 +1,9 @@
|
||||
#
|
||||
#=DESCRIPTION basic unspec network tests
|
||||
#=EXRESULT PASS
|
||||
#
|
||||
/usr/bin/foo {
|
||||
network unspec stream,
|
||||
network unspec dgram,
|
||||
network unspec raw,
|
||||
}
|
9
parser/tst/simple_tests/profile/profile_ns_bad5.sd
Normal file
9
parser/tst/simple_tests/profile/profile_ns_bad5.sd
Normal file
@@ -0,0 +1,9 @@
|
||||
#
|
||||
#=DESCRIPTION namespace with no profile name
|
||||
#=EXRESULT FAIL
|
||||
# vim:syntax=apparmor
|
||||
# Last Modified: Thu Feb 11 00:14:20 2016
|
||||
#
|
||||
:namespace: {
|
||||
/does/not/exist r,
|
||||
}
|
13
parser/tst/simple_tests/profile/profile_ns_bad6.sd
Normal file
13
parser/tst/simple_tests/profile/profile_ns_bad6.sd
Normal file
@@ -0,0 +1,13 @@
|
||||
#
|
||||
#=DESCRIPTION collision same profile, same namespace with profile keyword
|
||||
#=EXRESULT FAIL
|
||||
# vim:syntax=apparmor
|
||||
# Last Modified: Thu Feb 11 00:14:20 2016
|
||||
#
|
||||
profile :ns:/t {
|
||||
/does/not/exist r,
|
||||
}
|
||||
|
||||
profile :ns:/t {
|
||||
/does/not/exist r,
|
||||
}
|
13
parser/tst/simple_tests/profile/profile_ns_bad7.sd
Normal file
13
parser/tst/simple_tests/profile/profile_ns_bad7.sd
Normal file
@@ -0,0 +1,13 @@
|
||||
#
|
||||
#=DESCRIPTION collision same profile, same namespace w/ and w/o profile keyword
|
||||
#=EXRESULT FAIL
|
||||
# vim:syntax=apparmor
|
||||
# Last Modified: Thu Feb 11 00:14:20 2016
|
||||
#
|
||||
:ns:/t {
|
||||
/does/not/exist r,
|
||||
}
|
||||
|
||||
profile :ns:/t {
|
||||
/does/not/exist r,
|
||||
}
|
9
parser/tst/simple_tests/profile/profile_ns_bad8.sd
Normal file
9
parser/tst/simple_tests/profile/profile_ns_bad8.sd
Normal file
@@ -0,0 +1,9 @@
|
||||
#
|
||||
#=DESCRIPTION no terminating ':' for ns namespace (w/ profile keyword)
|
||||
#=EXRESULT FAIL
|
||||
# vim:syntax=apparmor
|
||||
# Last Modified: Thu Feb 11 00:14:20 2016
|
||||
#
|
||||
profile :ns/t {
|
||||
/does/not/exist r,
|
||||
}
|
@@ -40,7 +40,7 @@ profile :foo:/does/not/exist2 {
|
||||
/bin/echo uxuxuxuxux,
|
||||
}
|
||||
|
||||
profile :foo:unattached {
|
||||
profile :foo:unattached2 {
|
||||
#include <includes/base>
|
||||
|
||||
/usr/X11R6/lib/lib*so* rrr,
|
||||
|
@@ -38,6 +38,9 @@
|
||||
# /etc/resolvconf/run/resolv.conf
|
||||
/{,var/}run/resolvconf/resolv.conf r,
|
||||
/etc/resolvconf/run/resolv.conf r,
|
||||
# on systems using systemd's networkd, /etc/resolv.conf is a symlink to
|
||||
# /run/systemd/resolve/resolv.conf
|
||||
/{,var/}run/systemd/resolve/resolv.conf r,
|
||||
|
||||
/etc/samba/lmhosts r,
|
||||
/etc/services r,
|
||||
|
@@ -10,18 +10,18 @@
|
||||
#
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
/usr/lib{,32,64}/python{2.[4-7],3.[0-4]}/**.{pyc,so} mr,
|
||||
/usr/lib{,32,64}/python{2.[4-7],3.[0-4]}/**.{egg,py,pth} r,
|
||||
/usr/lib{,32,64}/python{2.[4-7],3.[0-4]}/{site,dist}-packages/ r,
|
||||
/usr/lib{,32,64}/python3.[0-4]/lib-dynload/*.so mr,
|
||||
/usr/lib{,32,64}/python{2.[4-7],3.[0-5]}/**.{pyc,so} mr,
|
||||
/usr/lib{,32,64}/python{2.[4-7],3.[0-5]}/**.{egg,py,pth} r,
|
||||
/usr/lib{,32,64}/python{2.[4-7],3.[0-5]}/{site,dist}-packages/ r,
|
||||
/usr/lib{,32,64}/python3.[0-5]/lib-dynload/*.so mr,
|
||||
|
||||
/usr/local/lib{,32,64}/python{2.[4-7],3.[0-4]}/**.{pyc,so} mr,
|
||||
/usr/local/lib{,32,64}/python{2.[4-7],3.[0-4]}/**.{egg,py,pth} r,
|
||||
/usr/local/lib{,32,64}/python{2.[4-7],3.[0-4]}/{site,dist}-packages/ r,
|
||||
/usr/local/lib{,32,64}/python3.[0-4]/lib-dynload/*.so mr,
|
||||
/usr/local/lib{,32,64}/python{2.[4-7],3.[0-5]}/**.{pyc,so} mr,
|
||||
/usr/local/lib{,32,64}/python{2.[4-7],3.[0-5]}/**.{egg,py,pth} r,
|
||||
/usr/local/lib{,32,64}/python{2.[4-7],3.[0-5]}/{site,dist}-packages/ r,
|
||||
/usr/local/lib{,32,64}/python3.[0-5]/lib-dynload/*.so mr,
|
||||
|
||||
# Site-wide configuration
|
||||
/etc/python{2.[4-7],3.[0-4]}/** r,
|
||||
/etc/python{2.[4-7],3.[0-5]}/** r,
|
||||
|
||||
# shared python paths
|
||||
/usr/share/{pyshared,pycentral,python-support}/** r,
|
||||
@@ -34,4 +34,4 @@
|
||||
/usr/lib/wx/python/*.pth r,
|
||||
|
||||
# python build configuration and headers
|
||||
/usr/include/python{2.[4-7],3.[0-4]}*/pyconfig.h r,
|
||||
/usr/include/python{2.[4-7],3.[0-5]}*/pyconfig.h r,
|
||||
|
@@ -23,3 +23,7 @@
|
||||
/usr/local/share/ca-certificates/** r,
|
||||
/var/lib/ca-certificates/ r,
|
||||
/var/lib/ca-certificates/** r,
|
||||
|
||||
# acmetool
|
||||
/var/lib/acme/certs/*/chain r,
|
||||
/var/lib/acme/certs/*/cert r,
|
||||
|
@@ -16,3 +16,7 @@
|
||||
/etc/ssl/ r,
|
||||
/etc/ssl/** r,
|
||||
|
||||
# acmetool
|
||||
/var/lib/acme/live/* r,
|
||||
/var/lib/acme/certs/** r,
|
||||
/var/lib/acme/keys/** r,
|
||||
|
@@ -38,6 +38,7 @@
|
||||
/var/tmp/smtp_* rw,
|
||||
|
||||
/{var/,}run/dovecot/auth-token-secret.dat{,.tmp} rw,
|
||||
/{var/,}run/dovecot/stats-user w,
|
||||
|
||||
# Site-specific additions and overrides. See local/README for details.
|
||||
#include <local/usr.lib.dovecot.auth>
|
||||
|
@@ -1,6 +1,6 @@
|
||||
# ------------------------------------------------------------------
|
||||
#
|
||||
# Copyright (C) 2013 Christian Boltz
|
||||
# Copyright (C) 2013-2016 Christian Boltz
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of version 2 of the GNU General Public
|
||||
@@ -24,10 +24,65 @@
|
||||
|
||||
/etc/dovecot/** r,
|
||||
/proc/*/mounts r,
|
||||
owner /tmp/dovecot.lda.* rw,
|
||||
/{var/,}run/dovecot/mounts r,
|
||||
/usr/bin/doveconf mrix,
|
||||
/usr/lib/dovecot/dovecot-lda mrix,
|
||||
/usr/sbin/sendmail Cx,
|
||||
|
||||
# Site-specific additions and overrides. See local/README for details.
|
||||
#include <local/usr.lib.dovecot.dovecot-lda>
|
||||
|
||||
|
||||
profile /usr/sbin/sendmail flags=(attach_disconnected) {
|
||||
# this profile is based on the usr.sbin.sendmail profile in extras
|
||||
# and should support both postfix' and sendmail's sendmail binary
|
||||
|
||||
#include <abstractions/base>
|
||||
#include <abstractions/consoles>
|
||||
#include <abstractions/nameservice>
|
||||
#include <abstractions/user-tmp>
|
||||
#include <abstractions/postfix-common>
|
||||
|
||||
capability sys_ptrace,
|
||||
|
||||
/etc/aliases rw, # newaliases is a symlink to sendmail, so it's
|
||||
/etc/aliases.db rw, # actually the same binary
|
||||
/etc/fstab r,
|
||||
/etc/hosts.allow r,
|
||||
/etc/hosts.deny r,
|
||||
/etc/mail/* r,
|
||||
/etc/mail/statistics rw,
|
||||
/etc/mtab r,
|
||||
/etc/postfix/aliases r,
|
||||
/etc/postfix/aliases.db rw, # newaliases again
|
||||
/etc/sendmail.cf r,
|
||||
/etc/sendmail.cw r,
|
||||
/etc/shells r,
|
||||
/proc/loadavg r,
|
||||
/proc/net/if_inet6 r,
|
||||
/root/.forward r,
|
||||
/root/dead.letter w,
|
||||
/usr/bin/procmail Px,
|
||||
/usr/lib/postfix/master Px,
|
||||
/usr/lib/postfix/showq Px,
|
||||
/usr/lib/postfix/smtpd Px,
|
||||
/usr/sbin/postalias Px,
|
||||
/usr/sbin/postdrop Px,
|
||||
/usr/sbin/postfix Px,
|
||||
/usr/sbin/postqueue Px,
|
||||
/usr/sbin/sendmail mrix,
|
||||
/usr/sbin/sendmail.postfix mrix,
|
||||
/usr/sbin/sendmail.sendmail mrix,
|
||||
/{var/,}run/sendmail.pid rwl,
|
||||
/{var/,}run/sm-client.pid rwl,
|
||||
/{var/,}run/utmp rw,
|
||||
/var/spool/clientmqueue/* rwl,
|
||||
/var/spool/mail/* rwl,
|
||||
/var/spool/mqueue/* rwl,
|
||||
/var/spool/postfix/maildrop/* rwl,
|
||||
/var/spool/postfix/public/pickup w,
|
||||
/var/spool/postfix/public/qmgr w,
|
||||
/var/spool/postfix/public/showq w,
|
||||
}
|
||||
}
|
||||
|
@@ -12,7 +12,7 @@
|
||||
@{TFTP_DIR}=/var/tftp /srv/tftpboot
|
||||
|
||||
#include <tunables/global>
|
||||
/usr/sbin/dnsmasq {
|
||||
/usr/sbin/dnsmasq flags=(attach_disconnected) {
|
||||
#include <abstractions/base>
|
||||
#include <abstractions/dbus>
|
||||
#include <abstractions/nameservice>
|
||||
|
@@ -31,6 +31,7 @@
|
||||
/{var/cache,var/run,run}/nscd/{passwd,group,services,hosts,netgroup} rw,
|
||||
/{,var/}run/{nscd/,}nscd.pid rwl,
|
||||
/var/log/nscd.log rw,
|
||||
@{PROC}/@{pid}/cmdline r,
|
||||
@{PROC}/@{pid}/fd/ r,
|
||||
@{PROC}/@{pid}/fd/* r,
|
||||
@{PROC}/@{pid}/mounts r,
|
||||
|
@@ -17,6 +17,7 @@
|
||||
capability net_bind_service,
|
||||
capability setgid,
|
||||
capability setuid,
|
||||
capability sys_admin, # needed to store ACLS in the security.NTACL namespace
|
||||
capability sys_resource,
|
||||
capability sys_tty_config,
|
||||
|
||||
|
@@ -178,6 +178,7 @@ TESTS=access \
|
||||
mount \
|
||||
mult_mount \
|
||||
named_pipe \
|
||||
namespaces \
|
||||
net_raw \
|
||||
open \
|
||||
openat \
|
||||
|
@@ -29,6 +29,7 @@ fqsubbase="$pwd/changeprofile"
|
||||
fqsubtest="$fqsubbase//$subtest"
|
||||
subtest2="$pwd//sub2"
|
||||
subtest3="$pwd//sub3"
|
||||
nstest=":ns:changeprofile"
|
||||
|
||||
|
||||
touch $file $subfile
|
||||
@@ -70,3 +71,10 @@ runchecktest "CHANGEPROFILE_RE (nochange access file)" pass nochange $file
|
||||
runchecktest_errno EACCES "CHANGEPROFILE_RE (nochange access subfile)" fail nochange $subfile
|
||||
runchecktest_errno EACCES "CHANGEPROFILE_RE (access file)" fail $fqsubtest $file
|
||||
runchecktest "CHANGEPROFILE_RE (access sub file)" pass $fqsubtest $subfile
|
||||
|
||||
genprofile --stdin <<EOF
|
||||
$test { file, change_profile -> ${nstest}, }
|
||||
$nstest { $subfile ${okperm}, }
|
||||
EOF
|
||||
runchecktest "CHANGEPROFILE_NS (access sub file)" pass $nstest $subfile
|
||||
runchecktest "CHANGEPROFILE_NS (access file)" fail $nstest $file
|
||||
|
76
tests/regression/apparmor/namespaces.sh
Executable file
76
tests/regression/apparmor/namespaces.sh
Executable file
@@ -0,0 +1,76 @@
|
||||
#! /bin/bash
|
||||
# Copyright (C) 2016 Canonical, Ltd.
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License as
|
||||
# published by the Free Software Foundation, version 2 of the
|
||||
# License.
|
||||
|
||||
#=NAME namespaces
|
||||
#=DESCRIPTION
|
||||
# Verifies basic namespace functionality
|
||||
#=END
|
||||
|
||||
pwd=`dirname $0`
|
||||
pwd=`cd $pwd ; /bin/pwd`
|
||||
|
||||
bin=$pwd
|
||||
|
||||
. $bin/prologue.inc
|
||||
requires_namespace_interface
|
||||
|
||||
# unique_ns - Print a randomly generated, unused namespace identifier to stdout
|
||||
unique_ns() {
|
||||
# racy way of generating a namespace name that is likely to be unique
|
||||
local ns=$(mktemp --dry-run -dp /sys/kernel/security/apparmor/policy/namespaces -t test_namespaces_XXXXXX)
|
||||
basename "$ns"
|
||||
}
|
||||
|
||||
# genprofile_ns - Generate and load a profile using a randomly generated namespace
|
||||
# $1: The profile name to use (without a namespace)
|
||||
# $2: Non-zero if the 'profile' keyword should be prefixed to the declaration
|
||||
#
|
||||
# Returns the randomly generated namespace that the profile was loaded into
|
||||
genprofile_ns() {
|
||||
local prefix=""
|
||||
local ns=$(unique_ns)
|
||||
local prof=$1
|
||||
|
||||
if [ $2 -ne 0 ]; then
|
||||
prefix="profile "
|
||||
fi
|
||||
|
||||
# override the sys_profiles variable with a bad path so that genprofile
|
||||
# doesn't perform profile load checking in the wrong policy namespace
|
||||
echo "${prefix}:${ns}:${prof} {}" | sys_profiles="${sys_profiles}XXX" genprofile --stdin
|
||||
echo "$ns"
|
||||
}
|
||||
|
||||
# genprofile_ns_and_verify - Generate and load a profile into a namespace and
|
||||
# verify the creation of the profile and namespace
|
||||
# $1: A description of this test
|
||||
# $2: Non-zero if the 'profile' keyword should be prefixed to the declaration
|
||||
genprofile_ns_and_verify() {
|
||||
local desc=$1
|
||||
local prof="p"
|
||||
local ns=$(genprofile_ns "$prof" $2)
|
||||
|
||||
|
||||
[ -d /sys/kernel/security/apparmor/policy/namespaces/${ns} ]
|
||||
local dir_created=$?
|
||||
[ -d /sys/kernel/security/apparmor/policy/namespaces/${ns}/profiles/${prof}* ]
|
||||
local prof_created=$?
|
||||
removeprofile
|
||||
if [ $dir_created -ne 0 ]; then
|
||||
echo "Error: ${testname} failed. Test '${desc}' did not create the expected namespace directory in apparmorfs: policy/namespaces/${ns}"
|
||||
testfailed
|
||||
elif [ $prof_created -ne 0 ]; then
|
||||
echo "Error: ${testname} failed. Test '${desc}' did not create the expected namespaced profile directory in apparmorfs: policy/namespaces/${ns}/profiles/${prof}"
|
||||
testfailed
|
||||
elif [ -n "$VERBOSE" ]; then
|
||||
echo "ok: ${desc}"
|
||||
fi
|
||||
}
|
||||
|
||||
genprofile_ns_and_verify "NAMESPACES create unique ns (w/o profile keyword prefix)" 0
|
||||
genprofile_ns_and_verify "NAMESPACES create unique ns (w/ profile keyword prefix)" 1
|
@@ -49,6 +49,15 @@ requires_kernel_features()
|
||||
fi
|
||||
}
|
||||
|
||||
requires_namespace_interface()
|
||||
{
|
||||
if [ ! -e "/sys/kernel/security/apparmor/policy/namespaces" ]
|
||||
then
|
||||
echo "Namespaces in apparmorfs policy interface not supported. Skipping tests ..."
|
||||
exit 0
|
||||
fi
|
||||
}
|
||||
|
||||
requires_query_interface()
|
||||
{
|
||||
if [ ! -e "/sys/kernel/security/apparmor/.access" ]
|
||||
|
@@ -47,6 +47,8 @@ int interp_status(int status)
|
||||
# endif
|
||||
# elif defined(__arm__) || defined(__powerpc__) || defined(__powerpc64__)
|
||||
# define ARCH_REGS_STRUCT struct pt_regs
|
||||
# elif defined(__s390__) || defined(__s390x__)
|
||||
# define ARCH_REGS_STRUCT struct _user_regs_struct
|
||||
# else
|
||||
# error "Need to define ARCH_REGS_STRUCT for this architecture"
|
||||
# endif
|
||||
|
@@ -52,6 +52,10 @@ runchecktest "TCP (accept, connect) low numbered port/bind cap" pass 23
|
||||
genprofile network:inet
|
||||
runchecktest "TCP (accept, connect) low numbered port/no bind cap" fail 23
|
||||
|
||||
# FAIL TEST - make sure that unspec doesn't match
|
||||
genprofile network:unspec
|
||||
runchecktest "TCP (accept, connect) wrong socket family" fail 23
|
||||
|
||||
exit 0
|
||||
|
||||
# PASS TEST - accept via interface
|
||||
|
@@ -26,7 +26,7 @@ PYTOOLS = aa-easyprof aa-genprof aa-logprof aa-cleanprof aa-mergeprof \
|
||||
aa-status aa-unconfined
|
||||
TOOLS = ${PERLTOOLS} ${PYTOOLS} aa-decode
|
||||
PYSETUP = python-tools-setup.py
|
||||
PYMODULES = $(wildcard apparmor/*.py)
|
||||
PYMODULES = $(wildcard apparmor/*.py apparmor/rule/*.py)
|
||||
|
||||
MANPAGES = ${TOOLS:=.8} logprof.conf.5
|
||||
|
||||
|
@@ -10,7 +10,7 @@
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
import apparmor.easyprof
|
||||
from apparmor.easyprof import AppArmorException, error
|
||||
from apparmor.easyprof import error
|
||||
import os
|
||||
import sys
|
||||
|
||||
@@ -61,12 +61,7 @@ if __name__ == "__main__":
|
||||
for (binary, options) in profiles:
|
||||
if len(profiles) > 1:
|
||||
count += 1
|
||||
try:
|
||||
easyp = apparmor.easyprof.AppArmorEasyProfile(binary, options)
|
||||
except AppArmorException as e:
|
||||
error(e.value)
|
||||
except Exception:
|
||||
raise
|
||||
easyp = apparmor.easyprof.AppArmorEasyProfile(binary, options)
|
||||
|
||||
if options.list_templates:
|
||||
apparmor.easyprof.print_basefilenames(easyp.get_templates())
|
||||
@@ -118,7 +113,4 @@ if __name__ == "__main__":
|
||||
sys.stdout.write('%s\n' % easyp.gen_manifest(params))
|
||||
else:
|
||||
params['no_verify'] = options.no_verify
|
||||
try:
|
||||
easyp.output_policy(params, count, opt.output_directory)
|
||||
except AppArmorException as e:
|
||||
error(e)
|
||||
easyp.output_policy(params, count, opt.output_directory)
|
||||
|
@@ -12,9 +12,19 @@
|
||||
|
||||
import re, os, sys, errno
|
||||
|
||||
# PLEASE NOTE: we try to keep aa-status as minimal as possible, for
|
||||
# environments where installing all of the python utils and python
|
||||
# apparmor module may not make sense. Please think carefully before
|
||||
# importing anything from apparmor; see how the apparmor.fail import is
|
||||
# handled below.
|
||||
|
||||
# setup exception handling
|
||||
from apparmor.fail import enable_aa_exception_handler
|
||||
enable_aa_exception_handler()
|
||||
try:
|
||||
from apparmor.fail import enable_aa_exception_handler
|
||||
enable_aa_exception_handler()
|
||||
except ImportError:
|
||||
# just let normal python exceptions happen (LP: #1480492)
|
||||
pass
|
||||
|
||||
def cmd_enabled():
|
||||
'''Returns error code if AppArmor is not enabled'''
|
||||
|
@@ -431,7 +431,7 @@ def get_interpreter_and_abstraction(exec_target):
|
||||
abstraction = 'abstractions/perl'
|
||||
elif re.search('^python([23]|[23]\.[0-9]+)?$', interpreter):
|
||||
abstraction = 'abstractions/python'
|
||||
elif interpreter == 'ruby':
|
||||
elif re.search('^ruby([0-9]+(\.[0-9]+)*)?$', interpreter):
|
||||
abstraction = 'abstractions/ruby'
|
||||
else:
|
||||
abstraction = None
|
||||
@@ -443,12 +443,12 @@ def get_inactive_profile(local_profile):
|
||||
return {local_profile: extras[local_profile]}
|
||||
return dict()
|
||||
|
||||
def profile_storage():
|
||||
def profile_storage(profilename, hat, calledby):
|
||||
# keys used in aa[profile][hat]:
|
||||
# a) rules (as dict): alias, include, lvar
|
||||
# b) rules (as hasher): allow, deny
|
||||
# c) one for each rule class
|
||||
# d) other: external, flags, name, profile, attachment, initial_comment,
|
||||
# d) other: external, flags, name, profile, attachment, initial_comment, filename, info,
|
||||
# profile_keyword, header_comment (these two are currently only set by set_profile_flags())
|
||||
|
||||
# Note that this function doesn't explicitely init all those keys (yet).
|
||||
@@ -456,6 +456,9 @@ def profile_storage():
|
||||
|
||||
profile = hasher()
|
||||
|
||||
# profile['info'] isn't used anywhere, but can be helpful in debugging.
|
||||
profile['info'] = {'profile': profilename, 'hat': hat, 'calledby': calledby}
|
||||
|
||||
profile['capability'] = CapabilityRuleset()
|
||||
profile['change_profile'] = ChangeProfileRuleset()
|
||||
profile['network'] = NetworkRuleset()
|
||||
@@ -472,7 +475,7 @@ def profile_storage():
|
||||
|
||||
def create_new_profile(localfile, is_stub=False):
|
||||
local_profile = hasher()
|
||||
local_profile[localfile] = profile_storage()
|
||||
local_profile[localfile] = profile_storage('NEW', localfile, 'create_new_profile()')
|
||||
local_profile[localfile]['flags'] = 'complain'
|
||||
local_profile[localfile]['include']['abstractions/base'] = 1
|
||||
|
||||
@@ -504,7 +507,7 @@ def create_new_profile(localfile, is_stub=False):
|
||||
if re.search(hatglob, localfile):
|
||||
for hat in sorted(cfg['required_hats'][hatglob].split()):
|
||||
if not local_profile.get(hat, False):
|
||||
local_profile[hat] = profile_storage()
|
||||
local_profile[hat] = profile_storage('NEW', hat, 'create_new_profile() required_hats')
|
||||
local_profile[hat]['flags'] = 'complain'
|
||||
|
||||
if not is_stub:
|
||||
@@ -631,8 +634,6 @@ def activate_repo_profiles(url, profiles, complain):
|
||||
def autodep(bin_name, pname=''):
|
||||
bin_full = None
|
||||
global repo_cfg
|
||||
if not bin_name and pname.startswith('/'):
|
||||
bin_name = pname
|
||||
if not repo_cfg and not cfg['repository'].get('url', False):
|
||||
repo_conf = apparmor.config.Config('shell', CONFDIR)
|
||||
repo_cfg = repo_conf.read_config('repository.conf')
|
||||
@@ -1143,6 +1144,7 @@ def handle_children(profile, hat, root):
|
||||
detail = detail.replace('*', '\*')
|
||||
detail = detail.replace('{', '\{')
|
||||
detail = detail.replace('}', '\}')
|
||||
detail = detail.replace('!', '\!')
|
||||
|
||||
# Give Execute dialog if x access requested for something that's not a directory
|
||||
# For directories force an 'ix' Path dialog
|
||||
@@ -1156,25 +1158,7 @@ def handle_children(profile, hat, root):
|
||||
else:
|
||||
do_execute = True
|
||||
|
||||
if mode & apparmor.aamode.AA_MAY_LINK:
|
||||
regex_link = re.compile('^from (.+) to (.+)$')
|
||||
match = regex_link.search(detail)
|
||||
if match:
|
||||
path = match.groups()[0]
|
||||
target = match.groups()[1]
|
||||
|
||||
frommode = str_to_mode('lr')
|
||||
if prelog[aamode][profile][hat]['path'].get(path, False):
|
||||
frommode |= prelog[aamode][profile][hat]['path'][path]
|
||||
prelog[aamode][profile][hat]['path'][path] = frommode
|
||||
|
||||
tomode = str_to_mode('lr')
|
||||
if prelog[aamode][profile][hat]['path'].get(target, False):
|
||||
tomode |= prelog[aamode][profile][hat]['path'][target]
|
||||
prelog[aamode][profile][hat]['path'][target] = tomode
|
||||
else:
|
||||
continue
|
||||
elif mode:
|
||||
if mode:
|
||||
path = detail
|
||||
|
||||
if prelog[aamode][profile][hat]['path'].get(path, False):
|
||||
@@ -1491,7 +1475,7 @@ def handle_children(profile, hat, root):
|
||||
if ynans == 'y':
|
||||
hat = exec_target
|
||||
if not aa[profile].get(hat, False):
|
||||
aa[profile][hat] = profile_storage()
|
||||
aa[profile][hat] = profile_storage(profile, hat, 'handle_children()')
|
||||
aa[profile][hat]['profile'] = True
|
||||
|
||||
if profile != hat:
|
||||
@@ -1612,7 +1596,7 @@ def ask_the_questions():
|
||||
hats = [profile] + hats
|
||||
|
||||
for hat in hats:
|
||||
log_obj[profile][hat] = profile_storage()
|
||||
log_obj[profile][hat] = profile_storage(profile, hat, 'ask_the_questions()')
|
||||
|
||||
for capability in sorted(log_dict[aamode][profile][hat]['capability'].keys()):
|
||||
capability_obj = CapabilityRule(capability, log_event=aamode)
|
||||
@@ -2300,7 +2284,12 @@ def save_profiles():
|
||||
oldprofile = aa[which][which]['filename']
|
||||
else:
|
||||
oldprofile = get_profile_filename(which)
|
||||
newprofile = serialize_profile_from_old_profile(aa[which], which, '')
|
||||
|
||||
try:
|
||||
newprofile = serialize_profile_from_old_profile(aa[which], which, '')
|
||||
except AttributeError:
|
||||
# see https://bugs.launchpad.net/ubuntu/+source/apparmor/+bug/1528139
|
||||
newprofile = "###\n###\n### Internal error while generating diff, please use '%s' instead\n###\n###\n" % _('View Changes b/w (C)lean profiles')
|
||||
|
||||
display_changes_with_comments(oldprofile, newprofile)
|
||||
|
||||
@@ -2600,7 +2589,7 @@ def parse_profile_data(data, file, do_include):
|
||||
if do_include:
|
||||
profile = file
|
||||
hat = file
|
||||
profile_data[profile][hat] = profile_storage()
|
||||
profile_data[profile][hat] = profile_storage(profile, hat, 'parse_profile_data() do_include')
|
||||
profile_data[profile][hat]['filename'] = file
|
||||
|
||||
for lineno, line in enumerate(data):
|
||||
@@ -2619,7 +2608,7 @@ def parse_profile_data(data, file, do_include):
|
||||
raise AppArmorException('Profile %(profile)s defined twice in %(file)s, last found in line %(line)s' %
|
||||
{ 'file': file, 'line': lineno + 1, 'profile': combine_name(profile, hat) })
|
||||
|
||||
profile_data[profile][hat] = profile_storage()
|
||||
profile_data[profile][hat] = profile_storage(profile, hat, 'parse_profile_data() profile_start')
|
||||
|
||||
if attachment:
|
||||
profile_data[profile][hat]['attachment'] = attachment
|
||||
@@ -2739,7 +2728,7 @@ def parse_profile_data(data, file, do_include):
|
||||
|
||||
list_var = strip_quotes(matches[0])
|
||||
var_operation = matches[1]
|
||||
value = strip_quotes(matches[2])
|
||||
value = matches[2]
|
||||
|
||||
if profile:
|
||||
if not profile_data[profile][hat].get('lvar', False):
|
||||
@@ -2768,8 +2757,12 @@ def parse_profile_data(data, file, do_include):
|
||||
if not profile:
|
||||
raise AppArmorException(_('Syntax Error: Unexpected bare file rule found in file: %(file)s line: %(line)s') % { 'file': file, 'line': lineno + 1 })
|
||||
|
||||
audit, allow, allow_keyword, comment = parse_modifiers(matches)
|
||||
audit, deny, allow_keyword, comment = parse_modifiers(matches)
|
||||
# TODO: honor allow_keyword and comment
|
||||
if deny:
|
||||
allow = 'deny'
|
||||
else:
|
||||
allow = 'allow'
|
||||
|
||||
mode = apparmor.aamode.AA_BARE_FILE_MODE
|
||||
if not matches.group('owner'):
|
||||
@@ -3024,7 +3017,7 @@ def parse_profile_data(data, file, do_include):
|
||||
# if hat is already known, the filelist check some lines below will error out.
|
||||
# nevertheless, just to be sure, don't overwrite existing profile_data.
|
||||
if not profile_data[profile].get(hat, False):
|
||||
profile_data[profile][hat] = profile_storage()
|
||||
profile_data[profile][hat] = profile_storage(profile, hat, 'parse_profile_data() hat_def')
|
||||
profile_data[profile][hat]['filename'] = file
|
||||
|
||||
flags = matches.group('flags')
|
||||
@@ -3065,7 +3058,7 @@ def parse_profile_data(data, file, do_include):
|
||||
else:
|
||||
lastline = line
|
||||
else:
|
||||
raise AppArmorException(_('Syntax Error: Unknown line found in file: %(file)s line: %(line)s') % { 'file': file, 'line': lineno + 1 })
|
||||
raise AppArmorException(_('Syntax Error: Unknown line found in file %(file)s line %(lineno)s:\n %(line)s') % { 'file': file, 'lineno': lineno + 1, 'line': line })
|
||||
|
||||
# Below is not required I'd say
|
||||
if not do_include:
|
||||
@@ -3074,7 +3067,7 @@ def parse_profile_data(data, file, do_include):
|
||||
if re.search(hatglob, parsed_prof):
|
||||
for hat in cfg['required_hats'][hatglob].split():
|
||||
if not profile_data[parsed_prof].get(hat, False):
|
||||
profile_data[parsed_prof][hat] = profile_storage()
|
||||
profile_data[parsed_prof][hat] = profile_storage(parsed_prof, hat, 'parse_profile_data() required_hats')
|
||||
|
||||
# End of file reached but we're stuck in a profile
|
||||
if profile and not do_include:
|
||||
@@ -3120,12 +3113,16 @@ def parse_unix_rule(line):
|
||||
def separate_vars(vs):
|
||||
"""Returns a list of all the values for a variable"""
|
||||
data = set()
|
||||
vs = vs.strip()
|
||||
|
||||
RE_VARS = re.compile('\s*((\".+?\")|([^\"]\S+))\s*(.*)$')
|
||||
RE_VARS = re.compile('^(("[^"]*")|([^"\s]+))\s*(.*)$')
|
||||
while RE_VARS.search(vs):
|
||||
matches = RE_VARS.search(vs).groups()
|
||||
data.add(strip_quotes(matches[0]))
|
||||
vs = matches[3]
|
||||
vs = matches[3].strip()
|
||||
|
||||
if vs:
|
||||
raise AppArmorException('Variable assignments contains invalid parts (unbalanced quotes?): %s' % vs)
|
||||
|
||||
return data
|
||||
|
||||
@@ -3253,6 +3250,8 @@ def write_rlimits(prof_data, depth):
|
||||
def var_transform(ref):
|
||||
data = []
|
||||
for value in ref:
|
||||
if not value:
|
||||
value = '""'
|
||||
data.append(quote_if_needed(value))
|
||||
return ' '.join(data)
|
||||
|
||||
@@ -3361,6 +3360,24 @@ def write_pivot_root(prof_data, depth):
|
||||
data += write_pivot_root_rules(prof_data, depth, 'allow')
|
||||
return data
|
||||
|
||||
def write_unix_rules(prof_data, depth, allow):
|
||||
pre = ' ' * depth
|
||||
data = []
|
||||
|
||||
# no unix rules, so return
|
||||
if not prof_data[allow].get('unix', False):
|
||||
return data
|
||||
|
||||
for unix_rule in prof_data[allow]['unix']:
|
||||
data.append('%s%s' % (pre, unix_rule.serialize()))
|
||||
data.append('')
|
||||
return data
|
||||
|
||||
def write_unix(prof_data, depth):
|
||||
data = write_unix_rules(prof_data, depth, 'deny')
|
||||
data += write_unix_rules(prof_data, depth, 'allow')
|
||||
return data
|
||||
|
||||
def write_link_rules(prof_data, depth, allow):
|
||||
pre = ' ' * depth
|
||||
data = []
|
||||
@@ -3472,6 +3489,7 @@ def write_rules(prof_data, depth):
|
||||
data += write_signal(prof_data, depth)
|
||||
data += write_ptrace(prof_data, depth)
|
||||
data += write_pivot_root(prof_data, depth)
|
||||
data += write_unix(prof_data, depth)
|
||||
data += write_links(prof_data, depth)
|
||||
data += write_paths(prof_data, depth)
|
||||
data += write_change_profile(prof_data, depth)
|
||||
@@ -3591,6 +3609,11 @@ def serialize_profile_from_old_profile(profile_data, name, options):
|
||||
write_filelist = deepcopy(filelist[prof_filename])
|
||||
write_prof_data = deepcopy(profile_data)
|
||||
|
||||
# XXX profile_data / write_prof_data contain only one profile with its hats
|
||||
# XXX this will explode if a file contains multiple profiles, see https://bugs.launchpad.net/ubuntu/+source/apparmor/+bug/1528139
|
||||
# XXX fixing this needs lots of write_prof_data[hat] -> write_prof_data[profile][hat] changes (and of course also a change in the calling code)
|
||||
# XXX (the better option is a full rewrite of serialize_profile_from_old_profile())
|
||||
|
||||
if options: # and type(options) == dict:
|
||||
if options.get('METADATA', False):
|
||||
include_metadata = True
|
||||
@@ -3628,6 +3651,7 @@ def serialize_profile_from_old_profile(profile_data, name, options):
|
||||
'signal': write_signal,
|
||||
'ptrace': write_ptrace,
|
||||
'pivot_root': write_pivot_root,
|
||||
'unix': write_unix,
|
||||
'link': write_links,
|
||||
'path': write_paths,
|
||||
'change_profile': write_change_profile,
|
||||
@@ -3643,6 +3667,7 @@ def serialize_profile_from_old_profile(profile_data, name, options):
|
||||
'signal',
|
||||
'ptrace',
|
||||
'pivot_root',
|
||||
'unix',
|
||||
'link',
|
||||
'path',
|
||||
'change_profile',
|
||||
@@ -3659,6 +3684,7 @@ def serialize_profile_from_old_profile(profile_data, name, options):
|
||||
'signal': True, # not handled otherwise yet
|
||||
'ptrace': True, # not handled otherwise yet
|
||||
'pivot_root': True, # not handled otherwise yet
|
||||
'unix': True, # not handled otherwise yet
|
||||
'link': False,
|
||||
'path': False,
|
||||
'change_profile': False,
|
||||
@@ -3689,7 +3715,7 @@ def serialize_profile_from_old_profile(profile_data, name, options):
|
||||
if RE_PROFILE_START.search(line):
|
||||
|
||||
(profile, hat, attachment, flags, in_contained_hat, correct) = serialize_parse_profile_start(
|
||||
line, prof_filename, None, profile, hat, write_prof_data[profile][hat]['profile'], write_prof_data[profile][hat]['external'], correct)
|
||||
line, prof_filename, None, profile, hat, write_prof_data[hat]['profile'], write_prof_data[hat]['external'], correct)
|
||||
|
||||
if not write_prof_data[hat]['name'] == profile:
|
||||
correct = False
|
||||
@@ -3925,7 +3951,7 @@ def serialize_profile_from_old_profile(profile_data, name, options):
|
||||
if matches[0]:
|
||||
audit = mode
|
||||
|
||||
path_rule = write_prof_data[profile][hat][allow]['path'][ALL]
|
||||
path_rule = write_prof_data[hat][allow]['path'][ALL]
|
||||
if path_rule.get('mode', set()) & mode and \
|
||||
(not audit or path_rule.get('audit', set()) & audit) and \
|
||||
path_rule.get('file_prefix', set()):
|
||||
@@ -4071,7 +4097,11 @@ def write_profile(profile):
|
||||
|
||||
os.rename(newprof.name, prof_filename)
|
||||
|
||||
changed.pop(profile)
|
||||
if profile in changed:
|
||||
changed.pop(profile)
|
||||
else:
|
||||
debug_logger.info("Unchanged profile written: %s (not listed in 'changed' list)" % profile)
|
||||
|
||||
original_aa[profile] = deepcopy(aa[profile])
|
||||
|
||||
def matchliteral(aa_regexp, literal):
|
||||
|
@@ -245,6 +245,15 @@ def user_perm(prof_dir):
|
||||
return False
|
||||
return True
|
||||
|
||||
def type_is_str(var):
|
||||
''' returns True if the given variable is a str (or unicode string when using python 2)'''
|
||||
if type(var) == str:
|
||||
return True
|
||||
elif sys.version_info[0] < 3 and type(var) == unicode: # python 2 sometimes uses the 'unicode' type
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
class DebugLogger(object):
|
||||
def __init__(self, module_name=__name__):
|
||||
self.debugging = False
|
||||
|
@@ -8,12 +8,16 @@
|
||||
#
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
from __future__ import print_function # needed in py2 for print('...', file=sys.stderr)
|
||||
|
||||
import cgitb
|
||||
import os
|
||||
import sys
|
||||
import tempfile
|
||||
import traceback
|
||||
|
||||
from apparmor.common import error
|
||||
|
||||
#
|
||||
# Exception handling
|
||||
#
|
||||
@@ -27,8 +31,8 @@ def handle_exception(*exc_info):
|
||||
(ex_cls, ex, tb) = exc_info
|
||||
|
||||
if ex_cls.__name__ == 'AppArmorException': # I didn't find a way to get this working with isinstance() :-/
|
||||
print('')
|
||||
print(ex.value)
|
||||
print('', file=sys.stderr)
|
||||
error(ex.value)
|
||||
else:
|
||||
(fd, path) = tempfile.mkstemp(prefix='apparmor-bugreport-', suffix='.txt')
|
||||
file = os.fdopen(fd, 'w')
|
||||
@@ -40,13 +44,13 @@ def handle_exception(*exc_info):
|
||||
file.write('Please consider reporting a bug at https://bugs.launchpad.net/apparmor/\n')
|
||||
file.write('and attach this file.\n')
|
||||
|
||||
print(''.join(traceback.format_exception(*exc_info)))
|
||||
print('')
|
||||
print('An unexpected error occoured!')
|
||||
print('')
|
||||
print('For details, see %s' % path)
|
||||
print('Please consider reporting a bug at https://bugs.launchpad.net/apparmor/')
|
||||
print('and attach this file.')
|
||||
print(''.join(traceback.format_exception(*exc_info)), file=sys.stderr)
|
||||
print('', file=sys.stderr)
|
||||
print('An unexpected error occoured!', file=sys.stderr)
|
||||
print('', file=sys.stderr)
|
||||
print('For details, see %s' % path, file=sys.stderr)
|
||||
print('Please consider reporting a bug at https://bugs.launchpad.net/apparmor/', file=sys.stderr)
|
||||
print('and attach this file.', file=sys.stderr)
|
||||
|
||||
def enable_aa_exception_handler():
|
||||
'''Setup handle_exception() as exception handler'''
|
||||
|
@@ -17,7 +17,7 @@ import re
|
||||
import sys
|
||||
import time
|
||||
import LibAppArmor
|
||||
from apparmor.common import AppArmorException, open_file_read, DebugLogger
|
||||
from apparmor.common import AppArmorException, AppArmorBug, open_file_read, DebugLogger
|
||||
|
||||
from apparmor.aamode import validate_log_mode, log_str_to_mode, hide_log_mode, AA_MAY_EXEC
|
||||
|
||||
@@ -250,10 +250,10 @@ class ReadLog:
|
||||
if e['operation'] == 'change_hat':
|
||||
if aamode != 'HINT' and aamode != 'PERMITTING':
|
||||
return None
|
||||
profile = e['name']
|
||||
profile = e['name2']
|
||||
#hat = None
|
||||
if '//' in e['name']:
|
||||
profile, hat = e['name'].split('//')[:2]
|
||||
if '//' in e['name2']:
|
||||
profile, hat = e['name2'].split('//')[:2]
|
||||
|
||||
if not hat:
|
||||
hat = profile
|
||||
@@ -282,21 +282,22 @@ class ReadLog:
|
||||
'rename_dest', 'unlink', 'rmdir', 'symlink_create', 'link',
|
||||
'sysctl', 'getattr', 'setattr', 'xattr'] ):
|
||||
|
||||
# for some reason, we get file_perm and file_inherit log events without request_mask, see
|
||||
# https://bugs.launchpad.net/apparmor/+bug/1466812/ and https://bugs.launchpad.net/apparmor/+bug/1509030
|
||||
if e['operation'] in ['file_perm', 'file_inherit'] and e['request_mask'] is None:
|
||||
# for some kernel-side reason, we get file-related log events without request_mask, see
|
||||
# https://bugs.launchpad.net/apparmor/+bug/1466812/, https://bugs.launchpad.net/apparmor/+bug/1509030 and https://bugs.launchpad.net/apparmor/+bug/1540562
|
||||
# request_mask can also be '', see https://bugs.launchpad.net/ubuntu/+source/apparmor/+bug/1525119
|
||||
if not e['request_mask']:
|
||||
self.debug_logger.debug('UNHANDLED (missing request_mask): %s' % e)
|
||||
return None
|
||||
|
||||
# Map c (create) to a and d (delete) to w (logging is more detailed than the profile language)
|
||||
# Map c (create) and d (delete) to w (logging is more detailed than the profile language)
|
||||
rmask = e['request_mask']
|
||||
rmask = rmask.replace('c', 'a')
|
||||
rmask = rmask.replace('c', 'w')
|
||||
rmask = rmask.replace('d', 'w')
|
||||
if not validate_log_mode(hide_log_mode(rmask)):
|
||||
raise AppArmorException(_('Log contains unknown mode %s') % rmask)
|
||||
|
||||
dmask = e['denied_mask']
|
||||
dmask = dmask.replace('c', 'a')
|
||||
dmask = dmask.replace('c', 'w')
|
||||
dmask = dmask.replace('d', 'w')
|
||||
if not validate_log_mode(hide_log_mode(dmask)):
|
||||
raise AppArmorException(_('Log contains unknown mode %s') % dmask)
|
||||
@@ -385,7 +386,14 @@ class ReadLog:
|
||||
event = self.parse_log_record(line)
|
||||
#print(event)
|
||||
if event:
|
||||
self.add_event_to_tree(event)
|
||||
try:
|
||||
self.add_event_to_tree(event)
|
||||
except AppArmorException as e:
|
||||
ex_msg = ('%(msg)s\n\nThis error was caused by the log line:\n%(logline)s' %
|
||||
{'msg': e.value, 'logline': line})
|
||||
# when py3 only: Drop the original AppArmorException by passing None as the parent exception
|
||||
raise AppArmorBug(ex_msg) # py3-only: from None
|
||||
|
||||
self.LOG.close()
|
||||
self.logmark = ''
|
||||
return self.log
|
||||
|
@@ -52,7 +52,11 @@ class BaseRule(object):
|
||||
|
||||
def __repr__(self):
|
||||
classname = self.__class__.__name__
|
||||
return '<%s> ' % classname + self.get_raw()
|
||||
try:
|
||||
raw_content = self.get_raw() # will fail for BaseRule
|
||||
return '<%s> %s' % (classname, raw_content)
|
||||
except NotImplementedError:
|
||||
return '<%s (NotImplementedError - get_clean() not implemented?)>' % classname
|
||||
|
||||
@classmethod
|
||||
def match(cls, raw_rule):
|
||||
@@ -69,7 +73,7 @@ class BaseRule(object):
|
||||
@classmethod
|
||||
def _match(cls, raw_rule):
|
||||
'''parse raw_rule and return regex match object'''
|
||||
raise AppArmorBug("'%s' needs to implement _match(), but didn't" % (str(cls)))
|
||||
raise NotImplementedError("'%s' needs to implement _match(), but didn't" % (str(cls)))
|
||||
|
||||
@classmethod
|
||||
def parse(cls, raw_rule):
|
||||
@@ -83,12 +87,12 @@ class BaseRule(object):
|
||||
def _parse(cls, raw_rule):
|
||||
'''returns a Rule object created from parsing the raw rule.
|
||||
required to be implemented by subclasses; raise exception if not'''
|
||||
raise AppArmorBug("'%s' needs to implement _parse(), but didn't" % (str(cls)))
|
||||
raise NotImplementedError("'%s' needs to implement _parse(), but didn't" % (str(cls)))
|
||||
|
||||
# @abstractmethod FIXME - uncomment when python3 only
|
||||
def get_clean(self, depth=0):
|
||||
'''return clean rule (with default formatting, and leading whitespace as specified in the depth parameter)'''
|
||||
raise AppArmorBug("'%s' needs to implement get_clean(), but didn't" % (str(self.__class__)))
|
||||
raise NotImplementedError("'%s' needs to implement get_clean(), but didn't" % (str(self.__class__)))
|
||||
|
||||
def get_raw(self, depth=0):
|
||||
'''return raw rule (with original formatting, and leading whitespace in the depth parameter)'''
|
||||
@@ -121,7 +125,7 @@ class BaseRule(object):
|
||||
# @abstractmethod FIXME - uncomment when python3 only
|
||||
def is_covered_localvars(self, other_rule):
|
||||
'''check if the rule-specific parts of other_rule is covered by this rule object'''
|
||||
raise AppArmorBug("'%s' needs to implement is_covered_localvars(), but didn't" % (str(self)))
|
||||
raise NotImplementedError("'%s' needs to implement is_covered_localvars(), but didn't" % (str(self)))
|
||||
|
||||
def is_equal(self, rule_obj, strict=False):
|
||||
'''compare if rule_obj == self
|
||||
@@ -142,7 +146,7 @@ class BaseRule(object):
|
||||
# @abstractmethod FIXME - uncomment when python3 only
|
||||
def is_equal_localvars(self, other_rule):
|
||||
'''compare if rule-specific variables are equal'''
|
||||
raise AppArmorBug("'%s' needs to implement is_equal_localvars(), but didn't" % (str(self)))
|
||||
raise NotImplementedError("'%s' needs to implement is_equal_localvars(), but didn't" % (str(self)))
|
||||
|
||||
def severity(self, sev_db):
|
||||
'''return severity of this rule, which can be:
|
||||
@@ -178,7 +182,7 @@ class BaseRule(object):
|
||||
def logprof_header_localvars(self):
|
||||
'''return the headers (human-readable version of the rule) to display in aa-logprof for this rule object
|
||||
returns {'label1': 'value1', 'label2': 'value2'} '''
|
||||
raise AppArmorBug("'%s' needs to implement logprof_header(), but didn't" % (str(self)))
|
||||
raise NotImplementedError("'%s' needs to implement logprof_header(), but didn't" % (str(self)))
|
||||
|
||||
def modifiers_str(self):
|
||||
'''return the allow/deny and audit keyword as string, including whitespace'''
|
||||
@@ -217,7 +221,10 @@ class BaseRuleset(object):
|
||||
|
||||
def __repr__(self):
|
||||
classname = self.__class__.__name__
|
||||
return '<%s>\n' % classname + '\n'.join(self.get_raw(1)) + '</%s>' % classname
|
||||
if self.rules:
|
||||
return '<%s>\n' % classname + '\n'.join(self.get_raw(1)) + '</%s>' % classname
|
||||
else:
|
||||
return '<%s (empty) />' % classname
|
||||
|
||||
def add(self, rule):
|
||||
'''add a rule object'''
|
||||
@@ -336,7 +343,7 @@ class BaseRuleset(object):
|
||||
def get_glob_ext(self, path_or_rule):
|
||||
'''returns the next possible glob with extension (for file rules only).
|
||||
For all other rule types, raise an exception'''
|
||||
raise AppArmorBug("get_glob_ext is not available for this rule type!")
|
||||
raise NotImplementedError("get_glob_ext is not available for this rule type!")
|
||||
|
||||
|
||||
def parse_comment(matches):
|
||||
|
@@ -14,7 +14,7 @@
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
from apparmor.regex import RE_PROFILE_CAP
|
||||
from apparmor.common import AppArmorBug, AppArmorException
|
||||
from apparmor.common import AppArmorBug, AppArmorException, type_is_str
|
||||
from apparmor.rule import BaseRule, BaseRuleset, parse_modifiers
|
||||
import re
|
||||
|
||||
@@ -47,7 +47,7 @@ class CapabilityRule(BaseRule):
|
||||
self.all_caps = True
|
||||
self.capability = set()
|
||||
else:
|
||||
if type(cap_list) == str:
|
||||
if type_is_str(cap_list):
|
||||
self.capability = {cap_list}
|
||||
elif type(cap_list) == list and len(cap_list) > 0:
|
||||
self.capability = set(cap_list)
|
||||
|
@@ -14,7 +14,7 @@
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
from apparmor.regex import RE_PROFILE_CHANGE_PROFILE, strip_quotes
|
||||
from apparmor.common import AppArmorBug, AppArmorException
|
||||
from apparmor.common import AppArmorBug, AppArmorException, type_is_str
|
||||
from apparmor.rule import BaseRule, BaseRuleset, parse_modifiers, quote_if_needed
|
||||
|
||||
# setup module translations
|
||||
@@ -48,7 +48,7 @@ class ChangeProfileRule(BaseRule):
|
||||
self.all_execconds = False
|
||||
if execcond == ChangeProfileRule.ALL:
|
||||
self.all_execconds = True
|
||||
elif type(execcond) == str:
|
||||
elif type_is_str(execcond):
|
||||
if not execcond.strip():
|
||||
raise AppArmorBug('Empty exec condition in change_profile rule')
|
||||
elif execcond.startswith('/') or execcond.startswith('@'):
|
||||
@@ -62,7 +62,7 @@ class ChangeProfileRule(BaseRule):
|
||||
self.all_targetprofiles = False
|
||||
if targetprofile == ChangeProfileRule.ALL:
|
||||
self.all_targetprofiles = True
|
||||
elif type(targetprofile) == str:
|
||||
elif type_is_str(targetprofile):
|
||||
if targetprofile.strip():
|
||||
self.targetprofile = targetprofile
|
||||
else:
|
||||
|
@@ -16,7 +16,7 @@
|
||||
import re
|
||||
|
||||
from apparmor.regex import RE_PROFILE_NETWORK
|
||||
from apparmor.common import AppArmorBug, AppArmorException
|
||||
from apparmor.common import AppArmorBug, AppArmorException, type_is_str
|
||||
from apparmor.rule import BaseRule, BaseRuleset, parse_modifiers
|
||||
|
||||
# setup module translations
|
||||
@@ -24,7 +24,7 @@ from apparmor.translations import init_translation
|
||||
_ = init_translation()
|
||||
|
||||
|
||||
network_domain_keywords = [ 'unix', 'inet', 'ax25', 'ipx', 'appletalk', 'netrom', 'bridge', 'atmpvc', 'x25', 'inet6',
|
||||
network_domain_keywords = [ 'unspec', 'unix', 'inet', 'ax25', 'ipx', 'appletalk', 'netrom', 'bridge', 'atmpvc', 'x25', 'inet6',
|
||||
'rose', 'netbeui', 'security', 'key', 'netlink', 'packet', 'ash', 'econet', 'atmsvc', 'rds', 'sna',
|
||||
'irda', 'pppox', 'wanpipe', 'llc', 'can', 'tipc', 'bluetooth', 'iucv', 'rxrpc', 'isdn', 'phonet',
|
||||
'ieee802154', 'caif', 'alg', 'nfc', 'vsock', 'mpls', 'ib' ]
|
||||
@@ -66,7 +66,7 @@ class NetworkRule(BaseRule):
|
||||
self.all_domains = False
|
||||
if domain == NetworkRule.ALL:
|
||||
self.all_domains = True
|
||||
elif type(domain) == str:
|
||||
elif type_is_str(domain):
|
||||
if domain in network_domain_keywords:
|
||||
self.domain = domain
|
||||
else:
|
||||
@@ -78,7 +78,7 @@ class NetworkRule(BaseRule):
|
||||
self.all_type_or_protocols = False
|
||||
if type_or_protocol == NetworkRule.ALL:
|
||||
self.all_type_or_protocols = True
|
||||
elif type(type_or_protocol) == str:
|
||||
elif type_is_str(type_or_protocol):
|
||||
if type_or_protocol in network_protocol_keywords:
|
||||
self.type_or_protocol = type_or_protocol
|
||||
elif type_or_protocol in network_type_keywords:
|
||||
|
@@ -16,7 +16,7 @@
|
||||
import re
|
||||
|
||||
from apparmor.regex import RE_PROFILE_RLIMIT, strip_quotes
|
||||
from apparmor.common import AppArmorBug, AppArmorException
|
||||
from apparmor.common import AppArmorBug, AppArmorException, type_is_str
|
||||
from apparmor.rule import BaseRule, BaseRuleset, parse_comment, quote_if_needed
|
||||
|
||||
# setup module translations
|
||||
@@ -57,7 +57,7 @@ class RlimitRule(BaseRule):
|
||||
if audit or deny or allow_keyword:
|
||||
raise AppArmorBug('The audit, allow or deny keywords are not allowed in rlimit rules.')
|
||||
|
||||
if type(rlimit) == str:
|
||||
if type_is_str(rlimit):
|
||||
if rlimit in rlimit_all:
|
||||
self.rlimit = rlimit
|
||||
else:
|
||||
@@ -70,7 +70,7 @@ class RlimitRule(BaseRule):
|
||||
self.all_values = False
|
||||
if value == RlimitRule.ALL:
|
||||
self.all_values = True
|
||||
elif type(value) == str:
|
||||
elif type_is_str(value):
|
||||
if not value.strip():
|
||||
raise AppArmorBug('Empty value in rlimit rule')
|
||||
|
||||
|
@@ -106,6 +106,13 @@
|
||||
/usr/bin/killall = icn
|
||||
/usr/bin/nice = icn
|
||||
/usr/bin/perl = icn
|
||||
/usr/bin/python = icn
|
||||
/usr/bin/python2 = icn
|
||||
/usr/bin/python2.7 = icn
|
||||
/usr/bin/python3 = icn
|
||||
/usr/bin/python3.3 = icn
|
||||
/usr/bin/python3.4 = icn
|
||||
/usr/bin/python3.5 = icn
|
||||
/usr/bin/tr = icn
|
||||
|
||||
[required_hats]
|
||||
|
@@ -24,8 +24,10 @@ ifdef USE_SYSTEM
|
||||
LD_LIBRARY_PATH=
|
||||
PYTHONPATH=
|
||||
else
|
||||
# PYTHON_DIST_BUILD_PATH based on libapparmor/swig/python/test/Makefile.am
|
||||
PYTHON_DIST_BUILD_PATH = ../../libraries/libapparmor/swig/python/build/$$($(PYTHON) -c "import distutils.util; import platform; print(\"lib.%s-%s\" %(distutils.util.get_platform(), platform.python_version()[:3]))")
|
||||
LD_LIBRARY_PATH=../../libraries/libapparmor/src/.libs/
|
||||
PYTHONPATH=..
|
||||
PYTHONPATH=..:$(PYTHON_DIST_BUILD_PATH)
|
||||
endif
|
||||
|
||||
.PHONY: __libapparmor
|
||||
@@ -60,10 +62,10 @@ clean:
|
||||
rm -rf __pycache__/ .coverage htmlcov
|
||||
|
||||
check: __libapparmor
|
||||
export PYTHONPATH=$(PYTHONPATH) ; export LD_LIBRARY_PATH=$(LD_LIBRARY_PATH) ; $(foreach test, $(wildcard test-*.py), $(call pyalldo, $(test)))
|
||||
export PYTHONPATH=$(PYTHONPATH) ; export LD_LIBRARY_PATH=$(LD_LIBRARY_PATH) ; $(foreach test, $(wildcard test-*.py), echo ; echo === $(test) === ; $(call pyalldo, $(test)))
|
||||
|
||||
.coverage: $(wildcard ../aa-* ../apparmor/*.py test-*.py) __libapparmor
|
||||
export PYTHONPATH=$(PYTHONPATH) ; export LD_LIBRARY_PATH=$(LD_LIBRARY_PATH); $(COVERAGE_IGNORE_FAILURES_CMD) ; $(foreach test, $(wildcard test-*.py), $(PYTHON) -m coverage run --branch -p $(test); )
|
||||
export PYTHONPATH=$(PYTHONPATH) ; export LD_LIBRARY_PATH=$(LD_LIBRARY_PATH); $(COVERAGE_IGNORE_FAILURES_CMD) ; $(foreach test, $(wildcard test-*.py), echo ; echo === $(test) === ; $(PYTHON) -m coverage run --branch -p $(test); )
|
||||
$(PYTHON) -m coverage combine
|
||||
|
||||
coverage: .coverage
|
||||
|
@@ -8,6 +8,8 @@
|
||||
allow /usr/share/X11/locale/** r,
|
||||
allow /home/*/** r,
|
||||
|
||||
unix (receive) type=dgram,
|
||||
|
||||
^foo {
|
||||
/etc/fstab r,
|
||||
capability dac_override,
|
||||
|
@@ -6,6 +6,8 @@
|
||||
/usr/bin/a/simple/cleanprof/test/profile {
|
||||
#include <abstractions/base>
|
||||
|
||||
unix (receive) type=dgram,
|
||||
|
||||
/home/*/** r,
|
||||
/home/foo/** w,
|
||||
|
||||
|
@@ -17,7 +17,8 @@ import os
|
||||
|
||||
from apparmor.aa import (check_for_apparmor, get_interpreter_and_abstraction, create_new_profile,
|
||||
get_profile_flags, set_profile_flags, is_skippable_file, is_skippable_dir,
|
||||
parse_profile_start, parse_profile_data, separate_vars, store_list_var, write_header, serialize_parse_profile_start)
|
||||
parse_profile_start, parse_profile_data, separate_vars, store_list_var, write_header,
|
||||
var_transform, serialize_parse_profile_start)
|
||||
from apparmor.common import AppArmorException, AppArmorBug
|
||||
|
||||
class AaTestWithTempdir(AATest):
|
||||
@@ -74,7 +75,7 @@ class AaTest_check_for_apparmor(AaTestWithTempdir):
|
||||
class AaTest_create_new_profile(AATest):
|
||||
tests = [
|
||||
# file content expected interpreter expected abstraction (besides 'base')
|
||||
('#!/bin/bash\ntrue', ('/bin/bash', 'abstractions/bash')),
|
||||
('#!/bin/bash\ntrue', (u'/bin/bash', 'abstractions/bash')),
|
||||
('foo bar', (None, None)),
|
||||
]
|
||||
def _run_test(self, params, expected):
|
||||
@@ -88,16 +89,16 @@ class AaTest_create_new_profile(AATest):
|
||||
self.assertEqual(profile[program][program]['allow']['path'][exp_interpreter_path]['audit'], set() )
|
||||
self.assertEqual(profile[program][program]['allow']['path'][program]['mode'], {'r', '::r'} )
|
||||
self.assertEqual(profile[program][program]['allow']['path'][program]['audit'], set() )
|
||||
self.assertEqual(profile[program][program]['allow']['path'].keys(), {exp_interpreter_path, program} )
|
||||
self.assertEqual(set(profile[program][program]['allow']['path'].keys()), {program, exp_interpreter_path} )
|
||||
else:
|
||||
self.assertEqual(profile[program][program]['allow']['path'][program]['mode'], {'r', '::r', 'm', '::m'} )
|
||||
self.assertEqual(profile[program][program]['allow']['path'][program]['audit'], set() )
|
||||
self.assertEqual(profile[program][program]['allow']['path'].keys(), {program} )
|
||||
self.assertEqual(set(profile[program][program]['allow']['path'].keys()), {program} )
|
||||
|
||||
if exp_abstraction:
|
||||
self.assertEqual(profile[program][program]['include'].keys(), {exp_abstraction, 'abstractions/base'})
|
||||
self.assertEqual(set(profile[program][program]['include'].keys()), {exp_abstraction, 'abstractions/base'})
|
||||
else:
|
||||
self.assertEqual(profile[program][program]['include'].keys(), {'abstractions/base'})
|
||||
self.assertEqual(set(profile[program][program]['include'].keys()), {'abstractions/base'})
|
||||
|
||||
class AaTest_get_interpreter_and_abstraction(AATest):
|
||||
tests = [
|
||||
@@ -114,6 +115,8 @@ class AaTest_get_interpreter_and_abstraction(AATest):
|
||||
('#!/usr/bin/python3', ('/usr/bin/python3', 'abstractions/python')),
|
||||
('#!/usr/bin/python4', ('/usr/bin/python4', None)), # python abstraction is only applied to py2 and py3
|
||||
('#!/usr/bin/ruby', ('/usr/bin/ruby', 'abstractions/ruby')),
|
||||
('#!/usr/bin/ruby2.2', ('/usr/bin/ruby2.2', 'abstractions/ruby')),
|
||||
('#!/usr/bin/ruby1.9.1', ('/usr/bin/ruby1.9.1', 'abstractions/ruby')),
|
||||
('#!/usr/bin/foobarbaz', ('/usr/bin/foobarbaz', None)), # we don't have an abstraction for "foobarbaz"
|
||||
('foo', (None, None)), # no hashbang - not a script
|
||||
]
|
||||
@@ -484,20 +487,29 @@ class AaTest_separate_vars(AATest):
|
||||
('' , set() ),
|
||||
(' ' , set() ),
|
||||
(' foo bar' , {'foo', 'bar' }),
|
||||
('foo " ' , {'foo' }), # XXX " is ignored
|
||||
(' " foo ' , {' "', 'foo' }), # XXX really?
|
||||
('foo " ' , AppArmorException ),
|
||||
(' " foo ' , AppArmorException ), # half-quoted
|
||||
(' foo bar ' , {'foo', 'bar' }),
|
||||
(' foo bar # comment' , {'foo', 'bar', 'comment' }), # XXX should comments be stripped?
|
||||
(' foo bar # comment' , {'foo', 'bar', '#', 'comment'}), # XXX should comments be stripped?
|
||||
('foo' , {'foo' }),
|
||||
('"foo" "bar baz"' , {'foo', 'bar baz' }),
|
||||
('foo "bar baz" xy' , {'foo', 'bar baz', 'xy' }),
|
||||
('foo "bar baz ' , {'foo', 'bar', 'baz' }), # half-quoted
|
||||
('foo "bar baz ' , AppArmorException ), # half-quoted
|
||||
(' " foo" bar' , {' foo', 'bar' }),
|
||||
(' " foo" bar x' , {' foo', 'bar', 'x' }),
|
||||
('""' , {'' }), # empty value
|
||||
('"" foo' , {'', 'foo' }), # empty value + 'foo'
|
||||
('"" foo "bar"' , {'', 'foo', 'bar' }), # empty value + 'foo' + 'bar' (bar has superfluous quotes)
|
||||
('"bar"' , {'bar' }), # 'bar' with superfluous quotes
|
||||
]
|
||||
|
||||
def _run_test(self, params, expected):
|
||||
result = separate_vars(params)
|
||||
self.assertEqual(result, expected)
|
||||
if expected == AppArmorException:
|
||||
with self.assertRaises(expected):
|
||||
separate_vars(params)
|
||||
else:
|
||||
result = separate_vars(params)
|
||||
self.assertEqual(result, expected)
|
||||
|
||||
|
||||
class AaTest_store_list_var(AATest):
|
||||
@@ -579,6 +591,17 @@ class AaTest_write_header(AATest):
|
||||
result = write_header(prof_data, depth, name, embedded_hat, write_flags)
|
||||
self.assertEqual(result, [expected])
|
||||
|
||||
class AaTest_var_transform(AATest):
|
||||
tests = [
|
||||
(['foo', ''], 'foo ""' ),
|
||||
(['foo', 'bar'], 'foo bar' ),
|
||||
([''], '""' ),
|
||||
(['bar baz', 'foo'], '"bar baz" foo' ),
|
||||
]
|
||||
|
||||
def _run_test(self, params, expected):
|
||||
self.assertEqual(var_transform(params), expected)
|
||||
|
||||
class AaTest_serialize_parse_profile_start(AATest):
|
||||
def _parse(self, line, profile, hat, prof_data_profile, prof_data_external):
|
||||
# 'correct' is always True in the code that uses serialize_parse_profile_start() (set some lines above the function call)
|
||||
|
@@ -20,34 +20,34 @@ import re
|
||||
|
||||
class TestBaserule(AATest):
|
||||
def test_abstract__parse(self):
|
||||
with self.assertRaises(AppArmorBug):
|
||||
with self.assertRaises(NotImplementedError):
|
||||
BaseRule._parse('foo')
|
||||
|
||||
def test_abstract__parse_2(self):
|
||||
with self.assertRaises(AppArmorBug):
|
||||
with self.assertRaises(NotImplementedError):
|
||||
BaseRule.parse('foo')
|
||||
|
||||
def test_abstract__match(self):
|
||||
with self.assertRaises(AppArmorBug):
|
||||
with self.assertRaises(NotImplementedError):
|
||||
BaseRule._match('foo')
|
||||
|
||||
def test_abstract__match2(self):
|
||||
with self.assertRaises(AppArmorBug):
|
||||
with self.assertRaises(NotImplementedError):
|
||||
BaseRule.match('foo')
|
||||
|
||||
def test_abstract_get_clean(self):
|
||||
obj = BaseRule()
|
||||
with self.assertRaises(AppArmorBug):
|
||||
with self.assertRaises(NotImplementedError):
|
||||
obj.get_clean()
|
||||
|
||||
def test_is_equal_localvars(self):
|
||||
obj = BaseRule()
|
||||
with self.assertRaises(AppArmorBug):
|
||||
with self.assertRaises(NotImplementedError):
|
||||
obj.is_equal_localvars(BaseRule())
|
||||
|
||||
def test_is_covered_localvars(self):
|
||||
obj = BaseRule()
|
||||
with self.assertRaises(AppArmorBug):
|
||||
with self.assertRaises(NotImplementedError):
|
||||
obj.is_covered_localvars(None)
|
||||
|
||||
def test_parse_modifiers_invalid(self):
|
||||
@@ -65,7 +65,7 @@ class TestBaserule(AATest):
|
||||
|
||||
def test_logprof_header_localvars(self):
|
||||
obj = BaseRule()
|
||||
with self.assertRaises(AppArmorBug):
|
||||
with self.assertRaises(NotImplementedError):
|
||||
obj.logprof_header_localvars()
|
||||
|
||||
|
||||
|
@@ -635,7 +635,7 @@ class CapabilityGlobTest(AATest):
|
||||
self.assertEqual(self.ruleset.get_glob('capability net_raw,'), 'capability,')
|
||||
|
||||
def test_glob_ext(self):
|
||||
with self.assertRaises(AppArmorBug):
|
||||
with self.assertRaises(NotImplementedError):
|
||||
self.ruleset.get_glob_ext('capability net_raw,')
|
||||
|
||||
class CapabilityDeleteTest(AATest):
|
||||
|
@@ -449,7 +449,7 @@ class ChangeProfileGlobTestAATest(AATest):
|
||||
# self.assertEqual(self.ruleset.get_glob('change_profile /foo -> /bar,'), 'change_profile -> /bar,')
|
||||
|
||||
def test_glob_ext(self):
|
||||
with self.assertRaises(AppArmorBug):
|
||||
with self.assertRaises(NotImplementedError):
|
||||
# get_glob_ext is not available for change_profile rules
|
||||
self.ruleset.get_glob_ext('change_profile /foo -> /bar,')
|
||||
|
||||
|
32
utils/test/test-common.py
Normal file
32
utils/test/test-common.py
Normal file
@@ -0,0 +1,32 @@
|
||||
#! /usr/bin/env python
|
||||
# ------------------------------------------------------------------
|
||||
#
|
||||
# Copyright (C) 2015 Christian Boltz <apparmor@cboltz.de>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of version 2 of the GNU General Public
|
||||
# License published by the Free Software Foundation.
|
||||
#
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
import unittest
|
||||
from common_test import AATest, setup_all_loops
|
||||
|
||||
from apparmor.common import type_is_str
|
||||
|
||||
class TestIs_str_type(AATest):
|
||||
tests = [
|
||||
('foo', True),
|
||||
(u'foo', True),
|
||||
(42, False),
|
||||
(True, False),
|
||||
([], False),
|
||||
]
|
||||
|
||||
def _run_test(self, params, expected):
|
||||
self.assertEqual(type_is_str(params), expected)
|
||||
|
||||
|
||||
setup_all_loops(__name__)
|
||||
if __name__ == '__main__':
|
||||
unittest.main(verbosity=2)
|
@@ -441,7 +441,7 @@ class NetworkGlobTestAATest(AATest):
|
||||
# self.assertEqual(self.ruleset.get_glob('network inet raw,'), 'network inet,')
|
||||
|
||||
def test_glob_ext(self):
|
||||
with self.assertRaises(AppArmorBug):
|
||||
with self.assertRaises(NotImplementedError):
|
||||
# get_glob_ext is not available for network rules
|
||||
self.ruleset.get_glob_ext('network inet raw,')
|
||||
|
||||
|
@@ -125,6 +125,7 @@ exception_not_raised = [
|
||||
'profile/flags/flags_bad_debug_3.sd',
|
||||
'profile/flags/flags_bad_debug_4.sd',
|
||||
'profile/simple_bad_no_close_brace4.sd',
|
||||
'profile/profile_ns_bad8.sd', # 'profile :ns/t' without terminating ':'
|
||||
'ptrace/bad_01.sd',
|
||||
'ptrace/bad_02.sd',
|
||||
'ptrace/bad_03.sd',
|
||||
@@ -187,8 +188,6 @@ exception_not_raised = [
|
||||
'vars/boolean/boolean_bad_6.sd',
|
||||
'vars/boolean/boolean_bad_7.sd',
|
||||
'vars/boolean/boolean_bad_8.sd',
|
||||
'vars/vars_bad_1.sd',
|
||||
'vars/vars_bad_2.sd',
|
||||
'vars/vars_bad_3.sd',
|
||||
'vars/vars_bad_4.sd',
|
||||
'vars/vars_bad_5.sd',
|
||||
@@ -198,7 +197,6 @@ exception_not_raised = [
|
||||
'vars/vars_bad_trailing_comma_2.sd',
|
||||
'vars/vars_bad_trailing_comma_3.sd',
|
||||
'vars/vars_bad_trailing_comma_4.sd',
|
||||
'vars/vars_bad_trailing_garbage_1.sd',
|
||||
'vars/vars_dbus_bad_01.sd',
|
||||
'vars/vars_dbus_bad_02.sd',
|
||||
'vars/vars_dbus_bad_03.sd',
|
||||
|
@@ -411,7 +411,7 @@ class RlimitGlobTestAATest(AATest):
|
||||
# self.assertEqual(self.ruleset.get_glob('rlimit /foo -> /bar,'), 'rlimit -> /bar,')
|
||||
|
||||
def test_glob_ext(self):
|
||||
with self.assertRaises(AppArmorBug):
|
||||
with self.assertRaises(NotImplementedError):
|
||||
# get_glob_ext is not available for rlimit rules
|
||||
self.ruleset.get_glob_ext('set rlimit cpu <= 100,')
|
||||
|
||||
|
Reference in New Issue
Block a user