summaryrefslogtreecommitdiffstats
path: root/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'kernel')
-rw-r--r--kernel/audit.c93
-rw-r--r--kernel/auditsc.c47
2 files changed, 91 insertions, 49 deletions
diff --git a/kernel/audit.c b/kernel/audit.c
index ac26d4d960d..9c4f1af0c79 100644
--- a/kernel/audit.c
+++ b/kernel/audit.c
@@ -1,4 +1,4 @@
-/* audit.c -- Auditing support -*- linux-c -*-
+/* audit.c -- Auditing support
* Gateway between the kernel (e.g., selinux) and the user-space audit daemon.
* System-call specific features have moved to auditsc.c
*
@@ -38,7 +38,7 @@
* 6) Support low-overhead kernel-based filtering to minimize the
* information that must be passed to user-space.
*
- * Example user-space utilities: http://people.redhat.com/faith/audit/
+ * Example user-space utilities: http://people.redhat.com/sgrubb/audit/
*/
#include <linux/init.h>
@@ -142,7 +142,6 @@ struct audit_buffer {
int total;
int type;
int pid;
- int count; /* Times requeued */
};
void audit_set_type(struct audit_buffer *ab, int type)
@@ -239,36 +238,36 @@ void audit_log_lost(const char *message)
}
-static int audit_set_rate_limit(int limit)
+static int audit_set_rate_limit(int limit, uid_t loginuid)
{
int old = audit_rate_limit;
audit_rate_limit = limit;
- audit_log(current->audit_context, "audit_rate_limit=%d old=%d",
- audit_rate_limit, old);
+ audit_log(NULL, "audit_rate_limit=%d old=%d by auid %u",
+ audit_rate_limit, old, loginuid);
return old;
}
-static int audit_set_backlog_limit(int limit)
+static int audit_set_backlog_limit(int limit, uid_t loginuid)
{
int old = audit_backlog_limit;
audit_backlog_limit = limit;
- audit_log(current->audit_context, "audit_backlog_limit=%d old=%d",
- audit_backlog_limit, old);
+ audit_log(NULL, "audit_backlog_limit=%d old=%d by auid %u",
+ audit_backlog_limit, old, loginuid);
return old;
}
-static int audit_set_enabled(int state)
+static int audit_set_enabled(int state, uid_t loginuid)
{
int old = audit_enabled;
if (state != 0 && state != 1)
return -EINVAL;
audit_enabled = state;
- audit_log(current->audit_context, "audit_enabled=%d old=%d",
- audit_enabled, old);
+ audit_log(NULL, "audit_enabled=%d old=%d by auid %u",
+ audit_enabled, old, loginuid);
return old;
}
-static int audit_set_failure(int state)
+static int audit_set_failure(int state, uid_t loginuid)
{
int old = audit_failure;
if (state != AUDIT_FAIL_SILENT
@@ -276,8 +275,8 @@ static int audit_set_failure(int state)
&& state != AUDIT_FAIL_PANIC)
return -EINVAL;
audit_failure = state;
- audit_log(current->audit_context, "audit_failure=%d old=%d",
- audit_failure, old);
+ audit_log(NULL, "audit_failure=%d old=%d by auid %u",
+ audit_failure, old, loginuid);
return old;
}
@@ -344,6 +343,7 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
int err;
struct audit_buffer *ab;
u16 msg_type = nlh->nlmsg_type;
+ uid_t loginuid; /* loginuid of sender */
err = audit_netlink_ok(NETLINK_CB(skb).eff_cap, msg_type);
if (err)
@@ -351,6 +351,7 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
pid = NETLINK_CREDS(skb)->pid;
uid = NETLINK_CREDS(skb)->uid;
+ loginuid = NETLINK_CB(skb).loginuid;
seq = nlh->nlmsg_seq;
data = NLMSG_DATA(nlh);
@@ -371,34 +372,36 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
return -EINVAL;
status_get = (struct audit_status *)data;
if (status_get->mask & AUDIT_STATUS_ENABLED) {
- err = audit_set_enabled(status_get->enabled);
+ err = audit_set_enabled(status_get->enabled, loginuid);
if (err < 0) return err;
}
if (status_get->mask & AUDIT_STATUS_FAILURE) {
- err = audit_set_failure(status_get->failure);
+ err = audit_set_failure(status_get->failure, loginuid);
if (err < 0) return err;
}
if (status_get->mask & AUDIT_STATUS_PID) {
int old = audit_pid;
audit_pid = status_get->pid;
- audit_log(current->audit_context,
- "audit_pid=%d old=%d", audit_pid, old);
+ audit_log(NULL, "audit_pid=%d old=%d by auid %u",
+ audit_pid, old, loginuid);
}
if (status_get->mask & AUDIT_STATUS_RATE_LIMIT)
- audit_set_rate_limit(status_get->rate_limit);
+ audit_set_rate_limit(status_get->rate_limit, loginuid);
if (status_get->mask & AUDIT_STATUS_BACKLOG_LIMIT)
- audit_set_backlog_limit(status_get->backlog_limit);
+ audit_set_backlog_limit(status_get->backlog_limit,
+ loginuid);
break;
case AUDIT_USER:
ab = audit_log_start(NULL);
if (!ab)
break; /* audit_panic has been called */
audit_log_format(ab,
- "user pid=%d uid=%d length=%d msg='%.1024s'",
+ "user pid=%d uid=%d length=%d loginuid=%u"
+ " msg='%.1024s'",
pid, uid,
(int)(nlh->nlmsg_len
- ((char *)data - (char *)nlh)),
- (char *)data);
+ loginuid, (char *)data);
ab->type = AUDIT_USER;
ab->pid = pid;
audit_log_end(ab);
@@ -411,7 +414,7 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
case AUDIT_LIST:
#ifdef CONFIG_AUDITSYSCALL
err = audit_receive_filter(nlh->nlmsg_type, NETLINK_CB(skb).pid,
- uid, seq, data);
+ uid, seq, data, loginuid);
#else
err = -EOPNOTSUPP;
#endif
@@ -480,7 +483,7 @@ static void audit_log_move(struct audit_buffer *ab)
if (ab->len == 0)
return;
- skb = skb_peek(&ab->sklist);
+ skb = skb_peek_tail(&ab->sklist);
if (!skb || skb_tailroom(skb) <= ab->len + extra) {
skb = alloc_skb(2 * ab->len + extra, GFP_ATOMIC);
if (!skb) {
@@ -519,9 +522,9 @@ static inline int audit_log_drain(struct audit_buffer *ab)
retval = netlink_unicast(audit_sock, skb, audit_pid,
MSG_DONTWAIT);
}
- if (retval == -EAGAIN && ab->count < 5) {
- ++ab->count;
- skb_queue_tail(&ab->sklist, skb);
+ if (retval == -EAGAIN &&
+ (atomic_read(&audit_backlog)) < audit_backlog_limit) {
+ skb_queue_head(&ab->sklist, skb);
audit_log_end_irq(ab);
return 1;
}
@@ -537,8 +540,8 @@ static inline int audit_log_drain(struct audit_buffer *ab)
if (!audit_pid) { /* No daemon */
int offset = ab->nlh ? NLMSG_SPACE(0) : 0;
int len = skb->len - offset;
- printk(KERN_ERR "%*.*s\n",
- len, len, skb->data + offset);
+ skb->data[offset + len] = '\0';
+ printk(KERN_ERR "%s\n", skb->data + offset);
}
kfree_skb(skb);
ab->nlh = NULL;
@@ -617,7 +620,7 @@ struct audit_buffer *audit_log_start(struct audit_context *ctx)
struct audit_buffer *ab = NULL;
unsigned long flags;
struct timespec t;
- int serial = 0;
+ unsigned int serial;
if (!audit_initialized)
return NULL;
@@ -659,15 +662,16 @@ struct audit_buffer *audit_log_start(struct audit_context *ctx)
ab->total = 0;
ab->type = AUDIT_KERNEL;
ab->pid = 0;
- ab->count = 0;
#ifdef CONFIG_AUDITSYSCALL
if (ab->ctx)
audit_get_stamp(ab->ctx, &t, &serial);
else
#endif
+ {
t = CURRENT_TIME;
-
+ serial = 0;
+ }
audit_log_format(ab, "audit(%lu.%03lu:%u): ",
t.tv_sec, t.tv_nsec/1000000, serial);
return ab;
@@ -717,6 +721,29 @@ void audit_log_format(struct audit_buffer *ab, const char *fmt, ...)
va_end(args);
}
+void audit_log_hex(struct audit_buffer *ab, const unsigned char *buf, size_t len)
+{
+ int i;
+
+ for (i=0; i<len; i++)
+ audit_log_format(ab, "%02x", buf[i]);
+}
+
+void audit_log_untrustedstring(struct audit_buffer *ab, const char *string)
+{
+ const unsigned char *p = string;
+
+ while (*p) {
+ if (*p == '"' || *p == ' ' || *p < 0x20 || *p > 0x7f) {
+ audit_log_hex(ab, string, strlen(string));
+ return;
+ }
+ p++;
+ }
+ audit_log_format(ab, "\"%s\"", string);
+}
+
+
/* This is a helper-function to print the d_path without using a static
* buffer or allocating another buffer in addition to the one in
* audit_buffer. */
diff --git a/kernel/auditsc.c b/kernel/auditsc.c
index 6f1931381bc..37b3ac94bc4 100644
--- a/kernel/auditsc.c
+++ b/kernel/auditsc.c
@@ -1,4 +1,4 @@
-/* auditsc.c -- System-call auditing support -*- linux-c -*-
+/* auditsc.c -- System-call auditing support
* Handles all system-call specific auditing features.
*
* Copyright 2003-2004 Red Hat Inc., Durham, North Carolina.
@@ -123,7 +123,7 @@ struct audit_context {
int major; /* syscall number */
unsigned long argv[4]; /* syscall arguments */
int return_valid; /* return code is valid */
- int return_code;/* syscall return code */
+ long return_code;/* syscall return code */
int auditable; /* 1 if record should be written */
int name_count;
struct audit_names names[AUDIT_NAMES];
@@ -135,6 +135,7 @@ struct audit_context {
uid_t uid, euid, suid, fsuid;
gid_t gid, egid, sgid, fsgid;
unsigned long personality;
+ int arch;
#if AUDIT_DEBUG
int put_count;
@@ -250,7 +251,8 @@ static int audit_copy_rule(struct audit_rule *d, struct audit_rule *s)
return 0;
}
-int audit_receive_filter(int type, int pid, int uid, int seq, void *data)
+int audit_receive_filter(int type, int pid, int uid, int seq, void *data,
+ uid_t loginuid)
{
u32 flags;
struct audit_entry *entry;
@@ -285,6 +287,7 @@ int audit_receive_filter(int type, int pid, int uid, int seq, void *data)
err = audit_add_rule(entry, &audit_entlist);
if (!err && (flags & AUDIT_AT_EXIT))
err = audit_add_rule(entry, &audit_extlist);
+ audit_log(NULL, "auid %u added an audit rule\n", loginuid);
break;
case AUDIT_DEL:
flags =((struct audit_rule *)data)->flags;
@@ -294,6 +297,7 @@ int audit_receive_filter(int type, int pid, int uid, int seq, void *data)
err = audit_del_rule(data, &audit_entlist);
if (!err && (flags & AUDIT_AT_EXIT))
err = audit_del_rule(data, &audit_extlist);
+ audit_log(NULL, "auid %u removed an audit rule\n", loginuid);
break;
default:
return -EINVAL;
@@ -348,6 +352,10 @@ static int audit_filter_rules(struct task_struct *tsk,
case AUDIT_PERS:
result = (tsk->personality == value);
break;
+ case AUDIT_ARCH:
+ if (ctx)
+ result = (ctx->arch == value);
+ break;
case AUDIT_EXIT:
if (ctx && ctx->return_valid)
@@ -355,7 +363,7 @@ static int audit_filter_rules(struct task_struct *tsk,
break;
case AUDIT_SUCCESS:
if (ctx && ctx->return_valid)
- result = (ctx->return_code >= 0);
+ result = (ctx->return_valid == AUDITSC_SUCCESS);
break;
case AUDIT_DEVMAJOR:
if (ctx) {
@@ -648,8 +656,11 @@ static void audit_log_exit(struct audit_context *context)
audit_log_format(ab, "syscall=%d", context->major);
if (context->personality != PER_LINUX)
audit_log_format(ab, " per=%lx", context->personality);
+ audit_log_format(ab, " arch=%x", context->arch);
if (context->return_valid)
- audit_log_format(ab, " exit=%d", context->return_code);
+ audit_log_format(ab, " success=%s exit=%ld",
+ (context->return_valid==AUDITSC_SUCCESS)?"yes":"no",
+ context->return_code);
audit_log_format(ab,
" a0=%lx a1=%lx a2=%lx a3=%lx items=%d"
" pid=%d loginuid=%d uid=%d gid=%d"
@@ -696,9 +707,10 @@ static void audit_log_exit(struct audit_context *context)
if (!ab)
continue; /* audit_panic has been called */
audit_log_format(ab, "item=%d", i);
- if (context->names[i].name)
- audit_log_format(ab, " name=%s",
- context->names[i].name);
+ if (context->names[i].name) {
+ audit_log_format(ab, " name=");
+ audit_log_untrustedstring(ab, context->names[i].name);
+ }
if (context->names[i].ino != (unsigned long)-1)
audit_log_format(ab, " inode=%lu dev=%02x:%02x mode=%#o"
" uid=%d gid=%d rdev=%02x:%02x",
@@ -772,7 +784,7 @@ static inline unsigned int audit_serial(void)
* then the record will be written at syscall exit time (otherwise, it
* will only be written if another part of the kernel requests that it
* be written). */
-void audit_syscall_entry(struct task_struct *tsk, int major,
+void audit_syscall_entry(struct task_struct *tsk, int arch, int major,
unsigned long a1, unsigned long a2,
unsigned long a3, unsigned long a4)
{
@@ -826,6 +838,7 @@ void audit_syscall_entry(struct task_struct *tsk, int major,
if (!audit_enabled)
return;
+ context->arch = arch;
context->major = major;
context->argv[0] = a1;
context->argv[1] = a2;
@@ -849,13 +862,13 @@ void audit_syscall_entry(struct task_struct *tsk, int major,
* filtering, or because some other part of the kernel write an audit
* message), then write out the syscall information. In call cases,
* free the names stored from getname(). */
-void audit_syscall_exit(struct task_struct *tsk, int return_code)
+void audit_syscall_exit(struct task_struct *tsk, int valid, long return_code)
{
struct audit_context *context;
get_task_struct(tsk);
task_lock(tsk);
- context = audit_get_context(tsk, 1, return_code);
+ context = audit_get_context(tsk, valid, return_code);
task_unlock(tsk);
/* Not having a context here is ok, since the parent may have
@@ -868,6 +881,7 @@ void audit_syscall_exit(struct task_struct *tsk, int return_code)
context->in_syscall = 0;
context->auditable = 0;
+
if (context->previous) {
struct audit_context *new_context = context->previous;
context->previous = NULL;
@@ -981,7 +995,7 @@ void audit_inode(const char *name, const struct inode *inode)
}
void audit_get_stamp(struct audit_context *ctx,
- struct timespec *t, int *serial)
+ struct timespec *t, unsigned int *serial)
{
if (ctx) {
t->tv_sec = ctx->ctime.tv_sec;
@@ -996,20 +1010,21 @@ void audit_get_stamp(struct audit_context *ctx,
extern int audit_set_type(struct audit_buffer *ab, int type);
-int audit_set_loginuid(struct audit_context *ctx, uid_t loginuid)
+int audit_set_loginuid(struct task_struct *task, uid_t loginuid)
{
- if (ctx) {
+ if (task->audit_context) {
struct audit_buffer *ab;
ab = audit_log_start(NULL);
if (ab) {
audit_log_format(ab, "login pid=%d uid=%u "
"old loginuid=%u new loginuid=%u",
- ctx->pid, ctx->uid, ctx->loginuid, loginuid);
+ task->pid, task->uid,
+ task->audit_context->loginuid, loginuid);
audit_set_type(ab, AUDIT_LOGIN);
audit_log_end(ab);
}
- ctx->loginuid = loginuid;
+ task->audit_context->loginuid = loginuid;
}
return 0;
}