[RFC PATCH] kernel/audit: add new AUDIG_SIG_INFO_VERS audit messages

Eric Paris
2008-04-16T19:03:04Z

This adds a new versioned audit signal request response mechanism. This
new versioned mechanism will allow arbitrary future information to be
passed along this boundary which is impossible with the present
situation. The kernel always replies back to a userspace request so we
can easily tell the highest version supported by userspace and just
return EINVAL if userspace requests a version we don't know.

We should also notice that I added a new array of user information right
beside the current audit_sig_* stuff. So we don't have the current
problem where a SIGUSR1 quickly followed by SIGUSR2 would result in both
userspace requests getting information from the same sending process.
Its still possible that we may have races between multiple of the same
signal, but at least none of the data we get will be wrong.

comments?

-Eric

include/linux/audit.h | 22 +++++++++-
kernel/audit.c | 101 +++++++++++++++++++++++++++++++++++--------
kernel/audit.h | 17 +++++++
kernel/auditsc.c | 24 +++++++++-
security/selinux/nlmsgtab.c | 1 +
5 files changed, 143 insertions(+), 22 deletions(-)

diff --git a/include/linux/audit.h b/include/linux/audit.h
index 2af9ec0..1549c9f 100644
--- a/include/linux/audit.h
+++ b/include/linux/audit.h
@@ -59,7 +59,7 @@
#define AUDIT_WATCH_INS 1007 /* Insert file/dir watch entry */
#define AUDIT_WATCH_REM 1008 /* Remove file/dir watch entry */
#define AUDIT_WATCH_LIST 1009 /* List all file/dir watches */
-#define AUDIT_SIGNAL_INFO 1010 /* Get info about sender of signal to auditd */
+#define AUDIT_SIGNAL_INFO 1010 /* deprecated in favor of AUDIT_SIGNAL_INFO_VERS */
#define AUDIT_ADD_RULE 1011 /* Add syscall filtering rule */
#define AUDIT_DEL_RULE 1012 /* Delete syscall filtering rule */
#define AUDIT_LIST_RULES 1013 /* List syscall filtering rules */
@@ -67,6 +67,7 @@
#define AUDIT_MAKE_EQUIV 1015 /* Append to watched tree */
#define AUDIT_TTY_GET 1016 /* Get TTY auditing status */
#define AUDIT_TTY_SET 1017 /* Set TTY auditing status */
+#define AUDIT_SIGNAL_INFO_VERS 1018 /* Version controlled signal sender info request */

