* Steve Grubb (sgrubb(a)redhat.com) wrote:
The attached patch addresses the problem with getting the shutdown information
in a race-free way. It creates a new message type AUDIT_TERM_INFO, which is
used by the audit daemon to query who issued the shutdown. It requires the
placement of a hook function that gathers the information.
Heh, I was going to suggest something like this. However, I was
thinking it'd be an skb queued up, ready to go once asked for.
diff -urB linux-2.6.9.orig/include/linux/audit.h
linux-2.6.9/include/linux/audit.h
--- linux-2.6.9.orig/include/linux/audit.h 2005-04-26 15:41:58.000000000 -0400
+++ linux-2.6.9/include/linux/audit.h 2005-04-26 16:09:46.000000000 -0400
@@ -38,6 +38,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 watches */
+#define AUDIT_TERM_INFO 1010 /* Get termination information */
#define AUDIT_KERNEL 2000 /* Asynchronous audit record. NOT A REQUEST. */
/* Rule flags */
@@ -201,6 +202,11 @@
};
+struct audit_term_info {
+ uid_t uid;
+ pid_t pid;
+};
+
struct audit_buffer;
struct audit_context;
struct inode;
@@ -297,6 +303,7 @@
int done, int multi,
void *payload, int size);
extern void audit_log_lost(const char *message);
+extern void audit_kill_info(int sig, struct task_struct *t);
#else
#define audit_log(t,f,...) do { ; } while (0)
#define audit_log_start(t) ({ NULL; })
@@ -312,6 +319,7 @@
#define audit_set_backlog_limit(l) do { ; } while (0)
#define audit_set_enabled(s) do { ; } while (0)
#define audit_set_failure(s) do { ; } while (0)
+#define audit_kill_info(s,t) do { ; } while (0)
#endif
#endif
#endif
diff -urB linux-2.6.9.orig/kernel/audit.c linux-2.6.9/kernel/audit.c
--- linux-2.6.9.orig/kernel/audit.c 2005-04-26 15:42:00.000000000 -0400
+++ linux-2.6.9/kernel/audit.c 2005-04-26 16:09:46.000000000 -0400
@@ -68,7 +68,7 @@
/* If audit records are to be written to the netlink socket, audit_pid
* contains the (non-zero) pid. */
-static int audit_pid;
+int audit_pid;
/* If audit_limit is non-zero, limit the rate of sending audit records
* to that number per second. This prevents DoS attacks, but results in
@@ -79,6 +79,10 @@
static int audit_backlog_limit = 64;
static atomic_t audit_backlog = ATOMIC_INIT(0);
+/* The identity of the user shutting down the audit system. */
+uid_t audit_kill_uid;
+pid_t audit_kill_pid;
This is not race free.
+
/* 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]
@@ -320,6 +324,7 @@
case AUDIT_DEL:
case AUDIT_WATCH_INS:
case AUDIT_WATCH_REM:
+ case AUDIT_TERM_INFO:
if (!cap_raised(eff_cap, CAP_AUDIT_CONTROL))
err = -EPERM;
break;
@@ -343,6 +348,7 @@
struct audit_buffer *ab;
u16 msg_type = nlh->nlmsg_type;
uid_t loginuid; /* loginuid of sender */
+ struct audit_term_info term_data;
err = audit_netlink_ok(NETLINK_CB(skb).eff_cap, msg_type);
if (err)
@@ -429,6 +435,12 @@
NETLINK_CB(skb).pid,
uid, seq, data);
break;
+ case AUDIT_TERM_INFO:
+ term_data.uid = audit_kill_uid;
+ term_data.pid = audit_kill_pid;
+ audit_send_reply(NETLINK_CB(skb).pid, seq, AUDIT_TERM_INFO,
+ 0, 0, &term_data, sizeof(term_data));
+ break;
default:
err = -EINVAL;
break;
@@ -572,6 +584,8 @@
audit_panic("cannot initialize netlink socket");
audit_initialized = 1;
+ audit_kill_uid = -1;
+ audit_kill_pid = -1;
audit_enabled = audit_default;
audit_filesystem_init();
audit_log(NULL, "initialized");
diff -urB linux-2.6.9.orig/kernel/auditsc.c linux-2.6.9/kernel/auditsc.c
--- linux-2.6.9.orig/kernel/auditsc.c 2005-04-26 15:42:00.000000000 -0400
+++ linux-2.6.9/kernel/auditsc.c 2005-04-26 16:09:46.000000000 -0400
@@ -1134,3 +1134,22 @@
audit_notify_watch_exit:
return ret;
}
+
+void audit_kill_info(int sig, struct task_struct *t)
+{
+ extern pid_t audit_kill_pid;
+ extern uid_t audit_kill_uid;
+ extern int audit_pid;
+
+ if (unlikely(audit_pid && t->pid == audit_pid)) {
+ if (sig == SIGTERM || sig == SIGKILL) {
+ struct audit_context *ctx = current->audit_context;
+ audit_kill_pid = current->pid;
+ if (ctx)
+ audit_kill_uid = ctx->loginuid;
+ else
+ audit_kill_uid = current->uid;
+ }
Two threads can race and give inconsistent results here.
+ }
+}
+
diff -urB linux-2.6.9.orig/kernel/signal.c linux-2.6.9/kernel/signal.c
--- linux-2.6.9.orig/kernel/signal.c 2005-04-26 15:42:00.000000000 -0400
+++ linux-2.6.9/kernel/signal.c 2005-04-26 16:43:19.000000000 -0400
@@ -21,6 +21,7 @@
#include <linux/binfmts.h>
#include <linux/security.h>
#include <linux/ptrace.h>
+#include <linux/audit.h>
#include <asm/param.h>
#include <asm/uaccess.h>
#include <asm/unistd.h>
@@ -623,6 +624,10 @@
int error = -EINVAL;
if (sig < 0 || sig > _NSIG)
return error;
+
+ /* Let audit system examine the signal */
+ audit_kill_info(sig, t);
This is not secure, and the data isn't queued, so anybody can reset
these values.
+
error = -EPERM;
if ((!info || ((unsigned long)info != 1 &&
(unsigned long)info != 2 && SI_FROMUSER(info)))
diff -urB linux-2.6.9.orig/security/selinux/nlmsgtab.c
linux-2.6.9/security/selinux/nlmsgtab.c
--- linux-2.6.9.orig/security/selinux/nlmsgtab.c 2005-04-26 15:42:02.000000000 -0400
+++ linux-2.6.9/security/selinux/nlmsgtab.c 2005-04-26 16:09:46.000000000 -0400
@@ -97,6 +97,7 @@
{ AUDIT_WATCH_INS, NETLINK_AUDIT_SOCKET__NLMSG_WRITE },
{ AUDIT_WATCH_REM, NETLINK_AUDIT_SOCKET__NLMSG_WRITE },
{ AUDIT_WATCH_LIST, NETLINK_AUDIT_SOCKET__NLMSG_READPRIV },
+ { AUDIT_TERM_INFO, NETLINK_AUDIT_SOCKET__NLMSG_READ },
};