When auditing syscalls that send signals, log the pid and security
context for each target process. Optimize the data collection by
adding a counter for signal-related rules, and avoiding allocating an
aux struct unless we have more than one target process. Move the
audit_signal_info() hook up in check_kill_permission() so we audit
attempts where permission is denied.

Signed-off-by: Amy Griffis <[EMAIL PROTECTED]>
---
 include/linux/audit.h |    3 ++
 kernel/audit.h        |   12 ++++---
 kernel/auditfilter.c  |   14 ++++++++-
 kernel/auditsc.c      |   76 ++++++++++++++++++++++++++++++++++++++++++++++--
 kernel/signal.c       |   10 ++++--
 5 files changed, 100 insertions(+), 15 deletions(-)

diff --git a/include/linux/audit.h b/include/linux/audit.h
index 0194a9b..b1daf3c 100644
--- a/include/linux/audit.h
+++ b/include/linux/audit.h
@@ -90,6 +90,7 @@
 #define AUDIT_MQ_GETSETATTR    1315    /* POSIX MQ get/set attribute record 
type */
 #define AUDIT_KERNEL_OTHER     1316    /* For use by 3rd party modules */
 #define AUDIT_FD_PAIR          1317    /* audit record for pipe/socketpair */
+#define AUDIT_TARGET_PID       1318    /* audit record for a pid arg */
 
 #define AUDIT_AVC              1400    /* SE Linux avc denial or grant */
 #define AUDIT_SELINUX_ERR      1401    /* Internal SE Linux Errors */
@@ -443,6 +444,7 @@ static inline int audit_mq_getsetattr(mqd_t mqdes, struct 
mq_attr *mqstat)
        return 0;
 }
 extern int audit_n_rules;
+extern int audit_signals;
 #else
 #define audit_alloc(t) ({ 0; })
 #define audit_free(t) do { ; } while (0)
@@ -472,6 +474,7 @@ extern int audit_n_rules;
 #define audit_mq_notify(d,n) ({ 0; })
 #define audit_mq_getsetattr(d,s) ({ 0; })
 #define audit_n_rules 0
+#define audit_signals 0
 #endif
 
 #ifdef CONFIG_AUDIT
