mirror of
https://gitlab.com/apparmor/apparmor
synced 2025-09-04 16:25:10 +00:00
Compare commits
53 Commits
v5.0.0-alp
...
master
Author | SHA1 | Date | |
---|---|---|---|
|
93768757f5 | ||
|
7255edec09 | ||
|
7757c7130c | ||
|
e58f65d0c1 | ||
|
d72cc8f09e | ||
|
b80179834d | ||
|
5993ff21d2 | ||
|
0e755d24bb | ||
|
db74dda3c6 | ||
|
6f5a4219d7 | ||
|
0e58e3d7fb | ||
|
e7daccedc6 | ||
|
59e7fdd96a | ||
|
468f0096ee | ||
|
d993dfbb02 | ||
|
ba336533ac | ||
|
0d34f12d7e | ||
|
ebba635fa9 | ||
|
e477ccacfa | ||
|
9c5064529a | ||
|
862d8ec9fc | ||
|
fbd266c63f | ||
|
fcbf8e34ec | ||
|
01ab33202a | ||
|
24216d79e9 | ||
|
bef673f3c6 | ||
|
8210308508 | ||
|
a8875460ed | ||
|
eae49bf8de | ||
|
144d782ae8 | ||
|
df1a4c8782 | ||
|
4c30a0ac65 | ||
|
60ca491f21 | ||
|
43fa5f88a7 | ||
|
bb03d9ee08 | ||
|
d9866f0a24 | ||
|
fedcab2ad0 | ||
|
b6caed3b57 | ||
|
ae70dc38f8 | ||
|
51bdbec119 | ||
|
b8dee97ed3 | ||
|
8b2e2c3358 | ||
|
3faddfcf46 | ||
|
05458768cf | ||
|
cb0d66d55a | ||
|
0de9678d4f | ||
|
617d3021e8 | ||
|
63b46dd3d7 | ||
|
67382dcf15 | ||
|
d61295a249 | ||
|
a2f2ca6119 | ||
|
61e09c6ffa | ||
|
45a7cc1ed0 |
@@ -334,7 +334,16 @@ State *DFA::add_new_state(optflags const &opts, NodeSet *anodes,
|
|||||||
|
|
||||||
ProtoState proto;
|
ProtoState proto;
|
||||||
proto.init(nnodev, anodev);
|
proto.init(nnodev, anodev);
|
||||||
State *state = new State(opts, node_map.size(), proto, other, filedfa);
|
State *state;
|
||||||
|
try {
|
||||||
|
state = new State(opts, node_map.size(), proto, other, filedfa);
|
||||||
|
} catch(int error) {
|
||||||
|
/* this function is called in the DFA object creation,
|
||||||
|
* and the exception prevents the destructor from
|
||||||
|
* being called, so call the helper here */
|
||||||
|
cleanup();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
pair<NodeMap::iterator,bool> x = node_map.insert(proto, state);
|
pair<NodeMap::iterator,bool> x = node_map.insert(proto, state);
|
||||||
if (x.second == false) {
|
if (x.second == false) {
|
||||||
delete state;
|
delete state;
|
||||||
@@ -392,7 +401,17 @@ void DFA::update_state_transitions(optflags const &opts, State *state)
|
|||||||
*/
|
*/
|
||||||
for (Cases::iterator j = cases.begin(); j != cases.end(); j++) {
|
for (Cases::iterator j = cases.begin(); j != cases.end(); j++) {
|
||||||
State *target;
|
State *target;
|
||||||
target = add_new_state(opts, j->second, nonmatching);
|
try {
|
||||||
|
target = add_new_state(opts, j->second, nonmatching);
|
||||||
|
} catch (int error) {
|
||||||
|
/* when add_new_state fails, there could still
|
||||||
|
* be NodeSets in the rest of cases, so clean
|
||||||
|
* them up before re-throwing the exception */
|
||||||
|
for (Cases::iterator k = ++j; k != cases.end(); k++) {
|
||||||
|
delete k->second;
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
/* Don't insert transition that the otherwise transition
|
/* Don't insert transition that the otherwise transition
|
||||||
* already covers
|
* already covers
|
||||||
@@ -522,11 +541,7 @@ DFA::DFA(Node *root, optflags const &opts, bool buildfiledfa): root(root), filed
|
|||||||
|
|
||||||
DFA::~DFA()
|
DFA::~DFA()
|
||||||
{
|
{
|
||||||
anodes_cache.clear();
|
cleanup();
|
||||||
nnodes_cache.clear();
|
|
||||||
|
|
||||||
for (Partition::iterator i = states.begin(); i != states.end(); i++)
|
|
||||||
delete *i;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
State *DFA::match_len(State *state, const char *str, size_t len)
|
State *DFA::match_len(State *state, const char *str, size_t len)
|
||||||
|
@@ -368,6 +368,15 @@ class DFA {
|
|||||||
NodeMap node_map;
|
NodeMap node_map;
|
||||||
std::list<State *> work_queue;
|
std::list<State *> work_queue;
|
||||||
|
|
||||||
|
void cleanup(void) {
|
||||||
|
anodes_cache.clear();
|
||||||
|
nnodes_cache.clear();
|
||||||
|
|
||||||
|
for (Partition::iterator i = states.begin(); i != states.end(); i++) {
|
||||||
|
delete *i;
|
||||||
|
}
|
||||||
|
states.clear();
|
||||||
|
}
|
||||||
public:
|
public:
|
||||||
DFA(Node *root, optflags const &flags, bool filedfa);
|
DFA(Node *root, optflags const &flags, bool filedfa);
|
||||||
virtual ~DFA();
|
virtual ~DFA();
|
||||||
@@ -397,7 +406,7 @@ public:
|
|||||||
|
|
||||||
void compute_perms_table_ent(State *state, size_t pos,
|
void compute_perms_table_ent(State *state, size_t pos,
|
||||||
std::vector <aa_perms> &perms_table);
|
std::vector <aa_perms> &perms_table);
|
||||||
void compute_perms_table(std::vector <aa_perms> &perms_table);
|
void compute_perms_table(std::vector <aa_perms> &perms_table);
|
||||||
|
|
||||||
unsigned int diffcount;
|
unsigned int diffcount;
|
||||||
int oob_range;
|
int oob_range;
|
||||||
|
@@ -570,6 +570,8 @@ ostream &mnt_rule::dump(ostream &os)
|
|||||||
{
|
{
|
||||||
prefix_rule_t::dump(os);
|
prefix_rule_t::dump(os);
|
||||||
|
|
||||||
|
std::ios::fmtflags fmt(os.flags());
|
||||||
|
|
||||||
if (perms & AA_MAY_MOUNT)
|
if (perms & AA_MAY_MOUNT)
|
||||||
os << "mount";
|
os << "mount";
|
||||||
else if (perms & AA_MAY_UMOUNT)
|
else if (perms & AA_MAY_UMOUNT)
|
||||||
@@ -603,6 +605,7 @@ ostream &mnt_rule::dump(ostream &os)
|
|||||||
os << " " << "(0x" << hex << perms << "/0x" << (audit != AUDIT_UNSPECIFIED ? perms : 0) << ")";
|
os << " " << "(0x" << hex << perms << "/0x" << (audit != AUDIT_UNSPECIFIED ? perms : 0) << ")";
|
||||||
os << ",\n";
|
os << ",\n";
|
||||||
|
|
||||||
|
os.flags(fmt);
|
||||||
return os;
|
return os;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1163,21 +1166,7 @@ fail:
|
|||||||
void mnt_rule::post_parse_profile(Profile &prof)
|
void mnt_rule::post_parse_profile(Profile &prof)
|
||||||
{
|
{
|
||||||
if (trans) {
|
if (trans) {
|
||||||
perm32_t perms = 0;
|
/* TODO: pivot_root profile transition */
|
||||||
int n = add_entry_to_x_table(&prof, trans);
|
|
||||||
if (!n) {
|
|
||||||
PERROR("Profile %s has too many specified profile transitions.\n", prof.name);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (perms & AA_USER_EXEC)
|
|
||||||
perms |= SHIFT_PERMS(n << 10, AA_USER_SHIFT);
|
|
||||||
if (perms & AA_OTHER_EXEC)
|
|
||||||
perms |= SHIFT_PERMS(n << 10, AA_OTHER_SHIFT);
|
|
||||||
perms = ((perms & ~AA_ALL_EXEC_MODIFIERS) |
|
|
||||||
(perms & AA_ALL_EXEC_MODIFIERS));
|
|
||||||
|
|
||||||
trans = NULL;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -133,8 +133,10 @@ static void process_entries(const void *nodep, VISIT value, int level unused)
|
|||||||
if (entry->link_name &&
|
if (entry->link_name &&
|
||||||
strncmp((*t)->from, entry->link_name, len) == 0) {
|
strncmp((*t)->from, entry->link_name, len) == 0) {
|
||||||
char *n = do_alias(*t, entry->link_name);
|
char *n = do_alias(*t, entry->link_name);
|
||||||
if (!n)
|
if (!n) {
|
||||||
|
free_cod_entries(dup);
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
if (!dup)
|
if (!dup)
|
||||||
dup = copy_cod_entry(entry);
|
dup = copy_cod_entry(entry);
|
||||||
free(dup->link_name);
|
free(dup->link_name);
|
||||||
|
@@ -188,24 +188,21 @@ cleanup:
|
|||||||
if (prof->attachment) {
|
if (prof->attachment) {
|
||||||
tmp = symtab::delete_var(PROFILE_EXEC_VAR);
|
tmp = symtab::delete_var(PROFILE_EXEC_VAR);
|
||||||
delete tmp;
|
delete tmp;
|
||||||
if (saved_exec_path) {
|
if (saved_exec_path)
|
||||||
symtab::add_var(*saved_exec_path);
|
symtab::add_var(*saved_exec_path);
|
||||||
delete saved_exec_path;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
cleanup_attach:
|
cleanup_attach:
|
||||||
if (prof->attachment) {
|
if (prof->attachment) {
|
||||||
tmp = symtab::delete_var(PROFILE_ATTACH_VAR);
|
tmp = symtab::delete_var(PROFILE_ATTACH_VAR);
|
||||||
delete tmp;
|
delete tmp;
|
||||||
if (saved_attach_path) {
|
if (saved_attach_path)
|
||||||
symtab::add_var(*saved_attach_path);
|
symtab::add_var(*saved_attach_path);
|
||||||
delete saved_attach_path;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
cleanup_name:
|
cleanup_name:
|
||||||
tmp = symtab::delete_var(PROFILE_NAME_VARIABLE);
|
tmp = symtab::delete_var(PROFILE_NAME_VARIABLE);
|
||||||
delete tmp;
|
delete tmp;
|
||||||
|
delete saved_exec_path;
|
||||||
|
delete saved_attach_path;
|
||||||
out:
|
out:
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
@@ -577,6 +577,7 @@ flags: opt_flags TOK_OPENPAREN flagvals TOK_CLOSEPAREN
|
|||||||
flagvals: flagvals flagval
|
flagvals: flagvals flagval
|
||||||
{
|
{
|
||||||
$1.merge($2);
|
$1.merge($2);
|
||||||
|
$2.clear();
|
||||||
$$ = $1;
|
$$ = $1;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -78,6 +78,7 @@ void ProfileList::dump_profile_names(bool children)
|
|||||||
Profile::~Profile()
|
Profile::~Profile()
|
||||||
{
|
{
|
||||||
hat_table.clear();
|
hat_table.clear();
|
||||||
|
flags.clear();
|
||||||
free_cod_entries(entries);
|
free_cod_entries(entries);
|
||||||
free_cond_entry_list(xattrs);
|
free_cond_entry_list(xattrs);
|
||||||
|
|
||||||
@@ -97,10 +98,6 @@ Profile::~Profile()
|
|||||||
free(name);
|
free(name);
|
||||||
if (attachment)
|
if (attachment)
|
||||||
free(attachment);
|
free(attachment);
|
||||||
if (flags.disconnected_path)
|
|
||||||
free(flags.disconnected_path);
|
|
||||||
if (flags.disconnected_ipc)
|
|
||||||
free(flags.disconnected_ipc);
|
|
||||||
if (ns)
|
if (ns)
|
||||||
free(ns);
|
free(ns);
|
||||||
for (int i = (AA_EXEC_LOCAL >> 10) + 1; i < AA_EXEC_COUNT; i++)
|
for (int i = (AA_EXEC_LOCAL >> 10) + 1; i < AA_EXEC_COUNT; i++)
|
||||||
|
@@ -175,6 +175,12 @@ public:
|
|||||||
signal = 0;
|
signal = 0;
|
||||||
error = 0;
|
error = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void clear(void) {
|
||||||
|
free(disconnected_path);
|
||||||
|
free(disconnected_ipc);
|
||||||
|
}
|
||||||
|
|
||||||
void init(const char *str)
|
void init(const char *str)
|
||||||
{
|
{
|
||||||
init();
|
init();
|
||||||
@@ -301,7 +307,7 @@ public:
|
|||||||
}
|
}
|
||||||
// same ignore rhs.disconnect_path
|
// same ignore rhs.disconnect_path
|
||||||
} else {
|
} else {
|
||||||
disconnected_path = rhs.disconnected_path;
|
disconnected_path = strdup(rhs.disconnected_path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (rhs.disconnected_ipc) {
|
if (rhs.disconnected_ipc) {
|
||||||
@@ -311,7 +317,7 @@ public:
|
|||||||
}
|
}
|
||||||
// same so do nothing
|
// same so do nothing
|
||||||
} else {
|
} else {
|
||||||
disconnected_ipc = rhs.disconnected_ipc;
|
disconnected_ipc = strdup(rhs.disconnected_ipc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (rhs.signal) {
|
if (rhs.signal) {
|
||||||
|
@@ -431,11 +431,14 @@ public:
|
|||||||
ostream &dump(ostream &os) override {
|
ostream &dump(ostream &os) override {
|
||||||
class_rule_t::dump(os);
|
class_rule_t::dump(os);
|
||||||
|
|
||||||
|
std::ios::fmtflags fmt(os.flags());
|
||||||
|
|
||||||
if (saved)
|
if (saved)
|
||||||
os << "(0x" << std::hex << perms << "/orig " << saved << ") ";
|
os << "(0x" << std::hex << perms << "/orig " << saved << ") ";
|
||||||
else
|
else
|
||||||
os << "(0x" << std::hex << perms << ") ";
|
os << "(0x" << std::hex << perms << ") ";
|
||||||
|
|
||||||
|
os.flags(fmt);
|
||||||
return os;
|
return os;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -460,7 +463,11 @@ public:
|
|||||||
ostream &dump(ostream &os) override {
|
ostream &dump(ostream &os) override {
|
||||||
class_rule_t::dump(os);
|
class_rule_t::dump(os);
|
||||||
|
|
||||||
|
std::ios::fmtflags fmt(os.flags());
|
||||||
|
|
||||||
os << "(0x" << std::hex << perms << ") ";
|
os << "(0x" << std::hex << perms << ") ";
|
||||||
|
|
||||||
|
os.flags(fmt);
|
||||||
return os;
|
return os;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
7
parser/tst/simple_tests/vars/vars_file_evaluation_17.sd
Normal file
7
parser/tst/simple_tests/vars/vars_file_evaluation_17.sd
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
#=DESCRIPTION expansion of alternation after extra, unescaped @
|
||||||
|
#=EXRESULT PASS
|
||||||
|
@{uid} = {[0-9],[1-9][0-9]}
|
||||||
|
|
||||||
|
/usr/bin/foo {
|
||||||
|
/sys/fs/cgroup/user.slice/user-@{uid}.slice/user@@{uid}.service/cpu.max r,
|
||||||
|
}
|
@@ -189,11 +189,23 @@ static void trim_trailing_slash(std::string& str)
|
|||||||
str.clear(); // str is all '/'
|
str.clear(); // str is all '/'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int copy_value_to_name(const std::string& value, char **name)
|
||||||
|
{
|
||||||
|
free(*name);
|
||||||
|
*name = strdup(value.c_str());
|
||||||
|
if (!*name) {
|
||||||
|
errno = ENOMEM;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int variable::expand_by_alternation(char **name)
|
int variable::expand_by_alternation(char **name)
|
||||||
{
|
{
|
||||||
std::string expanded_name = "";
|
std::string expanded_name = "";
|
||||||
bool filter_leading_slash = false;
|
bool filter_leading_slash = false;
|
||||||
bool filter_trailing_slash = false;
|
bool filter_trailing_slash = false;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
if (!name) {
|
if (!name) {
|
||||||
PERROR("ASSERT: name to be expanded cannot be NULL\n");
|
PERROR("ASSERT: name to be expanded cannot be NULL\n");
|
||||||
@@ -226,8 +238,6 @@ int variable::expand_by_alternation(char **name)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
free(*name);
|
|
||||||
|
|
||||||
size_t setsize = ref->expanded.size();
|
size_t setsize = ref->expanded.size();
|
||||||
auto i = ref->expanded.begin();
|
auto i = ref->expanded.begin();
|
||||||
|
|
||||||
@@ -252,15 +262,19 @@ int variable::expand_by_alternation(char **name)
|
|||||||
if (setsize > 1) {
|
if (setsize > 1) {
|
||||||
expanded_name += "}";
|
expanded_name += "}";
|
||||||
}
|
}
|
||||||
|
/* don't include prefix */
|
||||||
expanded_name = prefix + expanded_name + suffix;
|
expanded_name = expanded_name + suffix;
|
||||||
*name = strdup(expanded_name.c_str());
|
ret = copy_value_to_name(expanded_name, name);
|
||||||
if (!*name) {
|
if (ret)
|
||||||
errno = ENOMEM;
|
return ret;
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
/* recursive until no variables are found in *name */
|
/* recursive until no variables are found in *name */
|
||||||
return expand_by_alternation(name);
|
ret = expand_by_alternation(name);
|
||||||
|
if (ret == 0) {
|
||||||
|
/* return prefix to name */
|
||||||
|
expanded_name = prefix + std::string(*name);
|
||||||
|
ret = copy_value_to_name(expanded_name, name);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
int variable::expand_variable()
|
int variable::expand_variable()
|
||||||
@@ -293,6 +307,7 @@ int variable::expand_variable()
|
|||||||
}
|
}
|
||||||
name = variable::process_var(var.c_str());
|
name = variable::process_var(var.c_str());
|
||||||
variable *ref = symtab::lookup_existing_symbol(name);
|
variable *ref = symtab::lookup_existing_symbol(name);
|
||||||
|
free(name);
|
||||||
if (!ref) {
|
if (!ref) {
|
||||||
PERROR("Failed to find declaration for: %s\n", var.c_str());
|
PERROR("Failed to find declaration for: %s\n", var.c_str());
|
||||||
rc = 1;
|
rc = 1;
|
||||||
@@ -322,7 +337,6 @@ int variable::expand_variable()
|
|||||||
}
|
}
|
||||||
|
|
||||||
out:
|
out:
|
||||||
free(name);
|
|
||||||
expanding = false;
|
expanding = false;
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
@@ -35,6 +35,10 @@
|
|||||||
owner @{HOME}/.gtkrc r,
|
owner @{HOME}/.gtkrc r,
|
||||||
owner @{HOME}/.gtkrc-2.0 r,
|
owner @{HOME}/.gtkrc-2.0 r,
|
||||||
owner @{HOME}/.gtk-bookmarks r,
|
owner @{HOME}/.gtk-bookmarks r,
|
||||||
|
|
||||||
|
owner @{HOME}/.cache/gtk-4.0/ rw,
|
||||||
|
owner @{HOME}/.cache/gtk-4.0/vulkan-pipeline-cache/{,*} rw,
|
||||||
|
|
||||||
owner @{HOME}/.config/gtkrc r,
|
owner @{HOME}/.config/gtkrc r,
|
||||||
owner @{HOME}/.config/gtkrc-2.0 r,
|
owner @{HOME}/.config/gtkrc-2.0 r,
|
||||||
owner @{HOME}/.config/gtk-{3,4}.0/ rw,
|
owner @{HOME}/.config/gtk-{3,4}.0/ rw,
|
||||||
|
@@ -11,12 +11,20 @@
|
|||||||
|
|
||||||
abi <abi/4.0>,
|
abi <abi/4.0>,
|
||||||
|
|
||||||
# this abstract profile can be included by applications that are
|
# This abstract profile can be included by applications that are
|
||||||
# dynamically linked to libnuma
|
# dynamically linked to libnuma.
|
||||||
|
|
||||||
# libnuma defines the function num_init() as the .init function
|
# libnuma defines the function num_init() as the .init function
|
||||||
# to be called by the runtime linker (ld) when libnuma is loaded
|
# to be called by the runtime linker (ld) when libnuma is loaded
|
||||||
|
# even if not any active usage of libnuma takes place
|
||||||
|
|
||||||
@{sys}/devices/system/cpu/node/ r,
|
@{sys}/devices/system/cpu/node/ r,
|
||||||
|
|
||||||
|
# Actually using libnuma functionality will need a few more
|
||||||
|
# sysfs entries to gather information about the system
|
||||||
|
@{sys}/devices/system/cpu/ r,
|
||||||
|
@{sys}/devices/system/node/node[0-9]*/meminfo r,
|
||||||
|
@{sys}/devices/system/node/*/cpumap r,
|
||||||
|
|
||||||
# Include additions to the abstraction
|
# Include additions to the abstraction
|
||||||
include if exists <abstractions/libnuma.d>
|
include if exists <abstractions/libnuma.d>
|
||||||
|
@@ -25,6 +25,7 @@
|
|||||||
@{run}/systemd/userdb/io.systemd.Home rw, # systemd-home dirs
|
@{run}/systemd/userdb/io.systemd.Home rw, # systemd-home dirs
|
||||||
@{run}/systemd/userdb/io.systemd.NameServiceSwitch rw, # UNIX/glibc NSS
|
@{run}/systemd/userdb/io.systemd.NameServiceSwitch rw, # UNIX/glibc NSS
|
||||||
@{run}/systemd/userdb/io.systemd.Machine rw, # systemd-machined
|
@{run}/systemd/userdb/io.systemd.Machine rw, # systemd-machined
|
||||||
|
@{run}/systemd/userdb/org.gnome.DisplayManager rw, # GDM implements a user database for its greeter
|
||||||
|
|
||||||
@{PROC}/sys/kernel/random/boot_id r,
|
@{PROC}/sys/kernel/random/boot_id r,
|
||||||
|
|
||||||
|
@@ -17,19 +17,19 @@ profile curl /usr/bin/curl {
|
|||||||
include <abstractions/private-files-strict>
|
include <abstractions/private-files-strict>
|
||||||
include <abstractions/ssl_certs>
|
include <abstractions/ssl_certs>
|
||||||
|
|
||||||
|
#can read/write data and configs from tmp
|
||||||
|
include <abstractions/user-tmp>
|
||||||
|
|
||||||
@{exec_path} mr,
|
@{exec_path} mr,
|
||||||
|
|
||||||
# allow reading configuration files from $HOME
|
# allow reading configuration files from $HOME
|
||||||
priority=1 file r @{HOME}/.curlrc,
|
priority=1 owner file r @{HOME}/.curlrc,
|
||||||
priority=1 file r @{HOME}/.config/curlrc,
|
priority=1 owner file r @{HOME}/.config/curlrc,
|
||||||
|
|
||||||
# allow reading other configuration files/certs from $HOME
|
# allow reading other configuration files/certs from $HOME
|
||||||
# (see --config, --cacert options)
|
# (see --config, --cacert options)
|
||||||
file r @{HOME}/**,
|
|
||||||
|
|
||||||
# allow writing output to $HOME, /tmp (see -o option)
|
# allow writing output to $HOME, /tmp (see -o option)
|
||||||
file w @{HOME}/**,
|
owner file rw @{HOME}/**,
|
||||||
file w /tmp/**,
|
|
||||||
|
|
||||||
# allows UDP (for DNS), TCP (for http, https, etc), abstract Unix sockets, IPv4, IPv6
|
# allows UDP (for DNS), TCP (for http, https, etc), abstract Unix sockets, IPv4, IPv6
|
||||||
network unix stream,
|
network unix stream,
|
||||||
@@ -39,6 +39,10 @@ profile curl /usr/bin/curl {
|
|||||||
network inet6 stream,
|
network inet6 stream,
|
||||||
network inet6 dgram,
|
network inet6 dgram,
|
||||||
|
|
||||||
|
# Allow access to the snap socket until we can revisit it with delegation
|
||||||
|
# or profile refactoring
|
||||||
|
file rw @{run}/snapd.socket,
|
||||||
|
|
||||||
# Site-specific additions and overrides. See local/README for details.
|
# Site-specific additions and overrides. See local/README for details.
|
||||||
include if exists <local/curl>
|
include if exists <local/curl>
|
||||||
}
|
}
|
||||||
|
@@ -30,6 +30,9 @@ profile unix-chkpwd /{,usr/}{,s}bin/unix_chkpwd {
|
|||||||
/run/host/userdb/*.user r,
|
/run/host/userdb/*.user r,
|
||||||
/run/host/userdb/*.user-privileged r,
|
/run/host/userdb/*.user-privileged r,
|
||||||
|
|
||||||
|
# authd socket for PAM
|
||||||
|
@{run}/authd.sock rw,
|
||||||
|
|
||||||
# file_inherit
|
# file_inherit
|
||||||
owner /dev/tty[0-9]* rw,
|
owner /dev/tty[0-9]* rw,
|
||||||
|
|
||||||
|
@@ -57,6 +57,26 @@ int fork_and_execvat(int exec_fd, char* const* argv) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int set_up_mount_separation() {
|
||||||
|
if (unshare(CLONE_NEWNS) == -1) {
|
||||||
|
perror("FAIL: could not unshare mount namespace");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* Try to remount / as MS_PRIVATE | MS_REC so that our mounts don't
|
||||||
|
* last outside of this particular process. In particular, the open_tree
|
||||||
|
* test's move_mount doesn't persist in 6.14 but persists in 6.15.
|
||||||
|
* This behavior change was brought about by the upstream commit
|
||||||
|
* 21107723831e (fs: mount detached mounts onto detached mounts) and its
|
||||||
|
* semantic change to the behavior of fs/namespace.c:dissolve_on_fput.
|
||||||
|
*/
|
||||||
|
if (mount(NULL, "/", NULL, MS_PRIVATE | MS_REC, NULL) == -1) {
|
||||||
|
perror("FAIL: could not make / mount rprivate");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int test_with_old_style_mount() {
|
int test_with_old_style_mount() {
|
||||||
// Set up directory fds for future reference
|
// Set up directory fds for future reference
|
||||||
DEBUG_PRINTF("Opening fds for shadowed and shadowing directories\n");
|
DEBUG_PRINTF("Opening fds for shadowed and shadowing directories\n");
|
||||||
@@ -85,6 +105,7 @@ int test_with_old_style_mount() {
|
|||||||
perror("FAIL: could not open executable file in shadowed dir");
|
perror("FAIL: could not open executable file in shadowed dir");
|
||||||
close(shadowed_file_fd);
|
close(shadowed_file_fd);
|
||||||
close(shadowing_dirfd);
|
close(shadowing_dirfd);
|
||||||
|
close(shadowed_dirfd);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -99,8 +120,7 @@ int test_with_old_style_mount() {
|
|||||||
|
|
||||||
DEBUG_PRINTF("Unshare mount ns and bind mount over shadowed dir\n");
|
DEBUG_PRINTF("Unshare mount ns and bind mount over shadowed dir\n");
|
||||||
// Call unshare() to step into a new mount namespace
|
// Call unshare() to step into a new mount namespace
|
||||||
if (unshare(CLONE_NEWNS) == -1) {
|
if (set_up_mount_separation() == -1) {
|
||||||
perror("FAIL: could not unshare mount namespace");
|
|
||||||
rc |= 1;
|
rc |= 1;
|
||||||
goto cleanup_fds;
|
goto cleanup_fds;
|
||||||
}
|
}
|
||||||
@@ -137,6 +157,11 @@ int test_with_old_style_mount() {
|
|||||||
|
|
||||||
DEBUG_PRINTF("Read from disconnected file\n");
|
DEBUG_PRINTF("Read from disconnected file\n");
|
||||||
char *file_contents_buf = calloc(shadowed_file_size+1, sizeof(char));
|
char *file_contents_buf = calloc(shadowed_file_size+1, sizeof(char));
|
||||||
|
if (file_contents_buf == NULL) {
|
||||||
|
perror("FAIL: could not allocate memory to read file");
|
||||||
|
rc |= 1;
|
||||||
|
goto cleanup_mount;
|
||||||
|
}
|
||||||
if (read(shadowed_file_fd, file_contents_buf, shadowed_file_size) == -1) {
|
if (read(shadowed_file_fd, file_contents_buf, shadowed_file_size) == -1) {
|
||||||
perror("FAIL: could not read from file after mount");
|
perror("FAIL: could not read from file after mount");
|
||||||
rc |= 1;
|
rc |= 1;
|
||||||
@@ -164,6 +189,7 @@ int test_with_old_style_mount() {
|
|||||||
rc |= 1;
|
rc |= 1;
|
||||||
}
|
}
|
||||||
cleanup_fds:
|
cleanup_fds:
|
||||||
|
close(shadowed_exec_fd);
|
||||||
close(shadowed_file_fd);
|
close(shadowed_file_fd);
|
||||||
close(shadowing_dirfd);
|
close(shadowing_dirfd);
|
||||||
close(shadowed_exec_fd);
|
close(shadowed_exec_fd);
|
||||||
@@ -178,8 +204,7 @@ int test_with_old_style_mount() {
|
|||||||
int test_with_open_tree_mount() {
|
int test_with_open_tree_mount() {
|
||||||
DEBUG_PRINTF("Unshare mount ns\n");
|
DEBUG_PRINTF("Unshare mount ns\n");
|
||||||
// Call unshare() to step into a new mount namespace
|
// Call unshare() to step into a new mount namespace
|
||||||
if (unshare(CLONE_NEWNS) == -1) {
|
if (set_up_mount_separation() == -1) {
|
||||||
perror("FAIL: could not unshare mount namespace");
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
DEBUG_PRINTF("bind mount shadowed using new mount API\n");
|
DEBUG_PRINTF("bind mount shadowed using new mount API\n");
|
||||||
@@ -268,8 +293,7 @@ int test_with_open_tree_mount() {
|
|||||||
int test_with_fsmount(const char *source) {
|
int test_with_fsmount(const char *source) {
|
||||||
DEBUG_PRINTF("Unshare mount ns\n");
|
DEBUG_PRINTF("Unshare mount ns\n");
|
||||||
// Call unshare() to step into a new mount namespace
|
// Call unshare() to step into a new mount namespace
|
||||||
if (unshare(CLONE_NEWNS) == -1) {
|
if (set_up_mount_separation() == -1) {
|
||||||
perror("FAIL: could not unshare mount namespace");
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -612,7 +612,8 @@ def add_to_profile(rule, profile_name):
|
|||||||
return
|
return
|
||||||
|
|
||||||
update_profile_path = update_profile.__file__
|
update_profile_path = update_profile.__file__
|
||||||
command = ['pkexec', '--keep-cwd', update_profile_path, 'add_rule', rule, profile_name]
|
|
||||||
|
command = ['pkexec', '--keep-cwd', update_profile_path, 'add_rule', args.local, rule, profile_name]
|
||||||
try:
|
try:
|
||||||
subprocess.run(command, check=True)
|
subprocess.run(command, check=True)
|
||||||
except subprocess.CalledProcessError as e:
|
except subprocess.CalledProcessError as e:
|
||||||
@@ -647,7 +648,7 @@ def allow_rules(clean_rules, allow_all=False):
|
|||||||
with open(tmp.name, mode='w') as f:
|
with open(tmp.name, mode='w') as f:
|
||||||
|
|
||||||
for profile_name, profile_rules in clean_rules.items():
|
for profile_name, profile_rules in clean_rules.items():
|
||||||
written += f.write(profile_rules.get_writable_rules(template_path))
|
written += f.write(profile_rules.get_writable_rules(template_path, args.local))
|
||||||
|
|
||||||
if written > 0:
|
if written > 0:
|
||||||
create_from_file(tmp.name)
|
create_from_file(tmp.name)
|
||||||
@@ -841,6 +842,7 @@ def main():
|
|||||||
parser = argparse.ArgumentParser(description=_('Display AppArmor notifications or messages for DENIED entries.'))
|
parser = argparse.ArgumentParser(description=_('Display AppArmor notifications or messages for DENIED entries.'))
|
||||||
parser.add_argument('-p', '--poll', action='store_true', help=_('poll AppArmor logs and display notifications'))
|
parser.add_argument('-p', '--poll', action='store_true', help=_('poll AppArmor logs and display notifications'))
|
||||||
parser.add_argument('--display', type=str, help=_('set the DISPLAY environment variable (might be needed if sudo resets $DISPLAY)'))
|
parser.add_argument('--display', type=str, help=_('set the DISPLAY environment variable (might be needed if sudo resets $DISPLAY)'))
|
||||||
|
parser.add_argument('--xauthority', type=str, help=_('set the XAUTHORITY environment variable (might be needed if sudo resets XAUTHORITY)'))
|
||||||
parser.add_argument('-f', '--file', type=str, help=_('search FILE for AppArmor messages'))
|
parser.add_argument('-f', '--file', type=str, help=_('search FILE for AppArmor messages'))
|
||||||
parser.add_argument('-l', '--since-last', action='store_true', help=_('display stats since last login'))
|
parser.add_argument('-l', '--since-last', action='store_true', help=_('display stats since last login'))
|
||||||
parser.add_argument('-s', '--since-days', type=int, metavar=('NUM'), help=_('show stats for last NUM days (can be used alone or with -p)'))
|
parser.add_argument('-s', '--since-days', type=int, metavar=('NUM'), help=_('show stats for last NUM days (can be used alone or with -p)'))
|
||||||
@@ -849,6 +851,7 @@ def main():
|
|||||||
parser.add_argument('-w', '--wait', type=int, metavar=('NUM'), help=_('wait NUM seconds before displaying notifications (with -p)'))
|
parser.add_argument('-w', '--wait', type=int, metavar=('NUM'), help=_('wait NUM seconds before displaying notifications (with -p)'))
|
||||||
parser.add_argument('-m', '--merge-notifications', action='store_true', help=_('Merge notification for improved readability (with -p)'))
|
parser.add_argument('-m', '--merge-notifications', action='store_true', help=_('Merge notification for improved readability (with -p)'))
|
||||||
parser.add_argument('-F', '--foreground', action='store_true', help=_('Do not fork to the background'))
|
parser.add_argument('-F', '--foreground', action='store_true', help=_('Do not fork to the background'))
|
||||||
|
parser.add_argument('-L', '--local', nargs='?', const='yes', choices=['yes', 'no', 'auto'], help=_('Add to local profile'))
|
||||||
parser.add_argument('--prompt-filter', type=str, metavar=('PF'), help=_('kind of operations which display a popup prompt'))
|
parser.add_argument('--prompt-filter', type=str, metavar=('PF'), help=_('kind of operations which display a popup prompt'))
|
||||||
parser.add_argument('--debug', action='store_true', help=_('debug mode'))
|
parser.add_argument('--debug', action='store_true', help=_('debug mode'))
|
||||||
parser.add_argument('--configdir', type=str, help=argparse.SUPPRESS)
|
parser.add_argument('--configdir', type=str, help=argparse.SUPPRESS)
|
||||||
@@ -938,6 +941,7 @@ def main():
|
|||||||
- prompt_filter
|
- prompt_filter
|
||||||
- maximum_number_notification_profiles
|
- maximum_number_notification_profiles
|
||||||
- keys_to_aggregate
|
- keys_to_aggregate
|
||||||
|
- use_local_profiles
|
||||||
- filter.profile,
|
- filter.profile,
|
||||||
- filter.operation,
|
- filter.operation,
|
||||||
- filter.name,
|
- filter.name,
|
||||||
@@ -960,6 +964,7 @@ def main():
|
|||||||
'message_footer',
|
'message_footer',
|
||||||
'maximum_number_notification_profiles',
|
'maximum_number_notification_profiles',
|
||||||
'keys_to_aggregate',
|
'keys_to_aggregate',
|
||||||
|
'use_local_profiles',
|
||||||
'filter.profile',
|
'filter.profile',
|
||||||
'filter.operation',
|
'filter.operation',
|
||||||
'filter.name',
|
'filter.name',
|
||||||
@@ -1069,6 +1074,19 @@ def main():
|
|||||||
else:
|
else:
|
||||||
keys_to_aggregate = {'operation', 'class', 'name', 'denied', 'target'}
|
keys_to_aggregate = {'operation', 'class', 'name', 'denied', 'target'}
|
||||||
|
|
||||||
|
if not args.local:
|
||||||
|
if 'use_local_profiles' in config['']:
|
||||||
|
if config['']['use_local_profiles'] in {'auto', 'yes', 'no'}:
|
||||||
|
args.local = config['']['use_local_profiles']
|
||||||
|
elif config['']['use_local_profiles'] is None:
|
||||||
|
args.local = 'yes'
|
||||||
|
else:
|
||||||
|
sys.exit(_('ERROR: using an invalid value for use_local_profiles in config {}\nSupported values: {}').format(
|
||||||
|
config['']['use_local_profiles'], ', '.join({'yes', 'auto', 'no'})
|
||||||
|
))
|
||||||
|
else:
|
||||||
|
args.local = 'auto'
|
||||||
|
|
||||||
if args.file:
|
if args.file:
|
||||||
logfile = args.file
|
logfile = args.file
|
||||||
elif os.path.isfile('/var/run/auditd.pid') and os.path.isfile('/var/log/audit/audit.log'):
|
elif os.path.isfile('/var/run/auditd.pid') and os.path.isfile('/var/log/audit/audit.log'):
|
||||||
@@ -1086,6 +1104,8 @@ def main():
|
|||||||
|
|
||||||
if args.display:
|
if args.display:
|
||||||
os.environ['DISPLAY'] = args.display
|
os.environ['DISPLAY'] = args.display
|
||||||
|
if args.xauthority:
|
||||||
|
os.environ['XAUTHORITY'] = args.xauthority
|
||||||
|
|
||||||
if args.poll:
|
if args.poll:
|
||||||
# Exit immediately if show_notifications is no or any of the options below
|
# Exit immediately if show_notifications is no or any of the options below
|
||||||
|
@@ -71,6 +71,14 @@ This has no effect when running under sudo.
|
|||||||
|
|
||||||
wait NUM seconds before displaying notifications (for use with -p)
|
wait NUM seconds before displaying notifications (for use with -p)
|
||||||
|
|
||||||
|
=item -L, --local [{yes,no,auto}]
|
||||||
|
|
||||||
|
add rules to a local profiles instead of the real profiles.
|
||||||
|
This simplify profiles' deployment by keeping local modifications self-contained.
|
||||||
|
- B<yes>: always use a local profile
|
||||||
|
- B<no>: never use a local profile
|
||||||
|
- B<auto>: use a local profile if the main profile already relies on a local profile
|
||||||
|
|
||||||
=item -v, --verbose
|
=item -v, --verbose
|
||||||
|
|
||||||
show messages with summaries.
|
show messages with summaries.
|
||||||
@@ -98,6 +106,9 @@ System-wide configuration for B<aa-notify> is done via
|
|||||||
# Binaries for which we ignore userns-related capability denials
|
# Binaries for which we ignore userns-related capability denials
|
||||||
ignore_denied_capability="sudo,su"
|
ignore_denied_capability="sudo,su"
|
||||||
|
|
||||||
|
# Write change to local profiles if enabled to preserve regular profiles and simplify upgrades (yes, no, auto)
|
||||||
|
use_local_profiles="yes"
|
||||||
|
|
||||||
# OPTIONAL - kind of operations which display a popup prompt.
|
# OPTIONAL - kind of operations which display a popup prompt.
|
||||||
prompt_filter="userns"
|
prompt_filter="userns"
|
||||||
|
|
||||||
|
@@ -72,7 +72,7 @@ Filter by profile name
|
|||||||
|
|
||||||
=item --filter.profile_attach PROFILE_ATTACH
|
=item --filter.profile_attach PROFILE_ATTACH
|
||||||
|
|
||||||
Filter by profile attachement (i.e. by path of the executable to which this profile applies)
|
Filter by profile attachment (i.e. by path of the executable to which this profile applies)
|
||||||
|
|
||||||
=item --filter.profile_path PROFILE_PATH
|
=item --filter.profile_path PROFILE_PATH
|
||||||
|
|
||||||
|
@@ -1703,6 +1703,71 @@ def read_profile(file, is_active_profile, read_error_fatal=False):
|
|||||||
extra_profiles.add_profile(filename, profile, attachment, profile_data[profile])
|
extra_profiles.add_profile(filename, profile, attachment, profile_data[profile])
|
||||||
|
|
||||||
|
|
||||||
|
# TODO: Split profiles' creating and saving.
|
||||||
|
def create_local_profile_if_needed(profile_name):
|
||||||
|
base_profile = profile_name
|
||||||
|
while True:
|
||||||
|
parent = active_profiles[base_profile].data.get('parent')
|
||||||
|
if parent == '':
|
||||||
|
break
|
||||||
|
base_profile = parent
|
||||||
|
|
||||||
|
local_include = active_profiles[profile_name].get_local_include()
|
||||||
|
|
||||||
|
# Not found: we add a mention of the local profile in the main profile
|
||||||
|
if not local_include:
|
||||||
|
local_include = "local/" + profile_name.replace('/', '.')
|
||||||
|
active_profiles[profile_name]['inc_ie'].add(IncludeRule(local_include, True, True))
|
||||||
|
write_profile_ui_feedback(base_profile)
|
||||||
|
|
||||||
|
inc_file = profile_dir + '/' + local_include
|
||||||
|
|
||||||
|
# Create the include if needed
|
||||||
|
if not include.get(inc_file, {}).get(inc_file, False):
|
||||||
|
include[inc_file] = dict()
|
||||||
|
include[inc_file][inc_file] = ProfileStorage(inc_file, inc_file, "create_local_profile_if_needed")
|
||||||
|
|
||||||
|
return inc_file
|
||||||
|
|
||||||
|
|
||||||
|
def serialize_include(prof_storage, include_metadata=True):
|
||||||
|
lines = []
|
||||||
|
if include_metadata:
|
||||||
|
lines.append('# Last Modified: %s' % time.asctime())
|
||||||
|
|
||||||
|
if prof_storage.get('initial_comment'):
|
||||||
|
lines.append(prof_storage['initial_comment'].rstrip())
|
||||||
|
|
||||||
|
lines.extend(prof_storage.get_rules_clean(0))
|
||||||
|
|
||||||
|
return '\n'.join(lines) + '\n'
|
||||||
|
|
||||||
|
|
||||||
|
def write_include_ui_feedback(include_data, incfile, out_dir=None, include_metadata=True):
|
||||||
|
aaui.UI_Info(_('Writing updated include file %s') % incfile)
|
||||||
|
write_include(include_data, incfile, out_dir, include_metadata)
|
||||||
|
|
||||||
|
|
||||||
|
def write_include(include_data, incfile, out_dir=None, include_metadata=True):
|
||||||
|
target_file = incfile if incfile.startswith('/') else os.path.join(profile_dir, incfile)
|
||||||
|
if out_dir:
|
||||||
|
target_file = os.path.join(out_dir, os.path.basename(target_file))
|
||||||
|
|
||||||
|
include_string = serialize_include(include_data, include_metadata=include_metadata)
|
||||||
|
|
||||||
|
with NamedTemporaryFile('w', suffix='~', delete=False, dir=profile_dir + "/local") as tmp:
|
||||||
|
if os.path.exists(target_file):
|
||||||
|
shutil.copymode(target_file, tmp.name)
|
||||||
|
else:
|
||||||
|
pass # 0o600 (NamedTemporaryFile default)
|
||||||
|
tmp.write(include_string)
|
||||||
|
|
||||||
|
try:
|
||||||
|
shutil.move(tmp.name, target_file)
|
||||||
|
except PermissionError:
|
||||||
|
aaui.UI_Important(_('WARNING: Can\'t write to %s. Please run this script with elevated privileges') % target_file)
|
||||||
|
|
||||||
|
|
||||||
def attach_profile_data(profiles, profile_data):
|
def attach_profile_data(profiles, profile_data):
|
||||||
profile_data = merged_to_split(profile_data)
|
profile_data = merged_to_split(profile_data)
|
||||||
# Make deep copy of data to avoid changes to
|
# Make deep copy of data to avoid changes to
|
||||||
|
@@ -100,12 +100,12 @@ class ProfileRules:
|
|||||||
for raw_rule in raw_rules:
|
for raw_rule in raw_rules:
|
||||||
self.rules.append(SelectableRule(raw_rule, self.selectable))
|
self.rules.append(SelectableRule(raw_rule, self.selectable))
|
||||||
|
|
||||||
def get_writable_rules(self, template_path, allow_all=False):
|
def get_writable_rules(self, template_path, local='yes', allow_all=False):
|
||||||
out = ''
|
out = ''
|
||||||
for rule in self.rules:
|
for rule in self.rules:
|
||||||
if allow_all or rule.selected.get():
|
if allow_all or rule.selected.get():
|
||||||
if not self.is_userns_profile:
|
if not self.is_userns_profile:
|
||||||
out += 'add_rule\t{}\t{}\n'.format(rule.rule, self.profile_name)
|
out += 'add_rule\t{}\t{}\t{}\n'.format(local, rule.rule, self.profile_name)
|
||||||
else:
|
else:
|
||||||
out += 'create_userns\t{}\t{}\t{}\t{}\t{}\n'.format(template_path, self.profile_name, self.bin_path, self.profile_path, 'allow')
|
out += 'create_userns\t{}\t{}\t{}\t{}\t{}\n'.format(template_path, self.profile_name, self.bin_path, self.profile_path, 'allow')
|
||||||
return out
|
return out
|
||||||
@@ -158,17 +158,18 @@ class ShowMoreGUIAggregated(GUI):
|
|||||||
|
|
||||||
self.text_display = tk.Text(self.label_frame, wrap='word', height=40, width=100, yscrollcommand=self.scrollbar.set)
|
self.text_display = tk.Text(self.label_frame, wrap='word', height=40, width=100, yscrollcommand=self.scrollbar.set)
|
||||||
|
|
||||||
|
kwargs = {
|
||||||
|
"height": self.text_display.winfo_reqheight() - 4, # The border are *inside* the canvas but *outside* the textbox. I need to remove 4px (2*the size of the borders) to get the same size
|
||||||
|
"width": self.text_display.winfo_reqwidth() - 4,
|
||||||
|
"borderwidth": self.text_display['borderwidth'],
|
||||||
|
"relief": self.text_display['relief'],
|
||||||
|
"yscrollcommand": self.scrollbar.set,
|
||||||
|
}
|
||||||
|
|
||||||
if ttkthemes:
|
if ttkthemes:
|
||||||
self.text_display.configure(background=self.bg_color, foreground=self.fg_color)
|
self.text_display.configure(background=self.bg_color, foreground=self.fg_color)
|
||||||
self.canvas = tk.Canvas(
|
kwargs['background'] = self.bg_color
|
||||||
self.label_frame,
|
self.canvas = tk.Canvas(self.label_frame, **kwargs)
|
||||||
background=self.bg_color,
|
|
||||||
height=self.text_display.winfo_reqheight() - 4, # The border are *inside* the canvas but *outside* the textbox. I need to remove 4px (2*the size of the borders) to get the same size
|
|
||||||
width=self.text_display.winfo_reqwidth() - 4,
|
|
||||||
borderwidth=self.text_display['borderwidth'],
|
|
||||||
relief=self.text_display['relief'],
|
|
||||||
yscrollcommand=self.scrollbar.set
|
|
||||||
)
|
|
||||||
|
|
||||||
self.inner_frame = ttk.Frame(self.canvas)
|
self.inner_frame = ttk.Frame(self.canvas)
|
||||||
self.canvas.create_window((2, 2), window=self.inner_frame, anchor='nw')
|
self.canvas.create_window((2, 2), window=self.inner_frame, anchor='nw')
|
||||||
|
@@ -199,6 +199,21 @@ class ProfileStorage:
|
|||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
def get_local_include(self):
|
||||||
|
inc = None
|
||||||
|
preferred_inc = self.data['name']
|
||||||
|
if preferred_inc.startswith('/'):
|
||||||
|
preferred_inc = preferred_inc[1:]
|
||||||
|
preferred_inc = 'local/' + preferred_inc.replace('/', '.')
|
||||||
|
|
||||||
|
# If a local profile already exists, we use it.
|
||||||
|
for rule in self.data['inc_ie'].rules:
|
||||||
|
if rule.path.startswith("local/"):
|
||||||
|
inc = rule.path
|
||||||
|
if rule.path == preferred_inc: # Prefer includes that matches the profile name.
|
||||||
|
break
|
||||||
|
return inc
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def parse(cls, line, file, lineno, profile, hat):
|
def parse(cls, line, file, lineno, profile, hat):
|
||||||
"""parse a profile start line (using parse_profile_startline()) and convert it to an instance of this class"""
|
"""parse a profile start line (using parse_profile_startline()) and convert it to an instance of this class"""
|
||||||
|
@@ -8,8 +8,19 @@ from apparmor import aa
|
|||||||
from apparmor.logparser import ReadLog
|
from apparmor.logparser import ReadLog
|
||||||
|
|
||||||
from apparmor.translations import init_translation
|
from apparmor.translations import init_translation
|
||||||
|
|
||||||
_ = init_translation()
|
_ = init_translation()
|
||||||
|
|
||||||
|
is_aa_inited = False
|
||||||
|
|
||||||
|
|
||||||
|
def init_if_needed():
|
||||||
|
global is_aa_inited
|
||||||
|
if not is_aa_inited:
|
||||||
|
aa.init_aa()
|
||||||
|
aa.read_profiles()
|
||||||
|
is_aa_inited = True
|
||||||
|
|
||||||
|
|
||||||
def create_userns(template_path, name, bin_path, profile_path, decision):
|
def create_userns(template_path, name, bin_path, profile_path, decision):
|
||||||
with open(template_path, 'r') as f:
|
with open(template_path, 'r') as f:
|
||||||
@@ -27,27 +38,48 @@ def create_userns(template_path, name, bin_path, profile_path, decision):
|
|||||||
exit(_('Cannot reload updated profile'))
|
exit(_('Cannot reload updated profile'))
|
||||||
|
|
||||||
|
|
||||||
def add_to_profile(rule, profile_name):
|
def add_to_profile(rule_obj, profile_name):
|
||||||
aa.init_aa()
|
aa.active_profiles[profile_name][rule_obj.rule_name].add(rule_obj, cleanup=True)
|
||||||
aa.update_profiles()
|
|
||||||
|
|
||||||
rule_type, rule_class = ReadLog('', '', '').get_rule_type(rule)
|
|
||||||
|
|
||||||
rule_obj = rule_class.create_instance(rule)
|
|
||||||
|
|
||||||
if not aa.active_profiles.profile_exists(profile_name):
|
|
||||||
exit(_('Cannot find {} in profiles').format(profile_name))
|
|
||||||
aa.active_profiles[profile_name][rule_type].add(rule_obj, cleanup=True)
|
|
||||||
|
|
||||||
# Save changes
|
# Save changes
|
||||||
aa.write_profile_ui_feedback(profile_name)
|
aa.write_profile_ui_feedback(profile_name)
|
||||||
|
|
||||||
|
|
||||||
|
def add_to_local_profile(rule_obj, profile_name):
|
||||||
|
inc_file = aa.create_local_profile_if_needed(profile_name)
|
||||||
|
|
||||||
|
aa.include[inc_file][inc_file].data[rule_obj.rule_name].add(rule_obj, cleanup=True)
|
||||||
|
aa.write_include_ui_feedback(aa.include[inc_file][inc_file], inc_file)
|
||||||
|
|
||||||
|
|
||||||
|
def add_rule(mode, rule, profile_name):
|
||||||
|
init_if_needed()
|
||||||
|
|
||||||
|
if not aa.active_profiles.profile_exists(profile_name):
|
||||||
|
exit(_('Cannot find {} in profiles').format(profile_name))
|
||||||
|
|
||||||
|
rule_type, rule_class = ReadLog('', '', '').get_rule_type(rule)
|
||||||
|
rule_obj = rule_class.create_instance(rule)
|
||||||
|
|
||||||
|
if mode == 'yes':
|
||||||
|
add_to_local_profile(rule_obj, profile_name)
|
||||||
|
elif mode == 'no':
|
||||||
|
add_to_profile(rule_obj, profile_name)
|
||||||
|
elif mode == 'auto':
|
||||||
|
if aa.active_profiles[profile_name].get_local_include():
|
||||||
|
add_to_local_profile(rule_obj, profile_name)
|
||||||
|
else:
|
||||||
|
add_to_profile(rule_obj, profile_name)
|
||||||
|
else:
|
||||||
|
usage(False)
|
||||||
|
|
||||||
aa.reload_base(profile_name)
|
aa.reload_base(profile_name)
|
||||||
|
|
||||||
|
|
||||||
def usage(is_help):
|
def usage(is_help):
|
||||||
print('This tool is a low level tool - do not use it directly')
|
print('This tool is a low level tool - do not use it directly')
|
||||||
print('{} create_userns <template_path> <name> <bin_path> <profile_path> <decision>'.format(sys.argv[0]))
|
print('{} create_userns <template_path> <name> <bin_path> <profile_path> <decision>'.format(sys.argv[0]))
|
||||||
print('{} add_rule <rule> <profile_name>'.format(sys.argv[0]))
|
print('{} add_rule <mode=yes|no|auto> <rule> <profile_name>'.format(sys.argv[0]))
|
||||||
print('{} from_file <file>'.format(sys.argv[0]))
|
print('{} from_file <file>'.format(sys.argv[0]))
|
||||||
if is_help:
|
if is_help:
|
||||||
exit(0)
|
exit(0)
|
||||||
@@ -76,9 +108,9 @@ def do_command(command, args):
|
|||||||
usage(False)
|
usage(False)
|
||||||
create_userns(args[1], args[2], args[3], args[4], args[5])
|
create_userns(args[1], args[2], args[3], args[4], args[5])
|
||||||
elif command == 'add_rule':
|
elif command == 'add_rule':
|
||||||
if not len(args) == 3:
|
if not len(args) == 4:
|
||||||
usage(False)
|
usage(False)
|
||||||
add_to_profile(args[1], args[2])
|
add_rule(args[1], args[2], args[3])
|
||||||
elif command == 'help':
|
elif command == 'help':
|
||||||
usage(True)
|
usage(True)
|
||||||
else:
|
else:
|
||||||
|
@@ -20,6 +20,9 @@ interface_theme="ubuntu"
|
|||||||
# Binaries for which we ignore userns-related capability denials
|
# Binaries for which we ignore userns-related capability denials
|
||||||
ignore_denied_capability="sudo,su"
|
ignore_denied_capability="sudo,su"
|
||||||
|
|
||||||
|
# OPTIONAL - Write changes to local profiles to preserve regular profiles and simplify upgrades (yes, no, auto)
|
||||||
|
# use_local_profiles="yes"
|
||||||
|
|
||||||
# OPTIONAL - kind of operations which display a popup prompt.
|
# OPTIONAL - kind of operations which display a popup prompt.
|
||||||
# prompt_filter="userns"
|
# prompt_filter="userns"
|
||||||
|
|
||||||
|
@@ -167,8 +167,9 @@ class AANotifyTest(AANotifyBase):
|
|||||||
|
|
||||||
expected_return_code = 0
|
expected_return_code = 0
|
||||||
expected_output_1 = \
|
expected_output_1 = \
|
||||||
'''usage: aa-notify [-h] [-p] [--display DISPLAY] [-f FILE] [-l] [-s NUM] [-v]
|
'''usage: aa-notify [-h] [-p] [--display DISPLAY] [--xauthority XAUTHORITY]
|
||||||
[-u USER] [-w NUM] [-m] [-F] [--prompt-filter PF] [--debug]
|
[-f FILE] [-l] [-s NUM] [-v] [-u USER] [-w NUM] [-m] [-F]
|
||||||
|
[-L [{yes,no,auto}]] [--prompt-filter PF] [--debug]
|
||||||
[--filter.profile PROFILE] [--filter.operation OPERATION]
|
[--filter.profile PROFILE] [--filter.operation OPERATION]
|
||||||
[--filter.name NAME] [--filter.denied DENIED]
|
[--filter.name NAME] [--filter.denied DENIED]
|
||||||
[--filter.family FAMILY] [--filter.socket SOCKET]
|
[--filter.family FAMILY] [--filter.socket SOCKET]
|
||||||
@@ -182,6 +183,9 @@ Display AppArmor notifications or messages for DENIED entries.
|
|||||||
-p, --poll poll AppArmor logs and display notifications
|
-p, --poll poll AppArmor logs and display notifications
|
||||||
--display DISPLAY set the DISPLAY environment variable (might be needed if
|
--display DISPLAY set the DISPLAY environment variable (might be needed if
|
||||||
sudo resets $DISPLAY)
|
sudo resets $DISPLAY)
|
||||||
|
--xauthority XAUTHORITY
|
||||||
|
set the XAUTHORITY environment variable (might be needed
|
||||||
|
if sudo resets XAUTHORITY)
|
||||||
-f, --file FILE search FILE for AppArmor messages
|
-f, --file FILE search FILE for AppArmor messages
|
||||||
-l, --since-last display stats since last login
|
-l, --since-last display stats since last login
|
||||||
-s, --since-days NUM show stats for last NUM days (can be used alone or with
|
-s, --since-days NUM show stats for last NUM days (can be used alone or with
|
||||||
@@ -193,6 +197,8 @@ Display AppArmor notifications or messages for DENIED entries.
|
|||||||
-m, --merge-notifications
|
-m, --merge-notifications
|
||||||
Merge notification for improved readability (with -p)
|
Merge notification for improved readability (with -p)
|
||||||
-F, --foreground Do not fork to the background
|
-F, --foreground Do not fork to the background
|
||||||
|
-L, --local [{yes,no,auto}]
|
||||||
|
Add to local profile
|
||||||
--prompt-filter PF kind of operations which display a popup prompt
|
--prompt-filter PF kind of operations which display a popup prompt
|
||||||
--debug debug mode
|
--debug debug mode
|
||||||
|
|
||||||
@@ -231,6 +237,11 @@ Filtering options:
|
|||||||
), (
|
), (
|
||||||
', --wait NUM ',
|
', --wait NUM ',
|
||||||
' NUM, --wait NUM',
|
' NUM, --wait NUM',
|
||||||
|
), (
|
||||||
|
' -L, --local [{yes,no,auto}]\n'
|
||||||
|
+ ' Add to local profile',
|
||||||
|
' -L [{yes,no,auto}], --local [{yes,no,auto}]\n'
|
||||||
|
+ ' Add to local profile'
|
||||||
)]
|
)]
|
||||||
for patch in patches:
|
for patch in patches:
|
||||||
expected_output_2 = expected_output_2.replace(patch[0], patch[1])
|
expected_output_2 = expected_output_2.replace(patch[0], patch[1])
|
||||||
|
@@ -92,7 +92,16 @@ Filtering options:
|
|||||||
if line.startswith(' Profile '):
|
if line.startswith(' Profile '):
|
||||||
nb_profile += 1
|
nb_profile += 1
|
||||||
|
|
||||||
command = ['grep', '-Er', r'flags=.*unconfined.*\{', '--', aa.profile_dir]
|
command = ['grep', '-Er', r'flags=.*unconfined.*\{']
|
||||||
|
|
||||||
|
# Remove disabled profiles from grep
|
||||||
|
disable_dir = os.path.join(aa.profile_dir, 'disable')
|
||||||
|
if os.path.isdir(disable_dir):
|
||||||
|
for name in os.listdir(disable_dir):
|
||||||
|
command.append('--exclude=' + name)
|
||||||
|
|
||||||
|
command.extend(['--', aa.profile_dir])
|
||||||
|
|
||||||
result = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL, text=True, check=False)
|
result = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL, text=True, check=False)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
len(result.stdout.splitlines()), nb_profile,
|
len(result.stdout.splitlines()), nb_profile,
|
||||||
|
@@ -14,6 +14,7 @@ import unittest
|
|||||||
from apparmor.common import AppArmorBug, AppArmorException
|
from apparmor.common import AppArmorBug, AppArmorException
|
||||||
from apparmor.profile_storage import ProfileStorage, add_or_remove_flag, split_flags, var_transform
|
from apparmor.profile_storage import ProfileStorage, add_or_remove_flag, split_flags, var_transform
|
||||||
from apparmor.rule.capability import CapabilityRule
|
from apparmor.rule.capability import CapabilityRule
|
||||||
|
from apparmor.rule.include import IncludeRule
|
||||||
from common_test import AATest, setup_all_loops
|
from common_test import AATest, setup_all_loops
|
||||||
|
|
||||||
|
|
||||||
@@ -313,6 +314,27 @@ class AaTest_var_transform(AATest):
|
|||||||
self.assertEqual(var_transform(params), expected)
|
self.assertEqual(var_transform(params), expected)
|
||||||
|
|
||||||
|
|
||||||
|
class AaTest_include(AATest):
|
||||||
|
tests = (
|
||||||
|
(('profile foo /foo {', []), None), # No include
|
||||||
|
(('profile foo /foo {', ['elsewhere/foo']), None), # No include in local/
|
||||||
|
(('profile foo /foo {', ['local/foo']), "local/foo"), # Single include, we pick it
|
||||||
|
(('profile foo /foo {', ['local/bar']), "local/bar"), # Single include, we pick it
|
||||||
|
(('profile x//y /y {', ['local/x..y', 'local/y']), "local/x..y"), # Pick the include that matches the profile nam
|
||||||
|
(('profile foo /foo {', ['local/bar', 'local/foo', 'local/baz']), "local/foo"), # Pick the include that matches the profile name
|
||||||
|
(('/usr/bin/xx {', ['local/usr.bin.xx', 'local/xx']), "local/usr.bin.xx"), # Pick the include that matches the profile name
|
||||||
|
(('profile foo /foo {', ['local/bar', 'local/baz', 'local/qux']), "local/qux"), # No match, pick the last one
|
||||||
|
)
|
||||||
|
|
||||||
|
def _run_test(self, params, expected):
|
||||||
|
(profile, hat, prof_storage) = ProfileStorage.parse(params[0], 'somefile', 1, None, None)
|
||||||
|
|
||||||
|
for inc in params[1]:
|
||||||
|
prof_storage.data['inc_ie'].add(IncludeRule(inc, True, True))
|
||||||
|
|
||||||
|
self.assertEqual(prof_storage.get_local_include(), expected)
|
||||||
|
|
||||||
|
|
||||||
setup_all_loops(__name__)
|
setup_all_loops(__name__)
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main(verbosity=1)
|
unittest.main(verbosity=1)
|
||||||
|
Reference in New Issue
Block a user