[PATCH] Race-free auditd shutdown credentials
Chris Wright
chrisw at osdl.org
Tue Apr 26 21:53:36 UTC 2005
* Steve Grubb (sgrubb at 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 },
> };
>
>
More information about the Linux-audit
mailing list