* 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  },
  };