From 83c7d09173fdb6b06b109e65895392db3e49ac9c Mon Sep 17 00:00:00 2001 From: Date: Fri, 29 Apr 2005 15:54:44 +0100 Subject: AUDIT: Avoid log pollution by untrusted strings. We log strings from userspace, such as arguments to open(). These could be formatted to contain \n followed by fake audit log entries. Provide a function for logging such strings, which gives a hex dump when the string contains anything but basic printable ASCII characters. Use it for logging filenames. Signed-off-by: David Woodhouse --- kernel/audit.c | 23 +++++++++++++++++++++++ kernel/auditsc.c | 7 ++++--- 2 files changed, 27 insertions(+), 3 deletions(-) (limited to 'kernel') diff --git a/kernel/audit.c b/kernel/audit.c index 0f84dd7af2..dca7b99615 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -720,6 +720,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 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 6f1931381b..00e87ffff1 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -696,9 +696,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", -- cgit v1.2.2 From 81b7854d52d35ed2353dd47033ae630d18322a2d Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Fri, 29 Apr 2005 15:59:11 +0100 Subject: audit_log_untrustedstring() warning fix kernel/audit.c: In function `audit_log_untrustedstring': kernel/audit.c:736: warning: comparison is always false due to limited range of data type Signed-off-by: Andrew Morton Signed-off-by: David Woodhouse --- kernel/audit.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'kernel') diff --git a/kernel/audit.c b/kernel/audit.c index dca7b99615..e7bff8000d 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -730,7 +730,7 @@ void audit_log_hex(struct audit_buffer *ab, const unsigned char *buf, size_t len void audit_log_untrustedstring(struct audit_buffer *ab, const char *string) { - const char *p = string; + const unsigned char *p = string; while (*p) { if (*p == '"' || *p == ' ' || *p < 0x20 || *p > 0x7f) { -- cgit v1.2.2 From 2fd6f58ba6efc82ea2c9c2630f7ff5ed9eeaf34a Mon Sep 17 00:00:00 2001 From: Date: Fri, 29 Apr 2005 16:08:28 +0100 Subject: [AUDIT] Don't allow ptrace to fool auditing, log arch of audited syscalls. We were calling ptrace_notify() after auditing the syscall and arguments, but the debugger could have _changed_ them before the syscall was actually invoked. Reorder the calls to fix that. While we're touching ever call to audit_syscall_entry(), we also make it take an extra argument: the architecture of the syscall which was made, because some architectures allow more than one type of syscall. Also add an explicit success/failure flag to audit_syscall_exit(), for the benefit of architectures which return that in a condition register rather than only returning a single register. Change type of syscall return value to 'long' not 'int'. Signed-off-by: David Woodhouse --- kernel/auditsc.c | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) (limited to 'kernel') diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 00e87ffff1..77e92592de 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -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; @@ -348,6 +349,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 +360,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 +653,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" @@ -773,7 +781,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) { @@ -827,6 +835,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; @@ -850,13 +859,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 @@ -869,6 +878,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; -- cgit v1.2.2 From d812ddbb89e323d054a7d073466225966c8350c8 Mon Sep 17 00:00:00 2001 From: Steve Grubb Date: Fri, 29 Apr 2005 16:09:52 +0100 Subject: [AUDIT] Fix signedness of 'serial' in various routines. Attached is a patch that corrects a signed/unsigned warning. I also noticed that we needlessly init serial to 0. That only needs to occur if the kernel was compiled without the audit system. -Steve Grubb Signed-off-by: David Woodhouse --- kernel/audit.c | 6 ++++-- kernel/auditsc.c | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) (limited to 'kernel') diff --git a/kernel/audit.c b/kernel/audit.c index e7bff8000d..aa35422c0c 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -620,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; @@ -669,8 +669,10 @@ struct audit_buffer *audit_log_start(struct audit_context *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; diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 77e92592de..49ecd707b9 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -992,7 +992,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; -- cgit v1.2.2 From c7fcb0ee74ef4cfdea02befacb55945c93641e44 Mon Sep 17 00:00:00 2001 From: Peter Martuccelli Date: Fri, 29 Apr 2005 16:10:24 +0100 Subject: [AUDIT] Avoid using %*.*s format strings. They don't seem to work correctly (investigation ongoing), but we don't actually need to do it anyway. Patch from Peter Martuccelli Signed-off-by: David Woodhouse --- kernel/audit.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'kernel') diff --git a/kernel/audit.c b/kernel/audit.c index aa35422c0c..42ce282728 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -540,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; -- cgit v1.2.2 From 85c8721ff3bc96b702427a440616079e8daf8a2f Mon Sep 17 00:00:00 2001 From: Date: Fri, 29 Apr 2005 16:23:29 +0100 Subject: audit: update pointer to userspace tools, remove emacs mode tags --- kernel/audit.c | 4 ++-- kernel/auditsc.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'kernel') diff --git a/kernel/audit.c b/kernel/audit.c index 42ce282728..58c7d7e472 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 diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 49ecd707b9..9ff2c1b103 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. -- cgit v1.2.2 From c94c257c88c517f251da273a15c654224c7b6e21 Mon Sep 17 00:00:00 2001 From: Serge Hallyn Date: Fri, 29 Apr 2005 16:27:17 +0100 Subject: Add audit uid to netlink credentials Most audit control messages are sent over netlink.In order to properly log the identity of the sender of audit control messages, we would like to add the loginuid to the netlink_creds structure, as per the attached patch. Signed-off-by: Serge Hallyn Signed-off-by: David Woodhouse --- kernel/audit.c | 46 +++++++++++++++++++++++++--------------------- kernel/auditsc.c | 5 ++++- 2 files changed, 29 insertions(+), 22 deletions(-) (limited to 'kernel') diff --git a/kernel/audit.c b/kernel/audit.c index 58c7d7e472..587d3b2eba 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -239,36 +239,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 +276,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 +344,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 +352,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 +373,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 +415,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 diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 9ff2c1b103..66148f81d7 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -251,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; @@ -286,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; @@ -295,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; -- cgit v1.2.2 From 37509e749dc2072e667db806ef24b9e897f61b8a Mon Sep 17 00:00:00 2001 From: Chris Wright Date: Fri, 29 Apr 2005 17:19:14 +0100 Subject: [AUDIT] Requeue messages at head of queue, up to audit_backlog If netlink_unicast() fails, requeue the skb back at the head of the queue it just came from, instead of the tail. And do so unless we've exceeded the audit_backlog limit; not according to some other arbitrary limit. From: Chris Wright Signed-off-by: David Woodhouse --- kernel/audit.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'kernel') diff --git a/kernel/audit.c b/kernel/audit.c index 587d3b2eba..4a697c73fa 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -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) @@ -526,9 +525,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; } @@ -666,7 +665,6 @@ 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) -- cgit v1.2.2 From 456be6cd90dbbb9b0ea01d56932d56d110d51cf7 Mon Sep 17 00:00:00 2001 From: Steve Grubb Date: Fri, 29 Apr 2005 17:30:07 +0100 Subject: [AUDIT] LOGIN message credentials Attached is a new patch that solves the issue of getting valid credentials into the LOGIN message. The current code was assuming that the audit context had already been copied. This is not always the case for LOGIN messages. To solve the problem, the patch passes the task struct to the function that emits the message where it can get valid credentials. Signed-off-by: Steve Grubb Signed-off-by: David Woodhouse --- kernel/auditsc.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'kernel') diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 66148f81d7..37b3ac94bc 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -1010,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; } -- cgit v1.2.2 From 0dd8e06bdaa0a97e706ee1a489a1f6176c4ddc64 Mon Sep 17 00:00:00 2001 From: Chris Wright Date: Tue, 3 May 2005 14:01:15 +0100 Subject: [PATCH] add new audit data to last skb When adding more formatted audit data to an skb for delivery to userspace, the kernel will attempt to reuse an skb that has spare room. However, if the audit message has already been fragmented to multiple skb's, the search for spare room in the skb uses the head of the list. This will corrupt the audit message with trailing bytes being placed midway through the stream. Fix is to look at the end of the list. Signed-off-by: Chris Wright Signed-off-by: David Woodhouse --- kernel/audit.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'kernel') diff --git a/kernel/audit.c b/kernel/audit.c index 4a697c73fa..00455a9cf0 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -486,7 +486,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) { -- cgit v1.2.2