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 <amy.griffis(a)hp.com>
---
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, §x, &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, §x, &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