mirror of
https://gitlab.com/apparmor/apparmor.git
synced 2025-03-04 08:24:42 +01:00
Fold together the audit rework.
This commit is contained in:
parent
024b9d702a
commit
97df59697c
22 changed files with 743 additions and 3576 deletions
|
@ -12,9 +12,9 @@ Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
|
|||
Signed-off-by: John Johansen <jjohansen@suse.de>
|
||||
|
||||
---
|
||||
include/linux/audit.h | 7 ++++++-
|
||||
include/linux/audit.h | 12 +++++++++++-
|
||||
kernel/audit.c | 6 ++++--
|
||||
2 files changed, 10 insertions(+), 3 deletions(-)
|
||||
2 files changed, 15 insertions(+), 3 deletions(-)
|
||||
|
||||
--- a/include/linux/audit.h
|
||||
+++ b/include/linux/audit.h
|
||||
|
@ -27,16 +27,21 @@ Signed-off-by: John Johansen <jjohansen@suse.de>
|
|||
* 1600 - 1699 kernel crypto events
|
||||
* 1700 - 1799 kernel anomaly records
|
||||
* 1800 - 1999 future kernel use (maybe integrity labels and related events)
|
||||
@@ -114,6 +114,8 @@
|
||||
#define AUDIT_ANOM_PROMISCUOUS 1700 /* Device changed promiscuous mode */
|
||||
#define AUDIT_ANOM_ABEND 1701 /* Process ended abnormally */
|
||||
@@ -109,6 +109,13 @@
|
||||
#define AUDIT_MAC_IPSEC_ADDSPD 1413 /* Add a XFRM policy */
|
||||
#define AUDIT_MAC_IPSEC_DELSPD 1414 /* Delete a XFRM policy */
|
||||
|
||||
+#define AUDIT_APPARMOR 1500 /* AppArmor audit */
|
||||
+#define AUDIT_APPARMOR_AUDIT 1501 /* AppArmor audited grants */
|
||||
+#define AUDIT_APPARMOR_ALLOWED 1502 /* Allowed Access for learning */
|
||||
+#define AUDIT_APPARMOR_DENIED 1503
|
||||
+#define AUDIT_APPARMOR_HINT 1504 /* Process Tracking information */
|
||||
+#define AUDIT_APPARMOR_STATUS 1505 /* Changes in config */
|
||||
+#define AUDIT_APPARMOR_ERROR 1506 /* Internal AppArmor Errors */
|
||||
+
|
||||
#define AUDIT_KERNEL 2000 /* Asynchronous audit record. NOT A REQUEST. */
|
||||
|
||||
/* Rule flags */
|
||||
@@ -499,6 +501,9 @@ extern void audit_log(struct audit_
|
||||
#define AUDIT_FIRST_KERN_ANOM_MSG 1700
|
||||
#define AUDIT_LAST_KERN_ANOM_MSG 1799
|
||||
#define AUDIT_ANOM_PROMISCUOUS 1700 /* Device changed promiscuous mode */
|
||||
@@ -499,6 +506,9 @@ extern void audit_log(struct audit_
|
||||
__attribute__((format(printf,4,5)));
|
||||
|
||||
extern struct audit_buffer *audit_log_start(struct audit_context *ctx, gfp_t gfp_mask, int type);
|
||||
|
|
|
@ -1,145 +0,0 @@
|
|||
From: John Johansen <jjohansen@suse.de>
|
||||
Subject: AppArmor: add lock subtyping so lockdep does not report false dependencies
|
||||
|
||||
AppArmor uses lock subtyping to avoid false positives from lockdep. The
|
||||
profile lock is often taken nested, but it is guaranteed to be in a lock
|
||||
safe order and not the same lock when done, so it is safe.
|
||||
|
||||
A third lock type (aa_lock_task_release) is given to the profile lock
|
||||
when it is taken in soft irq context during task release (aa_release).
|
||||
This is to avoid a false positive between the task lock and the profile
|
||||
lock. In task context the profile lock wraps the task lock with irqs
|
||||
off, but the kernel takes the task lock with irqs enabled. This won't
|
||||
ever result in a deadlock because aa_release doesn't need to take the
|
||||
task lock of the dead task that is released.
|
||||
|
||||
Signed-off-by: John Johansen <jjohansen@suse.de>
|
||||
Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
|
||||
Cc: Ingo Molnar <mingo@elte.hu>
|
||||
|
||||
---
|
||||
security/apparmor/apparmor.h | 7 +++++++
|
||||
security/apparmor/inline.h | 25 ++++++++++++++++++-------
|
||||
security/apparmor/locking.txt | 21 +++++++++++++++------
|
||||
security/apparmor/main.c | 6 +++---
|
||||
4 files changed, 43 insertions(+), 16 deletions(-)
|
||||
|
||||
--- a/security/apparmor/apparmor.h
|
||||
+++ b/security/apparmor/apparmor.h
|
||||
@@ -185,6 +185,13 @@ struct aa_audit {
|
||||
#define AA_CHECK_DIR 2 /* file type is directory */
|
||||
#define AA_CHECK_MANGLE 4 /* leave extra room for name mangling */
|
||||
|
||||
+/* lock subtypes so lockdep does not raise false dependencies */
|
||||
+enum aa_lock_class {
|
||||
+ aa_lock_normal,
|
||||
+ aa_lock_nested,
|
||||
+ aa_lock_task_release
|
||||
+};
|
||||
+
|
||||
/* main.c */
|
||||
extern int alloc_null_complain_profile(void);
|
||||
extern void free_null_complain_profile(void);
|
||||
--- a/security/apparmor/inline.h
|
||||
+++ b/security/apparmor/inline.h
|
||||
@@ -99,7 +99,8 @@ static inline void aa_free_task_context(
|
||||
* While the profile is locked, local interrupts are disabled. This also
|
||||
* gives us RCU reader safety.
|
||||
*/
|
||||
-static inline void lock_profile(struct aa_profile *profile)
|
||||
+static inline void lock_profile_nested(struct aa_profile *profile,
|
||||
+ enum aa_lock_class lock_class)
|
||||
{
|
||||
/* We always lock top-level profiles instead of children. */
|
||||
if (profile)
|
||||
@@ -112,7 +113,13 @@ static inline void lock_profile(struct a
|
||||
* the task_free_security hook, which may run in RCU context.
|
||||
*/
|
||||
if (profile)
|
||||
- spin_lock_irqsave(&profile->lock, profile->int_flags);
|
||||
+ spin_lock_irqsave_nested(&profile->lock, profile->int_flags,
|
||||
+ lock_class);
|
||||
+}
|
||||
+
|
||||
+static inline void lock_profile(struct aa_profile *profile)
|
||||
+{
|
||||
+ lock_profile_nested(profile, aa_lock_normal);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -161,17 +168,21 @@ static inline void lock_both_profiles(st
|
||||
*/
|
||||
if (!profile1 || profile1 == profile2) {
|
||||
if (profile2)
|
||||
- spin_lock_irqsave(&profile2->lock, profile2->int_flags);
|
||||
+ spin_lock_irqsave_nested(&profile2->lock,
|
||||
+ profile2->int_flags,
|
||||
+ aa_lock_normal);
|
||||
} else if (profile1 > profile2) {
|
||||
/* profile1 cannot be NULL here. */
|
||||
- spin_lock_irqsave(&profile1->lock, profile1->int_flags);
|
||||
+ spin_lock_irqsave_nested(&profile1->lock, profile1->int_flags,
|
||||
+ aa_lock_normal);
|
||||
if (profile2)
|
||||
- spin_lock(&profile2->lock);
|
||||
+ spin_lock_nested(&profile2->lock, aa_lock_nested);
|
||||
|
||||
} else {
|
||||
/* profile2 cannot be NULL here. */
|
||||
- spin_lock_irqsave(&profile2->lock, profile2->int_flags);
|
||||
- spin_lock(&profile1->lock);
|
||||
+ spin_lock_irqsave_nested(&profile2->lock, profile2->int_flags,
|
||||
+ aa_lock_normal);
|
||||
+ spin_lock_nested(&profile1->lock, aa_lock_nested);
|
||||
}
|
||||
}
|
||||
|
||||
--- a/security/apparmor/locking.txt
|
||||
+++ b/security/apparmor/locking.txt
|
||||
@@ -51,9 +51,18 @@ list, and can sleep. This ensures that p
|
||||
won't race with itself. We release the profile_list_lock as soon as
|
||||
possible to avoid stalling exec during profile loading/replacement/removal.
|
||||
|
||||
-lock_dep reports a false 'possible irq lock inversion dependency detected'
|
||||
-when the profile lock is taken in aa_release. This is due to that the
|
||||
-task_lock is often taken inside the profile lock but other kernel code
|
||||
-takes the task_lock with interrupts enabled. A deadlock will not actually
|
||||
-occur because apparmor does not take the task_lock in hard_irq or soft_irq
|
||||
-context.
|
||||
+AppArmor uses lock subtyping to avoid false positives from lockdep. The
|
||||
+profile lock is often taken nested, but it is guaranteed to be in a lock
|
||||
+safe order and not the same lock when done, so it is safe.
|
||||
+
|
||||
+A third lock type (aa_lock_task_release) is given to the profile lock
|
||||
+when it is taken in soft irq context during task release (aa_release).
|
||||
+This is to avoid a false positive between the task lock and the profile
|
||||
+lock. In task context the profile lock wraps the task lock with irqs
|
||||
+off, but the kernel takes the task lock with irqs enabled. This won't
|
||||
+result in a deadlock because for a deadlock to occur the kernel must
|
||||
+take dead task A's lock (irqs on), the rcu callback hook freeing
|
||||
+dead task A must be run and AppArmor must be changing the profile on
|
||||
+dead task A. The kernel should not be taking a dead task's task_lock
|
||||
+at the same time the task is being freed by task rcu cleanup other wise
|
||||
+the task would not be out of its quiescent period.
|
||||
--- a/security/apparmor/main.c
|
||||
+++ b/security/apparmor/main.c
|
||||
@@ -1081,8 +1081,8 @@ void aa_release(struct task_struct *task
|
||||
* sufficient to prevent the replacement race so we do not lock
|
||||
* the task.
|
||||
*
|
||||
- * lock_dep reports a false 'possible irq lock inversion dependency'
|
||||
- * between the profile lock and the task_lock.
|
||||
+ * Use lock subtyping to avoid lockdep reporting a false irq
|
||||
+ * possible inversion between the task_lock and profile_lock
|
||||
*
|
||||
* We also avoid taking the task_lock here because lock_dep
|
||||
* would report another false {softirq-on-W} potential irq_lock
|
||||
@@ -1095,7 +1095,7 @@ void aa_release(struct task_struct *task
|
||||
repeat:
|
||||
profile = aa_get_profile(task);
|
||||
if (profile) {
|
||||
- lock_profile(profile);
|
||||
+ lock_profile_nested(profile, aa_lock_task_release);
|
||||
cxt = aa_task_context(task);
|
||||
if (unlikely(!cxt || cxt->profile != profile)) {
|
||||
unlock_profile(profile);
|
File diff suppressed because it is too large
Load diff
|
@ -13,13 +13,13 @@ Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
|
|||
---
|
||||
security/apparmor/Kconfig | 9 +
|
||||
security/apparmor/Makefile | 13 ++
|
||||
security/apparmor/apparmor.h | 259 +++++++++++++++++++++++++++++++++++++++++
|
||||
security/apparmor/apparmorfs.c | 250 +++++++++++++++++++++++++++++++++++++++
|
||||
security/apparmor/inline.h | 219 ++++++++++++++++++++++++++++++++++
|
||||
security/apparmor/apparmor.h | 262 +++++++++++++++++++++++++++++++++++++++++
|
||||
security/apparmor/apparmorfs.c | 252 +++++++++++++++++++++++++++++++++++++++
|
||||
security/apparmor/inline.h | 230 +++++++++++++++++++++++++++++++++++
|
||||
security/apparmor/list.c | 94 ++++++++++++++
|
||||
security/apparmor/locking.txt | 59 +++++++++
|
||||
security/apparmor/procattr.c | 138 +++++++++++++++++++++
|
||||
8 files changed, 1041 insertions(+)
|
||||
security/apparmor/locking.txt | 68 ++++++++++
|
||||
security/apparmor/procattr.c | 137 +++++++++++++++++++++
|
||||
8 files changed, 1065 insertions(+)
|
||||
|
||||
--- /dev/null
|
||||
+++ b/security/apparmor/Kconfig
|
||||
|
@ -51,7 +51,7 @@ Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
|
|||
+ $(call cmd,make-caps)
|
||||
--- /dev/null
|
||||
+++ b/security/apparmor/apparmor.h
|
||||
@@ -0,0 +1,259 @@
|
||||
@@ -0,0 +1,262 @@
|
||||
+/*
|
||||
+ * Copyright (C) 1998-2007 Novell/SUSE
|
||||
+ *
|
||||
|
@ -86,8 +86,13 @@ Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
|
|||
+ AA_EXEC_UNCONFINED | \
|
||||
+ AA_EXEC_PROFILE)
|
||||
+
|
||||
+#define AA_VALID_PERM_MASK (MAY_READ | MAY_WRITE | MAY_EXEC | \
|
||||
+ AA_MAY_LINK | AA_EXEC_MODIFIERS | \
|
||||
+ AA_EXEC_MMAP | AA_EXEC_UNSAFE)
|
||||
+
|
||||
+#define AA_SECURE_EXEC_NEEDED 1
|
||||
+
|
||||
+
|
||||
+/* Control parameters (0 or 1), settable thru module/boot flags or
|
||||
+ * via /sys/kernel/security/apparmor/control */
|
||||
+extern int apparmor_complain;
|
||||
|
@ -199,68 +204,66 @@ Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
|
|||
+ */
|
||||
+
|
||||
+struct aa_audit {
|
||||
+ unsigned short type, flags;
|
||||
+ unsigned int result;
|
||||
+ const char *operation;
|
||||
+ gfp_t gfp_mask;
|
||||
+ int error_code;
|
||||
+ const char *info;
|
||||
+ const char *name;
|
||||
+ char *buffer;
|
||||
+ union {
|
||||
+ int mask;
|
||||
+ int capability;
|
||||
+ struct {
|
||||
+ const char *name2;
|
||||
+ char *buffer2;
|
||||
+ };
|
||||
+ struct iattr *iattr;
|
||||
+ va_list vaval;
|
||||
+ };
|
||||
+ const char *name2;
|
||||
+ int requested_mask, denied_mask;
|
||||
+ struct iattr *iattr;
|
||||
+ pid_t task, parent;
|
||||
+ u64 magic_token;
|
||||
+ int error_code;
|
||||
+};
|
||||
+
|
||||
+/* audit types */
|
||||
+#define AA_MANGLE_NAME 32
|
||||
+#define AA_MANGLE_NAME2 64
|
||||
+#define AA_AUDITTYPE_FILE (1 | AA_MANGLE_NAME)
|
||||
+#define AA_AUDITTYPE_DIR (2 | AA_MANGLE_NAME)
|
||||
+#define AA_AUDITTYPE_ATTR (3 | AA_MANGLE_NAME)
|
||||
+#define AA_AUDITTYPE_XATTR (4 | AA_MANGLE_NAME)
|
||||
+#define AA_AUDITTYPE_LINK (5 | AA_MANGLE_NAME | AA_MANGLE_NAME2)
|
||||
+#define AA_AUDITTYPE_FILE 1
|
||||
+#define AA_AUDITTYPE_DIR 2
|
||||
+#define AA_AUDITTYPE_ATTR 3
|
||||
+#define AA_AUDITTYPE_XATTR 4
|
||||
+#define AA_AUDITTYPE_LINK 5
|
||||
+#define AA_AUDITTYPE_CAP 6
|
||||
+#define AA_AUDITTYPE_MSG 7
|
||||
+#define AA_AUDITTYPE_SYSCALL 8
|
||||
+
|
||||
+/* audit flags */
|
||||
+#define AA_AUDITFLAG_AUDITSS_SYSCALL 1 /* log syscall context */
|
||||
+#define AA_AUDITFLAG_LOGERR 2 /* log operations that failed due to
|
||||
+ non permission errors */
|
||||
+
|
||||
+/* Flags for the permission check functions */
|
||||
+#define AA_CHECK_FD 1 /* coming from a file descriptor */
|
||||
+#define AA_CHECK_DIR 2 /* file type is directory */
|
||||
+#define AA_CHECK_MANGLE 4 /* leave extra room for name mangling */
|
||||
+
|
||||
+/* lock subtypes so lockdep does not raise false dependencies */
|
||||
+enum aa_lock_class {
|
||||
+ aa_lock_normal,
|
||||
+ aa_lock_nested,
|
||||
+ aa_lock_task_release
|
||||
+};
|
||||
+
|
||||
+/* main.c */
|
||||
+extern int alloc_null_complain_profile(void);
|
||||
+extern void free_null_complain_profile(void);
|
||||
+extern int attach_nullprofile(struct aa_profile *profile);
|
||||
+extern int aa_audit_message(struct aa_profile *profile, gfp_t gfp,
|
||||
+ const char *, ...)
|
||||
+ __attribute__ ((format (printf, 3, 4)));
|
||||
+extern int aa_audit_message(struct aa_profile *profile, struct aa_audit *sa,
|
||||
+ int type);
|
||||
+void aa_audit_hint(struct aa_profile *profile, struct aa_audit *sa);
|
||||
+void aa_audit_status(struct aa_profile *profile, struct aa_audit *sa);
|
||||
+int aa_audit_reject(struct aa_profile *profile, struct aa_audit *sa);
|
||||
+extern int aa_audit_syscallreject(struct aa_profile *profile, gfp_t gfp,
|
||||
+ const char *);
|
||||
+extern int aa_audit(struct aa_profile *profile, struct aa_audit *);
|
||||
+
|
||||
+extern int aa_attr(struct aa_profile *profile, struct dentry *dentry,
|
||||
+ struct vfsmount *mnt, struct iattr *iattr);
|
||||
+extern int aa_perm_xattr(struct aa_profile *profile, struct dentry *dentry,
|
||||
+ struct vfsmount *mnt, const char *operation, int mask,
|
||||
+ int check);
|
||||
+extern int aa_perm_xattr(struct aa_profile *profile, const char *operation,
|
||||
+ struct dentry *dentry, struct vfsmount *mnt,
|
||||
+ int mask, int check);
|
||||
+extern int aa_capability(struct aa_task_context *cxt, int cap);
|
||||
+extern int aa_perm(struct aa_profile *profile, struct dentry *dentry,
|
||||
+ struct vfsmount *mnt, int mask, int check);
|
||||
+extern int aa_perm_dir(struct aa_profile *profile, struct dentry *dentry,
|
||||
+ struct vfsmount *mnt, const char *operation, int mask);
|
||||
+extern int aa_perm_path(struct aa_profile *, const char *, int);
|
||||
+extern int aa_perm(struct aa_profile *profile, const char *operation,
|
||||
+ struct dentry *dentry, struct vfsmount *mnt, int mask,
|
||||
+ int check);
|
||||
+extern int aa_perm_dir(struct aa_profile *profile, const char *operation,
|
||||
+ struct dentry *dentry, struct vfsmount *mnt,
|
||||
+ int mask);
|
||||
+extern int aa_perm_path(struct aa_profile *, const char *operation,
|
||||
+ const char *name, int);
|
||||
+extern int aa_link(struct aa_profile *profile,
|
||||
+ struct dentry *link, struct vfsmount *link_mnt,
|
||||
+ struct dentry *target, struct vfsmount *target_mnt);
|
||||
|
@ -313,7 +316,7 @@ Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
|
|||
+#endif /* __APPARMOR_H */
|
||||
--- /dev/null
|
||||
+++ b/security/apparmor/apparmorfs.c
|
||||
@@ -0,0 +1,250 @@
|
||||
@@ -0,0 +1,252 @@
|
||||
+/*
|
||||
+ * Copyright (C) 1998-2007 Novell/SUSE
|
||||
+ *
|
||||
|
@ -336,7 +339,7 @@ Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
|
|||
+
|
||||
+static char *aa_simple_write_to_buffer(const char __user *userbuf,
|
||||
+ size_t alloc_size, size_t copy_size,
|
||||
+ loff_t *pos, const char *msg)
|
||||
+ loff_t *pos, const char *operation)
|
||||
+{
|
||||
+ struct aa_profile *profile;
|
||||
+ char *data;
|
||||
|
@ -354,13 +357,13 @@ Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
|
|||
+ */
|
||||
+ profile = aa_get_profile(current);
|
||||
+ if (profile) {
|
||||
+ aa_audit_message(NULL, GFP_KERNEL, "REJECTING access to "
|
||||
+ "profile %s (%d profile %s active %s)",
|
||||
+ msg, current->pid, profile->parent->name,
|
||||
+ profile->name);
|
||||
+ struct aa_audit sa;
|
||||
+ memset(&sa, 0, sizeof(sa));
|
||||
+ sa.operation = operation;
|
||||
+ sa.gfp_mask = GFP_KERNEL;
|
||||
+ sa.error_code = -EACCES;
|
||||
+ data = ERR_PTR(aa_audit_reject(profile, &sa));
|
||||
+ aa_put_profile(profile);
|
||||
+
|
||||
+ data = ERR_PTR(-EPERM);
|
||||
+ goto out;
|
||||
+ }
|
||||
+
|
||||
|
@ -422,7 +425,7 @@ Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
|
|||
+ char *data;
|
||||
+ ssize_t error;
|
||||
+
|
||||
+ data = aa_simple_write_to_buffer(buf, size, size, pos, "load");
|
||||
+ data = aa_simple_write_to_buffer(buf, size, size, pos, "profile_load");
|
||||
+
|
||||
+ error = PTR_ERR(data);
|
||||
+ if (!IS_ERR(data)) {
|
||||
|
@ -445,7 +448,8 @@ Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
|
|||
+ char *data;
|
||||
+ ssize_t error;
|
||||
+
|
||||
+ data = aa_simple_write_to_buffer(buf, size, size, pos, "replacement");
|
||||
+ data = aa_simple_write_to_buffer(buf, size, size, pos,
|
||||
+ "profile_replace");
|
||||
+
|
||||
+ error = PTR_ERR(data);
|
||||
+ if (!IS_ERR(data)) {
|
||||
|
@ -472,7 +476,8 @@ Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
|
|||
+ * aa_remove_profile needs a null terminated string so 1 extra
|
||||
+ * byte is allocated and the copied data is null terminated.
|
||||
+ */
|
||||
+ data = aa_simple_write_to_buffer(buf, size + 1, size, pos, "removal");
|
||||
+ data = aa_simple_write_to_buffer(buf, size + 1, size, pos,
|
||||
+ "profile_remove");
|
||||
+
|
||||
+ error = PTR_ERR(data);
|
||||
+ if (!IS_ERR(data)) {
|
||||
|
@ -566,7 +571,7 @@ Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
|
|||
+
|
||||
--- /dev/null
|
||||
+++ b/security/apparmor/inline.h
|
||||
@@ -0,0 +1,219 @@
|
||||
@@ -0,0 +1,230 @@
|
||||
+/*
|
||||
+ * Copyright (C) 1998-2007 Novell/SUSE
|
||||
+ *
|
||||
|
@ -668,7 +673,8 @@ Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
|
|||
+ * While the profile is locked, local interrupts are disabled. This also
|
||||
+ * gives us RCU reader safety.
|
||||
+ */
|
||||
+static inline void lock_profile(struct aa_profile *profile)
|
||||
+static inline void lock_profile_nested(struct aa_profile *profile,
|
||||
+ enum aa_lock_class lock_class)
|
||||
+{
|
||||
+ /* We always lock top-level profiles instead of children. */
|
||||
+ if (profile)
|
||||
|
@ -681,7 +687,13 @@ Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
|
|||
+ * the task_free_security hook, which may run in RCU context.
|
||||
+ */
|
||||
+ if (profile)
|
||||
+ spin_lock_irqsave(&profile->lock, profile->int_flags);
|
||||
+ spin_lock_irqsave_nested(&profile->lock, profile->int_flags,
|
||||
+ lock_class);
|
||||
+}
|
||||
+
|
||||
+static inline void lock_profile(struct aa_profile *profile)
|
||||
+{
|
||||
+ lock_profile_nested(profile, aa_lock_normal);
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
|
@ -730,17 +742,21 @@ Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
|
|||
+ */
|
||||
+ if (!profile1 || profile1 == profile2) {
|
||||
+ if (profile2)
|
||||
+ spin_lock_irqsave(&profile2->lock, profile2->int_flags);
|
||||
+ spin_lock_irqsave_nested(&profile2->lock,
|
||||
+ profile2->int_flags,
|
||||
+ aa_lock_normal);
|
||||
+ } else if (profile1 > profile2) {
|
||||
+ /* profile1 cannot be NULL here. */
|
||||
+ spin_lock_irqsave(&profile1->lock, profile1->int_flags);
|
||||
+ spin_lock_irqsave_nested(&profile1->lock, profile1->int_flags,
|
||||
+ aa_lock_normal);
|
||||
+ if (profile2)
|
||||
+ spin_lock(&profile2->lock);
|
||||
+ spin_lock_nested(&profile2->lock, aa_lock_nested);
|
||||
+
|
||||
+ } else {
|
||||
+ /* profile2 cannot be NULL here. */
|
||||
+ spin_lock_irqsave(&profile2->lock, profile2->int_flags);
|
||||
+ spin_lock(&profile1->lock);
|
||||
+ spin_lock_irqsave_nested(&profile2->lock, profile2->int_flags,
|
||||
+ aa_lock_normal);
|
||||
+ spin_lock_nested(&profile1->lock, aa_lock_nested);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
|
@ -885,7 +901,7 @@ Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
|
|||
+};
|
||||
--- /dev/null
|
||||
+++ b/security/apparmor/locking.txt
|
||||
@@ -0,0 +1,59 @@
|
||||
@@ -0,0 +1,68 @@
|
||||
+Locking in AppArmor
|
||||
+===================
|
||||
+
|
||||
|
@ -939,15 +955,24 @@ Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
|
|||
+won't race with itself. We release the profile_list_lock as soon as
|
||||
+possible to avoid stalling exec during profile loading/replacement/removal.
|
||||
+
|
||||
+lock_dep reports a false 'possible irq lock inversion dependency detected'
|
||||
+when the profile lock is taken in aa_release. This is due to that the
|
||||
+task_lock is often taken inside the profile lock but other kernel code
|
||||
+takes the task_lock with interrupts enabled. A deadlock will not actually
|
||||
+occur because apparmor does not take the task_lock in hard_irq or soft_irq
|
||||
+context.
|
||||
+AppArmor uses lock subtyping to avoid false positives from lockdep. The
|
||||
+profile lock is often taken nested, but it is guaranteed to be in a lock
|
||||
+safe order and not the same lock when done, so it is safe.
|
||||
+
|
||||
+A third lock type (aa_lock_task_release) is given to the profile lock
|
||||
+when it is taken in soft irq context during task release (aa_release).
|
||||
+This is to avoid a false positive between the task lock and the profile
|
||||
+lock. In task context the profile lock wraps the task lock with irqs
|
||||
+off, but the kernel takes the task lock with irqs enabled. This won't
|
||||
+result in a deadlock because for a deadlock to occur the kernel must
|
||||
+take dead task A's lock (irqs on), the rcu callback hook freeing
|
||||
+dead task A must be run and AppArmor must be changing the profile on
|
||||
+dead task A. The kernel should not be taking a dead task's task_lock
|
||||
+at the same time the task is being freed by task rcu cleanup other wise
|
||||
+the task would not be out of its quiescent period.
|
||||
--- /dev/null
|
||||
+++ b/security/apparmor/procattr.c
|
||||
@@ -0,0 +1,138 @@
|
||||
@@ -0,0 +1,137 @@
|
||||
+/*
|
||||
+ * Copyright (C) 1998-2007 Novell/SUSE
|
||||
+ *
|
||||
|
@ -1031,6 +1056,12 @@ Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
|
|||
+int aa_setprocattr_setprofile(struct task_struct *task, char *args)
|
||||
+{
|
||||
+ struct aa_profile *old_profile, *new_profile;
|
||||
+ struct aa_audit sa;
|
||||
+
|
||||
+ memset(&sa, 0, sizeof(sa));
|
||||
+ sa.operation = "profile_set";
|
||||
+ sa.gfp_mask = GFP_KERNEL;
|
||||
+ sa.task = task->pid;
|
||||
+
|
||||
+ AA_DEBUG("%s: current %d\n",
|
||||
+ __FUNCTION__, current->pid);
|
||||
|
@ -1041,11 +1072,9 @@ Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
|
|||
+ else {
|
||||
+ new_profile = aa_find_profile(args);
|
||||
+ if (!new_profile) {
|
||||
+ aa_audit_message(NULL, GFP_KERNEL, "Unable to switch "
|
||||
+ "task %d to profile '%s'. No such "
|
||||
+ "profile.",
|
||||
+ task->pid, args);
|
||||
+
|
||||
+ sa.name = args;
|
||||
+ sa.info = "unknown profile";
|
||||
+ aa_audit_reject(NULL, &sa);
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+ }
|
||||
|
@ -1062,25 +1091,20 @@ Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
|
|||
+ }
|
||||
+
|
||||
+ if (new_profile) {
|
||||
+ aa_audit_message(NULL, GFP_KERNEL, "Switching task %d profile "
|
||||
+ "%s active %s to new profile %s",
|
||||
+ task->pid, old_profile ?
|
||||
+ old_profile->parent->name : "unconfined",
|
||||
+ old_profile ? old_profile->name : "unconfined",
|
||||
+ args);
|
||||
+ sa.name = args;
|
||||
+ sa.name2 = old_profile ? old_profile->parent->name :
|
||||
+ "unconfined";
|
||||
+ aa_audit_status(NULL, &sa);
|
||||
+ } else {
|
||||
+ if (old_profile) {
|
||||
+ aa_audit_message(NULL, GFP_KERNEL, "Unconfining task "
|
||||
+ "%d profile %s active %s",
|
||||
+ task->pid, old_profile->parent->name,
|
||||
+ old_profile->name);
|
||||
+ sa.name = "unconfined";
|
||||
+ sa.name2 = old_profile->parent->name;
|
||||
+ aa_audit_status(NULL, &sa);
|
||||
+ } else {
|
||||
+ aa_audit_message(NULL, GFP_KERNEL, "task %d is already "
|
||||
+ "unconfined",
|
||||
+ task->pid);
|
||||
+ sa.info = "task is unconfined";
|
||||
+ aa_audit_status(NULL, &sa);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ aa_put_profile(old_profile);
|
||||
+ aa_put_profile(new_profile);
|
||||
+
|
||||
|
|
|
@ -8,14 +8,14 @@ Signed-off-by: John Johansen <jjohansen@suse.de>
|
|||
Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
|
||||
|
||||
---
|
||||
security/apparmor/match.c | 232 ++++++++++++
|
||||
security/apparmor/match.c | 248 +++++++++++++
|
||||
security/apparmor/match.h | 83 ++++
|
||||
security/apparmor/module_interface.c | 643 +++++++++++++++++++++++++++++++++++
|
||||
3 files changed, 958 insertions(+)
|
||||
security/apparmor/module_interface.c | 661 +++++++++++++++++++++++++++++++++++
|
||||
3 files changed, 992 insertions(+)
|
||||
|
||||
--- /dev/null
|
||||
+++ b/security/apparmor/match.c
|
||||
@@ -0,0 +1,232 @@
|
||||
@@ -0,0 +1,248 @@
|
||||
+/*
|
||||
+ * Copyright (C) 2007 Novell/SUSE
|
||||
+ *
|
||||
|
@ -30,6 +30,7 @@ Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
|
|||
+#include <linux/kernel.h>
|
||||
+#include <linux/slab.h>
|
||||
+#include <linux/errno.h>
|
||||
+#include "apparmor.h"
|
||||
+#include "match.h"
|
||||
+
|
||||
+static struct table_header *unpack_table(void *blob, size_t bsize)
|
||||
|
@ -188,6 +189,21 @@ Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
|
|||
+ goto out;
|
||||
+ }
|
||||
+
|
||||
+ /* verify accept permissions */
|
||||
+ for (i = 0; i < state_count; i++) {
|
||||
+ int mode = ACCEPT_TABLE(dfa)[i];
|
||||
+
|
||||
+ if (mode & ~AA_VALID_PERM_MASK)
|
||||
+ goto out;
|
||||
+
|
||||
+ /* if MAY_EXEC, exactly one exec modifier must be set */
|
||||
+ if (mode & MAY_EXEC) {
|
||||
+ mode &= AA_EXEC_MODIFIERS;
|
||||
+ if (mode & (mode - 1))
|
||||
+ goto out;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ error = 0;
|
||||
+out:
|
||||
+ return error;
|
||||
|
@ -336,7 +352,7 @@ Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
|
|||
+#endif /* __MATCH_H */
|
||||
--- /dev/null
|
||||
+++ b/security/apparmor/module_interface.c
|
||||
@@ -0,0 +1,643 @@
|
||||
@@ -0,0 +1,661 @@
|
||||
+/*
|
||||
+ * Copyright (C) 1998-2007 Novell/SUSE
|
||||
+ *
|
||||
|
@ -580,11 +596,14 @@ Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
|
|||
+/**
|
||||
+ * aa_unpack_profile - unpack a serialized profile
|
||||
+ * @e: serialized data extent information
|
||||
+ * @error: error code returned if unpacking fails
|
||||
+ * @depth: recursion depth of unpack
|
||||
+ * @operation: operation profile is being unpacked for
|
||||
+ */
|
||||
+static struct aa_profile *aa_unpack_profile(struct aa_ext *e, int depth)
|
||||
+static struct aa_profile *aa_unpack_profile(struct aa_ext *e, int depth,
|
||||
+ const char *operation)
|
||||
+{
|
||||
+ struct aa_profile *profile = NULL;
|
||||
+ struct aa_audit sa;
|
||||
+
|
||||
+ int error = -EPROTO;
|
||||
+
|
||||
|
@ -627,7 +646,7 @@ Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
|
|||
+ goto fail;
|
||||
+ while (!aa_is_nameX(e, AA_LISTEND, NULL)) {
|
||||
+ struct aa_profile *subprofile;
|
||||
+ subprofile = aa_unpack_profile(e, depth + 1);
|
||||
+ subprofile = aa_unpack_profile(e, depth + 1, operation);
|
||||
+ if (IS_ERR(subprofile)) {
|
||||
+ error = PTR_ERR(subprofile);
|
||||
+ goto fail;
|
||||
|
@ -643,8 +662,12 @@ Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
|
|||
+ return profile;
|
||||
+
|
||||
+fail:
|
||||
+ aa_audit_message(NULL, GFP_KERNEL, "Invalid profile %s",
|
||||
+ profile && profile->name ? profile->name : "unknown");
|
||||
+ memset(&sa, 0, sizeof(sa));
|
||||
+ sa.operation = operation;
|
||||
+ sa.gfp_mask = GFP_KERNEL;
|
||||
+ sa.name = profile && profile->name ? profile->name : "unknown";
|
||||
+ sa.info = "failed to unpack profile";
|
||||
+ aa_audit_status(NULL, &sa);
|
||||
+
|
||||
+ if (profile)
|
||||
+ free_aa_profile(profile);
|
||||
|
@ -659,9 +682,10 @@ Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
|
|||
+ * check interface version unpack a profile and all its hats and patch
|
||||
+ * in any extra information that the profile needs.
|
||||
+ */
|
||||
+static struct aa_profile *aa_unpack_profile_wrapper(struct aa_ext *e)
|
||||
+static struct aa_profile *aa_unpack_profile_wrapper(struct aa_ext *e,
|
||||
+ const char *operation)
|
||||
+{
|
||||
+ struct aa_profile *profile = aa_unpack_profile(e, 0);
|
||||
+ struct aa_profile *profile = aa_unpack_profile(e, 0, operation);
|
||||
+ if (!IS_ERR(profile) &&
|
||||
+ (!list_empty(&profile->sub) || profile->flags.complain)) {
|
||||
+ int error;
|
||||
|
@ -677,21 +701,31 @@ Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
|
|||
+/**
|
||||
+ * aa_verify_head - unpack serialized stream header
|
||||
+ * @e: serialized data read head
|
||||
+ * @operation: operation header is being verified for
|
||||
+ *
|
||||
+ * returns error or 0 if header is good
|
||||
+ */
|
||||
+static int aa_verify_header(struct aa_ext *e)
|
||||
+static int aa_verify_header(struct aa_ext *e, const char *operation)
|
||||
+{
|
||||
+ /* get the interface version */
|
||||
+ if (!aa_is_u32(e, &e->version, "version")) {
|
||||
+ aa_audit_message(NULL, GFP_KERNEL, "Interface version missing");
|
||||
+ struct aa_audit sa;
|
||||
+ memset(&sa, 0, sizeof(sa));
|
||||
+ sa.operation = operation;
|
||||
+ sa.gfp_mask = GFP_KERNEL;
|
||||
+ sa.info = "invalid profile format";
|
||||
+ aa_audit_status(NULL, &sa);
|
||||
+ return -EPROTONOSUPPORT;
|
||||
+ }
|
||||
+
|
||||
+ /* check that the interface version is currently supported */
|
||||
+ if (e->version != 3) {
|
||||
+ aa_audit_message(NULL, GFP_KERNEL, "Unsupported interface "
|
||||
+ "version (%d)", e->version);
|
||||
+ struct aa_audit sa;
|
||||
+ memset(&sa, 0, sizeof(sa));
|
||||
+ sa.operation = operation;
|
||||
+ sa.gfp_mask = GFP_KERNEL;
|
||||
+ sa.info = "unsupported interface version";
|
||||
+ aa_audit_status(NULL, &sa);
|
||||
+ return -EPROTONOSUPPORT;
|
||||
+ }
|
||||
+ return 0;
|
||||
|
@ -710,11 +744,11 @@ Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
|
|||
+ .end = data + size,
|
||||
+ .pos = data
|
||||
+ };
|
||||
+ ssize_t error = aa_verify_header(&e);
|
||||
+ ssize_t error = aa_verify_header(&e, "profile_load");
|
||||
+ if (error)
|
||||
+ return error;
|
||||
+
|
||||
+ profile = aa_unpack_profile_wrapper(&e);
|
||||
+ profile = aa_unpack_profile_wrapper(&e, "profile_load");
|
||||
+ if (IS_ERR(profile))
|
||||
+ return PTR_ERR(profile);
|
||||
+
|
||||
|
@ -790,11 +824,11 @@ Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
|
|||
+ .end = udata + size,
|
||||
+ .pos = udata
|
||||
+ };
|
||||
+ ssize_t error = aa_verify_header(&e);
|
||||
+ ssize_t error = aa_verify_header(&e, "profile_replace");
|
||||
+ if (error)
|
||||
+ return error;
|
||||
+
|
||||
+ new_profile = aa_unpack_profile_wrapper(&e);
|
||||
+ new_profile = aa_unpack_profile_wrapper(&e, "profile_replace");
|
||||
+ if (IS_ERR(new_profile))
|
||||
+ return PTR_ERR(new_profile);
|
||||
+
|
||||
|
|
|
@ -1,10 +1,18 @@
|
|||
---
|
||||
security/apparmor/lsm.c | 37 ++++++++++++++++++++-----------------
|
||||
1 file changed, 20 insertions(+), 17 deletions(-)
|
||||
security/apparmor/lsm.c | 135 ++++++++++++++++++++++++++++--------------------
|
||||
1 file changed, 79 insertions(+), 56 deletions(-)
|
||||
|
||||
--- a/security/apparmor/lsm.c
|
||||
+++ b/security/apparmor/lsm.c
|
||||
@@ -108,8 +108,6 @@ static int apparmor_ptrace(struct task_s
|
||||
@@ -17,6 +17,7 @@
|
||||
#include <linux/namei.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/sysctl.h>
|
||||
+#include <linux/audit.h>
|
||||
|
||||
#include "apparmor.h"
|
||||
#include "inline.h"
|
||||
@@ -108,8 +109,6 @@ static int apparmor_ptrace(struct task_s
|
||||
struct task_struct *child)
|
||||
{
|
||||
struct aa_task_context *cxt;
|
||||
|
@ -13,7 +21,7 @@
|
|||
int error = 0;
|
||||
|
||||
/*
|
||||
@@ -123,22 +121,27 @@ static int apparmor_ptrace(struct task_s
|
||||
@@ -123,22 +122,32 @@ static int apparmor_ptrace(struct task_s
|
||||
|
||||
rcu_read_lock();
|
||||
cxt = aa_task_context(parent);
|
||||
|
@ -27,17 +35,22 @@
|
|||
- } else {
|
||||
- error = aa_may_ptrace(cxt, child_profile);
|
||||
- if (cxt && PROFILE_COMPLAIN(cxt->profile)) {
|
||||
+ if (cxt) {
|
||||
+ if (parent->nsproxy != child->nsproxy) {
|
||||
aa_audit_message(cxt->profile, GFP_ATOMIC,
|
||||
- aa_audit_message(cxt->profile, GFP_ATOMIC,
|
||||
- "LOGPROF-HINT ptrace pid=%d child=%d "
|
||||
- "(%d profile %s active %s)",
|
||||
- current->pid, child->pid, current->pid,
|
||||
- cxt->profile->parent->name,
|
||||
- cxt->profile->name);
|
||||
+ "REJECTING ptrace across "
|
||||
+ "namespace of %d by %d",
|
||||
+ parent->pid, child->pid);
|
||||
+ if (cxt) {
|
||||
+ if (parent->nsproxy != child->nsproxy) {
|
||||
+ struct aa_audit sa;
|
||||
+ memset(&sa, 0, sizeof(sa));
|
||||
+ sa.operation = "ptrace";
|
||||
+ sa.gfp_mask = GFP_ATOMIC;
|
||||
+ sa.parent = parent->pid;
|
||||
+ sa.task = child->pid;
|
||||
+ sa.info = "different namespaces";
|
||||
+ aa_audit_reject(cxt->profile, &sa);
|
||||
+ error = -EPERM;
|
||||
+ } else {
|
||||
+ struct aa_task_context *child_cxt =
|
||||
|
@ -46,13 +59,244 @@
|
|||
+ error = aa_may_ptrace(cxt, child_cxt ?
|
||||
+ child_cxt->profile : NULL);
|
||||
+ if (PROFILE_COMPLAIN(cxt->profile)) {
|
||||
+ aa_audit_message(cxt->profile, GFP_ATOMIC,
|
||||
+ "LOGPROF-HINT ptrace pid=%d child=%d "
|
||||
+ "(%d profile %s active %s)",
|
||||
+ current->pid, child->pid, current->pid,
|
||||
+ cxt->profile->parent->name,
|
||||
+ cxt->profile->name);
|
||||
+ struct aa_audit sa;
|
||||
+ memset(&sa, 0, sizeof(sa));
|
||||
+ sa.operation = "ptrace";
|
||||
+ sa.gfp_mask = GFP_ATOMIC;
|
||||
+ sa.parent = parent->pid;
|
||||
+ sa.task = child->pid;
|
||||
+ aa_audit_hint(cxt->profile, &sa);
|
||||
+ }
|
||||
}
|
||||
}
|
||||
rcu_read_unlock();
|
||||
@@ -189,7 +198,7 @@ static int apparmor_sysctl(struct ctl_ta
|
||||
if (name && name - buffer >= 5) {
|
||||
name -= 5;
|
||||
memcpy(name, "/proc", 5);
|
||||
- error = aa_perm_path(profile, name, mask);
|
||||
+ error = aa_perm_path(profile, "sysctl", name, mask);
|
||||
}
|
||||
free_page((unsigned long)buffer);
|
||||
}
|
||||
@@ -244,7 +253,8 @@ static int apparmor_inode_mkdir(struct i
|
||||
profile = aa_get_profile(current);
|
||||
|
||||
if (profile)
|
||||
- error = aa_perm_dir(profile, dentry, mnt, "mkdir", MAY_WRITE);
|
||||
+ error = aa_perm_dir(profile, "inode_mkdir", dentry, mnt,
|
||||
+ MAY_WRITE);
|
||||
|
||||
aa_put_profile(profile);
|
||||
|
||||
@@ -264,7 +274,8 @@ static int apparmor_inode_rmdir(struct i
|
||||
profile = aa_get_profile(current);
|
||||
|
||||
if (profile)
|
||||
- error = aa_perm_dir(profile, dentry, mnt, "rmdir", MAY_WRITE);
|
||||
+ error = aa_perm_dir(profile, "inode_rmdir", dentry, mnt,
|
||||
+ MAY_WRITE);
|
||||
|
||||
aa_put_profile(profile);
|
||||
|
||||
@@ -272,8 +283,9 @@ out:
|
||||
return error;
|
||||
}
|
||||
|
||||
-static int aa_permission(struct inode *inode, struct dentry *dentry,
|
||||
- struct vfsmount *mnt, int mask, int check)
|
||||
+static int aa_permission(struct inode *inode, const char *operation,
|
||||
+ struct dentry *dentry, struct vfsmount *mnt,
|
||||
+ int mask, int check)
|
||||
{
|
||||
int error = 0;
|
||||
|
||||
@@ -282,7 +294,8 @@ static int aa_permission(struct inode *i
|
||||
|
||||
profile = aa_get_profile(current);
|
||||
if (profile)
|
||||
- error = aa_perm(profile, dentry, mnt, mask, check);
|
||||
+ error = aa_perm(profile, operation, dentry, mnt, mask,
|
||||
+ check);
|
||||
aa_put_profile(profile);
|
||||
}
|
||||
return error;
|
||||
@@ -291,7 +304,7 @@ static int aa_permission(struct inode *i
|
||||
static int apparmor_inode_create(struct inode *dir, struct dentry *dentry,
|
||||
struct vfsmount *mnt, int mask)
|
||||
{
|
||||
- return aa_permission(dir, dentry, mnt, MAY_WRITE, 0);
|
||||
+ return aa_permission(dir, "inode_create", dentry, mnt, MAY_WRITE, 0);
|
||||
}
|
||||
|
||||
static int apparmor_inode_link(struct dentry *old_dentry,
|
||||
@@ -324,19 +337,20 @@ static int apparmor_inode_unlink(struct
|
||||
|
||||
if (S_ISDIR(dentry->d_inode->i_mode))
|
||||
check |= AA_CHECK_DIR;
|
||||
- return aa_permission(dir, dentry, mnt, MAY_WRITE, check);
|
||||
+ return aa_permission(dir, "inode_unlink", dentry, mnt, MAY_WRITE,
|
||||
+ check);
|
||||
}
|
||||
|
||||
static int apparmor_inode_symlink(struct inode *dir, struct dentry *dentry,
|
||||
struct vfsmount *mnt, const char *old_name)
|
||||
{
|
||||
- return aa_permission(dir, dentry, mnt, MAY_WRITE, 0);
|
||||
+ return aa_permission(dir, "inode_symlink", dentry, mnt, MAY_WRITE, 0);
|
||||
}
|
||||
|
||||
static int apparmor_inode_mknod(struct inode *dir, struct dentry *dentry,
|
||||
struct vfsmount *mnt, int mode, dev_t dev)
|
||||
{
|
||||
- return aa_permission(dir, dentry, mnt, MAY_WRITE, 0);
|
||||
+ return aa_permission(dir, "inode_mknod", dentry, mnt, MAY_WRITE, 0);
|
||||
}
|
||||
|
||||
static int apparmor_inode_rename(struct inode *old_dir,
|
||||
@@ -361,12 +375,12 @@ static int apparmor_inode_rename(struct
|
||||
if (inode && S_ISDIR(inode->i_mode))
|
||||
check |= AA_CHECK_DIR;
|
||||
if (old_mnt)
|
||||
- error = aa_perm(profile, old_dentry, old_mnt,
|
||||
- MAY_READ | MAY_WRITE, check);
|
||||
+ error = aa_perm(profile, "inode_rename", old_dentry,
|
||||
+ old_mnt, MAY_READ | MAY_WRITE, check);
|
||||
|
||||
if (!error && new_mnt) {
|
||||
- error = aa_perm(profile, new_dentry, new_mnt,
|
||||
- MAY_WRITE, check);
|
||||
+ error = aa_perm(profile, "inode_rename", new_dentry,
|
||||
+ new_mnt, MAY_WRITE, check);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -389,7 +403,8 @@ static int apparmor_inode_permission(str
|
||||
/* allow traverse accesses to directories */
|
||||
mask &= ~MAY_EXEC;
|
||||
}
|
||||
- return aa_permission(inode, nd->dentry, nd->mnt, mask, check);
|
||||
+ return aa_permission(inode, "inode_permission", nd->dentry, nd->mnt,
|
||||
+ mask, check);
|
||||
}
|
||||
|
||||
static int apparmor_inode_setattr(struct dentry *dentry, struct vfsmount *mnt,
|
||||
@@ -429,7 +444,7 @@ static int aa_xattr_permission(struct de
|
||||
int check = file ? AA_CHECK_FD : 0;
|
||||
|
||||
if (profile)
|
||||
- error = aa_perm_xattr(profile, dentry, mnt, operation,
|
||||
+ error = aa_perm_xattr(profile, operation, dentry, mnt,
|
||||
mask, check);
|
||||
aa_put_profile(profile);
|
||||
}
|
||||
@@ -491,7 +506,8 @@ static int apparmor_file_permission(stru
|
||||
if (S_ISDIR(inode->i_mode))
|
||||
check |= AA_CHECK_DIR;
|
||||
mask &= (MAY_READ | MAY_WRITE | MAY_EXEC);
|
||||
- error = aa_permission(inode, dentry, mnt, mask, check);
|
||||
+ error = aa_permission(inode, "file_permission", dentry, mnt,
|
||||
+ mask, check);
|
||||
}
|
||||
aa_put_profile(profile);
|
||||
|
||||
@@ -517,8 +533,8 @@ static void apparmor_file_free_security(
|
||||
aa_put_profile(file_profile);
|
||||
}
|
||||
|
||||
-static inline int aa_mmap(struct file *file, unsigned long prot,
|
||||
- unsigned long flags)
|
||||
+static inline int aa_mmap(struct file *file, const char *operation,
|
||||
+ unsigned long prot, unsigned long flags)
|
||||
{
|
||||
struct dentry *dentry;
|
||||
int mask = 0;
|
||||
@@ -536,20 +552,20 @@ static inline int aa_mmap(struct file *f
|
||||
mask |= AA_EXEC_MMAP;
|
||||
|
||||
dentry = file->f_dentry;
|
||||
- return aa_permission(dentry->d_inode, dentry, file->f_vfsmnt, mask,
|
||||
- AA_CHECK_FD);
|
||||
+ return aa_permission(dentry->d_inode, operation, dentry,
|
||||
+ file->f_vfsmnt, mask, AA_CHECK_FD);
|
||||
}
|
||||
|
||||
static int apparmor_file_mmap(struct file *file, unsigned long reqprot,
|
||||
unsigned long prot, unsigned long flags)
|
||||
{
|
||||
- return aa_mmap(file, prot, flags);
|
||||
+ return aa_mmap(file, "file_mmap", prot, flags);
|
||||
}
|
||||
|
||||
static int apparmor_file_mprotect(struct vm_area_struct *vma,
|
||||
unsigned long reqprot, unsigned long prot)
|
||||
{
|
||||
- return aa_mmap(vma->vm_file, prot,
|
||||
+ return aa_mmap(vma->vm_file, "file_mprotect", prot,
|
||||
!(vma->vm_flags & VM_SHARED) ? MAP_PRIVATE : 0);
|
||||
}
|
||||
|
||||
@@ -625,24 +641,27 @@ static int apparmor_setprocattr(struct t
|
||||
|
||||
profile = aa_get_profile(current);
|
||||
if (profile) {
|
||||
+ struct aa_audit sa;
|
||||
+ memset(&sa, 0, sizeof(sa));
|
||||
+ sa.operation = "profile_set";
|
||||
+ sa.gfp_mask = GFP_KERNEL;
|
||||
+ sa.task = task->pid;
|
||||
+ sa.info = "from confined process";
|
||||
+ aa_audit_reject(profile, &sa);
|
||||
aa_put_profile(profile);
|
||||
- aa_audit_message(NULL, GFP_KERNEL, "Attempt by "
|
||||
- "confined task %d [user %d] to "
|
||||
- "assign profile to task %d",
|
||||
- current->pid, current->uid,
|
||||
- task->pid);
|
||||
return -EACCES;
|
||||
}
|
||||
error = aa_setprocattr_setprofile(task, args);
|
||||
} else {
|
||||
- AA_ERROR("Unknown setprocattr command '%.*s' "
|
||||
- "by task %d [user %d] for task %d",
|
||||
- size < 16 ? (int)size : 16,
|
||||
- command,
|
||||
- current->pid,
|
||||
- current->uid,
|
||||
- task->pid);
|
||||
- error = -EINVAL;
|
||||
+ struct aa_audit sa;
|
||||
+ memset(&sa, 0, sizeof(sa));
|
||||
+ sa.operation = "setprocattr";
|
||||
+ sa.gfp_mask = GFP_KERNEL;
|
||||
+ sa.info = "invalid command";
|
||||
+ sa.name = command;
|
||||
+ sa.task = task->pid;
|
||||
+ aa_audit_reject(NULL, &sa);
|
||||
+ return -EINVAL;
|
||||
}
|
||||
|
||||
if (!error)
|
||||
@@ -700,8 +719,12 @@ struct security_operations apparmor_ops
|
||||
|
||||
static void info_message(const char *str)
|
||||
{
|
||||
+ struct aa_audit sa;
|
||||
+ memset(&sa, 0, sizeof(sa));
|
||||
+ sa.gfp_mask = GFP_KERNEL;
|
||||
+ sa.info = str;
|
||||
printk(KERN_INFO "AppArmor: %s", str);
|
||||
- aa_audit_message(NULL, GFP_KERNEL, "%s", str);
|
||||
+ aa_audit_message(NULL, &sa, AUDIT_APPARMOR_STATUS);
|
||||
}
|
||||
|
||||
static int __init apparmor_init(void)
|
||||
@@ -719,7 +742,7 @@ static int __init apparmor_init(void)
|
||||
}
|
||||
|
||||
if ((error = register_security(&apparmor_ops))) {
|
||||
- AA_ERROR("Unable to load AppArmor\n");
|
||||
+ AA_ERROR("Unable to register AppArmor\n");
|
||||
goto register_security_out;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,27 +0,0 @@
|
|||
---
|
||||
include/linux/audit.h | 9 +++++++--
|
||||
1 file changed, 7 insertions(+), 2 deletions(-)
|
||||
|
||||
--- a/include/linux/audit.h
|
||||
+++ b/include/linux/audit.h
|
||||
@@ -109,13 +109,18 @@
|
||||
#define AUDIT_MAC_IPSEC_ADDSPD 1413 /* Add a XFRM policy */
|
||||
#define AUDIT_MAC_IPSEC_DELSPD 1414 /* Delete a XFRM policy */
|
||||
|
||||
+#define AUDIT_APPARMOR_AUDIT 1501 /* AppArmor audited grants */
|
||||
+#define AUDIT_APPARMOR_ALLOWED 1502 /* Allowed Access for learning */
|
||||
+#define AUDIT_APPARMOR_DENIED 1503
|
||||
+#define AUDIT_APPARMOR_HINT 1504 /* Process Tracking information */
|
||||
+#define AUDIT_APPARMOR_STATUS 1505 /* Changes in config */
|
||||
+#define AUDIT_APPARMOR_ERROR 1506 /* Internal AppArmor Errors */
|
||||
+
|
||||
#define AUDIT_FIRST_KERN_ANOM_MSG 1700
|
||||
#define AUDIT_LAST_KERN_ANOM_MSG 1799
|
||||
#define AUDIT_ANOM_PROMISCUOUS 1700 /* Device changed promiscuous mode */
|
||||
#define AUDIT_ANOM_ABEND 1701 /* Process ended abnormally */
|
||||
|
||||
-#define AUDIT_APPARMOR 1500 /* AppArmor audit */
|
||||
-
|
||||
#define AUDIT_KERNEL 2000 /* Asynchronous audit record. NOT A REQUEST. */
|
||||
|
||||
/* Rule flags */
|
|
@ -1,87 +0,0 @@
|
|||
---
|
||||
security/apparmor/main.c | 43 +++++++++++++++++++++++--------------------
|
||||
1 file changed, 23 insertions(+), 20 deletions(-)
|
||||
|
||||
--- a/security/apparmor/main.c
|
||||
+++ b/security/apparmor/main.c
|
||||
@@ -58,35 +58,41 @@ static int aa_file_denied(struct aa_prof
|
||||
* @profile: profile to check against
|
||||
* @link: pathname of link being created
|
||||
* @target: pathname of target to be linked to
|
||||
- *
|
||||
+ * @request_mask: the permissions subset valid only if link succeeds
|
||||
* Return %0 on success, or else the permissions that the profile denies.
|
||||
*/
|
||||
static int aa_link_denied(struct aa_profile *profile, const char *link,
|
||||
- const char *target)
|
||||
+ const char *target, int *request_mask)
|
||||
{
|
||||
- int l_mode, t_mode;
|
||||
+ int l_mode, t_mode, denied_mask;
|
||||
|
||||
l_mode = aa_match(profile->file_rules, link);
|
||||
t_mode = aa_match(profile->file_rules, target);
|
||||
+ *request_mask = l_mode;
|
||||
|
||||
/* Link always requires 'l' on the link, a subset of the
|
||||
* target's 'r', 'w', 'x', 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').
|
||||
*/
|
||||
-#define RWXM (MAY_READ | MAY_WRITE | MAY_EXEC | AA_EXEC_MMAP)
|
||||
- if ((l_mode & AA_MAY_LINK) &&
|
||||
- (l_mode & RWXM) && !(l_mode & ~t_mode & RWXM) &&
|
||||
- (!(l_mode & MAY_EXEC) ||
|
||||
- ((l_mode & AA_EXEC_MODIFIERS) == (t_mode & AA_EXEC_MODIFIERS) &&
|
||||
- (l_mode & AA_EXEC_UNSAFE) == (t_mode & AA_EXEC_UNSAFE))))
|
||||
- return 0;
|
||||
-#undef RWXM
|
||||
- /* FIXME: There currenly is no way to report which permissions
|
||||
- * we expect in t_mode, so linking could fail even after learning
|
||||
- * the required l_mode.
|
||||
+ denied_mask = ~l_mode & AA_MAY_LINK;
|
||||
+ denied_mask |= l_mode & ~t_mode;
|
||||
+ if (denied_mask & AA_EXEC_MODIFIERS)
|
||||
+ denied_mask |= MAY_EXEC;
|
||||
+
|
||||
+ /* FIXME: denied mask has no way of reporting that the secure
|
||||
+ * execmode required is safe exec. This means that if link
|
||||
+ * has safe exec and target unsafe exec, the difference is not
|
||||
+ * reported, back, this isn't a significant problem since
|
||||
+ * safe exec is a subset of unsafe exec, but it violates the
|
||||
+ * exec should be exactly equal rule.
|
||||
+ *
|
||||
+ * The reverse situation does not cause a problem, if link
|
||||
+ * requires an unsafe exec and target a safe exec we report
|
||||
+ * the missing unsafe exec bit.
|
||||
*/
|
||||
- return AA_MAY_LINK;
|
||||
+
|
||||
+ return denied_mask;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -648,21 +654,18 @@ int aa_link(struct aa_profile *profile,
|
||||
sa.name2 = aa_get_name(target, target_mnt, &sa.buffer2, check);
|
||||
|
||||
if (IS_ERR(sa.name)) {
|
||||
- sa.requested_mask = 0;
|
||||
- sa.denied_mask = 0;
|
||||
sa.error_code = PTR_ERR(sa.name);
|
||||
sa.name = NULL;
|
||||
}
|
||||
if (IS_ERR(sa.name2)) {
|
||||
- sa.requested_mask = 0;
|
||||
- sa.denied_mask = 0;
|
||||
sa.error_code = PTR_ERR(sa.name2);
|
||||
sa.name2 = NULL;
|
||||
}
|
||||
|
||||
if (sa.name && sa.name2) {
|
||||
sa.requested_mask = AA_MAY_LINK;
|
||||
- sa.denied_mask = aa_link_denied(profile, sa.name, sa.name2);
|
||||
+ sa.denied_mask = aa_link_denied(profile, sa.name, sa.name2,
|
||||
+ &sa.requested_mask);
|
||||
sa.error_code = sa.denied_mask ? -EACCES : 0;
|
||||
}
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
---
|
||||
security/apparmor/main.c | 10 +++++-----
|
||||
1 file changed, 5 insertions(+), 5 deletions(-)
|
||||
|
||||
--- a/security/apparmor/main.c
|
||||
+++ b/security/apparmor/main.c
|
||||
@@ -331,10 +331,10 @@ static int aa_audit_base(struct aa_profi
|
||||
}
|
||||
|
||||
if (sa->task)
|
||||
- audit_log_format(ab, " task=\"%d\"", sa->task);
|
||||
+ audit_log_format(ab, " task=%d", sa->task);
|
||||
|
||||
if (sa->parent)
|
||||
- audit_log_format(ab, "parent=\"%d\"", sa->parent);
|
||||
+ audit_log_format(ab, " parent=%d", sa->parent);
|
||||
|
||||
if (sa->name) {
|
||||
audit_log_format(ab, " name=");
|
||||
@@ -347,9 +347,9 @@ static int aa_audit_base(struct aa_profi
|
||||
}
|
||||
|
||||
if (sa->magic_token)
|
||||
- audit_log_format(ab, " magic_token=\"%llu\"", sa->magic_token);
|
||||
+ audit_log_format(ab, " magic_token=%llu", sa->magic_token);
|
||||
|
||||
- audit_log_format(ab, "pid=\"%d\"", current->pid);
|
||||
+ audit_log_format(ab, " pid=%d", current->pid);
|
||||
|
||||
if (profile) {
|
||||
audit_log_format(ab, " profile=");
|
||||
@@ -854,7 +854,7 @@ repeat:
|
||||
goto repeat;
|
||||
if (PTR_ERR(old_profile) == -EPERM) {
|
||||
sa.denied_mask = MAY_EXEC;
|
||||
- sa.info = "Unable to set profile due to ptrace";
|
||||
+ sa.info = "unable to set profile due to ptrace";
|
||||
sa.task = current->parent->pid;
|
||||
aa_audit_reject(profile, &sa);
|
||||
}
|
|
@ -1,64 +0,0 @@
|
|||
---
|
||||
security/apparmor/main.c | 37 +++++++++++++++++++++++--------------
|
||||
1 file changed, 23 insertions(+), 14 deletions(-)
|
||||
|
||||
--- a/security/apparmor/main.c
|
||||
+++ b/security/apparmor/main.c
|
||||
@@ -274,6 +274,22 @@ void free_null_complain_profile(void)
|
||||
null_complain_profile = NULL;
|
||||
}
|
||||
|
||||
+static void aa_audit_file_mask(struct audit_buffer *ab, const char *name,
|
||||
+ int mask)
|
||||
+{
|
||||
+ audit_log_format(ab, " %s=\"%s%s%s%s%s%s%s%s%s\"",
|
||||
+ name,
|
||||
+ mask & AA_EXEC_UNSAFE ? "unsafe " : "",
|
||||
+ mask & AA_EXEC_MMAP ? "m" : "",
|
||||
+ mask & MAY_READ ? "r" : "",
|
||||
+ mask & MAY_WRITE ? "w" : "",
|
||||
+ mask & AA_EXEC_INHERIT ? "i" : "",
|
||||
+ mask & AA_EXEC_UNCONFINED ? "u" : "",
|
||||
+ mask & AA_EXEC_PROFILE ? "p" : "",
|
||||
+ mask & MAY_EXEC ? "x" : "",
|
||||
+ mask & AA_MAY_LINK ? "l" : "");
|
||||
+}
|
||||
+
|
||||
/**
|
||||
* aa_audit - Log an audit event to the audit subsystem
|
||||
* @profile: profile to check against
|
||||
@@ -291,9 +307,8 @@ static int aa_audit_base(struct aa_profi
|
||||
if (!ab) {
|
||||
AA_ERROR("Unable to log event (%d) to audit subsys\n",
|
||||
type);
|
||||
- /* FIXME: do we want to keep old behavior - as below
|
||||
- * don't fail operations in complain mode even if logging
|
||||
- * fails */
|
||||
+ /* don't fail operations in complain mode even if logging
|
||||
+ * fails */
|
||||
return type == AUDIT_APPARMOR_ALLOWED ? 0 : -ENOMEM;
|
||||
}
|
||||
|
||||
@@ -303,17 +318,11 @@ static int aa_audit_base(struct aa_profi
|
||||
if (sa->info)
|
||||
audit_log_format(ab, " info=\"%s\"", sa->info);
|
||||
|
||||
- if (sa->requested_mask | sa->denied_mask) {
|
||||
- int mask = sa->denied_mask ? sa->denied_mask :
|
||||
- sa->requested_mask;
|
||||
-
|
||||
- audit_log_format(ab, " mask=\"%s%s%s%s%s\"",
|
||||
- mask & AA_EXEC_MMAP ? "m" : "",
|
||||
- mask & MAY_READ ? "r" : "",
|
||||
- mask & MAY_WRITE ? "w" : "",
|
||||
- mask & MAY_EXEC ? "x" : "",
|
||||
- mask & AA_MAY_LINK ? "l" : "");
|
||||
- }
|
||||
+ if (sa->requested_mask)
|
||||
+ aa_audit_file_mask(ab, "requested_mask", sa->requested_mask);
|
||||
+
|
||||
+ if (sa->denied_mask)
|
||||
+ aa_audit_file_mask(ab, "denied_mask", sa->denied_mask);
|
||||
|
||||
if (sa->iattr) {
|
||||
struct iattr *iattr = sa->iattr;
|
|
@ -1,71 +0,0 @@
|
|||
---
|
||||
security/apparmor/main.c | 51 +++++++++++++++++++++++++++++++++--------------
|
||||
1 file changed, 36 insertions(+), 15 deletions(-)
|
||||
|
||||
--- a/security/apparmor/main.c
|
||||
+++ b/security/apparmor/main.c
|
||||
@@ -277,17 +277,38 @@ void free_null_complain_profile(void)
|
||||
static void aa_audit_file_mask(struct audit_buffer *ab, const char *name,
|
||||
int mask)
|
||||
{
|
||||
- audit_log_format(ab, " %s=\"%s%s%s%s%s%s%s%s%s\"",
|
||||
- name,
|
||||
- mask & AA_EXEC_UNSAFE ? "unsafe " : "",
|
||||
- mask & AA_EXEC_MMAP ? "m" : "",
|
||||
- mask & MAY_READ ? "r" : "",
|
||||
- mask & MAY_WRITE ? "w" : "",
|
||||
- mask & AA_EXEC_INHERIT ? "i" : "",
|
||||
- mask & AA_EXEC_UNCONFINED ? "u" : "",
|
||||
- mask & AA_EXEC_PROFILE ? "p" : "",
|
||||
- mask & MAY_EXEC ? "x" : "",
|
||||
- mask & AA_MAY_LINK ? "l" : "");
|
||||
+ char mask_str[10], *m = mask_str;
|
||||
+
|
||||
+ if (mask & AA_EXEC_MMAP)
|
||||
+ *m++ = 'm';
|
||||
+ if (mask & MAY_READ)
|
||||
+ *m++ = 'r';
|
||||
+ if (mask & MAY_WRITE)
|
||||
+ *m++ = 'w';
|
||||
+ if (mask & (MAY_EXEC | AA_EXEC_MODIFIERS)) {
|
||||
+ if (mask & AA_EXEC_UNSAFE) {
|
||||
+ if (mask & AA_EXEC_INHERIT)
|
||||
+ *m++ = 'i';
|
||||
+ if (mask & AA_EXEC_UNCONFINED)
|
||||
+ *m++ = 'u';
|
||||
+ if (mask & AA_EXEC_PROFILE)
|
||||
+ *m++ = 'p';
|
||||
+ } else {
|
||||
+ if (mask & AA_EXEC_INHERIT)
|
||||
+ *m++ = 'I';
|
||||
+ if (mask & AA_EXEC_UNCONFINED)
|
||||
+ *m++ = 'U';
|
||||
+ if (mask & AA_EXEC_PROFILE)
|
||||
+ *m++ = 'P';
|
||||
+ }
|
||||
+ if (mask & MAY_EXEC)
|
||||
+ *m++ = 'x';
|
||||
+ }
|
||||
+ if (mask & AA_MAY_LINK)
|
||||
+ *m++ = 'l';
|
||||
+ *m++ = '\0';
|
||||
+
|
||||
+ audit_log_format(ab, " %s=\"%s\"", name, mask_str);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -332,10 +353,10 @@ static int aa_audit_base(struct aa_profi
|
||||
iattr->ia_valid & ATTR_UID ? "uid," : "",
|
||||
iattr->ia_valid & ATTR_GID ? "gid," : "",
|
||||
iattr->ia_valid & ATTR_SIZE ? "size," : "",
|
||||
- ((iattr->ia_valid & ATTR_ATIME_SET) ||
|
||||
- (iattr->ia_valid & ATTR_ATIME)) ? "atime," : "",
|
||||
- ((iattr->ia_valid & ATTR_MTIME_SET) ||
|
||||
- (iattr->ia_valid & ATTR_MTIME)) ? "mtime," : "",
|
||||
+ iattr->ia_valid & (ATTR_ATIME | ATTR_ATIME_SET) ?
|
||||
+ "atime," : "",
|
||||
+ iattr->ia_valid & (ATTR_MTIME | ATTR_MTIME_SET) ?
|
||||
+ "mtime," : "",
|
||||
iattr->ia_valid & ATTR_CTIME ? "ctime," : "");
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load diff
|
@ -1,69 +0,0 @@
|
|||
---
|
||||
security/apparmor/apparmor.h | 2 --
|
||||
security/apparmor/main.c | 17 ++++++++---------
|
||||
2 files changed, 8 insertions(+), 11 deletions(-)
|
||||
|
||||
--- a/security/apparmor/apparmor.h
|
||||
+++ b/security/apparmor/apparmor.h
|
||||
@@ -154,9 +154,7 @@ struct aa_audit {
|
||||
gfp_t gfp_mask;
|
||||
const char *info;
|
||||
const char *name;
|
||||
- char *buffer;
|
||||
const char *name2;
|
||||
- char *buffer2;
|
||||
int requested_mask, denied_mask;
|
||||
struct iattr *iattr;
|
||||
pid_t task, parent;
|
||||
--- a/security/apparmor/main.c
|
||||
+++ b/security/apparmor/main.c
|
||||
@@ -177,9 +177,9 @@ static int aa_perm_dentry(struct aa_prof
|
||||
struct vfsmount *mnt, struct aa_audit *sa, int check)
|
||||
{
|
||||
int error;
|
||||
+ char *buffer = NULL;
|
||||
|
||||
- sa->buffer = NULL;
|
||||
- sa->name = aa_get_name(dentry, mnt, &sa->buffer, check);
|
||||
+ sa->name = aa_get_name(dentry, mnt, &buffer, check);
|
||||
|
||||
if (IS_ERR(sa->name)) {
|
||||
/*
|
||||
@@ -199,7 +199,7 @@ static int aa_perm_dentry(struct aa_prof
|
||||
sa->error_code = 0;
|
||||
|
||||
error = aa_audit(profile, sa);
|
||||
- aa_put_name_buffer(sa->buffer);
|
||||
+ aa_put_name_buffer(buffer);
|
||||
|
||||
return error;
|
||||
}
|
||||
@@ -644,14 +644,13 @@ int aa_link(struct aa_profile *profile,
|
||||
{
|
||||
int error, check = 0;
|
||||
struct aa_audit sa;
|
||||
+ char *buffer = NULL, *buffer2 = NULL;
|
||||
|
||||
memset(&sa, 0, sizeof(sa));
|
||||
sa.operation = "inode_link";
|
||||
sa.gfp_mask = GFP_KERNEL;
|
||||
- sa.buffer = NULL;
|
||||
- sa.name = aa_get_name(link, link_mnt, &sa.buffer, check);
|
||||
- sa.buffer2 = NULL;
|
||||
- sa.name2 = aa_get_name(target, target_mnt, &sa.buffer2, check);
|
||||
+ sa.name = aa_get_name(link, link_mnt, &buffer, check);
|
||||
+ sa.name2 = aa_get_name(target, target_mnt, &buffer2, check);
|
||||
|
||||
if (IS_ERR(sa.name)) {
|
||||
sa.error_code = PTR_ERR(sa.name);
|
||||
@@ -671,8 +670,8 @@ int aa_link(struct aa_profile *profile,
|
||||
|
||||
error = aa_audit(profile, &sa);
|
||||
|
||||
- aa_put_name_buffer(sa.buffer);
|
||||
- aa_put_name_buffer(sa.buffer2);
|
||||
+ aa_put_name_buffer(buffer);
|
||||
+ aa_put_name_buffer(buffer2);
|
||||
|
||||
return error;
|
||||
}
|
|
@ -1,250 +0,0 @@
|
|||
---
|
||||
security/apparmor/apparmor.h | 13 +----
|
||||
security/apparmor/main.c | 108 +++----------------------------------------
|
||||
2 files changed, 14 insertions(+), 107 deletions(-)
|
||||
|
||||
--- a/security/apparmor/apparmor.h
|
||||
+++ b/security/apparmor/apparmor.h
|
||||
@@ -168,13 +168,11 @@ struct aa_audit {
|
||||
};
|
||||
|
||||
/* audit types */
|
||||
-#define AA_MANGLE_NAME 32
|
||||
-#define AA_MANGLE_NAME2 64
|
||||
-#define AA_AUDITTYPE_FILE (1 | AA_MANGLE_NAME)
|
||||
-#define AA_AUDITTYPE_DIR (2 | AA_MANGLE_NAME)
|
||||
-#define AA_AUDITTYPE_ATTR (3 | AA_MANGLE_NAME)
|
||||
-#define AA_AUDITTYPE_XATTR (4 | AA_MANGLE_NAME)
|
||||
-#define AA_AUDITTYPE_LINK (5 | AA_MANGLE_NAME | AA_MANGLE_NAME2)
|
||||
+#define AA_AUDITTYPE_FILE 1
|
||||
+#define AA_AUDITTYPE_DIR 2
|
||||
+#define AA_AUDITTYPE_ATTR 3
|
||||
+#define AA_AUDITTYPE_XATTR 4
|
||||
+#define AA_AUDITTYPE_LINK 5
|
||||
#define AA_AUDITTYPE_CAP 6
|
||||
#define AA_AUDITTYPE_MSG 7
|
||||
#define AA_AUDITTYPE_SYSCALL 8
|
||||
@@ -182,7 +180,6 @@ struct aa_audit {
|
||||
/* Flags for the permission check functions */
|
||||
#define AA_CHECK_FD 1 /* coming from a file descriptor */
|
||||
#define AA_CHECK_DIR 2 /* file type is directory */
|
||||
-#define AA_CHECK_MANGLE 4 /* leave extra room for name mangling */
|
||||
|
||||
/* lock subtypes so lockdep does not raise false dependencies */
|
||||
enum aa_lock_class {
|
||||
--- a/security/apparmor/main.c
|
||||
+++ b/security/apparmor/main.c
|
||||
@@ -90,60 +90,6 @@ static int aa_link_denied(struct aa_prof
|
||||
}
|
||||
|
||||
/**
|
||||
- * mangle -- escape special characters in str
|
||||
- * @str: string to escape
|
||||
- * @buffer: buffer containing str
|
||||
- *
|
||||
- * Escape special characters in @str, which is contained in @buffer. @str must
|
||||
- * be aligned to the end of the buffer, and the space between @buffer and @str
|
||||
- * may be used for escaping.
|
||||
- *
|
||||
- * Returns @str if no escaping was necessary, a pointer to the beginning of the
|
||||
- * escaped string, or NULL if there was not enough space in @buffer. When
|
||||
- * called with a NULL buffer, the return value tells whether any escaping is
|
||||
- * necessary.
|
||||
- */
|
||||
-static const char *mangle(const char *str, char *buffer)
|
||||
-{
|
||||
- static const char c_escape[] = {
|
||||
- ['\a'] = 'a', ['\b'] = 'b',
|
||||
- ['\f'] = 'f', ['\n'] = 'n',
|
||||
- ['\r'] = 'r', ['\t'] = 't',
|
||||
- ['\v'] = 'v',
|
||||
- [' '] = ' ', ['\\'] = '\\',
|
||||
- };
|
||||
- const char *s;
|
||||
- char *t, c;
|
||||
-
|
||||
-#define mangle_escape(c) \
|
||||
- unlikely((unsigned char)(c) < ARRAY_SIZE(c_escape) && \
|
||||
- c_escape[(unsigned char)c])
|
||||
-
|
||||
- for (s = (char *)str; (c = *s) != '\0'; s++)
|
||||
- if (mangle_escape(c))
|
||||
- goto escape;
|
||||
- return str;
|
||||
-
|
||||
-escape:
|
||||
- if (!buffer)
|
||||
- return NULL;
|
||||
- for (s = str, t = buffer; (c = *s) != '\0'; s++) {
|
||||
- if (mangle_escape(c)) {
|
||||
- if (t == s)
|
||||
- return NULL;
|
||||
- *t++ = '\\';
|
||||
- *t++ = c_escape[(unsigned char)c];
|
||||
- } else
|
||||
- *t++ = c;
|
||||
- }
|
||||
- *t++ = '\0';
|
||||
-
|
||||
-#undef mangle_escape
|
||||
-
|
||||
- return buffer;
|
||||
-}
|
||||
-
|
||||
-/**
|
||||
* aa_get_name - compute the pathname of a file
|
||||
* @dentry: dentry of the file
|
||||
* @mnt: vfsmount of the file
|
||||
@@ -170,12 +116,6 @@ static char *aa_get_name(struct dentry *
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
name = d_namespace_path(dentry, mnt, buf, size - is_dir);
|
||||
-
|
||||
- /* Make sure we have enough space for name mangling. */
|
||||
- if (!IS_ERR(name) &&
|
||||
- (check & AA_CHECK_MANGLE) && name - buf <= size / 2)
|
||||
- name = ERR_PTR(-ENAMETOOLONG);
|
||||
-
|
||||
if (!IS_ERR(name)) {
|
||||
if (name[0] != '/') {
|
||||
/*
|
||||
@@ -232,7 +172,6 @@ static int aa_perm_dentry(struct aa_prof
|
||||
{
|
||||
int error;
|
||||
|
||||
-again:
|
||||
sa->buffer = NULL;
|
||||
sa->name = aa_get_name(dentry, mnt, &sa->buffer, check);
|
||||
|
||||
@@ -254,13 +193,7 @@ again:
|
||||
sa->error_code = 0;
|
||||
|
||||
error = aa_audit(profile, sa);
|
||||
-
|
||||
aa_put_name_buffer(sa->buffer);
|
||||
- if (error == -ENAMETOOLONG) {
|
||||
- BUG_ON(check & AA_CHECK_MANGLE);
|
||||
- check |= AA_CHECK_MANGLE;
|
||||
- goto again;
|
||||
- }
|
||||
|
||||
return error;
|
||||
}
|
||||
@@ -443,25 +376,12 @@ int aa_audit(struct aa_profile *profile,
|
||||
goto out;
|
||||
}
|
||||
|
||||
- if (sa->type & AA_MANGLE_NAME) {
|
||||
- sa->name = mangle(sa->name, sa->buffer);
|
||||
- if (!sa->name)
|
||||
- return -ENAMETOOLONG;
|
||||
- }
|
||||
- if (sa->type & AA_MANGLE_NAME2) {
|
||||
- sa->name2 = mangle(sa->name2, sa->buffer2);
|
||||
- if (!sa->name2)
|
||||
- return -ENAMETOOLONG;
|
||||
- }
|
||||
-
|
||||
/* log operation */
|
||||
|
||||
audit_log_format(ab, "%s ", logcls); /* REJECTING/ALLOWING/etc */
|
||||
|
||||
-#define NOFLAGS(x) ((x) & ~(AA_MANGLE_NAME | AA_MANGLE_NAME2))
|
||||
-
|
||||
- switch(NOFLAGS(sa->type)) {
|
||||
- case NOFLAGS(AA_AUDITTYPE_FILE): {
|
||||
+ switch(sa->type) {
|
||||
+ case AA_AUDITTYPE_FILE: {
|
||||
int mask = PROFILE_AUDIT(profile) ?
|
||||
sa->requested_mask : sa->denied_mask;
|
||||
|
||||
@@ -474,10 +394,10 @@ int aa_audit(struct aa_profile *profile,
|
||||
sa->name);
|
||||
break;
|
||||
}
|
||||
- case NOFLAGS(AA_AUDITTYPE_DIR):
|
||||
+ case AA_AUDITTYPE_DIR:
|
||||
audit_log_format(ab, "%s on %s ", sa->name2, sa->name);
|
||||
break;
|
||||
- case NOFLAGS(AA_AUDITTYPE_ATTR): {
|
||||
+ case AA_AUDITTYPE_ATTR: {
|
||||
struct iattr *iattr = sa->iattr;
|
||||
|
||||
audit_log_format(ab,
|
||||
@@ -494,18 +414,18 @@ int aa_audit(struct aa_profile *profile,
|
||||
sa->name);
|
||||
break;
|
||||
}
|
||||
- case NOFLAGS(AA_AUDITTYPE_XATTR):
|
||||
+ case AA_AUDITTYPE_XATTR:
|
||||
audit_log_format(ab, "%s on %s ", sa->name2, sa->name);
|
||||
break;
|
||||
- case NOFLAGS(AA_AUDITTYPE_LINK):
|
||||
+ case AA_AUDITTYPE_LINK:
|
||||
audit_log_format(ab, "link access from %s to %s ", sa->name,
|
||||
sa->name2);
|
||||
break;
|
||||
- case NOFLAGS(AA_AUDITTYPE_CAP):
|
||||
+ case AA_AUDITTYPE_CAP:
|
||||
audit_log_format(ab, "access to capability '%s' ",
|
||||
capability_names[sa->capability]);
|
||||
break;
|
||||
- case NOFLAGS(AA_AUDITTYPE_SYSCALL):
|
||||
+ case AA_AUDITTYPE_SYSCALL:
|
||||
audit_log_format(ab, "access to syscall '%s' ", sa->name);
|
||||
break;
|
||||
default:
|
||||
@@ -720,7 +640,6 @@ int aa_link(struct aa_profile *profile,
|
||||
int error, check = 0;
|
||||
struct aa_audit sa;
|
||||
|
||||
-again:
|
||||
sa.buffer = NULL;
|
||||
sa.name = aa_get_name(link, link_mnt, &sa.buffer, check);
|
||||
sa.buffer2 = NULL;
|
||||
@@ -752,11 +671,6 @@ again:
|
||||
|
||||
aa_put_name_buffer(sa.buffer);
|
||||
aa_put_name_buffer(sa.buffer2);
|
||||
- if (error == -ENAMETOOLONG) {
|
||||
- BUG_ON(check & AA_CHECK_MANGLE);
|
||||
- check |= AA_CHECK_MANGLE;
|
||||
- goto again;
|
||||
- }
|
||||
|
||||
return error;
|
||||
}
|
||||
@@ -829,7 +743,6 @@ aa_register_find(struct aa_profile *prof
|
||||
AA_DEBUG("%s: setting profile %s\n",
|
||||
__FUNCTION__, new_profile->name);
|
||||
} else if (mandatory && profile) {
|
||||
- name = mangle(name, buffer);
|
||||
if (complain) {
|
||||
aa_audit_message(profile, GFP_KERNEL, "LOGPROF-HINT "
|
||||
"missing_mandatory_profile image '%s' "
|
||||
@@ -874,8 +787,7 @@ int aa_register(struct linux_binprm *bpr
|
||||
|
||||
AA_DEBUG("%s\n", __FUNCTION__);
|
||||
|
||||
- filename = aa_get_name(filp->f_dentry, filp->f_vfsmnt, &buffer,
|
||||
- AA_CHECK_MANGLE);
|
||||
+ filename = aa_get_name(filp->f_dentry, filp->f_vfsmnt, &buffer, 0);
|
||||
if (IS_ERR(filename)) {
|
||||
AA_ERROR("%s: Failed to get filename", __FUNCTION__);
|
||||
return -ENOENT;
|
||||
@@ -928,7 +840,6 @@ repeat:
|
||||
new_profile = aa_dup_profile(null_complain_profile);
|
||||
exec_mode |= AA_EXEC_UNSAFE;
|
||||
} else {
|
||||
- filename = mangle(filename, buffer);
|
||||
aa_audit_message(profile, GFP_KERNEL, "REJECTING "
|
||||
"exec(2) of image '%s'. Unable to "
|
||||
"determine exec qualifier. "
|
||||
@@ -954,7 +865,6 @@ repeat:
|
||||
if (PTR_ERR(old_profile) == -ESTALE)
|
||||
goto repeat;
|
||||
if (PTR_ERR(old_profile) == -EPERM) {
|
||||
- filename = mangle(filename, buffer);
|
||||
aa_audit_message(profile, GFP_KERNEL,
|
||||
"REJECTING exec(2) of image '%s'. "
|
||||
"Unable to change profile, ptraced by "
|
|
@ -1,15 +0,0 @@
|
|||
---
|
||||
security/apparmor/main.c | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
--- a/security/apparmor/main.c
|
||||
+++ b/security/apparmor/main.c
|
||||
@@ -735,7 +735,7 @@ again:
|
||||
if (IS_ERR(sa.name2)) {
|
||||
sa.requested_mask = 0;
|
||||
sa.denied_mask = 0;
|
||||
- sa.error_code = PTR_ERR(sa.name);
|
||||
+ sa.error_code = PTR_ERR(sa.name2);
|
||||
sa.name2 = NULL;
|
||||
}
|
||||
|
|
@ -1,45 +0,0 @@
|
|||
link perms were failing when 1 of the permissions was empty
|
||||
|
||||
---
|
||||
security/apparmor/main.c | 20 +++++++-------------
|
||||
1 file changed, 7 insertions(+), 13 deletions(-)
|
||||
|
||||
--- a/security/apparmor/main.c
|
||||
+++ b/security/apparmor/main.c
|
||||
@@ -68,29 +68,23 @@ static int aa_link_denied(struct aa_prof
|
||||
|
||||
l_mode = aa_match(profile->file_rules, link);
|
||||
t_mode = aa_match(profile->file_rules, target);
|
||||
- *request_mask = l_mode;
|
||||
+ *request_mask = l_mode | AA_MAY_LINK;
|
||||
|
||||
/* Link always requires 'l' on the link, a subset of the
|
||||
* target's 'r', 'w', 'x', 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').
|
||||
*/
|
||||
+#define RWXM (MAY_READ | MAY_WRITE | MAY_EXEC | AA_EXEC_MMAP)
|
||||
denied_mask = ~l_mode & AA_MAY_LINK;
|
||||
- denied_mask |= l_mode & ~t_mode;
|
||||
+ if (l_mode & RWXM)
|
||||
+ denied_mask |= (l_mode & ~ AA_MAY_LINK) & ~t_mode;
|
||||
+ else
|
||||
+ denied_mask |= t_mode | AA_MAY_LINK;
|
||||
if (denied_mask & AA_EXEC_MODIFIERS)
|
||||
denied_mask |= MAY_EXEC;
|
||||
|
||||
- /* FIXME: denied mask has no way of reporting that the secure
|
||||
- * execmode required is safe exec. This means that if link
|
||||
- * has safe exec and target unsafe exec, the difference is not
|
||||
- * reported, back, this isn't a significant problem since
|
||||
- * safe exec is a subset of unsafe exec, but it violates the
|
||||
- * exec should be exactly equal rule.
|
||||
- *
|
||||
- * The reverse situation does not cause a problem, if link
|
||||
- * requires an unsafe exec and target a safe exec we report
|
||||
- * the missing unsafe exec bit.
|
||||
- */
|
||||
+#undef RWXM
|
||||
|
||||
return denied_mask;
|
||||
}
|
|
@ -1,133 +0,0 @@
|
|||
---
|
||||
security/apparmor/apparmor.h | 7 +------
|
||||
security/apparmor/main.c | 20 +-------------------
|
||||
2 files changed, 2 insertions(+), 25 deletions(-)
|
||||
|
||||
--- a/security/apparmor/apparmor.h
|
||||
+++ b/security/apparmor/apparmor.h
|
||||
@@ -145,7 +145,7 @@ extern struct aa_profile *null_complain_
|
||||
*/
|
||||
|
||||
struct aa_audit {
|
||||
- unsigned short type, flags;
|
||||
+ unsigned short type;
|
||||
unsigned int result;
|
||||
gfp_t gfp_mask;
|
||||
int error_code;
|
||||
@@ -175,11 +175,6 @@ struct aa_audit {
|
||||
#define AA_AUDITTYPE_MSG 7
|
||||
#define AA_AUDITTYPE_SYSCALL 8
|
||||
|
||||
-/* audit flags */
|
||||
-#define AA_AUDITFLAG_AUDITSS_SYSCALL 1 /* log syscall context */
|
||||
-#define AA_AUDITFLAG_LOGERR 2 /* log operations that failed due to
|
||||
- non permission errors */
|
||||
-
|
||||
/* Flags for the permission check functions */
|
||||
#define AA_CHECK_FD 1 /* coming from a file descriptor */
|
||||
#define AA_CHECK_DIR 2 /* file type is directory */
|
||||
--- a/security/apparmor/main.c
|
||||
+++ b/security/apparmor/main.c
|
||||
@@ -367,7 +367,6 @@ int aa_audit_message(struct aa_profile *
|
||||
sa.type = AA_AUDITTYPE_MSG;
|
||||
sa.name = fmt;
|
||||
va_start(sa.vaval, fmt);
|
||||
- sa.flags = 0;
|
||||
sa.gfp_mask = gfp;
|
||||
sa.error_code = 0;
|
||||
sa.result = 0; /* fake failure: force message to be logged */
|
||||
@@ -392,7 +391,6 @@ int aa_audit_syscallreject(struct aa_pro
|
||||
|
||||
sa.type = AA_AUDITTYPE_SYSCALL;
|
||||
sa.name = msg;
|
||||
- sa.flags = 0;
|
||||
sa.gfp_mask = gfp;
|
||||
sa.error_code = 0;
|
||||
sa.result = 0; /* failure */
|
||||
@@ -411,7 +409,6 @@ int aa_audit(struct aa_profile *profile,
|
||||
struct audit_context *audit_cxt;
|
||||
|
||||
const char *logcls;
|
||||
- unsigned int flags;
|
||||
int audit = 0,
|
||||
complain = 0,
|
||||
error = -EINVAL,
|
||||
@@ -453,21 +450,13 @@ int aa_audit(struct aa_profile *profile,
|
||||
logcls = complain ? "PERMITTING" : "REJECTING";
|
||||
}
|
||||
|
||||
- /* In future extend w/ per-profile flags
|
||||
- * (flags |= sa->profile->flags)
|
||||
- */
|
||||
- flags = sa->flags;
|
||||
- if (apparmor_logsyscall)
|
||||
- flags |= AA_AUDITFLAG_AUDITSS_SYSCALL;
|
||||
-
|
||||
-
|
||||
/* Force full audit syscall logging regardless of global setting if
|
||||
* we are rejecting a syscall
|
||||
*/
|
||||
if (sa->type == AA_AUDITTYPE_SYSCALL) {
|
||||
audit_cxt = current->audit_context;
|
||||
} else {
|
||||
- audit_cxt = (flags & AA_AUDITFLAG_AUDITSS_SYSCALL) ?
|
||||
+ audit_cxt = apparmor_logsyscall ?
|
||||
current->audit_context : NULL;
|
||||
}
|
||||
|
||||
@@ -593,7 +582,6 @@ int aa_attr(struct aa_profile *profile,
|
||||
|
||||
sa.type = AA_AUDITTYPE_ATTR;
|
||||
sa.iattr = iattr;
|
||||
- sa.flags = 0;
|
||||
sa.gfp_mask = GFP_KERNEL;
|
||||
|
||||
check = 0;
|
||||
@@ -626,7 +614,6 @@ int aa_perm_xattr(struct aa_profile *pro
|
||||
|
||||
sa.type = AA_AUDITTYPE_XATTR;
|
||||
sa.name2 = operation;
|
||||
- sa.flags = 0;
|
||||
sa.gfp_mask = GFP_KERNEL;
|
||||
|
||||
if (inode && S_ISDIR(inode->i_mode))
|
||||
@@ -659,7 +646,6 @@ int aa_perm(struct aa_profile *profile,
|
||||
|
||||
sa.type = AA_AUDITTYPE_FILE;
|
||||
sa.mask = mask;
|
||||
- sa.flags = 0;
|
||||
sa.gfp_mask = GFP_KERNEL;
|
||||
error = aa_perm_dentry(profile, dentry, mnt, &sa, mask, check);
|
||||
|
||||
@@ -686,7 +672,6 @@ int aa_perm_dir(struct aa_profile *profi
|
||||
|
||||
sa.type = AA_AUDITTYPE_DIR;
|
||||
sa.name2 = operation;
|
||||
- sa.flags = 0;
|
||||
sa.gfp_mask = GFP_KERNEL;
|
||||
|
||||
return aa_perm_dentry(profile, dentry, mnt, &sa, mask, AA_CHECK_DIR);
|
||||
@@ -699,7 +684,6 @@ int aa_perm_path(struct aa_profile *prof
|
||||
|
||||
sa.type = AA_AUDITTYPE_FILE;
|
||||
sa.mask = mask;
|
||||
- sa.flags = 0;
|
||||
sa.gfp_mask = GFP_KERNEL;
|
||||
sa.name = name;
|
||||
|
||||
@@ -739,7 +723,6 @@ int aa_capability(struct aa_task_context
|
||||
sa.type = AA_AUDITTYPE_CAP;
|
||||
sa.name = NULL;
|
||||
sa.capability = cap;
|
||||
- sa.flags = 0;
|
||||
sa.error_code = 0;
|
||||
sa.result = !error;
|
||||
sa.gfp_mask = GFP_ATOMIC;
|
||||
@@ -795,7 +778,6 @@ again:
|
||||
aa_permerror2result(denied_mask, &sa);
|
||||
|
||||
sa.type = AA_AUDITTYPE_LINK;
|
||||
- sa.flags = 0;
|
||||
sa.gfp_mask = GFP_KERNEL;
|
||||
|
||||
error = aa_audit(profile, &sa);
|
|
@ -1,268 +0,0 @@
|
|||
---
|
||||
security/apparmor/apparmor.h | 2
|
||||
security/apparmor/module_interface.c | 177 +++++++++++++++++++++++------------
|
||||
2 files changed, 122 insertions(+), 57 deletions(-)
|
||||
|
||||
--- a/security/apparmor/apparmor.h
|
||||
+++ b/security/apparmor/apparmor.h
|
||||
@@ -83,6 +83,7 @@ extern unsigned int apparmor_path_max;
|
||||
* @flags: flags controlling profile behavior
|
||||
* @null_profile: if needed per profile learning and null confinement profile
|
||||
* @isstale: flag indicating if profile is stale
|
||||
+ * @replacement: profile replacing this profile
|
||||
* @capabilities: capabilities granted by the process
|
||||
* @count: reference count of the profile
|
||||
*
|
||||
@@ -109,6 +110,7 @@ struct aa_profile {
|
||||
} flags;
|
||||
struct aa_profile *null_profile;
|
||||
int isstale;
|
||||
+ struct aa_profile *replacement;
|
||||
|
||||
kernel_cap_t capabilities;
|
||||
struct kref count;
|
||||
--- a/security/apparmor/module_interface.c
|
||||
+++ b/security/apparmor/module_interface.c
|
||||
@@ -346,14 +346,15 @@ static struct aa_profile *aa_unpack_prof
|
||||
/**
|
||||
* aa_verify_head - unpack serialized stream header
|
||||
* @e: serialized data read head
|
||||
+ * @log: log a message on failure
|
||||
* @operation: operation header is being verified for
|
||||
*
|
||||
* returns error or 0 if header is good
|
||||
*/
|
||||
-static int aa_verify_header(struct aa_ext *e, const char *operation)
|
||||
+static int aa_verify_header(struct aa_ext *e, int log, const char *operation)
|
||||
{
|
||||
/* get the interface version */
|
||||
- if (!aa_is_u32(e, &e->version, "version")) {
|
||||
+ if (!aa_is_u32(e, &e->version, "version") && log) {
|
||||
struct aa_audit sa;
|
||||
memset(&sa, 0, sizeof(sa));
|
||||
sa.operation = operation;
|
||||
@@ -364,7 +365,7 @@ static int aa_verify_header(struct aa_ex
|
||||
}
|
||||
|
||||
/* check that the interface version is currently supported */
|
||||
- if (e->version != 3) {
|
||||
+ if (e->version != 3 && log) {
|
||||
struct aa_audit sa;
|
||||
memset(&sa, 0, sizeof(sa));
|
||||
sa.operation = operation;
|
||||
@@ -376,37 +377,82 @@ static int aa_verify_header(struct aa_ex
|
||||
return 0;
|
||||
}
|
||||
|
||||
+static void free_list_of_profiles(struct list_head *lh)
|
||||
+{
|
||||
+ struct aa_profile *profile, *tmp;
|
||||
+ list_for_each_entry_safe(profile, tmp, lh, list) {
|
||||
+ list_del_init(&profile->list);
|
||||
+ aa_put_profile(profile);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * aa_unpack_profile_set - unpack a set of profiles an place on @lh
|
||||
+ * @e: serialized data read head
|
||||
+ * @lh: list to place profiles on (preinit)
|
||||
+ * @op: which interface operation is unpacking the profile set
|
||||
+ *
|
||||
+ * returns error or 0 if all profiles are good
|
||||
+ */
|
||||
+static int aa_unpack_profile_set(struct aa_ext *e, struct list_head *lh,
|
||||
+ const char *op)
|
||||
+{
|
||||
+ struct aa_profile *profile;
|
||||
+ size_t error = aa_verify_header(e, 1, op);
|
||||
+ if (error)
|
||||
+ return error;
|
||||
+
|
||||
+ while (aa_inbounds(e, 1)) {
|
||||
+ profile = aa_unpack_profile_wrapper(e, op);
|
||||
+ if (IS_ERR(profile)) {
|
||||
+ free_list_of_profiles(lh);
|
||||
+ return PTR_ERR(profile);
|
||||
+ }
|
||||
+ list_add(&profile->list, lh);
|
||||
+ aa_verify_header(e, 0, op); /* skip header if present */
|
||||
+ }
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
/**
|
||||
- * aa_add_profile - Unpack and add a new profile to the profile list
|
||||
+ * aa_add_profile - Unpack and add a new profile(s) to the profile list
|
||||
* @data: serialized data stream
|
||||
* @size: size of the serialized data stream
|
||||
+ *
|
||||
+ * The profile load is an all or nothing operation. If any of the
|
||||
+ * profiles being loaded already exist the load will fail and
|
||||
+ * non of the profiles will be loaded.
|
||||
*/
|
||||
ssize_t aa_add_profile(void *data, size_t size)
|
||||
{
|
||||
- struct aa_profile *profile = NULL;
|
||||
+ LIST_HEAD(add_set);
|
||||
+ struct aa_profile *profile, *tmp = NULL;
|
||||
struct aa_ext e = {
|
||||
.start = data,
|
||||
.end = data + size,
|
||||
.pos = data
|
||||
};
|
||||
- ssize_t error = aa_verify_header(&e, "profile_load");
|
||||
+ ssize_t error = aa_unpack_profile_set(&e, &add_set, "profile_load");
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
- profile = aa_unpack_profile_wrapper(&e, "profile_load");
|
||||
- if (IS_ERR(profile))
|
||||
- return PTR_ERR(profile);
|
||||
-
|
||||
mutex_lock(&aa_interface_lock);
|
||||
write_lock(&profile_list_lock);
|
||||
- if (__aa_find_profile(profile->name, &profile_list)) {
|
||||
- /* A profile with this name exists already. */
|
||||
- write_unlock(&profile_list_lock);
|
||||
- mutex_unlock(&aa_interface_lock);
|
||||
- aa_put_profile(profile);
|
||||
- return -EEXIST;
|
||||
+ list_for_each_entry(profile, &add_set, list) {
|
||||
+ if (__aa_find_profile(profile->name, &profile_list)) {
|
||||
+ /* A profile with this name exists already.
|
||||
+ * It would be nice if we could return which profile
|
||||
+ * already exists
|
||||
+ */
|
||||
+ write_unlock(&profile_list_lock);
|
||||
+ mutex_unlock(&aa_interface_lock);
|
||||
+ free_list_of_profiles(&add_set);
|
||||
+ return -EEXIST;
|
||||
+ }
|
||||
}
|
||||
- list_add(&profile->list, &profile_list);
|
||||
+ list_for_each_entry_safe(profile, tmp, &add_set, list) {
|
||||
+ list_move(&profile->list, &profile_list);
|
||||
+ }
|
||||
write_unlock(&profile_list_lock);
|
||||
mutex_unlock(&aa_interface_lock);
|
||||
|
||||
@@ -451,47 +497,10 @@ static inline void task_replace(struct t
|
||||
cxt->hat_magic);
|
||||
}
|
||||
|
||||
-/**
|
||||
- * aa_replace_profile - replace a profile on the profile list
|
||||
- * @udata: serialized data stream
|
||||
- * @size: size of the serialized data stream
|
||||
- *
|
||||
- * unpack and replace a profile on the profile list and uses of that profile
|
||||
- * by any aa_task_context. If the profile does not exist on the profile list
|
||||
- * it is added. Return %0 or error.
|
||||
- */
|
||||
-ssize_t aa_replace_profile(void *udata, size_t size)
|
||||
+static void replace_profiles_context_list(struct aa_profile *old_profile,
|
||||
+ struct aa_profile *new_profile)
|
||||
{
|
||||
- struct aa_profile *old_profile, *new_profile;
|
||||
struct aa_task_context *new_cxt;
|
||||
- struct aa_ext e = {
|
||||
- .start = udata,
|
||||
- .end = udata + size,
|
||||
- .pos = udata
|
||||
- };
|
||||
- ssize_t error = aa_verify_header(&e, "profile_replace");
|
||||
- if (error)
|
||||
- return error;
|
||||
-
|
||||
- new_profile = aa_unpack_profile_wrapper(&e, "profile_replace");
|
||||
- if (IS_ERR(new_profile))
|
||||
- return PTR_ERR(new_profile);
|
||||
-
|
||||
- mutex_lock(&aa_interface_lock);
|
||||
- write_lock(&profile_list_lock);
|
||||
- old_profile = __aa_find_profile(new_profile->name, &profile_list);
|
||||
- if (old_profile) {
|
||||
- lock_profile(old_profile);
|
||||
- old_profile->isstale = 1;
|
||||
- unlock_profile(old_profile);
|
||||
- list_del_init(&old_profile->list);
|
||||
- }
|
||||
- list_add(&new_profile->list, &profile_list);
|
||||
- write_unlock(&profile_list_lock);
|
||||
-
|
||||
- if (!old_profile)
|
||||
- goto out;
|
||||
-
|
||||
/*
|
||||
* Replacement needs to allocate a new aa_task_context for each
|
||||
* task confined by old_profile. To do this the profile locks
|
||||
@@ -517,13 +526,67 @@ ssize_t aa_replace_profile(void *udata,
|
||||
unlock_both_profiles(old_profile, new_profile);
|
||||
} while (!new_cxt);
|
||||
aa_free_task_context(new_cxt);
|
||||
- aa_put_profile(old_profile);
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * aa_replace_profile - replace a profile(s) on the profile list
|
||||
+ * @udata: serialized data stream
|
||||
+ * @size: size of the serialized data stream
|
||||
+ *
|
||||
+ * unpack and replace a profile(s) on the profile list and uses of that profile
|
||||
+ * by any aa_task_context. If the profile(s) does not exist on the profile
|
||||
+ * list it is added. Return %0 or error.
|
||||
+ */
|
||||
+ssize_t aa_replace_profile(void *udata, size_t size)
|
||||
+{
|
||||
+ LIST_HEAD(new_set);
|
||||
+ LIST_HEAD(old_set);
|
||||
+ struct aa_profile *old_profile, *new_profile, *tmp;
|
||||
+ struct aa_ext e = {
|
||||
+ .start = udata,
|
||||
+ .end = udata + size,
|
||||
+ .pos = udata
|
||||
+ };
|
||||
+ ssize_t error = aa_unpack_profile_set(&e, &new_set, "profile_replace");
|
||||
+ if (error)
|
||||
+ return error;
|
||||
+
|
||||
+ mutex_lock(&aa_interface_lock);
|
||||
+ write_lock(&profile_list_lock);
|
||||
+ list_for_each_entry_safe(new_profile, tmp, &new_set, list) {
|
||||
+ old_profile = __aa_find_profile(new_profile->name,
|
||||
+ &profile_list);
|
||||
+ if (old_profile) {
|
||||
+ lock_profile(old_profile);
|
||||
+ old_profile->isstale = 1;
|
||||
+ old_profile->replacement = aa_dup_profile(new_profile);
|
||||
+ unlock_profile(old_profile);
|
||||
+ list_move(&old_profile->list, &old_set);
|
||||
+ }
|
||||
+ list_move(&new_profile->list, &profile_list);
|
||||
+ }
|
||||
+ write_unlock(&profile_list_lock);
|
||||
+
|
||||
+ if (list_empty(&old_set))
|
||||
+ goto out;
|
||||
+
|
||||
+ while(!list_empty(&old_set)) {
|
||||
+ old_profile = list_entry(old_set.next, struct aa_profile,
|
||||
+ list);
|
||||
+ list_del_init(&old_profile->list);
|
||||
+ replace_profiles_context_list(old_profile,
|
||||
+ old_profile->replacement);
|
||||
+ aa_put_profile(old_profile->replacement);
|
||||
+ old_profile->replacement = NULL;
|
||||
+ aa_put_profile(old_profile);
|
||||
+ }
|
||||
|
||||
out:
|
||||
mutex_unlock(&aa_interface_lock);
|
||||
return size;
|
||||
}
|
||||
|
||||
+
|
||||
/**
|
||||
* aa_remove_profile - remove a profile from the system
|
||||
* @name: name of the profile to remove
|
|
@ -1,21 +0,0 @@
|
|||
---
|
||||
security/apparmor/main.c | 8 --------
|
||||
1 file changed, 8 deletions(-)
|
||||
|
||||
--- a/security/apparmor/main.c
|
||||
+++ b/security/apparmor/main.c
|
||||
@@ -432,14 +432,6 @@ int aa_audit(struct aa_profile *profile,
|
||||
audit = 1;
|
||||
logcls = "AUDITING";
|
||||
}
|
||||
- } else if (sa->error_code < 0) {
|
||||
- audit_log(current->audit_context, gfp_mask, AUDIT_APPARMOR,
|
||||
- "Internal error auditing event type %d (error %d)",
|
||||
- sa->type, sa->error_code);
|
||||
- AA_ERROR("Internal error auditing event type %d (error %d)\n",
|
||||
- sa->type, sa->error_code);
|
||||
- error = sa->error_code;
|
||||
- goto out;
|
||||
} else if (sa->type == AA_AUDITTYPE_SYSCALL) {
|
||||
/* Currently AA_AUDITTYPE_SYSCALL is for rejects only.
|
||||
* Values set by aa_audit_syscallreject will get us here.
|
|
@ -1,75 +0,0 @@
|
|||
---
|
||||
security/apparmor/apparmor.h | 5 +++++
|
||||
security/apparmor/main.c | 12 ------------
|
||||
security/apparmor/match.c | 16 ++++++++++++++++
|
||||
3 files changed, 21 insertions(+), 12 deletions(-)
|
||||
|
||||
--- a/security/apparmor/apparmor.h
|
||||
+++ b/security/apparmor/apparmor.h
|
||||
@@ -32,8 +32,13 @@
|
||||
AA_EXEC_UNCONFINED | \
|
||||
AA_EXEC_PROFILE)
|
||||
|
||||
+#define AA_VALID_PERM_MASK (MAY_READ | MAY_WRITE | MAY_EXEC | \
|
||||
+ AA_MAY_LINK | AA_EXEC_MODIFIERS | \
|
||||
+ AA_EXEC_MMAP | AA_EXEC_UNSAFE)
|
||||
+
|
||||
#define AA_SECURE_EXEC_NEEDED 1
|
||||
|
||||
+
|
||||
/* Control parameters (0 or 1), settable thru module/boot flags or
|
||||
* via /sys/kernel/security/apparmor/control */
|
||||
extern int apparmor_complain;
|
||||
--- a/security/apparmor/main.c
|
||||
+++ b/security/apparmor/main.c
|
||||
@@ -918,18 +918,6 @@ repeat:
|
||||
buffer, 1,
|
||||
complain);
|
||||
break;
|
||||
-
|
||||
- default:
|
||||
- AA_ERROR("Rejecting exec(2) of image '%s'. "
|
||||
- "Unknown exec qualifier %x "
|
||||
- "(%d profile %s active %s)\n",
|
||||
- filename,
|
||||
- exec_mode & AA_EXEC_MODIFIERS,
|
||||
- current->pid,
|
||||
- profile->parent->name,
|
||||
- profile->name);
|
||||
- new_profile = ERR_PTR(-EPERM);
|
||||
- break;
|
||||
}
|
||||
|
||||
} else if (complain) {
|
||||
--- a/security/apparmor/match.c
|
||||
+++ b/security/apparmor/match.c
|
||||
@@ -12,6 +12,7 @@
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/errno.h>
|
||||
+#include "apparmor.h"
|
||||
#include "match.h"
|
||||
|
||||
static struct table_header *unpack_table(void *blob, size_t bsize)
|
||||
@@ -170,6 +171,21 @@ int verify_dfa(struct aa_dfa *dfa)
|
||||
goto out;
|
||||
}
|
||||
|
||||
+ /* verify accept permissions */
|
||||
+ for (i = 0; i < state_count; i++) {
|
||||
+ int mode = ACCEPT_TABLE(dfa)[i];
|
||||
+
|
||||
+ if (mode & ~AA_VALID_PERM_MASK)
|
||||
+ goto out;
|
||||
+
|
||||
+ /* if MAY_EXEC, exactly one exec modifier must be set */
|
||||
+ if (mode & MAY_EXEC) {
|
||||
+ mode &= AA_EXEC_MODIFIERS;
|
||||
+ if (mode & (mode - 1))
|
||||
+ goto out;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
error = 0;
|
||||
out:
|
||||
return error;
|
|
@ -45,22 +45,6 @@ apparmor_ptrace-cleanup.diff
|
|||
apparmor-module_interface.diff
|
||||
apparmor-misc.diff
|
||||
apparmor-intree.diff
|
||||
apparmor-lockdep.diff
|
||||
get-rid-of-sa-flags.diff
|
||||
no-internal-audit-errors.diff
|
||||
simplify-audit-status.diff
|
||||
no-unknown-exec-mod.diff
|
||||
audit-defines.diff
|
||||
fix-link-name2.diff
|
||||
audit-remove-mangle.diff
|
||||
audit-pairs.diff
|
||||
audit-pairs-2.diff
|
||||
audit-pairs-3.diff
|
||||
audit-pairs-4.diff
|
||||
audit-link-perms.diff
|
||||
audit-remove-buffer.diff
|
||||
#multi-profile-load.diff
|
||||
fix_link_perm.diff
|
||||
change_profile.diff
|
||||
change_profile-2.diff
|
||||
flatten-hats.diff
|
||||
|
|
|
@ -1,367 +0,0 @@
|
|||
---
|
||||
security/apparmor/apparmor.h | 7 --
|
||||
security/apparmor/main.c | 140 +++++++++++++++++--------------------------
|
||||
2 files changed, 61 insertions(+), 86 deletions(-)
|
||||
|
||||
--- a/security/apparmor/apparmor.h
|
||||
+++ b/security/apparmor/apparmor.h
|
||||
@@ -146,13 +146,12 @@ extern struct aa_profile *null_complain_
|
||||
|
||||
struct aa_audit {
|
||||
unsigned short type;
|
||||
- unsigned int result;
|
||||
gfp_t gfp_mask;
|
||||
- int error_code;
|
||||
const char *name;
|
||||
char *buffer;
|
||||
+ int requested_mask, denied_mask;
|
||||
+ int error_code;
|
||||
union {
|
||||
- int mask;
|
||||
int capability;
|
||||
struct {
|
||||
const char *name2;
|
||||
@@ -191,7 +190,7 @@ enum aa_lock_class {
|
||||
extern int alloc_null_complain_profile(void);
|
||||
extern void free_null_complain_profile(void);
|
||||
extern int attach_nullprofile(struct aa_profile *profile);
|
||||
-extern int aa_audit_message(struct aa_profile *profile, gfp_t gfp,
|
||||
+extern void aa_audit_message(struct aa_profile *profile, gfp_t gfp,
|
||||
const char *, ...)
|
||||
__attribute__ ((format (printf, 3, 4)));
|
||||
extern int aa_audit_syscallreject(struct aa_profile *profile, gfp_t gfp,
|
||||
--- a/security/apparmor/main.c
|
||||
+++ b/security/apparmor/main.c
|
||||
@@ -38,17 +38,6 @@ static const char *capability_names[] =
|
||||
*/
|
||||
struct aa_profile *null_complain_profile;
|
||||
|
||||
-static inline void aa_permerror2result(int perm_result, struct aa_audit *sa)
|
||||
-{
|
||||
- if (perm_result == 0) { /* success */
|
||||
- sa->result = 1;
|
||||
- sa->error_code = 0;
|
||||
- } else { /* -ve internal error code or +ve mask of denied perms */
|
||||
- sa->result = 0;
|
||||
- sa->error_code = perm_result;
|
||||
- }
|
||||
-}
|
||||
-
|
||||
/**
|
||||
* aa_file_denied - check for @mask access on a file
|
||||
* @profile: profile to check against
|
||||
@@ -239,10 +228,9 @@ static inline void aa_put_name_buffer(ch
|
||||
* an open file descriptor (AA_CHECK_FD) or not.
|
||||
*/
|
||||
static int aa_perm_dentry(struct aa_profile *profile, struct dentry *dentry,
|
||||
- struct vfsmount *mnt, struct aa_audit *sa, int mask,
|
||||
- int check)
|
||||
+ struct vfsmount *mnt, struct aa_audit *sa, int check)
|
||||
{
|
||||
- int denied_mask, error;
|
||||
+ int error;
|
||||
|
||||
again:
|
||||
sa->buffer = NULL;
|
||||
@@ -254,14 +242,16 @@ again:
|
||||
* accessed through a file descriptor.
|
||||
*/
|
||||
if (PTR_ERR(sa->name) == -ENOENT && (check & AA_CHECK_FD))
|
||||
- denied_mask = 0;
|
||||
+ sa->denied_mask = 0;
|
||||
else
|
||||
- denied_mask = PTR_ERR(sa->name);
|
||||
+ sa->denied_mask = PTR_ERR(sa->name);
|
||||
sa->name = NULL;
|
||||
} else
|
||||
- denied_mask = aa_file_denied(profile, sa->name, mask);
|
||||
+ sa->denied_mask = aa_file_denied(profile, sa->name,
|
||||
+ sa->requested_mask);
|
||||
|
||||
- aa_permerror2result(denied_mask, sa);
|
||||
+ if (!sa->denied_mask)
|
||||
+ sa->error_code = 0;
|
||||
|
||||
error = aa_audit(profile, sa);
|
||||
|
||||
@@ -358,24 +348,22 @@ void free_null_complain_profile(void)
|
||||
* @flags: audit flags
|
||||
* @fmt: varargs fmt
|
||||
*/
|
||||
-int aa_audit_message(struct aa_profile *profile, gfp_t gfp, const char *fmt,
|
||||
+void aa_audit_message(struct aa_profile *profile, gfp_t gfp, const char *fmt,
|
||||
...)
|
||||
{
|
||||
- int ret;
|
||||
struct aa_audit sa;
|
||||
|
||||
sa.type = AA_AUDITTYPE_MSG;
|
||||
sa.name = fmt;
|
||||
va_start(sa.vaval, fmt);
|
||||
sa.gfp_mask = gfp;
|
||||
- sa.error_code = 0;
|
||||
- sa.result = 0; /* fake failure: force message to be logged */
|
||||
+ sa.requested_mask = 0;
|
||||
+ sa.denied_mask = 0;
|
||||
+ sa.error_code = -EAGAIN; /* never reaches user space */
|
||||
|
||||
- ret = aa_audit(profile, &sa);
|
||||
+ (void)aa_audit(profile, &sa);
|
||||
|
||||
va_end(sa.vaval);
|
||||
-
|
||||
- return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -392,8 +380,9 @@ int aa_audit_syscallreject(struct aa_pro
|
||||
sa.type = AA_AUDITTYPE_SYSCALL;
|
||||
sa.name = msg;
|
||||
sa.gfp_mask = gfp;
|
||||
- sa.error_code = 0;
|
||||
- sa.result = 0; /* failure */
|
||||
+ sa.requested_mask = 0;
|
||||
+ sa.denied_mask = 0;
|
||||
+ sa.error_code = -EPERM;
|
||||
|
||||
return aa_audit(profile, &sa);
|
||||
}
|
||||
@@ -409,29 +398,16 @@ int aa_audit(struct aa_profile *profile,
|
||||
struct audit_context *audit_cxt;
|
||||
|
||||
const char *logcls;
|
||||
- int audit = 0,
|
||||
- complain = 0,
|
||||
- error = -EINVAL,
|
||||
- opspec_error = -EACCES;
|
||||
+ int complain = 0;
|
||||
|
||||
const gfp_t gfp_mask = sa->gfp_mask;
|
||||
|
||||
- /*
|
||||
- * sa->result: 1 success, 0 failure
|
||||
- * sa->error_code: success: 0
|
||||
- * failure: +ve mask of failed permissions or -ve
|
||||
- * system error
|
||||
- */
|
||||
-
|
||||
- if (likely(sa->result)) {
|
||||
+ if (likely(!sa->error_code)) {
|
||||
if (likely(!PROFILE_AUDIT(profile))) {
|
||||
/* nothing to log */
|
||||
- error = 0;
|
||||
- goto out;
|
||||
- } else {
|
||||
- audit = 1;
|
||||
+ return 0;
|
||||
+ } else
|
||||
logcls = "AUDITING";
|
||||
- }
|
||||
} else if (sa->type == AA_AUDITTYPE_SYSCALL) {
|
||||
/* Currently AA_AUDITTYPE_SYSCALL is for rejects only.
|
||||
* Values set by aa_audit_syscallreject will get us here.
|
||||
@@ -457,16 +433,13 @@ int aa_audit(struct aa_profile *profile,
|
||||
if (!ab) {
|
||||
AA_ERROR("Unable to log event (%d) to audit subsys\n",
|
||||
sa->type);
|
||||
- if (complain)
|
||||
- error = 0;
|
||||
- goto out;
|
||||
+ return -ENOMEM; /* FIXME: was -EINVAL if !complain: ??? */
|
||||
}
|
||||
|
||||
/* messages get special handling */
|
||||
if (sa->type == AA_AUDITTYPE_MSG) {
|
||||
audit_log_vformat(ab, sa->name, sa->vaval);
|
||||
audit_log_end(ab);
|
||||
- error = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -479,7 +452,6 @@ int aa_audit(struct aa_profile *profile,
|
||||
sa->name2 = mangle(sa->name2, sa->buffer2);
|
||||
if (!sa->name2)
|
||||
return -ENAMETOOLONG;
|
||||
-
|
||||
}
|
||||
|
||||
/* log operation */
|
||||
@@ -490,16 +462,16 @@ int aa_audit(struct aa_profile *profile,
|
||||
|
||||
switch(NOFLAGS(sa->type)) {
|
||||
case NOFLAGS(AA_AUDITTYPE_FILE): {
|
||||
- int perm = audit ? sa->mask : sa->error_code;
|
||||
+ int mask = PROFILE_AUDIT(profile) ?
|
||||
+ sa->requested_mask : sa->denied_mask;
|
||||
|
||||
audit_log_format(ab, "%s%s%s%s%s access to %s ",
|
||||
- perm & AA_EXEC_MMAP ? "m" : "",
|
||||
- perm & MAY_READ ? "r" : "",
|
||||
- perm & MAY_WRITE ? "w" : "",
|
||||
- perm & MAY_EXEC ? "x" : "",
|
||||
- perm & AA_MAY_LINK ? "l" : "",
|
||||
+ mask & AA_EXEC_MMAP ? "m" : "",
|
||||
+ mask & MAY_READ ? "r" : "",
|
||||
+ mask & MAY_WRITE ? "w" : "",
|
||||
+ mask & MAY_EXEC ? "x" : "",
|
||||
+ mask & AA_MAY_LINK ? "l" : "",
|
||||
sa->name);
|
||||
- opspec_error = -EPERM;
|
||||
break;
|
||||
}
|
||||
case NOFLAGS(AA_AUDITTYPE_DIR):
|
||||
@@ -532,15 +504,13 @@ int aa_audit(struct aa_profile *profile,
|
||||
case NOFLAGS(AA_AUDITTYPE_CAP):
|
||||
audit_log_format(ab, "access to capability '%s' ",
|
||||
capability_names[sa->capability]);
|
||||
- opspec_error = -EPERM;
|
||||
break;
|
||||
case NOFLAGS(AA_AUDITTYPE_SYSCALL):
|
||||
audit_log_format(ab, "access to syscall '%s' ", sa->name);
|
||||
- opspec_error = -EPERM;
|
||||
break;
|
||||
default:
|
||||
WARN_ON(1);
|
||||
- return error;
|
||||
+ return -EINVAL;
|
||||
}
|
||||
|
||||
#undef NOFLAGS
|
||||
@@ -550,12 +520,8 @@ int aa_audit(struct aa_profile *profile,
|
||||
|
||||
audit_log_end(ab);
|
||||
|
||||
- if (complain)
|
||||
- error = 0;
|
||||
- else
|
||||
- error = sa->result ? 0 : opspec_error;
|
||||
out:
|
||||
- return error;
|
||||
+ return complain ? 0 : sa->error_code;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -575,6 +541,8 @@ int aa_attr(struct aa_profile *profile,
|
||||
sa.type = AA_AUDITTYPE_ATTR;
|
||||
sa.iattr = iattr;
|
||||
sa.gfp_mask = GFP_KERNEL;
|
||||
+ sa.requested_mask = MAY_WRITE;
|
||||
+ sa.error_code = -EACCES;
|
||||
|
||||
check = 0;
|
||||
if (inode && S_ISDIR(inode->i_mode))
|
||||
@@ -582,7 +550,7 @@ int aa_attr(struct aa_profile *profile,
|
||||
if (iattr->ia_valid & ATTR_FILE)
|
||||
check |= AA_CHECK_FD;
|
||||
|
||||
- error = aa_perm_dentry(profile, dentry, mnt, &sa, MAY_WRITE, check);
|
||||
+ error = aa_perm_dentry(profile, dentry, mnt, &sa, check);
|
||||
|
||||
return error;
|
||||
}
|
||||
@@ -607,11 +575,13 @@ int aa_perm_xattr(struct aa_profile *pro
|
||||
sa.type = AA_AUDITTYPE_XATTR;
|
||||
sa.name2 = operation;
|
||||
sa.gfp_mask = GFP_KERNEL;
|
||||
+ sa.requested_mask = mask;
|
||||
+ sa.error_code = -EACCES;
|
||||
|
||||
if (inode && S_ISDIR(inode->i_mode))
|
||||
check |= AA_CHECK_DIR;
|
||||
|
||||
- error = aa_perm_dentry(profile, dentry, mnt, &sa, mask, check);
|
||||
+ error = aa_perm_dentry(profile, dentry, mnt, &sa, check);
|
||||
|
||||
return error;
|
||||
}
|
||||
@@ -637,9 +607,10 @@ int aa_perm(struct aa_profile *profile,
|
||||
goto out;
|
||||
|
||||
sa.type = AA_AUDITTYPE_FILE;
|
||||
- sa.mask = mask;
|
||||
sa.gfp_mask = GFP_KERNEL;
|
||||
- error = aa_perm_dentry(profile, dentry, mnt, &sa, mask, check);
|
||||
+ sa.requested_mask = mask;
|
||||
+ sa.error_code = -EACCES; /* FIXME: was -EPERM before: ??? */
|
||||
+ error = aa_perm_dentry(profile, dentry, mnt, &sa, check);
|
||||
|
||||
out:
|
||||
return error;
|
||||
@@ -665,22 +636,23 @@ int aa_perm_dir(struct aa_profile *profi
|
||||
sa.type = AA_AUDITTYPE_DIR;
|
||||
sa.name2 = operation;
|
||||
sa.gfp_mask = GFP_KERNEL;
|
||||
+ sa.requested_mask = mask;
|
||||
+ sa.error_code = -EACCES;
|
||||
|
||||
- return aa_perm_dentry(profile, dentry, mnt, &sa, mask, AA_CHECK_DIR);
|
||||
+ return aa_perm_dentry(profile, dentry, mnt, &sa, AA_CHECK_DIR);
|
||||
}
|
||||
|
||||
int aa_perm_path(struct aa_profile *profile, const char *name, int mask)
|
||||
{
|
||||
struct aa_audit sa;
|
||||
- int denied_mask;
|
||||
|
||||
sa.type = AA_AUDITTYPE_FILE;
|
||||
- sa.mask = mask;
|
||||
+ sa.requested_mask = mask;
|
||||
sa.gfp_mask = GFP_KERNEL;
|
||||
sa.name = name;
|
||||
|
||||
- denied_mask = aa_file_denied(profile, name, mask);
|
||||
- aa_permerror2result(denied_mask, &sa);
|
||||
+ sa.denied_mask = aa_file_denied(profile, name, mask);
|
||||
+ sa.error_code = sa.denied_mask ? -EACCES : 0;
|
||||
|
||||
return aa_audit(profile, &sa);
|
||||
}
|
||||
@@ -715,8 +687,7 @@ int aa_capability(struct aa_task_context
|
||||
sa.type = AA_AUDITTYPE_CAP;
|
||||
sa.name = NULL;
|
||||
sa.capability = cap;
|
||||
- sa.error_code = 0;
|
||||
- sa.result = !error;
|
||||
+ sa.error_code = error;
|
||||
sa.gfp_mask = GFP_ATOMIC;
|
||||
|
||||
error = aa_audit(cxt->profile, &sa);
|
||||
@@ -746,7 +717,7 @@ int aa_link(struct aa_profile *profile,
|
||||
struct dentry *link, struct vfsmount *link_mnt,
|
||||
struct dentry *target, struct vfsmount *target_mnt)
|
||||
{
|
||||
- int denied_mask = -EPERM, error, check = 0;
|
||||
+ int error, check = 0;
|
||||
struct aa_audit sa;
|
||||
|
||||
again:
|
||||
@@ -756,18 +727,23 @@ again:
|
||||
sa.name2 = aa_get_name(target, target_mnt, &sa.buffer2, check);
|
||||
|
||||
if (IS_ERR(sa.name)) {
|
||||
- denied_mask = PTR_ERR(sa.name);
|
||||
+ sa.requested_mask = 0;
|
||||
+ sa.denied_mask = 0;
|
||||
+ sa.error_code = PTR_ERR(sa.name);
|
||||
sa.name = NULL;
|
||||
}
|
||||
if (IS_ERR(sa.name2)) {
|
||||
- denied_mask = PTR_ERR(sa.name2);
|
||||
+ sa.requested_mask = 0;
|
||||
+ sa.denied_mask = 0;
|
||||
+ sa.error_code = PTR_ERR(sa.name);
|
||||
sa.name2 = NULL;
|
||||
}
|
||||
|
||||
- if (sa.name && sa.name2)
|
||||
- denied_mask = aa_link_denied(profile, sa.name, sa.name2);
|
||||
-
|
||||
- aa_permerror2result(denied_mask, &sa);
|
||||
+ if (sa.name && sa.name2) {
|
||||
+ sa.requested_mask = AA_MAY_LINK;
|
||||
+ sa.denied_mask = aa_link_denied(profile, sa.name, sa.name2);
|
||||
+ sa.error_code = sa.denied_mask ? -EPERM : 0;
|
||||
+ }
|
||||
|
||||
sa.type = AA_AUDITTYPE_LINK;
|
||||
sa.gfp_mask = GFP_KERNEL;
|
Loading…
Add table
Reference in a new issue