#define AUDIT_FIRST_USER_MSG 1100 /* Userspace messages mostly uninteresting to kernel */
#define AUDIT_USER_AVC 1107 /* We filter this differently */
@@ -340,6 +341,25 @@ struct audit_rule { /* for AUDIT_LIST, AUDIT_ADD, and AUDIT_DEL */
#ifdef __KERNEL__
#include <linux/sched.h>

+struct audit_usr_info {
+ uid_t uid;
+ uid_t auid;
+ pid_t pid;
+ __u32 session;
+ __u32 sid;
+};
+
+/* version then sig MUST be the first 2 fields of all sig_info versions */
+struct audit_sig_info_ver1 {
+ __u32 version;
+ __u32 sig;
+ uid_t uid;
+ uid_t auid;
+ pid_t pid;
+ __u32 session;
+ char ctx[0];
+};
+
struct audit_sig_info {
uid_t uid;
pid_t pid;
diff --git a/kernel/audit.c b/kernel/audit.c
index b782b04..80abeb7 100644
--- a/kernel/audit.c
+++ b/kernel/audit.c
@@ -101,6 +101,9 @@ uid_t audit_sig_uid = -1;
pid_t audit_sig_pid = -1;
u32 audit_sig_sid = 0;

+/* identity of the user signaling audit by signal type */
+struct audit_usr_info audit_usr_sig_info[4];
+
/* Records can be lost in several ways:
0) [suppressed in audit_alloc]
1) out of memory in audit_log_start [kmalloc of struct audit_buffer]
@@ -514,6 +517,7 @@ static int audit_netlink_ok(struct sk_buff *skb, u16 msg_type)
case AUDIT_DEL:
case AUDIT_DEL_RULE:
case AUDIT_SIGNAL_INFO:
+ case AUDIT_SIGNAL_INFO_VERS:
case AUDIT_TTY_GET:
case AUDIT_TTY_SET:
case AUDIT_TRIM:
@@ -561,6 +565,80 @@ static int audit_log_common_recv_msg(struct audit_buffer **ab, u16 msg_type,
return rc;
}

+static int audit_handle_sig_request(int msg_type, pid_t nlk_pid, u32 seq, struct audit_sig_info_ver1 *sig_info)
+{
+ int err;
+ char *ctx = NULL;
+ u32 len, msg_len;
+ void *data = NULL;
+
+
+ if (msg_type == AUDIT_SIGNAL_INFO_VERS) {
+ int offset;
+ struct audit_sig_info_ver1 *sig_data;
+
+ /* someday we might cast sig_info to some other version... */
+ if (sig_info->version != 1) {
+ printk(KERN_WARNING "Audit: userspace signal version:%d unknown, please update kernel\n", sig_info->version);
+ return -EINVAL;
+ }
+
+ offset = audit_signal_to_offset(sig_info->sig);
+
+ err = selinux_sid_to_string(audit_usr_sig_info[offset].sid, &ctx, &len);
+ if (err)
+ return err;
+
+ sig_data = kmalloc(sizeof(*sig_data) + len, GFP_KERNEL);
+ if (!sig_data) {
+ kfree(ctx);
+ return -ENOMEM;
+ }
+
+ sig_data->version = 1;
+ sig_data->sig = sig_info->sig;
+ sig_data->uid = audit_usr_sig_info[offset].uid;
+ sig_data->auid = audit_usr_sig_info[offset].auid;
+ sig_data->pid = audit_usr_sig_info[offset].pid;
+ sig_data->session = audit_usr_sig_info[offset].session;
+ memcpy(sig_data->ctx, ctx, len);
+
+ data = sig_data;
+ msg_len = sizeof(*sig_data) + len;
+
+ audit_usr_sig_info[offset].uid = -1;
+ audit_usr_sig_info[offset].auid = -1;
+ audit_usr_sig_info[offset].pid = -1;
+ audit_usr_sig_info[offset].session = -1;
+ } else {
+ struct audit_sig_info *sig_data;
+
+ err = selinux_sid_to_string(audit_sig_sid, &ctx, &len);
+ if (err)
+ return err;
+
+ sig_data = kmalloc(sizeof(*sig_data) + len, GFP_KERNEL);
+ if (!sig_data) {
+ kfree(ctx);
+ return -ENOMEM;
+ }
+ sig_data->uid = audit_sig_uid;
+ sig_data->pid = audit_sig_pid;
+ memcpy(sig_data->ctx, ctx, len);
+
+ data = sig_data;
+ msg_len = sizeof(*sig_data) + len;
+ }
+ audit_sig_uid = -1;
+ audit_sig_pid = -1;
+ audit_sig_sid = 0;
+ audit_send_reply(nlk_pid, seq, msg_type, 0, 0, data, msg_len);
+ kfree(data);
+ kfree(ctx);
+
+ return 0;
+}
+
static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
{
u32 uid, pid, seq, sid;
@@ -570,9 +648,6 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
struct audit_buffer *ab;
u16 msg_type = nlh->nlmsg_type;
uid_t loginuid; /* loginuid of sender */
- struct audit_sig_info *sig_data;
- char *ctx = NULL;
- u32 len;

err = audit_netlink_ok(skb, msg_type);
if (err)
@@ -757,23 +832,11 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
kfree(new);
break;
}
- case AUDIT_SIGNAL_INFO:
- err = selinux_sid_to_string(audit_sig_sid, &ctx, &len);
- if (err)
- return err;
- sig_data = kmalloc(sizeof(*sig_data) + len, GFP_KERNEL);
- if (!sig_data) {
- kfree(ctx);
- return -ENOMEM;
- }
- sig_data->uid = audit_sig_uid;
- sig_data->pid = audit_sig_pid;
- memcpy(sig_data->ctx, ctx, len);
- kfree(ctx);
- audit_send_reply(NETLINK_CB(skb).pid, seq, AUDIT_SIGNAL_INFO,
- 0, 0, sig_data, sizeof(*sig_data) + len);
- kfree(sig_data);
+ case AUDIT_SIGNAL_INFO:
+ case AUDIT_SIGNAL_INFO_VERS: {
+ err = audit_handle_sig_request(msg_type, NETLINK_CB(skb).pid, seq, data);
break;
+ }
case AUDIT_TTY_GET: {
struct audit_tty_status s;
struct task_struct *tsk;
diff --git a/kernel/audit.h b/kernel/audit.h
index 2554bd5..4401e3a 100644
--- a/kernel/audit.h
+++ b/kernel/audit.h
@@ -109,6 +109,23 @@ static inline int audit_hash_ino(u32 ino)
return (ino & (AUDIT_INODE_BUCKETS-1));
}

