mirror of
https://gitlab.com/apparmor/apparmor.git
synced 2025-03-06 09:21:00 +01:00
162 lines
5.7 KiB
Diff
162 lines
5.7 KiB
Diff
Change link to use pairs to provide tighter control of how links
|
|
can be made. The permission subset test is controled by an
|
|
extra flag of the link perm, this allows for compatibility with
|
|
old style links which have a broad pair, but also allows for
|
|
pairs to not require the subset test when needed.
|
|
|
|
The pairs are stored as two matches in the dfa seperated by the
|
|
null transition. Both matches must be made and have the link
|
|
perm set. If the second match has the AA_LINK_SUBSET_TEST perm
|
|
set, then the subset test is done.
|
|
|
|
|
|
The link keyword can be used or just the link mask
|
|
User side these pairs are expressed as follows
|
|
link [link_mask] linkname -> [subset_mask] targetname,
|
|
[link_mask] linkname -> [subset_mask] targetname,
|
|
|
|
link /linkname -> /targetname, #link to targetname, link is perm subset
|
|
|
|
if user:group:other link specification is desired then a user:group:other
|
|
mask containing only the link perm in the appropriate positions can be
|
|
specified
|
|
eg.
|
|
link l:: /linkname -> /targetname, # links to targetname if owned by
|
|
or
|
|
l:: /linkname -> /targetname,
|
|
|
|
Both the linkname and the target support full AppArmor globbing.
|
|
link l:: /** -> /**, # allow any link to target owned by user
|
|
|
|
to override the default subset test
|
|
l:: /linkname -> px /targetname,
|
|
|
|
|
|
Traditional AA style links are still supported and are mapped by the
|
|
parser into the newer link pair for the kernel, with the LINK_SUBSET_TEST
|
|
bits set.
|
|
/linkname rwl,
|
|
is mapped to
|
|
link /linkname -> /**,
|
|
/linkname rw,
|
|
|
|
---
|
|
security/apparmor/apparmor.h | 4 +++
|
|
security/apparmor/main.c | 45 ++++++++++++++++++++++++++++++++-----------
|
|
2 files changed, 38 insertions(+), 11 deletions(-)
|
|
|
|
--- a/security/apparmor/apparmor.h
|
|
+++ b/security/apparmor/apparmor.h
|
|
@@ -34,6 +34,7 @@
|
|
AA_MAY_LOCK | AA_EXEC_MMAP | \
|
|
AA_EXEC_UNSAFE | AA_EXEC_MOD_0 | \
|
|
AA_EXEC_MOD_1)
|
|
+#define AA_LINK_SUBSET_TEST 0x0020
|
|
|
|
#define AA_EXEC_UNCONFINED 0
|
|
#define AA_EXEC_INHERIT AA_EXEC_MOD_0
|
|
@@ -59,6 +60,9 @@
|
|
#define AA_USER_EXEC_MODS (AA_EXEC_MODIFIERS << AA_USER_SHIFT)
|
|
#define AA_OTHER_EXEC_MODS (AA_EXEC_MODIFIERS << AA_OTHER_SHIFT)
|
|
|
|
+#define AA_USER_EXEC_UNSAFE (AA_EXEC_UNSAFE << AA_USER_SHIFT)
|
|
+#define AA_OTHER_EXEC_UNSAFE (AA_EXEC_UNSAFE << AA_OTHER_SHIFT)
|
|
+
|
|
#define AA_EXEC_BITS (AA_USER_EXEC | AA_OTHER_EXEC)
|
|
|
|
#define AA_ALL_EXEC_MODS (AA_USER_EXEC_MODS | \
|
|
--- a/security/apparmor/main.c
|
|
+++ b/security/apparmor/main.c
|
|
@@ -67,10 +67,27 @@ static int aa_link_denied(struct aa_prof
|
|
const char *target, int target_mode,
|
|
int *request_mask)
|
|
{
|
|
- int l_mode, t_mode, denied_mask;
|
|
+ unsigned int state;
|
|
+ int l_mode, t_mode, denied_mask = 0;
|
|
int link_mask = AA_MAY_LINK << target_mode;
|
|
|
|
- l_mode = aa_match(profile->file_rules, link);
|
|
+ *request_mask = link_mask;
|
|
+
|
|
+ l_mode = aa_match_state(profile->file_rules, DFA_START, link, &state);
|
|
+ if (l_mode & link_mask) {
|
|
+ int mode;
|
|
+ /* test to see if target can be paired with link */
|
|
+ state = aa_dfa_null_transition(profile->file_rules, state);
|
|
+ mode = aa_match_state(profile->file_rules, state, target,
|
|
+ NULL);
|
|
+
|
|
+ if (!(mode & link_mask))
|
|
+ denied_mask |= link_mask;
|
|
+ if (!(mode & (AA_LINK_SUBSET_TEST << target_mode)))
|
|
+ return denied_mask;
|
|
+ }
|
|
+
|
|
+ /* do link perm subset test */
|
|
t_mode = aa_match(profile->file_rules, target);
|
|
|
|
/* Ignore valid-profile-transition flags. */
|
|
@@ -79,23 +96,30 @@ static int aa_link_denied(struct aa_prof
|
|
|
|
*request_mask = l_mode | link_mask;
|
|
|
|
- /* Link always requires 'l' on the link, a subset for user:other
|
|
- * of the target's 'r', 'w', 'x', 'a', 'z', and 'm' permissions on
|
|
- * the link, and if the link has 'x', an exact match of all the
|
|
- * execute flags ('i', 'u', 'U', 'p', 'P').
|
|
+ /* Link always requires 'l' on the link for both parts of the pair.
|
|
+ * If a subset test is required a permission subset test of the
|
|
+ * perms for the link are done against the user:group:other of the
|
|
+ * target's 'r', 'w', 'x', 'a', 'z', and 'm' permissions.
|
|
+ *
|
|
+ * If the link has 'x', an exact match of all the execute flags
|
|
+ * ('i', 'u', 'p'). safe exec is treated as a subset of unsafe exec
|
|
*/
|
|
#define SUBSET_PERMS (AA_FILE_PERMS & ~AA_LINK_BITS)
|
|
- denied_mask = ~l_mode & link_mask;
|
|
+ denied_mask |= ~l_mode & link_mask;
|
|
if (l_mode & SUBSET_PERMS) {
|
|
denied_mask |= (l_mode & SUBSET_PERMS) & ~t_mode;
|
|
if (denied_mask & AA_EXEC_BITS)
|
|
denied_mask |= l_mode & AA_ALL_EXEC_MODS;
|
|
else if (l_mode & AA_EXEC_BITS) {
|
|
+ if (!(l_mode & AA_USER_EXEC_UNSAFE))
|
|
+ l_mode |= t_mode & AA_USER_EXEC_UNSAFE;
|
|
if (l_mode & AA_USER_EXEC &&
|
|
(l_mode & AA_USER_EXEC_MODS) !=
|
|
(t_mode & AA_USER_EXEC_MODS))
|
|
denied_mask |= AA_USER_EXEC |
|
|
(l_mode & AA_USER_EXEC_MODS);
|
|
+ if (!(l_mode & AA_OTHER_EXEC_UNSAFE))
|
|
+ l_mode |= t_mode & AA_OTHER_EXEC_UNSAFE;
|
|
if (l_mode & AA_OTHER_EXEC &&
|
|
(l_mode & AA_OTHER_EXEC_MODS) !=
|
|
(t_mode & AA_OTHER_EXEC_MODS))
|
|
@@ -703,15 +727,15 @@ int aa_link(struct aa_profile *profile,
|
|
struct dentry *link, struct vfsmount *link_mnt,
|
|
struct dentry *target, struct vfsmount *target_mnt)
|
|
{
|
|
- int error, check = 0;
|
|
+ int error;
|
|
struct aa_audit sa;
|
|
char *buffer = NULL, *buffer2 = NULL;
|
|
|
|
memset(&sa, 0, sizeof(sa));
|
|
sa.operation = "inode_link";
|
|
sa.gfp_mask = GFP_KERNEL;
|
|
- sa.name = aa_get_name(link, link_mnt, &buffer, check);
|
|
- sa.name2 = aa_get_name(target, target_mnt, &buffer2, check);
|
|
+ sa.name = aa_get_name(link, link_mnt, &buffer, 0);
|
|
+ sa.name2 = aa_get_name(target, target_mnt, &buffer2, 0);
|
|
|
|
if (IS_ERR(sa.name)) {
|
|
sa.error_code = PTR_ERR(sa.name);
|
|
@@ -723,7 +747,6 @@ int aa_link(struct aa_profile *profile,
|
|
}
|
|
|
|
if (sa.name && sa.name2) {
|
|
- sa.request_mask = AA_MAY_LINK;
|
|
sa.denied_mask = aa_link_denied(profile, sa.name, sa.name2,
|
|
aa_inode_mode(target->d_inode),
|
|
&sa.request_mask);
|