[PATCH] Auditd shutdown credentials

Chris Wright chrisw at osdl.org
Wed Apr 27 20:09:34 UTC 2005


* Steve Grubb (sgrubb at redhat.com) wrote:
> The attached patch addresses the problem with getting the audit daemon 
> shutdown credential information. 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. The 
> hook is after the DAC & MAC checks and before the function returns. Racing 
> threads could overwrite the uid & pid - but they would have to be root and 
> have policy that allows signalling the audit daemon. That should be a 
> manageable risk.

Thanks, that does fix pure spoofing.  It's still just a best guess since
it could be pid from one thread and uid from another (simple spinlock
would at least guarantee consistency).  If it were queued, you'd be able
to replay the history (at a cost).

> The userspace component will be released later in audit 0.7.2. When it 
> receives the TERM signal, it queries the kernel for shutdown information. 
> When it receives it, it writes the message and exits. The message looks 
> like this:
> 
> type=DAEMON msg=auditd(1114551182.000) auditd normal halt, sending pid=2650 
> uid=525, auditd pid=1685
> 
> Signed-off-by: Steve Grubb <sgrubb at redhat.com>

> 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-27 11:23:26.000000000 -0400
> +++ linux-2.6.9/include/linux/audit.h	2005-04-27 11:22:51.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)

I might have gotten mixed up since this audit.h is different from mine,
but it looks like this symbol is declared for CONFIG_AUDIT, whereas
definiion is under CONFIG_AUDITSYSCALL

>  #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-27 11:23:29.000000000 -0400
> +++ linux-2.6.9/kernel/audit.c	2005-04-27 11:22:51.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;
> +
>  /* 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));

Hmmm, there's still room trouble here.  The queue could be full, or you'd
still need to drain all messages.  So you can guarantee that if you read
until queue is empty you either got this message, or it was dropped (not
the best guarantee).  Would some trivially simple sysfs file help you?

> +		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;

These can go at declaration

>  	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-27 11:23:29.000000000 -0400
> +++ linux-2.6.9/kernel/auditsc.c	2005-04-27 11:22:51.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) {

It's impossible to use on SIGKILL, since auditd can't catch that signal.

> +			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;
> +		}
> +	}
> +}

Any reason this should be unavailable when syscall auditing is off?
Perhaps it should be in audit core, and then make the pid/uid bits
static again.

> +
> 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-27 11:23:28.000000000 -0400
> +++ linux-2.6.9/kernel/signal.c	2005-04-27 11:28:12.154629232 -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>
> @@ -632,7 +633,11 @@
>  	    && (current->uid ^ t->suid) && (current->uid ^ t->uid)
>  	    && !capable(CAP_KILL))
>  		return error;
> -	return security_task_kill(t, info, sig);
> +
> +	error = security_task_kill(t, info, sig);
> +	if (!error)
> +		audit_kill_info(sig, t); /* Let audit system see the signal */
> +	return error;
>  }
>  
>  /* forward decl */
> 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-27 11:23:31.000000000 -0400
> +++ linux-2.6.9/security/selinux/nlmsgtab.c	2005-04-27 11:22:51.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  },
>  };
>  
>  




More information about the Linux-audit mailing list