+/* offsets into signal arrays */
+static inline int audit_signal_to_offset(int sig)
+{
+ switch (sig) {
+ case SIGTERM:
+ return 0;
+ case SIGHUP:
+ return 1;
+ case SIGUSR1:
+ return 2;
+ case SIGUSR2:
+ return 3;
+ default:
+ BUG();
+ };
+}
+
extern int audit_match_class(int class, unsigned syscall);
extern int audit_comparator(const u32 left, const u32 op, const u32 right);
extern int audit_compare_dname_path(const char *dname, const char *path,
diff --git a/kernel/auditsc.c b/kernel/auditsc.c
index 782262e..f3c4045 100644
--- a/kernel/auditsc.c
+++ b/kernel/auditsc.c
@@ -2360,18 +2360,38 @@ int __audit_signal_info(int sig, struct task_struct *t)
struct audit_aux_data_pids *axp;
struct task_struct *tsk = current;
struct audit_context *ctx = tsk->audit_context;
+
+ extern struct audit_usr_info audit_usr_sig_info[];
+
+
extern pid_t audit_sig_pid;
extern uid_t audit_sig_uid;
extern u32 audit_sig_sid;

if (audit_pid && t->tgid == audit_pid) {
- if (sig == SIGTERM || sig == SIGHUP || sig == SIGUSR1) {
+ /*
+ * if the change the number of signals here you need to update
+ * the size of the _array structures and update _to_offset()
+ */
+ if (sig == SIGTERM || sig == SIGHUP || sig == SIGUSR1 ||
+ sig == SIGUSR2) {
+ int offset = audit_signal_to_offset(sig);
+ u32 sid;
+
+ selinux_get_task_sid(tsk, &sid);
+
audit_sig_pid = tsk->pid;
if (tsk->loginuid != -1)
audit_sig_uid = tsk->loginuid;
else
audit_sig_uid = tsk->uid;
- selinux_get_task_sid(tsk, &audit_sig_sid);
+ audit_sig_sid = sid;
+
+ audit_usr_sig_info[offset].pid = tsk->pid;
+ audit_usr_sig_info[offset].uid = tsk->uid;
+ audit_usr_sig_info[offset].auid = tsk->loginuid;
+ audit_usr_sig_info[offset].session = tsk->sessionid;
+ audit_usr_sig_info[offset].sid = sid;
}
if (!audit_signals || audit_dummy_context())
return 0;
diff --git a/security/selinux/nlmsgtab.c b/security/selinux/nlmsgtab.c
index eddc7b4..3378659 100644
--- a/security/selinux/nlmsgtab.c
+++ b/security/selinux/nlmsgtab.c
@@ -112,6 +112,7 @@ static struct nlmsg_perm nlmsg_audit_perms[] =
{ AUDIT_SIGNAL_INFO, NETLINK_AUDIT_SOCKET__NLMSG_READ },
{ AUDIT_TTY_GET, NETLINK_AUDIT_SOCKET__NLMSG_READ },
{ AUDIT_TTY_SET, NETLINK_AUDIT_SOCKET__NLMSG_WRITE },
+ { AUDIT_SIGNAL_INFO_VERS, NETLINK_AUDIT_SOCKET__NLMSG_READ },
};