diff --git a/kernel/audit.h b/kernel/audit.h
index a337023..36960f7 100644
--- a/kernel/audit.h
+++ b/kernel/audit.h
@@ -131,17 +131,19 @@ extern void audit_handle_ievent(struct inotify_watch *, 
u32, u32, u32,
 extern int selinux_audit_rule_update(void);
 
 #ifdef CONFIG_AUDITSYSCALL
-extern void __audit_signal_info(int sig, struct task_struct *t);
-static inline void audit_signal_info(int sig, struct task_struct *t)
+extern int __audit_signal_info(int sig, struct task_struct *t);
+static inline int audit_signal_info(int sig, struct task_struct *t)
 {
-       if (unlikely(audit_pid && t->tgid == audit_pid))
-               __audit_signal_info(sig, t);
+       if (unlikely((audit_pid && t->tgid == audit_pid) ||
+                    (audit_signals && !audit_dummy_context())))
+               return __audit_signal_info(sig, t);
+       return 0;
 }
 extern enum audit_state audit_filter_inodes(struct task_struct *,
                                            struct audit_context *);
 extern void audit_set_auditable(struct audit_context *);
 #else
-#define audit_signal_info(s,t)
+#define audit_signal_info(s,t) AUDIT_DISABLED
 #define audit_filter_inodes(t,c) AUDIT_DISABLED
 #define audit_set_auditable(c)
 #endif
diff --git a/kernel/auditfilter.c b/kernel/auditfilter.c
index 3749193..b90d121 100644
--- a/kernel/auditfilter.c
+++ b/kernel/auditfilter.c
@@ -1171,7 +1171,7 @@ static inline int audit_add_rule(struct audit_entry 
*entry,
        struct nameidata *ndp, *ndw;
        int h, err, putnd_needed = 0;
 #ifdef CONFIG_AUDITSYSCALL
-       int dont_count = 0;
+       int i, dont_count = 0;
 
        /* If either of these, don't count towards total */
        if (entry->rule.listnr == AUDIT_FILTER_USER ||
@@ -1221,6 +1221,11 @@ static inline int audit_add_rule(struct audit_entry 
*entry,
 #ifdef CONFIG_AUDITSYSCALL
        if (!dont_count)
                audit_n_rules++;
+
+       for (i = 0; i < AUDIT_BITMASK_SIZE; i++)
+               if ((entry->rule.mask[i] & classes[AUDIT_CLASS_SIGNAL][i]) ||
+                   (entry->rule.mask[i] & classes[AUDIT_CLASS_SIGNAL_32][i]))
+                       audit_signals++;
 #endif
        mutex_unlock(&audit_filter_mutex);
 
@@ -1247,7 +1252,7 @@ static inline int audit_del_rule(struct audit_entry 
*entry,
        LIST_HEAD(inotify_list);
        int h, ret = 0;
 #ifdef CONFIG_AUDITSYSCALL
-       int dont_count = 0;
+       int i, dont_count = 0;
 
        /* If either of these, don't count towards total */
        if (entry->rule.listnr == AUDIT_FILTER_USER ||
@@ -1294,6 +1299,11 @@ static inline int audit_del_rule(struct audit_entry 
*entry,
 #ifdef CONFIG_AUDITSYSCALL
        if (!dont_count)
                audit_n_rules--;
+
+       for (i = 0; i < AUDIT_BITMASK_SIZE; i++)
+               if ((entry->rule.mask[i] & classes[AUDIT_CLASS_SIGNAL][i]) ||
+                   (entry->rule.mask[i] & classes[AUDIT_CLASS_SIGNAL_32][i]))
+                       audit_signals--;
 #endif
        mutex_unlock(&audit_filter_mutex);
 
diff --git a/kernel/auditsc.c b/kernel/auditsc.c
index 1b427d9..2da8c2c 100644
--- a/kernel/auditsc.c
+++ b/kernel/auditsc.c
@@ -84,6 +84,9 @@ extern int audit_enabled;
 /* number of audit rules */
 int audit_n_rules;
 
+/* determines whether we collect data for signals sent */
+int audit_signals;
+
 /* When fs/namei.c:getname() is called, we store the pointer in name and
  * we don't let putname() free it (instead we free all of the saved
  * pointers at syscall exit time).
@@ -176,6 +179,12 @@ struct audit_aux_data_path {
        struct vfsmount         *mnt;
 };
 
+struct audit_aux_data_pid {
+       struct audit_aux_data   d;
+       pid_t                   opid;
+       u32                     osid;
+};
+
 /* The per-task audit context. */
 struct audit_context {
        int                 dummy;      /* must be the first element */
@@ -204,6 +213,10 @@ struct audit_context {
        unsigned long       personality;
        int                 arch;
 
+                               /* optimize collection for single target pid */
+       pid_t               opid;
+       u32                 osid;
+
 #if AUDIT_DEBUG
        int                 put_count;
        int                 ino_count;
@@ -860,6 +873,21 @@ static void audit_log_exit(struct audit_context *context, 
struct task_struct *ts
                audit_log_format(ab, " key=(null)");
        audit_log_end(ab);
 
+       if (context->opid) {
+           char *sectx = NULL;
+           u32 len;
+
+           ab = audit_log_start(context, GFP_KERNEL, AUDIT_TARGET_PID);
+           audit_log_format(ab, "opid=%d ", context->opid);
+           if (selinux_sid_to_string(context->osid, &sectx, &len)) {
+                   audit_log_format(ab, "osid=%u", context->osid);
+                   call_panic = 2;
+           } else
+                   audit_log_format(ab, " obj=%s", sectx);
+           kfree(sectx);
+           audit_log_end(ab);
+       }
+
        for (aux = context->aux; aux; aux = aux->next) {
 
                ab = audit_log_start(context, GFP_KERNEL, aux->type);
@@ -867,6 +895,20 @@ static void audit_log_exit(struct audit_context *context, 
struct task_struct *ts
                        continue; /* audit_panic has been called */
 
                switch (aux->type) {
+               case AUDIT_TARGET_PID: {
+                       struct audit_aux_data_pid *axi = (void *)aux;
+                       char *sectx = NULL;
+                       u32 len;
+
+                       audit_log_format(ab, "opid=%d ", axi->opid);
+                       if (selinux_sid_to_string(axi->osid, &sectx, &len)) {
+                               audit_log_format(ab, "osid=%u", axi->osid);
+                               call_panic = 2;
+                       } else
+                               audit_log_format(ab, " obj=%s", sectx);
+                       kfree(sectx);
+                       break; }
+
                case AUDIT_MQ_OPEN: {
                        struct audit_aux_data_mq_open *axi = (void *)aux;
                        audit_log_format(ab,
@@ -1916,15 +1958,17 @@ int audit_avc_path(struct dentry *dentry, struct 
vfsmount *mnt)
  * If the audit subsystem is being terminated, record the task (pid)
  * and uid that is doing that.
  */
-void __audit_signal_info(int sig, struct task_struct *t)
+int __audit_signal_info(int sig, struct task_struct *t)
 {
+       struct audit_aux_data_pid *ax;
+       struct task_struct *tsk = current;
+       struct audit_context *ctx = tsk->audit_context;
        extern pid_t audit_sig_pid;
        extern uid_t audit_sig_uid;
        extern u32 audit_sig_sid;
 
-       if (sig == SIGTERM || sig == SIGHUP || sig == SIGUSR1) {
-               struct task_struct *tsk = current;
-               struct audit_context *ctx = tsk->audit_context;
+       if (audit_pid && t->tgid == audit_pid &&
+           (sig == SIGTERM || sig == SIGHUP || sig == SIGUSR1)) {
                audit_sig_pid = tsk->pid;
                if (ctx)
                        audit_sig_uid = ctx->loginuid;
@@ -1932,4 +1976,28 @@ void __audit_signal_info(int sig, struct task_struct *t)
                        audit_sig_uid = tsk->uid;
                selinux_get_task_sid(tsk, &audit_sig_sid);
        }
+
+       if (!audit_signals) /* audit_context checked in wrapper */
+               return 0;
+
+       /* optimize the common case by putting first signal recipient directly
+        * in audit_context */
+       if (!ctx->opid) {
+               ctx->opid = t->tgid;
+               selinux_get_task_sid(t, &ctx->osid);
+               return 0;
+       }
+
+       ax = kmalloc(sizeof(*ax), GFP_KERNEL);
+       if (!ax) {
+               return -ENOMEM;
+       }
+
+       ax->opid = t->tgid;
+       selinux_get_task_sid(t, &ax->osid);
+
+       ax->d.type = AUDIT_TARGET_PID;
+       ax->d.next = ctx->aux;
+       ctx->aux = (void *)ax;
+       return 0;
 }
diff --git a/kernel/signal.c b/kernel/signal.c
index e2a7d4b..10c183a 100644
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -607,6 +607,11 @@ static int check_kill_permission(int sig, struct siginfo 
*info,
        int error = -EINVAL;
        if (!valid_signal(sig))
                return error;
+
+       error = audit_signal_info(sig, t); /* Let audit system see the signal */
+       if (error)
+               return error;
+
        error = -EPERM;
        if ((info == SEND_SIG_NOINFO || (!is_si_special(info) && 
SI_FROMUSER(info)))
            && ((sig != SIGCONT) ||
@@ -616,10 +621,7 @@ static int check_kill_permission(int sig, struct siginfo 
*info,
            && !capable(CAP_KILL))
                return error;
 
-       error = security_task_kill(t, info, sig, 0);
-       if (!error)
-               audit_signal_info(sig, t); /* Let audit system see the signal */
-       return error;
+       return security_task_kill(t, info, sig, 0);
 }
 
 /* forward decl */
-- 
1.4.4.4

--
Linux-audit mailing list
[email protected]
https://www.redhat.com/mailman/listinfo/linux-audit

Reply via email to