From 37eebe39c9731a76535f08de455db97eb93894ae Mon Sep 17 00:00:00 2001 From: Matvejchikov Ilya Date: Tue, 13 Dec 2011 23:09:08 +0300 Subject: [PATCH 01/31] audit: improve GID/EGID comparation logic It is useful to extend GID/EGID comparation logic to be able to match not only the exact EID/EGID values but the group/egroup also. Signed-off-by: Matvejchikov Ilya Signed-off-by: Eric Paris --- kernel/auditsc.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/kernel/auditsc.c b/kernel/auditsc.c index a371f857a0a9..77c705c302f7 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -633,9 +633,23 @@ static int audit_filter_rules(struct task_struct *tsk, break; case AUDIT_GID: result = audit_gid_comparator(cred->gid, f->op, f->gid); + if (f->op == Audit_equal) { + if (!result) + result = in_group_p(f->gid); + } else if (f->op == Audit_not_equal) { + if (result) + result = !in_group_p(f->gid); + } break; case AUDIT_EGID: result = audit_gid_comparator(cred->egid, f->op, f->gid); + if (f->op == Audit_equal) { + if (!result) + result = in_egroup_p(f->gid); + } else if (f->op == Audit_not_equal) { + if (result) + result = !in_egroup_p(f->gid); + } break; case AUDIT_SGID: result = audit_gid_comparator(cred->sgid, f->op, f->gid); From b551d1d98197b7dd58fc3ead8d4d01830c09567d Mon Sep 17 00:00:00 2001 From: Richard Guy Briggs Date: Thu, 24 Jan 2013 13:15:10 -0500 Subject: [PATCH 02/31] audit: refactor hold queue flush The hold queue flush code is an autonomous chunk of code that can be refactored, removed from kauditd_thread() into flush_hold_queue() and flattenned for better legibility. Signed-off-by: Richard Guy Briggs Signed-off-by: Eric Paris --- kernel/audit.c | 62 ++++++++++++++++++++++++++++++++------------------ 1 file changed, 40 insertions(+), 22 deletions(-) diff --git a/kernel/audit.c b/kernel/audit.c index d596e5355f15..4bf486c3e9e8 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -417,34 +417,52 @@ static void kauditd_send_skb(struct sk_buff *skb) consume_skb(skb); } +/* + * flush_hold_queue - empty the hold queue if auditd appears + * + * If auditd just started, drain the queue of messages already + * sent to syslog/printk. Remember loss here is ok. We already + * called audit_log_lost() if it didn't go out normally. so the + * race between the skb_dequeue and the next check for audit_pid + * doesn't matter. + * + * If you ever find kauditd to be too slow we can get a perf win + * by doing our own locking and keeping better track if there + * are messages in this queue. I don't see the need now, but + * in 5 years when I want to play with this again I'll see this + * note and still have no friggin idea what i'm thinking today. + */ +static void flush_hold_queue(void) +{ + struct sk_buff *skb; + + if (!audit_default || !audit_pid) + return; + + skb = skb_dequeue(&audit_skb_hold_queue); + if (likely(!skb)) + return; + + while (skb && audit_pid) { + kauditd_send_skb(skb); + skb = skb_dequeue(&audit_skb_hold_queue); + } + + /* + * if auditd just disappeared but we + * dequeued an skb we need to drop ref + */ + if (skb) + consume_skb(skb); +} + static int kauditd_thread(void *dummy) { struct sk_buff *skb; set_freezable(); while (!kthread_should_stop()) { - /* - * if auditd just started drain the queue of messages already - * sent to syslog/printk. remember loss here is ok. we already - * called audit_log_lost() if it didn't go out normally. so the - * race between the skb_dequeue and the next check for audit_pid - * doesn't matter. - * - * if you ever find kauditd to be too slow we can get a perf win - * by doing our own locking and keeping better track if there - * are messages in this queue. I don't see the need now, but - * in 5 years when I want to play with this again I'll see this - * note and still have no friggin idea what i'm thinking today. - */ - if (audit_default && audit_pid) { - skb = skb_dequeue(&audit_skb_hold_queue); - if (unlikely(skb)) { - while (skb && audit_pid) { - kauditd_send_skb(skb); - skb = skb_dequeue(&audit_skb_hold_queue); - } - } - } + flush_hold_queue(); skb = skb_dequeue(&audit_skb_queue); wake_up(&audit_backlog_wait); From 3320c5133dd83df58b8fbc529b5419e02ca16fe6 Mon Sep 17 00:00:00 2001 From: Richard Guy Briggs Date: Thu, 24 Jan 2013 13:15:11 -0500 Subject: [PATCH 03/31] audit: flatten kauditd_thread wait queue code The wait queue control code in kauditd_thread() was nested deeper than necessary. The function has been flattened for better legibility. Signed-off-by: Richard Guy Briggs Signed-off-by: Eric Paris --- kernel/audit.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/kernel/audit.c b/kernel/audit.c index 4bf486c3e9e8..1531efbd11e2 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -458,10 +458,11 @@ static void flush_hold_queue(void) static int kauditd_thread(void *dummy) { - struct sk_buff *skb; - set_freezable(); while (!kthread_should_stop()) { + struct sk_buff *skb; + DECLARE_WAITQUEUE(wait, current); + flush_hold_queue(); skb = skb_dequeue(&audit_skb_queue); @@ -471,19 +472,18 @@ static int kauditd_thread(void *dummy) kauditd_send_skb(skb); else audit_printk_skb(skb); - } else { - DECLARE_WAITQUEUE(wait, current); - set_current_state(TASK_INTERRUPTIBLE); - add_wait_queue(&kauditd_wait, &wait); - - if (!skb_queue_len(&audit_skb_queue)) { - try_to_freeze(); - schedule(); - } - - __set_current_state(TASK_RUNNING); - remove_wait_queue(&kauditd_wait, &wait); + continue; } + set_current_state(TASK_INTERRUPTIBLE); + add_wait_queue(&kauditd_wait, &wait); + + if (!skb_queue_len(&audit_skb_queue)) { + try_to_freeze(); + schedule(); + } + + __set_current_state(TASK_RUNNING); + remove_wait_queue(&kauditd_wait, &wait); } return 0; } From 6ff5e45985c2fcb97947818f66d1eeaf9d6600b2 Mon Sep 17 00:00:00 2001 From: Richard Guy Briggs Date: Thu, 24 Jan 2013 13:15:12 -0500 Subject: [PATCH 04/31] audit: move kaudit thread start from auditd registration to kaudit init The kauditd_thread() task was started only after the auditd userspace daemon registers itself with kaudit. This was fine when only auditd consumed messages from the kaudit netlink unicast socket. With the addition of a multicast group to that socket it is more convenient to have the thread start on init of the kaudit kernel subsystem. Signed-off-by: Richard Guy Briggs Signed-off-by: Eric Paris --- kernel/audit.c | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/kernel/audit.c b/kernel/audit.c index 1531efbd11e2..02a5d9eefa82 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -676,16 +676,6 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) if (err) return err; - /* As soon as there's any sign of userspace auditd, - * start kauditd to talk to it */ - if (!kauditd_task) - kauditd_task = kthread_run(kauditd_thread, NULL, "kauditd"); - if (IS_ERR(kauditd_task)) { - err = PTR_ERR(kauditd_task); - kauditd_task = NULL; - return err; - } - loginuid = audit_get_loginuid(current); sessionid = audit_get_sessionid(current); security_task_getsecid(current, &sid); @@ -974,6 +964,10 @@ static int __init audit_init(void) else audit_sock->sk_sndtimeo = MAX_SCHEDULE_TIMEOUT; + kauditd_task = kthread_run(kauditd_thread, NULL, "kauditd"); + if (IS_ERR(kauditd_task)) + return PTR_ERR(kauditd_task); + skb_queue_head_init(&audit_skb_queue); skb_queue_head_init(&audit_skb_hold_queue); audit_initialized = AUDIT_INITIALIZED; From cdee3904b4ce7c03d1013ed6dd704b43ae7fc2e9 Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Wed, 9 Jan 2013 10:46:17 +1100 Subject: [PATCH 05/31] audit: Syscall rules are not applied to existing processes on non-x86 Commit b05d8447e782 (audit: inline audit_syscall_entry to reduce burden on archs) changed audit_syscall_entry to check for a dummy context before calling __audit_syscall_entry. Unfortunately the dummy context state is maintained in __audit_syscall_entry so once set it never gets cleared, even if the audit rules change. As a result, if there are no auditing rules when a process starts then it will never be subject to any rules added later. x86 doesn't see this because it has an assembly fast path that calls directly into __audit_syscall_entry. I noticed this issue when working on audit performance optimisations. I wrote a set of simple test cases available at: http://ozlabs.org/~anton/junkcode/audit_tests.tar.gz 02_new_rule.py fails without the patch and passes with it. The test case clears all rules, starts a process, adds a rule then verifies the process produces a syscall audit record. Signed-off-by: Anton Blanchard Cc: # 3.3+ Signed-off-by: Eric Paris --- include/linux/audit.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/audit.h b/include/linux/audit.h index 5a6d718adf34..37464c592c92 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -120,7 +120,7 @@ static inline void audit_syscall_entry(int arch, int major, unsigned long a0, unsigned long a1, unsigned long a2, unsigned long a3) { - if (unlikely(!audit_dummy_context())) + if (unlikely(current->audit_context)) __audit_syscall_entry(arch, major, a0, a1, a2, a3); } static inline void audit_syscall_exit(void *pt_regs) From 2540334adc1c7241a7f91a9cf7ec56776d3f601b Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Wed, 9 Jan 2013 10:47:36 +1100 Subject: [PATCH 06/31] powerpc: Remove static branch prediction in 64bit traced syscall path Some distros enable auditing by default which forces us through the syscall trace path. Remove the static branch prediction in our 64bit syscall handler and let the hardware do the prediction. Signed-off-by: Anton Blanchard Signed-off-by: Eric Paris --- arch/powerpc/kernel/entry_64.S | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/powerpc/kernel/entry_64.S b/arch/powerpc/kernel/entry_64.S index 3d990d3bd8ba..c20d9bf4aa26 100644 --- a/arch/powerpc/kernel/entry_64.S +++ b/arch/powerpc/kernel/entry_64.S @@ -149,7 +149,7 @@ END_FW_FTR_SECTION_IFSET(FW_FEATURE_SPLPAR) CURRENT_THREAD_INFO(r11, r1) ld r10,TI_FLAGS(r11) andi. r11,r10,_TIF_SYSCALL_T_OR_A - bne- syscall_dotrace + bne syscall_dotrace .Lsyscall_dotrace_cont: cmpldi 0,r0,NR_syscalls bge- syscall_enosys From 65ada7bc02e2dcea6dea1f11876e712d5ea7e9ba Mon Sep 17 00:00:00 2001 From: Dmitry Monakhov Date: Mon, 1 Apr 2013 11:00:00 +0400 Subject: [PATCH 07/31] audit: destroy long filenames correctly filename should be destroyed via final_putname() instead of __putname() Otherwise this result in following BUGON() in case of long names: kernel BUG at mm/slab.c:3006! Call Trace: kmem_cache_free+0x1c1/0x850 audit_putname+0x88/0x90 putname+0x73/0x80 sys_symlinkat+0x120/0x150 sys_symlink+0x16/0x20 system_call_fastpath+0x16/0x1b Introduced-in: 7950e3852 Signed-off-by: Dmitry Monakhov Reviewed-by: Jeff Layton Signed-off-by: Eric Paris --- kernel/auditsc.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 77c705c302f7..b59ffb293ded 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -1024,7 +1024,7 @@ static inline void audit_free_names(struct audit_context *context) list_for_each_entry_safe(n, next, &context->names_list, list) { list_del(&n->list); if (n->name && n->name_put) - __putname(n->name); + final_putname(n->name); if (n->should_free) kfree(n); } @@ -2050,7 +2050,7 @@ void audit_putname(struct filename *name) BUG_ON(!context); if (!context->in_syscall) { #if AUDIT_DEBUG == 2 - printk(KERN_ERR "%s:%d(:%d): __putname(%p)\n", + printk(KERN_ERR "%s:%d(:%d): final_putname(%p)\n", __FILE__, __LINE__, context->serial, name); if (context->name_count) { struct audit_names *n; @@ -2061,7 +2061,7 @@ void audit_putname(struct filename *name) n->name, n->name->name ?: "(null)"); } #endif - __putname(name); + final_putname(name); } #if AUDIT_DEBUG else { From 2950fa9d3291b90e9b7663b6a409ea37a97a5e35 Mon Sep 17 00:00:00 2001 From: Chen Gang Date: Sun, 7 Apr 2013 16:55:23 +0800 Subject: [PATCH 08/31] kernel: audit: beautify code, for extern function, better to check its parameters by itself __audit_socketcall is an extern function. better to check its parameters by itself. also can return error code, when fail (find invalid parameters). also use macro instead of real hard code number also give related comments for it. Signed-off-by: Chen Gang [eparis: fix the return value when !CONFIG_AUDIT] Signed-off-by: Eric Paris --- include/linux/audit.h | 16 +++++++++++----- kernel/auditsc.c | 9 ++++++--- net/socket.c | 6 ++++-- 3 files changed, 21 insertions(+), 10 deletions(-) diff --git a/include/linux/audit.h b/include/linux/audit.h index 37464c592c92..8f92e1dea966 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -84,6 +84,9 @@ extern int audit_classify_arch(int arch); #define AUDIT_TYPE_CHILD_DELETE 3 /* a child being deleted */ #define AUDIT_TYPE_CHILD_CREATE 4 /* a child being created */ +/* maximized args number that audit_socketcall can process */ +#define AUDITSC_ARGS 6 + struct filename; #ifdef CONFIG_AUDITSYSCALL @@ -190,7 +193,7 @@ extern void audit_log_task_info(struct audit_buffer *ab, struct task_struct *tsk extern void __audit_ipc_obj(struct kern_ipc_perm *ipcp); extern void __audit_ipc_set_perm(unsigned long qbytes, uid_t uid, gid_t gid, umode_t mode); extern int __audit_bprm(struct linux_binprm *bprm); -extern void __audit_socketcall(int nargs, unsigned long *args); +extern int __audit_socketcall(int nargs, unsigned long *args); extern int __audit_sockaddr(int len, void *addr); extern void __audit_fd_pair(int fd1, int fd2); extern void __audit_mq_open(int oflag, umode_t mode, struct mq_attr *attr); @@ -224,10 +227,11 @@ static inline int audit_bprm(struct linux_binprm *bprm) return __audit_bprm(bprm); return 0; } -static inline void audit_socketcall(int nargs, unsigned long *args) +static inline int audit_socketcall(int nargs, unsigned long *args) { if (unlikely(!audit_dummy_context())) - __audit_socketcall(nargs, args); + return __audit_socketcall(nargs, args); + return 0; } static inline int audit_sockaddr(int len, void *addr) { @@ -354,8 +358,10 @@ static inline int audit_bprm(struct linux_binprm *bprm) { return 0; } -static inline void audit_socketcall(int nargs, unsigned long *args) -{ } +static inline int audit_socketcall(int nargs, unsigned long *args) +{ + return 0; +} static inline void audit_fd_pair(int fd1, int fd2) { } static inline int audit_sockaddr(int len, void *addr) diff --git a/kernel/auditsc.c b/kernel/auditsc.c index b59ffb293ded..d57ad32db367 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -226,7 +226,7 @@ struct audit_context { union { struct { int nargs; - long args[6]; + long args[AUDITSC_ARGS]; } socketcall; struct { kuid_t uid; @@ -2491,17 +2491,20 @@ int __audit_bprm(struct linux_binprm *bprm) /** * audit_socketcall - record audit data for sys_socketcall - * @nargs: number of args + * @nargs: number of args, which should not be more than AUDITSC_ARGS. * @args: args array * */ -void __audit_socketcall(int nargs, unsigned long *args) +int __audit_socketcall(int nargs, unsigned long *args) { struct audit_context *context = current->audit_context; + if (nargs <= 0 || nargs > AUDITSC_ARGS || !args) + return -EINVAL; context->type = AUDIT_SOCKETCALL; context->socketcall.nargs = nargs; memcpy(context->socketcall.args, args, nargs * sizeof(unsigned long)); + return 0; } /** diff --git a/net/socket.c b/net/socket.c index 2ca51c719ef9..1bbc37b7a312 100644 --- a/net/socket.c +++ b/net/socket.c @@ -2436,7 +2436,7 @@ static const unsigned char nargs[21] = { SYSCALL_DEFINE2(socketcall, int, call, unsigned long __user *, args) { - unsigned long a[6]; + unsigned long a[AUDITSC_ARGS]; unsigned long a0, a1; int err; unsigned int len; @@ -2452,7 +2452,9 @@ SYSCALL_DEFINE2(socketcall, int, call, unsigned long __user *, args) if (copy_from_user(a, args, len)) return -EFAULT; - audit_socketcall(nargs[call] / sizeof(unsigned long), a); + err = audit_socketcall(nargs[call] / sizeof(unsigned long), a); + if (err) + return err; a0 = a[0]; a1 = a[1]; From 17c6ee707a32c8e67861a442f387def5b7f64cec Mon Sep 17 00:00:00 2001 From: Rakib Mullick Date: Sun, 7 Apr 2013 16:14:18 +0600 Subject: [PATCH 09/31] auditsc: Use kzalloc instead of kmalloc+memset. In function audit_alloc_context(), use kzalloc, instead of kmalloc+memset. Patch also renames audit_zero_context() to audit_set_context(), to represent it's inner workings properly. Signed-off-by: Rakib Mullick Signed-off-by: Eric Paris --- kernel/auditsc.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/kernel/auditsc.c b/kernel/auditsc.c index d57ad32db367..9dc3bae9793d 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -1048,10 +1048,9 @@ static inline void audit_free_aux(struct audit_context *context) } } -static inline void audit_zero_context(struct audit_context *context, +static inline void audit_set_context(struct audit_context *context, enum audit_state state) { - memset(context, 0, sizeof(*context)); context->state = state; context->prio = state == AUDIT_RECORD_CONTEXT ? ~0ULL : 0; } @@ -1060,9 +1059,10 @@ static inline struct audit_context *audit_alloc_context(enum audit_state state) { struct audit_context *context; - if (!(context = kmalloc(sizeof(*context), GFP_KERNEL))) + context = kzalloc(sizeof(*context), GFP_KERNEL); + if (!context) return NULL; - audit_zero_context(context, state); + audit_set_context(context, state); INIT_LIST_HEAD(&context->killed_trees); INIT_LIST_HEAD(&context->names_list); return context; From e2c5adc88a0ffd4a715f630c3b83a1d5cbfd1cff Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Mon, 8 Apr 2013 14:43:41 -0700 Subject: [PATCH 10/31] auditsc: remove audit_set_context() altogether - fold it into its caller > In function audit_alloc_context(), use kzalloc, instead of kmalloc+memset. Patch also renames audit_zero_context() to > audit_set_context(), to represent it's inner workings properly. Fair enough. I'd go futher... Cc: Al Viro Cc: Eric Paris Cc: Rakib Mullick Signed-off-by: Andrew Morton Signed-off-by: Eric Paris --- kernel/auditsc.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 9dc3bae9793d..b536d22fe56f 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -1048,13 +1048,6 @@ static inline void audit_free_aux(struct audit_context *context) } } -static inline void audit_set_context(struct audit_context *context, - enum audit_state state) -{ - context->state = state; - context->prio = state == AUDIT_RECORD_CONTEXT ? ~0ULL : 0; -} - static inline struct audit_context *audit_alloc_context(enum audit_state state) { struct audit_context *context; @@ -1062,7 +1055,8 @@ static inline struct audit_context *audit_alloc_context(enum audit_state state) context = kzalloc(sizeof(*context), GFP_KERNEL); if (!context) return NULL; - audit_set_context(context, state); + context->state = state; + context->prio = state == AUDIT_RECORD_CONTEXT ? ~0ULL : 0; INIT_LIST_HEAD(&context->killed_trees); INIT_LIST_HEAD(&context->names_list); return context; From f7616102d6f62d51cffb796d4672ad81fef00fea Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 11 Apr 2013 11:25:00 -0400 Subject: [PATCH 11/31] audit: use data= not msg= for AUDIT_USER_TTY messages Userspace parsing libraries assume that msg= is only for userspace audit records, not for user tty records. Make this consistent with the other tty records. Reported-by: Steve Grubb Signed-off-by: Eric Paris --- kernel/audit.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/audit.c b/kernel/audit.c index 02a5d9eefa82..c45e6d2809d7 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -755,7 +755,7 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) else { int size; - audit_log_format(ab, " msg="); + audit_log_format(ab, " data="); size = nlmsg_len(nlh); if (size > 0 && ((unsigned char *)data)[size - 1] == '\0') From ad395abece974e50cfd7ddd509a4faae8e238a40 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Tue, 23 Oct 2012 08:58:35 -0400 Subject: [PATCH 12/31] Audit: do not print error when LSMs disabled RHBZ: 785936 If the audit system collects a record about one process sending a signal to another process it includes in that collection the 'secid' or 'an int used to represet an LSM label.' If there is no LSM enabled it will collect a 0. The problem is that when we attempt to print that record we ask the LSM to convert the secid back to a string. Since there is no LSM it returns EOPNOTSUPP. Most code in the audit system checks if the secid is 0 and does not print LSM info in that case. The signal information code however forgot that check. Thus users will see a message in syslog indicating that converting the sid to string failed. Add the right check. Signed-off-by: Eric Paris --- kernel/auditsc.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/kernel/auditsc.c b/kernel/auditsc.c index b536d22fe56f..67df4ee1d3b6 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -1205,12 +1205,14 @@ static int audit_log_pid_context(struct audit_context *context, pid_t pid, audit_log_format(ab, "opid=%d oauid=%d ouid=%d oses=%d", pid, from_kuid(&init_user_ns, auid), from_kuid(&init_user_ns, uid), sessionid); - if (security_secid_to_secctx(sid, &ctx, &len)) { - audit_log_format(ab, " obj=(none)"); - rc = 1; - } else { - audit_log_format(ab, " obj=%s", ctx); - security_release_secctx(ctx, len); + if (sid) { + if (security_secid_to_secctx(sid, &ctx, &len)) { + audit_log_format(ab, " obj=(none)"); + rc = 1; + } else { + audit_log_format(ab, " obj=%s", ctx); + security_release_secctx(ctx, len); + } } audit_log_format(ab, " ocomm="); audit_log_untrustedstring(ab, comm); From 72199caa8dc7f71d29328069b588340201ee73d7 Mon Sep 17 00:00:00 2001 From: Gao feng Date: Fri, 12 Apr 2013 17:34:20 +0800 Subject: [PATCH 13/31] audit: remove duplicate export of audit_enabled audit_enabled has already been exported in include/linux/audit.h. and kernel/audit.h includes include/linux/audit.h, no need to export aduit_enabled again in kernel/audit.h Signed-off-by: Gao feng Signed-off-by: Eric Paris --- kernel/audit.h | 1 - 1 file changed, 1 deletion(-) diff --git a/kernel/audit.h b/kernel/audit.h index d51cba868e1b..d06ffc144f81 100644 --- a/kernel/audit.h +++ b/kernel/audit.h @@ -60,7 +60,6 @@ struct audit_entry { }; #ifdef CONFIG_AUDIT -extern int audit_enabled; extern int audit_ever_enabled; #endif From 34c474de7b4bd451396d67647ac728b0433379a9 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Tue, 16 Apr 2013 10:17:02 -0400 Subject: [PATCH 14/31] audit: fix build break when AUDIT_DEBUG == 2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Looks like this one has been around since 5195d8e21: kernel/auditsc.c: In function ‘audit_free_names’: kernel/auditsc.c:998: error: ‘i’ undeclared (first use in this function) ...and this warning: kernel/auditsc.c: In function ‘audit_putname’: kernel/auditsc.c:2045: warning: ‘i’ may be used uninitialized in this function Signed-off-by: Jeff Layton Signed-off-by: Eric Paris --- kernel/auditsc.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 67df4ee1d3b6..4baf61d39836 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -1001,6 +1001,8 @@ static inline void audit_free_names(struct audit_context *context) #if AUDIT_DEBUG == 2 if (context->put_count + context->ino_count != context->name_count) { + int i = 0; + printk(KERN_ERR "%s:%d(:%d): major=%d in_syscall=%d" " name_count=%d put_count=%d" " ino_count=%d [NOT freeing]\n", @@ -1009,7 +1011,7 @@ static inline void audit_free_names(struct audit_context *context) context->name_count, context->put_count, context->ino_count); list_for_each_entry(n, &context->names_list, list) { - printk(KERN_ERR "names[%d] = %p = %s\n", i, + printk(KERN_ERR "names[%d] = %p = %s\n", i++, n->name, n->name->name ?: "(null)"); } dump_stack(); @@ -2050,10 +2052,10 @@ void audit_putname(struct filename *name) __FILE__, __LINE__, context->serial, name); if (context->name_count) { struct audit_names *n; - int i; + int i = 0; list_for_each_entry(n, &context->names_list, list) - printk(KERN_ERR "name[%d] = %p = %s\n", i, + printk(KERN_ERR "name[%d] = %p = %s\n", i++, n->name, n->name->name ?: "(null)"); } #endif From 62062cf8a3a99a933efdac549da380f230dbe982 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Tue, 16 Apr 2013 13:08:43 -0400 Subject: [PATCH 15/31] audit: allow checking the type of audit message in the user filter When userspace sends messages to the audit system it includes a type. We want to be able to filter messages based on that type without have to do the all or nothing option currently available on the AUDIT_FILTER_TYPE filter list. Instead we should be able to use the AUDIT_FILTER_USER filter list and just use the message type as one part of the matching decision. Signed-off-by: Eric Paris --- include/linux/audit.h | 2 +- kernel/audit.c | 2 +- kernel/auditfilter.c | 28 +++++++++++++++++++++++++--- 3 files changed, 27 insertions(+), 5 deletions(-) diff --git a/include/linux/audit.h b/include/linux/audit.h index 8f92e1dea966..b26d7f121ac5 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -438,7 +438,7 @@ static inline void audit_log_secctx(struct audit_buffer *ab, u32 secid) extern int audit_update_lsm_rules(void); /* Private API (for audit.c only) */ -extern int audit_filter_user(void); +extern int audit_filter_user(int type); extern int audit_filter_type(int type); extern int audit_receive_filter(int type, int pid, int seq, void *data, size_t datasz, kuid_t loginuid, diff --git a/kernel/audit.c b/kernel/audit.c index c45e6d2809d7..132271448b89 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -737,7 +737,7 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) if (!audit_enabled && msg_type != AUDIT_USER_AVC) return 0; - err = audit_filter_user(); + err = audit_filter_user(msg_type); if (err == 1) { err = 0; if (msg_type == AUDIT_USER_TTY) { diff --git a/kernel/auditfilter.c b/kernel/auditfilter.c index f9fc54bbe06f..9e666004e0dc 100644 --- a/kernel/auditfilter.c +++ b/kernel/auditfilter.c @@ -310,6 +310,18 @@ static u32 audit_to_op(u32 op) return n; } +/* check if a field is valid for a given list */ +static int audit_field_valid(struct audit_entry *entry, struct audit_field *f) +{ + switch(f->type) { + case AUDIT_MSGTYPE: + if (entry->rule.listnr != AUDIT_FILTER_TYPE && + entry->rule.listnr != AUDIT_FILTER_USER) + return -EINVAL; + break; + }; + return 0; +} /* Translate struct audit_rule to kernel's rule respresentation. * Exists for backward compatibility with userspace. */ @@ -459,6 +471,13 @@ static struct audit_entry *audit_data_to_entry(struct audit_rule_data *data, f->gid = INVALID_GID; f->lsm_str = NULL; f->lsm_rule = NULL; + + err = audit_field_valid(entry, f); + if (err) + goto exit_free; + + err = -EINVAL; + switch(f->type) { case AUDIT_UID: case AUDIT_EUID: @@ -1354,7 +1373,7 @@ int audit_compare_dname_path(const char *dname, const char *path, int parentlen) return strncmp(p, dname, dlen); } -static int audit_filter_user_rules(struct audit_krule *rule, +static int audit_filter_user_rules(struct audit_krule *rule, int type, enum audit_state *state) { int i; @@ -1378,6 +1397,9 @@ static int audit_filter_user_rules(struct audit_krule *rule, result = audit_uid_comparator(audit_get_loginuid(current), f->op, f->uid); break; + case AUDIT_MSGTYPE: + result = audit_comparator(type, f->op, f->val); + break; case AUDIT_SUBJ_USER: case AUDIT_SUBJ_ROLE: case AUDIT_SUBJ_TYPE: @@ -1404,7 +1426,7 @@ static int audit_filter_user_rules(struct audit_krule *rule, return 1; } -int audit_filter_user(void) +int audit_filter_user(int type) { enum audit_state state = AUDIT_DISABLED; struct audit_entry *e; @@ -1412,7 +1434,7 @@ int audit_filter_user(void) rcu_read_lock(); list_for_each_entry_rcu(e, &audit_filter_list[AUDIT_FILTER_USER], list) { - if (audit_filter_user_rules(&e->rule, &state)) { + if (audit_filter_user_rules(&e->rule, type, &state)) { if (state == AUDIT_DISABLED) ret = 0; break; From ab61d38ed8cf670946d12dc46b9198b521c790ea Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Tue, 16 Apr 2013 17:26:51 -0400 Subject: [PATCH 16/31] audit: make validity checking generic We have 2 interfaces to send audit rules. Rather than check validity of things in 2 places make a helper function. Signed-off-by: Eric Paris --- kernel/auditfilter.c | 146 +++++++++++++++++++++---------------------- 1 file changed, 70 insertions(+), 76 deletions(-) diff --git a/kernel/auditfilter.c b/kernel/auditfilter.c index 9e666004e0dc..ff6e09d89278 100644 --- a/kernel/auditfilter.c +++ b/kernel/auditfilter.c @@ -310,7 +310,7 @@ static u32 audit_to_op(u32 op) return n; } -/* check if a field is valid for a given list */ +/* check if an audit field is valid */ static int audit_field_valid(struct audit_entry *entry, struct audit_field *f) { switch(f->type) { @@ -320,6 +320,69 @@ static int audit_field_valid(struct audit_entry *entry, struct audit_field *f) return -EINVAL; break; }; + + switch(f->type) { + default: + return -EINVAL; + case AUDIT_UID: + case AUDIT_EUID: + case AUDIT_SUID: + case AUDIT_FSUID: + case AUDIT_LOGINUID: + case AUDIT_OBJ_UID: + case AUDIT_GID: + case AUDIT_EGID: + case AUDIT_SGID: + case AUDIT_FSGID: + case AUDIT_OBJ_GID: + case AUDIT_PID: + case AUDIT_PERS: + case AUDIT_MSGTYPE: + case AUDIT_PPID: + case AUDIT_DEVMAJOR: + case AUDIT_DEVMINOR: + case AUDIT_EXIT: + case AUDIT_SUCCESS: + /* bit ops are only useful on syscall args */ + if (f->op == Audit_bitmask || f->op == Audit_bittest) + return -EINVAL; + break; + case AUDIT_ARG0: + case AUDIT_ARG1: + case AUDIT_ARG2: + case AUDIT_ARG3: + case AUDIT_SUBJ_USER: + case AUDIT_SUBJ_ROLE: + case AUDIT_SUBJ_TYPE: + case AUDIT_SUBJ_SEN: + case AUDIT_SUBJ_CLR: + case AUDIT_OBJ_USER: + case AUDIT_OBJ_ROLE: + case AUDIT_OBJ_TYPE: + case AUDIT_OBJ_LEV_LOW: + case AUDIT_OBJ_LEV_HIGH: + case AUDIT_WATCH: + case AUDIT_DIR: + case AUDIT_FILTERKEY: + break; + /* arch is only allowed to be = or != */ + case AUDIT_ARCH: + if (f->op != Audit_not_equal && f->op != Audit_equal) + return -EINVAL; + break; + case AUDIT_PERM: + if (f->val & ~15) + return -EINVAL; + break; + case AUDIT_FILETYPE: + if (f->val & ~S_IFMT) + return -EINVAL; + break; + case AUDIT_FIELD_COMPARE: + if (f->val > AUDIT_MAX_FIELD_COMPARE) + return -EINVAL; + break; + }; return 0; } @@ -361,18 +424,17 @@ static struct audit_entry *audit_rule_to_entry(struct audit_rule *rule) if (f->op == Audit_bad) goto exit_free; - switch(f->type) { - default: + err = audit_field_valid(entry, f); + if (err) goto exit_free; + + err = -EINVAL; + switch (f->type) { case AUDIT_UID: case AUDIT_EUID: case AUDIT_SUID: case AUDIT_FSUID: case AUDIT_LOGINUID: - /* bit ops not implemented for uid comparisons */ - if (f->op == Audit_bitmask || f->op == Audit_bittest) - goto exit_free; - f->uid = make_kuid(current_user_ns(), f->val); if (!uid_valid(f->uid)) goto exit_free; @@ -381,45 +443,13 @@ static struct audit_entry *audit_rule_to_entry(struct audit_rule *rule) case AUDIT_EGID: case AUDIT_SGID: case AUDIT_FSGID: - /* bit ops not implemented for gid comparisons */ - if (f->op == Audit_bitmask || f->op == Audit_bittest) - goto exit_free; - f->gid = make_kgid(current_user_ns(), f->val); if (!gid_valid(f->gid)) goto exit_free; break; - case AUDIT_PID: - case AUDIT_PERS: - case AUDIT_MSGTYPE: - case AUDIT_PPID: - case AUDIT_DEVMAJOR: - case AUDIT_DEVMINOR: - case AUDIT_EXIT: - case AUDIT_SUCCESS: - /* bit ops are only useful on syscall args */ - if (f->op == Audit_bitmask || f->op == Audit_bittest) - goto exit_free; - break; - case AUDIT_ARG0: - case AUDIT_ARG1: - case AUDIT_ARG2: - case AUDIT_ARG3: - break; - /* arch is only allowed to be = or != */ case AUDIT_ARCH: - if (f->op != Audit_not_equal && f->op != Audit_equal) - goto exit_free; entry->rule.arch_f = f; break; - case AUDIT_PERM: - if (f->val & ~15) - goto exit_free; - break; - case AUDIT_FILETYPE: - if (f->val & ~S_IFMT) - goto exit_free; - break; case AUDIT_INODE: err = audit_to_inode(&entry->rule, f); if (err) @@ -477,18 +507,13 @@ static struct audit_entry *audit_data_to_entry(struct audit_rule_data *data, goto exit_free; err = -EINVAL; - - switch(f->type) { + switch (f->type) { case AUDIT_UID: case AUDIT_EUID: case AUDIT_SUID: case AUDIT_FSUID: case AUDIT_LOGINUID: case AUDIT_OBJ_UID: - /* bit ops not implemented for uid comparisons */ - if (f->op == Audit_bitmask || f->op == Audit_bittest) - goto exit_free; - f->uid = make_kuid(current_user_ns(), f->val); if (!uid_valid(f->uid)) goto exit_free; @@ -498,27 +523,10 @@ static struct audit_entry *audit_data_to_entry(struct audit_rule_data *data, case AUDIT_SGID: case AUDIT_FSGID: case AUDIT_OBJ_GID: - /* bit ops not implemented for gid comparisons */ - if (f->op == Audit_bitmask || f->op == Audit_bittest) - goto exit_free; - f->gid = make_kgid(current_user_ns(), f->val); if (!gid_valid(f->gid)) goto exit_free; break; - case AUDIT_PID: - case AUDIT_PERS: - case AUDIT_MSGTYPE: - case AUDIT_PPID: - case AUDIT_DEVMAJOR: - case AUDIT_DEVMINOR: - case AUDIT_EXIT: - case AUDIT_SUCCESS: - case AUDIT_ARG0: - case AUDIT_ARG1: - case AUDIT_ARG2: - case AUDIT_ARG3: - break; case AUDIT_ARCH: entry->rule.arch_f = f; break; @@ -589,20 +597,6 @@ static struct audit_entry *audit_data_to_entry(struct audit_rule_data *data, entry->rule.buflen += f->val; entry->rule.filterkey = str; break; - case AUDIT_PERM: - if (f->val & ~15) - goto exit_free; - break; - case AUDIT_FILETYPE: - if (f->val & ~S_IFMT) - goto exit_free; - break; - case AUDIT_FIELD_COMPARE: - if (f->val > AUDIT_MAX_FIELD_COMPARE) - goto exit_free; - break; - default: - goto exit_free; } } From 18900909163758baf2152c9102b1a0953f7f1c30 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 18 Apr 2013 19:16:36 -0400 Subject: [PATCH 17/31] audit: remove the old depricated kernel interface We used to have an inflexible mechanism to add audit rules to the kernel. It hasn't been used in a long time. Get rid of that stuff. Signed-off-by: Eric Paris --- kernel/audit.c | 28 ++------ kernel/auditfilter.c | 160 +------------------------------------------ 2 files changed, 8 insertions(+), 180 deletions(-) diff --git a/kernel/audit.c b/kernel/audit.c index 132271448b89..274882d308d3 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -597,13 +597,14 @@ static int audit_netlink_ok(struct sk_buff *skb, u16 msg_type) return -EPERM; switch (msg_type) { - case AUDIT_GET: case AUDIT_LIST: - case AUDIT_LIST_RULES: - case AUDIT_SET: case AUDIT_ADD: - case AUDIT_ADD_RULE: case AUDIT_DEL: + return -EOPNOTSUPP; + case AUDIT_GET: + case AUDIT_SET: + case AUDIT_LIST_RULES: + case AUDIT_ADD_RULE: case AUDIT_DEL_RULE: case AUDIT_SIGNAL_INFO: case AUDIT_TTY_GET: @@ -766,25 +767,6 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) audit_log_end(ab); } break; - case AUDIT_ADD: - case AUDIT_DEL: - if (nlmsg_len(nlh) < sizeof(struct audit_rule)) - return -EINVAL; - if (audit_enabled == AUDIT_LOCKED) { - audit_log_common_recv_msg(&ab, AUDIT_CONFIG_CHANGE, - loginuid, sessionid, sid); - - audit_log_format(ab, " audit_enabled=%d res=0", - audit_enabled); - audit_log_end(ab); - return -EPERM; - } - /* fallthrough */ - case AUDIT_LIST: - err = audit_receive_filter(msg_type, NETLINK_CB(skb).portid, - seq, data, nlmsg_len(nlh), - loginuid, sessionid, sid); - break; case AUDIT_ADD_RULE: case AUDIT_DEL_RULE: if (nlmsg_len(nlh) < sizeof(struct audit_rule_data)) diff --git a/kernel/auditfilter.c b/kernel/auditfilter.c index ff6e09d89278..ee9af6533327 100644 --- a/kernel/auditfilter.c +++ b/kernel/auditfilter.c @@ -386,89 +386,6 @@ static int audit_field_valid(struct audit_entry *entry, struct audit_field *f) return 0; } -/* Translate struct audit_rule to kernel's rule respresentation. - * Exists for backward compatibility with userspace. */ -static struct audit_entry *audit_rule_to_entry(struct audit_rule *rule) -{ - struct audit_entry *entry; - int err = 0; - int i; - - entry = audit_to_entry_common(rule); - if (IS_ERR(entry)) - goto exit_nofree; - - for (i = 0; i < rule->field_count; i++) { - struct audit_field *f = &entry->rule.fields[i]; - u32 n; - - n = rule->fields[i] & (AUDIT_NEGATE|AUDIT_OPERATORS); - - /* Support for legacy operators where - * AUDIT_NEGATE bit signifies != and otherwise assumes == */ - if (n & AUDIT_NEGATE) - f->op = Audit_not_equal; - else if (!n) - f->op = Audit_equal; - else - f->op = audit_to_op(n); - - entry->rule.vers_ops = (n & AUDIT_OPERATORS) ? 2 : 1; - - f->type = rule->fields[i] & ~(AUDIT_NEGATE|AUDIT_OPERATORS); - f->val = rule->values[i]; - f->uid = INVALID_UID; - f->gid = INVALID_GID; - - err = -EINVAL; - if (f->op == Audit_bad) - goto exit_free; - - err = audit_field_valid(entry, f); - if (err) - goto exit_free; - - err = -EINVAL; - switch (f->type) { - case AUDIT_UID: - case AUDIT_EUID: - case AUDIT_SUID: - case AUDIT_FSUID: - case AUDIT_LOGINUID: - f->uid = make_kuid(current_user_ns(), f->val); - if (!uid_valid(f->uid)) - goto exit_free; - break; - case AUDIT_GID: - case AUDIT_EGID: - case AUDIT_SGID: - case AUDIT_FSGID: - f->gid = make_kgid(current_user_ns(), f->val); - if (!gid_valid(f->gid)) - goto exit_free; - break; - case AUDIT_ARCH: - entry->rule.arch_f = f; - break; - case AUDIT_INODE: - err = audit_to_inode(&entry->rule, f); - if (err) - goto exit_free; - break; - } - } - - if (entry->rule.inode_f && entry->rule.inode_f->op == Audit_not_equal) - entry->rule.inode_f = NULL; - -exit_nofree: - return entry; - -exit_free: - audit_free_rule(entry); - return ERR_PTR(err); -} - /* Translate struct audit_rule_data to kernel's rule respresentation. */ static struct audit_entry *audit_data_to_entry(struct audit_rule_data *data, size_t datasz) @@ -622,36 +539,6 @@ static inline size_t audit_pack_string(void **bufp, const char *str) return len; } -/* Translate kernel rule respresentation to struct audit_rule. - * Exists for backward compatibility with userspace. */ -static struct audit_rule *audit_krule_to_rule(struct audit_krule *krule) -{ - struct audit_rule *rule; - int i; - - rule = kzalloc(sizeof(*rule), GFP_KERNEL); - if (unlikely(!rule)) - return NULL; - - rule->flags = krule->flags | krule->listnr; - rule->action = krule->action; - rule->field_count = krule->field_count; - for (i = 0; i < rule->field_count; i++) { - rule->values[i] = krule->fields[i].val; - rule->fields[i] = krule->fields[i].type; - - if (krule->vers_ops == 1) { - if (krule->fields[i].op == Audit_not_equal) - rule->fields[i] |= AUDIT_NEGATE; - } else { - rule->fields[i] |= audit_ops[krule->fields[i].op]; - } - } - for (i = 0; i < AUDIT_BITMASK_SIZE; i++) rule->mask[i] = krule->mask[i]; - - return rule; -} - /* Translate kernel rule respresentation to struct audit_rule_data. */ static struct audit_rule_data *audit_krule_to_data(struct audit_krule *krule) { @@ -1064,35 +951,6 @@ static inline int audit_del_rule(struct audit_entry *entry) return ret; } -/* List rules using struct audit_rule. Exists for backward - * compatibility with userspace. */ -static void audit_list(int pid, int seq, struct sk_buff_head *q) -{ - struct sk_buff *skb; - struct audit_krule *r; - int i; - - /* This is a blocking read, so use audit_filter_mutex instead of rcu - * iterator to sync with list writers. */ - for (i=0; iq); mutex_lock(&audit_filter_mutex); - if (type == AUDIT_LIST) - audit_list(pid, seq, &dest->q); - else - audit_list_rules(pid, seq, &dest->q); + audit_list_rules(pid, seq, &dest->q); mutex_unlock(&audit_filter_mutex); tsk = kthread_run(audit_send_list, dest, "audit_send_list"); @@ -1201,12 +1055,8 @@ int audit_receive_filter(int type, int pid, int seq, void *data, err = PTR_ERR(tsk); } break; - case AUDIT_ADD: case AUDIT_ADD_RULE: - if (type == AUDIT_ADD) - entry = audit_rule_to_entry(data); - else - entry = audit_data_to_entry(data, datasz); + entry = audit_data_to_entry(data, datasz); if (IS_ERR(entry)) return PTR_ERR(entry); @@ -1217,12 +1067,8 @@ int audit_receive_filter(int type, int pid, int seq, void *data, if (err) audit_free_rule(entry); break; - case AUDIT_DEL: case AUDIT_DEL_RULE: - if (type == AUDIT_DEL) - entry = audit_rule_to_entry(data); - else - entry = audit_data_to_entry(data, datasz); + entry = audit_data_to_entry(data, datasz); if (IS_ERR(entry)) return PTR_ERR(entry); From dc9eb698f441889f2d7926b1cc6f1e14f0787f00 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Fri, 19 Apr 2013 13:23:09 -0400 Subject: [PATCH 18/31] audit: stop pushing loginid, uid, sessionid as arguments We always use current. Stop pulling this when the skb comes in and pushing it around as arguments. Just get it at the end when you need it. Signed-off-by: Eric Paris --- drivers/tty/tty_audit.c | 4 +- include/linux/audit.h | 3 +- include/linux/tty.h | 6 +-- kernel/audit.c | 100 +++++++++++++++------------------------- kernel/auditfilter.c | 22 ++++----- 5 files changed, 52 insertions(+), 83 deletions(-) diff --git a/drivers/tty/tty_audit.c b/drivers/tty/tty_audit.c index 6953dc82850c..1e4e9f30ea09 100644 --- a/drivers/tty/tty_audit.c +++ b/drivers/tty/tty_audit.c @@ -202,10 +202,12 @@ void tty_audit_tiocsti(struct tty_struct *tty, char ch) * reference to the tty audit buffer if available. * Flush the buffer or return an appropriate error code. */ -int tty_audit_push_task(struct task_struct *tsk, kuid_t loginuid, u32 sessionid) +int tty_audit_push_task(struct task_struct *tsk) { struct tty_audit_buf *buf = ERR_PTR(-EPERM); unsigned long flags; + kuid_t loginuid = audit_get_loginuid(tsk); + u32 sessionid = audit_get_sessionid(tsk); if (!lock_task_sighand(tsk, &flags)) return -ESRCH; diff --git a/include/linux/audit.h b/include/linux/audit.h index b26d7f121ac5..a3a50cca1efb 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -441,8 +441,7 @@ extern int audit_update_lsm_rules(void); extern int audit_filter_user(int type); extern int audit_filter_type(int type); extern int audit_receive_filter(int type, int pid, int seq, - void *data, size_t datasz, kuid_t loginuid, - u32 sessionid, u32 sid); + void *data, size_t datasz); extern int audit_enabled; #else /* CONFIG_AUDIT */ static inline __printf(4, 5) diff --git a/include/linux/tty.h b/include/linux/tty.h index 8db1b569c37a..78e378b3971c 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -517,8 +517,7 @@ extern void tty_audit_exit(void); extern void tty_audit_fork(struct signal_struct *sig); extern void tty_audit_tiocsti(struct tty_struct *tty, char ch); extern void tty_audit_push(struct tty_struct *tty); -extern int tty_audit_push_task(struct task_struct *tsk, - kuid_t loginuid, u32 sessionid); +extern int tty_audit_push_task(struct task_struct *tsk); #else static inline void tty_audit_add_data(struct tty_struct *tty, unsigned char *data, size_t size, unsigned icanon) @@ -536,8 +535,7 @@ static inline void tty_audit_fork(struct signal_struct *sig) static inline void tty_audit_push(struct tty_struct *tty) { } -static inline int tty_audit_push_task(struct task_struct *tsk, - kuid_t loginuid, u32 sessionid) +static inline int tty_audit_push_task(struct task_struct *tsk) { return 0; } diff --git a/kernel/audit.c b/kernel/audit.c index 274882d308d3..bf1e1330cbb1 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -265,17 +265,22 @@ void audit_log_lost(const char *message) } static int audit_log_config_change(char *function_name, int new, int old, - kuid_t loginuid, u32 sessionid, u32 sid, int allow_changes) { struct audit_buffer *ab; int rc = 0; + u32 sessionid = audit_get_sessionid(current); + uid_t auid = from_kuid(&init_user_ns, audit_get_loginuid(current)); + u32 sid; + ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE); if (unlikely(!ab)) return rc; audit_log_format(ab, "%s=%d old=%d auid=%u ses=%u", function_name, new, - old, from_kuid(&init_user_ns, loginuid), sessionid); + old, auid, sessionid); + + security_task_getsecid(current, &sid); if (sid) { char *ctx = NULL; u32 len; @@ -294,9 +299,7 @@ static int audit_log_config_change(char *function_name, int new, int old, return rc; } -static int audit_do_config_change(char *function_name, int *to_change, - int new, kuid_t loginuid, u32 sessionid, - u32 sid) +static int audit_do_config_change(char *function_name, int *to_change, int new) { int allow_changes, rc = 0, old = *to_change; @@ -307,8 +310,7 @@ static int audit_do_config_change(char *function_name, int *to_change, allow_changes = 1; if (audit_enabled != AUDIT_OFF) { - rc = audit_log_config_change(function_name, new, old, loginuid, - sessionid, sid, allow_changes); + rc = audit_log_config_change(function_name, new, old, allow_changes); if (rc) allow_changes = 0; } @@ -322,44 +324,37 @@ static int audit_do_config_change(char *function_name, int *to_change, return rc; } -static int audit_set_rate_limit(int limit, kuid_t loginuid, u32 sessionid, - u32 sid) +static int audit_set_rate_limit(int limit) { - return audit_do_config_change("audit_rate_limit", &audit_rate_limit, - limit, loginuid, sessionid, sid); + return audit_do_config_change("audit_rate_limit", &audit_rate_limit, limit); } -static int audit_set_backlog_limit(int limit, kuid_t loginuid, u32 sessionid, - u32 sid) +static int audit_set_backlog_limit(int limit) { - return audit_do_config_change("audit_backlog_limit", &audit_backlog_limit, - limit, loginuid, sessionid, sid); + return audit_do_config_change("audit_backlog_limit", &audit_backlog_limit, limit); } -static int audit_set_enabled(int state, kuid_t loginuid, u32 sessionid, u32 sid) +static int audit_set_enabled(int state) { int rc; if (state < AUDIT_OFF || state > AUDIT_LOCKED) return -EINVAL; - rc = audit_do_config_change("audit_enabled", &audit_enabled, state, - loginuid, sessionid, sid); - + rc = audit_do_config_change("audit_enabled", &audit_enabled, state); if (!rc) audit_ever_enabled |= !!state; return rc; } -static int audit_set_failure(int state, kuid_t loginuid, u32 sessionid, u32 sid) +static int audit_set_failure(int state) { if (state != AUDIT_FAIL_SILENT && state != AUDIT_FAIL_PRINTK && state != AUDIT_FAIL_PANIC) return -EINVAL; - return audit_do_config_change("audit_failure", &audit_failure, state, - loginuid, sessionid, sid); + return audit_do_config_change("audit_failure", &audit_failure, state); } /* @@ -627,12 +622,15 @@ static int audit_netlink_ok(struct sk_buff *skb, u16 msg_type) return err; } -static int audit_log_common_recv_msg(struct audit_buffer **ab, u16 msg_type, - kuid_t auid, u32 ses, u32 sid) +static int audit_log_common_recv_msg(struct audit_buffer **ab, u16 msg_type) { int rc = 0; char *ctx = NULL; u32 len; + u32 sessionid = audit_get_sessionid(current); + uid_t uid = from_kuid(&init_user_ns, current_uid()); + uid_t auid = from_kuid(&init_user_ns, audit_get_loginuid(current)); + u32 sid; if (!audit_enabled) { *ab = NULL; @@ -643,9 +641,8 @@ static int audit_log_common_recv_msg(struct audit_buffer **ab, u16 msg_type, if (unlikely(!*ab)) return rc; audit_log_format(*ab, "pid=%d uid=%u auid=%u ses=%u", - task_tgid_vnr(current), - from_kuid(&init_user_ns, current_uid()), - from_kuid(&init_user_ns, auid), ses); + task_tgid_vnr(current), uid, auid, sessionid); + security_task_getsecid(current, &sid); if (sid) { rc = security_secid_to_secctx(sid, &ctx, &len); if (rc) @@ -661,14 +658,12 @@ static int audit_log_common_recv_msg(struct audit_buffer **ab, u16 msg_type, static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) { - u32 seq, sid; + u32 seq; void *data; struct audit_status *status_get, status_set; int err; struct audit_buffer *ab; u16 msg_type = nlh->nlmsg_type; - kuid_t loginuid; /* loginuid of sender */ - u32 sessionid; struct audit_sig_info *sig_data; char *ctx = NULL; u32 len; @@ -677,9 +672,6 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) if (err) return err; - loginuid = audit_get_loginuid(current); - sessionid = audit_get_sessionid(current); - security_task_getsecid(current, &sid); seq = nlh->nlmsg_seq; data = nlmsg_data(nlh); @@ -700,14 +692,12 @@ 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, - loginuid, sessionid, sid); + err = audit_set_enabled(status_get->enabled); if (err < 0) return err; } if (status_get->mask & AUDIT_STATUS_FAILURE) { - err = audit_set_failure(status_get->failure, - loginuid, sessionid, sid); + err = audit_set_failure(status_get->failure); if (err < 0) return err; } @@ -715,22 +705,17 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) int new_pid = status_get->pid; if (audit_enabled != AUDIT_OFF) - audit_log_config_change("audit_pid", new_pid, - audit_pid, loginuid, - sessionid, sid, 1); - + audit_log_config_change("audit_pid", new_pid, audit_pid, 1); audit_pid = new_pid; audit_nlk_portid = NETLINK_CB(skb).portid; } if (status_get->mask & AUDIT_STATUS_RATE_LIMIT) { - err = audit_set_rate_limit(status_get->rate_limit, - loginuid, sessionid, sid); + err = audit_set_rate_limit(status_get->rate_limit); if (err < 0) return err; } if (status_get->mask & AUDIT_STATUS_BACKLOG_LIMIT) - err = audit_set_backlog_limit(status_get->backlog_limit, - loginuid, sessionid, sid); + err = audit_set_backlog_limit(status_get->backlog_limit); break; case AUDIT_USER: case AUDIT_FIRST_USER_MSG ... AUDIT_LAST_USER_MSG: @@ -742,14 +727,11 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) if (err == 1) { err = 0; if (msg_type == AUDIT_USER_TTY) { - err = tty_audit_push_task(current, loginuid, - sessionid); + err = tty_audit_push_task(current); if (err) break; } - audit_log_common_recv_msg(&ab, msg_type, - loginuid, sessionid, sid); - + audit_log_common_recv_msg(&ab, msg_type); if (msg_type != AUDIT_USER_TTY) audit_log_format(ab, " msg='%.1024s'", (char *)data); @@ -772,26 +754,19 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) if (nlmsg_len(nlh) < sizeof(struct audit_rule_data)) return -EINVAL; if (audit_enabled == AUDIT_LOCKED) { - audit_log_common_recv_msg(&ab, AUDIT_CONFIG_CHANGE, - loginuid, sessionid, sid); - - audit_log_format(ab, " audit_enabled=%d res=0", - audit_enabled); + audit_log_common_recv_msg(&ab, AUDIT_CONFIG_CHANGE); + audit_log_format(ab, " audit_enabled=%d res=0", audit_enabled); audit_log_end(ab); return -EPERM; } /* fallthrough */ case AUDIT_LIST_RULES: err = audit_receive_filter(msg_type, NETLINK_CB(skb).portid, - seq, data, nlmsg_len(nlh), - loginuid, sessionid, sid); + seq, data, nlmsg_len(nlh)); break; case AUDIT_TRIM: audit_trim_trees(); - - audit_log_common_recv_msg(&ab, AUDIT_CONFIG_CHANGE, - loginuid, sessionid, sid); - + audit_log_common_recv_msg(&ab, AUDIT_CONFIG_CHANGE); audit_log_format(ab, " op=trim res=1"); audit_log_end(ab); break; @@ -821,8 +796,7 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) /* OK, here comes... */ err = audit_tag_tree(old, new); - audit_log_common_recv_msg(&ab, AUDIT_CONFIG_CHANGE, - loginuid, sessionid, sid); + audit_log_common_recv_msg(&ab, AUDIT_CONFIG_CHANGE); audit_log_format(ab, " op=make_equiv old="); audit_log_untrustedstring(ab, old); diff --git a/kernel/auditfilter.c b/kernel/auditfilter.c index ee9af6533327..f952234da2ca 100644 --- a/kernel/auditfilter.c +++ b/kernel/auditfilter.c @@ -980,11 +980,12 @@ static void audit_list_rules(int pid, int seq, struct sk_buff_head *q) } /* Log rule additions and removals */ -static void audit_log_rule_change(kuid_t loginuid, u32 sessionid, u32 sid, - char *action, struct audit_krule *rule, - int res) +static void audit_log_rule_change(char *action, struct audit_krule *rule, int res) { struct audit_buffer *ab; + uid_t loginuid = from_kuid(&init_user_ns, audit_get_loginuid(current)); + u32 sessionid = audit_get_sessionid(current); + u32 sid; if (!audit_enabled) return; @@ -992,8 +993,8 @@ static void audit_log_rule_change(kuid_t loginuid, u32 sessionid, u32 sid, ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE); if (!ab) return; - audit_log_format(ab, "auid=%u ses=%u", - from_kuid(&init_user_ns, loginuid), sessionid); + audit_log_format(ab, "auid=%u ses=%u" ,loginuid, sessionid); + security_task_getsecid(current, &sid); if (sid) { char *ctx = NULL; u32 len; @@ -1022,8 +1023,7 @@ static void audit_log_rule_change(kuid_t loginuid, u32 sessionid, u32 sid, * @sessionid: sessionid for netlink audit message * @sid: SE Linux Security ID of sender */ -int audit_receive_filter(int type, int pid, int seq, void *data, - size_t datasz, kuid_t loginuid, u32 sessionid, u32 sid) +int audit_receive_filter(int type, int pid, int seq, void *data, size_t datasz) { struct task_struct *tsk; struct audit_netlink_list *dest; @@ -1061,9 +1061,7 @@ int audit_receive_filter(int type, int pid, int seq, void *data, return PTR_ERR(entry); err = audit_add_rule(entry); - audit_log_rule_change(loginuid, sessionid, sid, "add rule", - &entry->rule, !err); - + audit_log_rule_change("add rule", &entry->rule, !err); if (err) audit_free_rule(entry); break; @@ -1073,9 +1071,7 @@ int audit_receive_filter(int type, int pid, int seq, void *data, return PTR_ERR(entry); err = audit_del_rule(entry); - audit_log_rule_change(loginuid, sessionid, sid, "remove rule", - &entry->rule, !err); - + audit_log_rule_change("remove rule", &entry->rule, !err); audit_free_rule(entry); break; default: From 152f497b9b5940f81de3205465840a5eb316458e Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Fri, 19 Apr 2013 13:56:11 -0400 Subject: [PATCH 19/31] audit: push loginuid and sessionid processing down Since we are always current, we can push a lot of this stuff to the bottom and get rid of useless interfaces and arguments. Signed-off-by: Eric Paris --- drivers/tty/tty_audit.c | 70 ++++++++++++++--------------------------- include/linux/tty.h | 4 +-- kernel/audit.c | 2 +- 3 files changed, 26 insertions(+), 50 deletions(-) diff --git a/drivers/tty/tty_audit.c b/drivers/tty/tty_audit.c index 1e4e9f30ea09..ea2e5ad71731 100644 --- a/drivers/tty/tty_audit.c +++ b/drivers/tty/tty_audit.c @@ -60,24 +60,22 @@ static void tty_audit_buf_put(struct tty_audit_buf *buf) tty_audit_buf_free(buf); } -static void tty_audit_log(const char *description, struct task_struct *tsk, - kuid_t loginuid, unsigned sessionid, int major, - int minor, unsigned char *data, size_t size) +static void tty_audit_log(const char *description, int major, int minor, + unsigned char *data, size_t size) { struct audit_buffer *ab; + struct task_struct *tsk = current; + uid_t uid = from_kuid(&init_user_ns, task_uid(tsk)); + uid_t loginuid = from_kuid(&init_user_ns, audit_get_loginuid(tsk)); + u32 sessionid = audit_get_sessionid(tsk); ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_TTY); if (ab) { char name[sizeof(tsk->comm)]; - kuid_t uid = task_uid(tsk); - audit_log_format(ab, "%s pid=%u uid=%u auid=%u ses=%u " - "major=%d minor=%d comm=", description, - tsk->pid, - from_kuid(&init_user_ns, uid), - from_kuid(&init_user_ns, loginuid), - sessionid, - major, minor); + audit_log_format(ab, "%s pid=%u uid=%u auid=%u ses=%u major=%d" + " minor=%d comm=", description, tsk->pid, uid, + loginuid, sessionid, major, minor); get_task_comm(name, tsk); audit_log_untrustedstring(ab, name); audit_log_format(ab, " data="); @@ -90,11 +88,9 @@ static void tty_audit_log(const char *description, struct task_struct *tsk, * tty_audit_buf_push - Push buffered data out * * Generate an audit message from the contents of @buf, which is owned by - * @tsk with @loginuid. @buf->mutex must be locked. + * the current task. @buf->mutex must be locked. */ -static void tty_audit_buf_push(struct task_struct *tsk, kuid_t loginuid, - unsigned int sessionid, - struct tty_audit_buf *buf) +static void tty_audit_buf_push(struct tty_audit_buf *buf) { if (buf->valid == 0) return; @@ -102,24 +98,10 @@ static void tty_audit_buf_push(struct task_struct *tsk, kuid_t loginuid, buf->valid = 0; return; } - tty_audit_log("tty", tsk, loginuid, sessionid, buf->major, buf->minor, - buf->data, buf->valid); + tty_audit_log("tty", buf->major, buf->minor, buf->data, buf->valid); buf->valid = 0; } -/** - * tty_audit_buf_push_current - Push buffered data out - * - * Generate an audit message from the contents of @buf, which is owned by - * the current task. @buf->mutex must be locked. - */ -static void tty_audit_buf_push_current(struct tty_audit_buf *buf) -{ - kuid_t auid = audit_get_loginuid(current); - unsigned int sessionid = audit_get_sessionid(current); - tty_audit_buf_push(current, auid, sessionid, buf); -} - /** * tty_audit_exit - Handle a task exit * @@ -138,7 +120,7 @@ void tty_audit_exit(void) return; mutex_lock(&buf->mutex); - tty_audit_buf_push_current(buf); + tty_audit_buf_push(buf); mutex_unlock(&buf->mutex); tty_audit_buf_put(buf); @@ -176,7 +158,7 @@ void tty_audit_tiocsti(struct tty_struct *tty, char ch) if (buf) { mutex_lock(&buf->mutex); if (buf->major == major && buf->minor == minor) - tty_audit_buf_push_current(buf); + tty_audit_buf_push(buf); mutex_unlock(&buf->mutex); tty_audit_buf_put(buf); } @@ -187,27 +169,21 @@ void tty_audit_tiocsti(struct tty_struct *tty, char ch) auid = audit_get_loginuid(current); sessionid = audit_get_sessionid(current); - tty_audit_log("ioctl=TIOCSTI", current, auid, sessionid, major, - minor, &ch, 1); + tty_audit_log("ioctl=TIOCSTI", major, minor, &ch, 1); } } /** - * tty_audit_push_task - Flush task's pending audit data - * @tsk: task pointer - * @loginuid: sender login uid - * @sessionid: sender session id + * tty_audit_push_current - Flush current's pending audit data * - * Called with a ref on @tsk held. Try to lock sighand and get a - * reference to the tty audit buffer if available. + * Try to lock sighand and get a reference to the tty audit buffer if available. * Flush the buffer or return an appropriate error code. */ -int tty_audit_push_task(struct task_struct *tsk) +int tty_audit_push_current(void) { struct tty_audit_buf *buf = ERR_PTR(-EPERM); + struct task_struct *tsk = current; unsigned long flags; - kuid_t loginuid = audit_get_loginuid(tsk); - u32 sessionid = audit_get_sessionid(tsk); if (!lock_task_sighand(tsk, &flags)) return -ESRCH; @@ -227,7 +203,7 @@ int tty_audit_push_task(struct task_struct *tsk) return PTR_ERR(buf); mutex_lock(&buf->mutex); - tty_audit_buf_push(tsk, loginuid, sessionid, buf); + tty_audit_buf_push(buf); mutex_unlock(&buf->mutex); tty_audit_buf_put(buf); @@ -311,7 +287,7 @@ void tty_audit_add_data(struct tty_struct *tty, unsigned char *data, minor = tty->driver->minor_start + tty->index; if (buf->major != major || buf->minor != minor || buf->icanon != icanon) { - tty_audit_buf_push_current(buf); + tty_audit_buf_push(buf); buf->major = major; buf->minor = minor; buf->icanon = icanon; @@ -327,7 +303,7 @@ void tty_audit_add_data(struct tty_struct *tty, unsigned char *data, data += run; size -= run; if (buf->valid == N_TTY_BUF_SIZE) - tty_audit_buf_push_current(buf); + tty_audit_buf_push(buf); } while (size != 0); mutex_unlock(&buf->mutex); tty_audit_buf_put(buf); @@ -359,7 +335,7 @@ void tty_audit_push(struct tty_struct *tty) minor = tty->driver->minor_start + tty->index; mutex_lock(&buf->mutex); if (buf->major == major && buf->minor == minor) - tty_audit_buf_push_current(buf); + tty_audit_buf_push(buf); mutex_unlock(&buf->mutex); tty_audit_buf_put(buf); } diff --git a/include/linux/tty.h b/include/linux/tty.h index 78e378b3971c..96d640b32cd8 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -517,7 +517,7 @@ extern void tty_audit_exit(void); extern void tty_audit_fork(struct signal_struct *sig); extern void tty_audit_tiocsti(struct tty_struct *tty, char ch); extern void tty_audit_push(struct tty_struct *tty); -extern int tty_audit_push_task(struct task_struct *tsk); +extern int tty_audit_push_current(void); #else static inline void tty_audit_add_data(struct tty_struct *tty, unsigned char *data, size_t size, unsigned icanon) @@ -535,7 +535,7 @@ static inline void tty_audit_fork(struct signal_struct *sig) static inline void tty_audit_push(struct tty_struct *tty) { } -static inline int tty_audit_push_task(struct task_struct *tsk) +static inline int tty_audit_push_current(void) { return 0; } diff --git a/kernel/audit.c b/kernel/audit.c index bf1e1330cbb1..79b42fd14c22 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -727,7 +727,7 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) if (err == 1) { err = 0; if (msg_type == AUDIT_USER_TTY) { - err = tty_audit_push_task(current); + err = tty_audit_push_current(); if (err) break; } From b122c3767c1d89763b4babca062c3171a71ed97c Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Fri, 19 Apr 2013 15:00:33 -0400 Subject: [PATCH 20/31] audit: use a consistent audit helper to log lsm information We have a number of places we were reimplementing the same code to write out lsm labels. Just do it one darn place. Signed-off-by: Eric Paris --- include/linux/audit.h | 8 +++++--- kernel/audit.c | 34 ++++------------------------------ kernel/auditfilter.c | 13 +------------ kernel/auditsc.c | 10 +++++----- 4 files changed, 15 insertions(+), 50 deletions(-) diff --git a/include/linux/audit.h b/include/linux/audit.h index a3a50cca1efb..e2dd9c124140 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -188,7 +188,7 @@ static inline int audit_get_sessionid(struct task_struct *tsk) return tsk->sessionid; } -extern void audit_log_task_context(struct audit_buffer *ab); +extern int audit_log_task_context(struct audit_buffer *ab); extern void audit_log_task_info(struct audit_buffer *ab, struct task_struct *tsk); extern void __audit_ipc_obj(struct kern_ipc_perm *ipcp); extern void __audit_ipc_set_perm(unsigned long qbytes, uid_t uid, gid_t gid, umode_t mode); @@ -344,8 +344,10 @@ static inline int audit_get_sessionid(struct task_struct *tsk) { return -1; } -static inline void audit_log_task_context(struct audit_buffer *ab) -{ } +static int void audit_log_task_context(struct audit_buffer *ab) +{ + return 0; +} static inline void audit_log_task_info(struct audit_buffer *ab, struct task_struct *tsk) { } diff --git a/kernel/audit.c b/kernel/audit.c index 79b42fd14c22..a3c77b979b5b 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -271,29 +271,15 @@ static int audit_log_config_change(char *function_name, int new, int old, int rc = 0; u32 sessionid = audit_get_sessionid(current); uid_t auid = from_kuid(&init_user_ns, audit_get_loginuid(current)); - u32 sid; - ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE); if (unlikely(!ab)) return rc; audit_log_format(ab, "%s=%d old=%d auid=%u ses=%u", function_name, new, old, auid, sessionid); - - security_task_getsecid(current, &sid); - if (sid) { - char *ctx = NULL; - u32 len; - - rc = security_secid_to_secctx(sid, &ctx, &len); - if (rc) { - audit_log_format(ab, " sid=%u", sid); - allow_changes = 0; /* Something weird, deny request */ - } else { - audit_log_format(ab, " subj=%s", ctx); - security_release_secctx(ctx, len); - } - } + rc = audit_log_task_context(ab); + if (rc) + allow_changes = 0; /* Something weird, deny request */ audit_log_format(ab, " res=%d", allow_changes); audit_log_end(ab); return rc; @@ -625,12 +611,9 @@ static int audit_netlink_ok(struct sk_buff *skb, u16 msg_type) static int audit_log_common_recv_msg(struct audit_buffer **ab, u16 msg_type) { int rc = 0; - char *ctx = NULL; - u32 len; u32 sessionid = audit_get_sessionid(current); uid_t uid = from_kuid(&init_user_ns, current_uid()); uid_t auid = from_kuid(&init_user_ns, audit_get_loginuid(current)); - u32 sid; if (!audit_enabled) { *ab = NULL; @@ -642,16 +625,7 @@ static int audit_log_common_recv_msg(struct audit_buffer **ab, u16 msg_type) return rc; audit_log_format(*ab, "pid=%d uid=%u auid=%u ses=%u", task_tgid_vnr(current), uid, auid, sessionid); - security_task_getsecid(current, &sid); - if (sid) { - rc = security_secid_to_secctx(sid, &ctx, &len); - if (rc) - audit_log_format(*ab, " ssid=%u", sid); - else { - audit_log_format(*ab, " subj=%s", ctx); - security_release_secctx(ctx, len); - } - } + audit_log_task_context(*ab); return rc; } diff --git a/kernel/auditfilter.c b/kernel/auditfilter.c index f952234da2ca..478f4602c96b 100644 --- a/kernel/auditfilter.c +++ b/kernel/auditfilter.c @@ -985,7 +985,6 @@ static void audit_log_rule_change(char *action, struct audit_krule *rule, int re struct audit_buffer *ab; uid_t loginuid = from_kuid(&init_user_ns, audit_get_loginuid(current)); u32 sessionid = audit_get_sessionid(current); - u32 sid; if (!audit_enabled) return; @@ -994,17 +993,7 @@ static void audit_log_rule_change(char *action, struct audit_krule *rule, int re if (!ab) return; audit_log_format(ab, "auid=%u ses=%u" ,loginuid, sessionid); - security_task_getsecid(current, &sid); - if (sid) { - char *ctx = NULL; - u32 len; - if (security_secid_to_secctx(sid, &ctx, &len)) - audit_log_format(ab, " ssid=%u", sid); - else { - audit_log_format(ab, " subj=%s", ctx); - security_release_secctx(ctx, len); - } - } + audit_log_task_context(ab); audit_log_format(ab, " op="); audit_log_string(ab, action); audit_log_key(ab, rule->filterkey); diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 4baf61d39836..17e9a260a545 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -1109,7 +1109,7 @@ static inline void audit_free_context(struct audit_context *context) kfree(context); } -void audit_log_task_context(struct audit_buffer *ab) +int audit_log_task_context(struct audit_buffer *ab) { char *ctx = NULL; unsigned len; @@ -1118,22 +1118,22 @@ void audit_log_task_context(struct audit_buffer *ab) security_task_getsecid(current, &sid); if (!sid) - return; + return 0; error = security_secid_to_secctx(sid, &ctx, &len); if (error) { if (error != -EINVAL) goto error_path; - return; + return 0; } audit_log_format(ab, " subj=%s", ctx); security_release_secctx(ctx, len); - return; + return 0; error_path: audit_panic("error in audit_log_task_context"); - return; + return error; } EXPORT_SYMBOL(audit_log_task_context); From 4d3fb709b285ac885c40950a837edbfc90029c5f Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Tue, 30 Apr 2013 09:53:34 -0400 Subject: [PATCH 21/31] helper for some session id stuff --- include/linux/audit.h | 2 ++ kernel/audit.c | 20 ++++++++++++-------- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/include/linux/audit.h b/include/linux/audit.h index e2dd9c124140..b76bfc8efc25 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -89,6 +89,8 @@ extern int audit_classify_arch(int arch); struct filename; +extern void audit_log_session_info(struct audit_buffer *ab); + #ifdef CONFIG_AUDITSYSCALL /* These are defined in auditsc.c */ /* Public API */ diff --git a/kernel/audit.c b/kernel/audit.c index a3c77b979b5b..44803f25b236 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -269,14 +269,12 @@ static int audit_log_config_change(char *function_name, int new, int old, { struct audit_buffer *ab; int rc = 0; - u32 sessionid = audit_get_sessionid(current); - uid_t auid = from_kuid(&init_user_ns, audit_get_loginuid(current)); ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE); if (unlikely(!ab)) return rc; - audit_log_format(ab, "%s=%d old=%d auid=%u ses=%u", function_name, new, - old, auid, sessionid); + audit_log_format(ab, "%s=%d old=%d", function_name, new, old); + audit_log_session_info(ab); rc = audit_log_task_context(ab); if (rc) allow_changes = 0; /* Something weird, deny request */ @@ -611,9 +609,7 @@ static int audit_netlink_ok(struct sk_buff *skb, u16 msg_type) static int audit_log_common_recv_msg(struct audit_buffer **ab, u16 msg_type) { int rc = 0; - u32 sessionid = audit_get_sessionid(current); uid_t uid = from_kuid(&init_user_ns, current_uid()); - uid_t auid = from_kuid(&init_user_ns, audit_get_loginuid(current)); if (!audit_enabled) { *ab = NULL; @@ -623,8 +619,8 @@ static int audit_log_common_recv_msg(struct audit_buffer **ab, u16 msg_type) *ab = audit_log_start(NULL, GFP_KERNEL, msg_type); if (unlikely(!*ab)) return rc; - audit_log_format(*ab, "pid=%d uid=%u auid=%u ses=%u", - task_tgid_vnr(current), uid, auid, sessionid); + audit_log_format(*ab, "pid=%d uid=%u", task_tgid_vnr(current), uid); + audit_log_session_info(*ab); audit_log_task_context(*ab); return rc; @@ -1376,6 +1372,14 @@ void audit_log_d_path(struct audit_buffer *ab, const char *prefix, kfree(pathname); } +void audit_log_session_info(struct audit_buffer *ab) +{ + u32 sessionid = audit_get_sessionid(current); + uid_t auid = from_kuid(&init_user_ns, audit_get_loginuid(current)); + + audit_log_format(ab, "auid=%u ses=%u\n", auid, sessionid); +} + void audit_log_key(struct audit_buffer *ab, char *key) { audit_log_format(ab, " key="); From bde02ca858448cf54a4226774dd1481f3bcc455e Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Tue, 30 Apr 2013 11:01:14 -0400 Subject: [PATCH 22/31] audit: use spin_lock_irqsave/restore in audit tty code Some of the callers of the audit tty function use spin_lock_irqsave/restore. We were using the forced always enable version, which seems really bad. Since I don't know every one of these code paths well enough, it makes sense to just switch everything to the safe version. Maybe it's a little overzealous, but it's a lot better than an unlucky deadlock when we return to a caller with irq enabled and they expect it to be disabled. Signed-off-by: Eric Paris --- drivers/tty/tty_audit.c | 32 +++++++++++++++++++------------- kernel/audit.c | 10 ++++++---- 2 files changed, 25 insertions(+), 17 deletions(-) diff --git a/drivers/tty/tty_audit.c b/drivers/tty/tty_audit.c index ea2e5ad71731..755d418019c8 100644 --- a/drivers/tty/tty_audit.c +++ b/drivers/tty/tty_audit.c @@ -111,11 +111,12 @@ static void tty_audit_buf_push(struct tty_audit_buf *buf) void tty_audit_exit(void) { struct tty_audit_buf *buf; + unsigned long flags; - spin_lock_irq(¤t->sighand->siglock); + spin_lock_irqsave(¤t->sighand->siglock, flags); buf = current->signal->tty_audit_buf; current->signal->tty_audit_buf = NULL; - spin_unlock_irq(¤t->sighand->siglock); + spin_unlock_irqrestore(¤t->sighand->siglock, flags); if (!buf) return; @@ -133,9 +134,11 @@ void tty_audit_exit(void) */ void tty_audit_fork(struct signal_struct *sig) { - spin_lock_irq(¤t->sighand->siglock); + unsigned long flags; + + spin_lock_irqsave(¤t->sighand->siglock, flags); sig->audit_tty = current->signal->audit_tty; - spin_unlock_irq(¤t->sighand->siglock); + spin_unlock_irqrestore(¤t->sighand->siglock, flags); } /** @@ -145,13 +148,14 @@ void tty_audit_tiocsti(struct tty_struct *tty, char ch) { struct tty_audit_buf *buf; int major, minor, should_audit; + unsigned long flags; - spin_lock_irq(¤t->sighand->siglock); + spin_lock_irqsave(¤t->sighand->siglock, flags); should_audit = current->signal->audit_tty; buf = current->signal->tty_audit_buf; if (buf) atomic_inc(&buf->count); - spin_unlock_irq(¤t->sighand->siglock); + spin_unlock_irqrestore(¤t->sighand->siglock, flags); major = tty->driver->major; minor = tty->driver->minor_start + tty->index; @@ -221,10 +225,11 @@ static struct tty_audit_buf *tty_audit_buf_get(struct tty_struct *tty, unsigned icanon) { struct tty_audit_buf *buf, *buf2; + unsigned long flags; buf = NULL; buf2 = NULL; - spin_lock_irq(¤t->sighand->siglock); + spin_lock_irqsave(¤t->sighand->siglock, flags); if (likely(!current->signal->audit_tty)) goto out; buf = current->signal->tty_audit_buf; @@ -232,7 +237,7 @@ static struct tty_audit_buf *tty_audit_buf_get(struct tty_struct *tty, atomic_inc(&buf->count); goto out; } - spin_unlock_irq(¤t->sighand->siglock); + spin_unlock_irqrestore(¤t->sighand->siglock, flags); buf2 = tty_audit_buf_alloc(tty->driver->major, tty->driver->minor_start + tty->index, @@ -242,7 +247,7 @@ static struct tty_audit_buf *tty_audit_buf_get(struct tty_struct *tty, return NULL; } - spin_lock_irq(¤t->sighand->siglock); + spin_lock_irqsave(¤t->sighand->siglock, flags); if (!current->signal->audit_tty) goto out; buf = current->signal->tty_audit_buf; @@ -254,7 +259,7 @@ static struct tty_audit_buf *tty_audit_buf_get(struct tty_struct *tty, atomic_inc(&buf->count); /* Fall through */ out: - spin_unlock_irq(¤t->sighand->siglock); + spin_unlock_irqrestore(¤t->sighand->siglock, flags); if (buf2) tty_audit_buf_free(buf2); return buf; @@ -317,16 +322,17 @@ void tty_audit_add_data(struct tty_struct *tty, unsigned char *data, void tty_audit_push(struct tty_struct *tty) { struct tty_audit_buf *buf; + unsigned long flags; - spin_lock_irq(¤t->sighand->siglock); + spin_lock_irqsave(¤t->sighand->siglock, flags); if (likely(!current->signal->audit_tty)) { - spin_unlock_irq(¤t->sighand->siglock); + spin_unlock_irqrestore(¤t->sighand->siglock, flags); return; } buf = current->signal->tty_audit_buf; if (buf) atomic_inc(&buf->count); - spin_unlock_irq(¤t->sighand->siglock); + spin_unlock_irqrestore(¤t->sighand->siglock, flags); if (buf) { int major, minor; diff --git a/kernel/audit.c b/kernel/audit.c index 44803f25b236..241aa8593fa8 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -804,10 +804,11 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) case AUDIT_TTY_GET: { struct audit_tty_status s; struct task_struct *tsk = current; + unsigned long flags; - spin_lock_irq(&tsk->sighand->siglock); + spin_lock_irqsave(&tsk->sighand->siglock, flags); s.enabled = tsk->signal->audit_tty != 0; - spin_unlock_irq(&tsk->sighand->siglock); + spin_unlock_irqrestore(&tsk->sighand->siglock, flags); audit_send_reply(NETLINK_CB(skb).portid, seq, AUDIT_TTY_GET, 0, 0, &s, sizeof(s)); @@ -816,6 +817,7 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) case AUDIT_TTY_SET: { struct audit_tty_status *s; struct task_struct *tsk = current; + unsigned long flags; if (nlh->nlmsg_len < sizeof(struct audit_tty_status)) return -EINVAL; @@ -823,9 +825,9 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) if (s->enabled != 0 && s->enabled != 1) return -EINVAL; - spin_lock_irq(&tsk->sighand->siglock); + spin_lock_irqsave(&tsk->sighand->siglock, flags); tsk->signal->audit_tty = s->enabled != 0; - spin_unlock_irq(&tsk->sighand->siglock); + spin_unlock_irqrestore(&tsk->sighand->siglock, flags); break; } default: From 46e959ea2969cc1668d09b0dc55226946cf781f1 Mon Sep 17 00:00:00 2001 From: Richard Guy Briggs Date: Fri, 3 May 2013 14:03:50 -0400 Subject: [PATCH 23/31] audit: add an option to control logging of passwords with pam_tty_audit Most commands are entered one line at a time and processed as complete lines in non-canonical mode. Commands that interactively require a password, enter canonical mode to do this while shutting off echo. This pair of features (icanon and !echo) can be used to avoid logging passwords by audit while still logging the rest of the command. Adding a member (log_passwd) to the struct audit_tty_status passed in by pam_tty_audit allows control of canonical mode without echo per task. Signed-off-by: Richard Guy Briggs Signed-off-by: Eric Paris --- drivers/tty/tty_audit.c | 9 +++++++++ include/linux/sched.h | 1 + include/uapi/linux/audit.h | 3 ++- kernel/audit.c | 16 ++++++++++------ 4 files changed, 22 insertions(+), 7 deletions(-) diff --git a/drivers/tty/tty_audit.c b/drivers/tty/tty_audit.c index 755d418019c8..5f3868202183 100644 --- a/drivers/tty/tty_audit.c +++ b/drivers/tty/tty_audit.c @@ -138,6 +138,7 @@ void tty_audit_fork(struct signal_struct *sig) spin_lock_irqsave(¤t->sighand->siglock, flags); sig->audit_tty = current->signal->audit_tty; + sig->audit_tty_log_passwd = current->signal->audit_tty_log_passwd; spin_unlock_irqrestore(¤t->sighand->siglock, flags); } @@ -275,10 +276,18 @@ void tty_audit_add_data(struct tty_struct *tty, unsigned char *data, { struct tty_audit_buf *buf; int major, minor; + int audit_log_tty_passwd; + unsigned long flags; if (unlikely(size == 0)) return; + spin_lock_irqsave(¤t->sighand->siglock, flags); + audit_log_tty_passwd = current->signal->audit_tty_log_passwd; + spin_unlock_irqrestore(¤t->sighand->siglock, flags); + if (!audit_log_tty_passwd && icanon && !L_ECHO(tty)) + return; + if (tty->driver->type == TTY_DRIVER_TYPE_PTY && tty->driver->subtype == PTY_TYPE_MASTER) return; diff --git a/include/linux/sched.h b/include/linux/sched.h index d2112477ff5e..c4689fe92864 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -641,6 +641,7 @@ struct signal_struct { #endif #ifdef CONFIG_AUDIT unsigned audit_tty; + unsigned audit_tty_log_passwd; struct tty_audit_buf *tty_audit_buf; #endif #ifdef CONFIG_CGROUPS diff --git a/include/uapi/linux/audit.h b/include/uapi/linux/audit.h index 9f096f1c0907..c058c24b97ac 100644 --- a/include/uapi/linux/audit.h +++ b/include/uapi/linux/audit.h @@ -369,7 +369,8 @@ struct audit_status { }; struct audit_tty_status { - __u32 enabled; /* 1 = enabled, 0 = disabled */ + __u32 enabled; /* 1 = enabled, 0 = disabled */ + __u32 log_passwd; /* 1 = enabled, 0 = disabled */ }; /* audit_rule_data supports filter rules with both integer and string diff --git a/kernel/audit.c b/kernel/audit.c index 241aa8593fa8..998a0d4155cf 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -49,6 +49,7 @@ #include #include #include +#include #include @@ -808,6 +809,7 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) spin_lock_irqsave(&tsk->sighand->siglock, flags); s.enabled = tsk->signal->audit_tty != 0; + s.log_passwd = tsk->signal->audit_tty_log_passwd; spin_unlock_irqrestore(&tsk->sighand->siglock, flags); audit_send_reply(NETLINK_CB(skb).portid, seq, @@ -815,18 +817,20 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) break; } case AUDIT_TTY_SET: { - struct audit_tty_status *s; + struct audit_tty_status s; struct task_struct *tsk = current; unsigned long flags; - if (nlh->nlmsg_len < sizeof(struct audit_tty_status)) - return -EINVAL; - s = data; - if (s->enabled != 0 && s->enabled != 1) + memset(&s, 0, sizeof(s)); + /* guard against past and future API changes */ + memcpy(&s, data, min(sizeof(s), (size_t)nlh->nlmsg_len)); + if ((s.enabled != 0 && s.enabled != 1) || + (s.log_passwd != 0 && s.log_passwd != 1)) return -EINVAL; spin_lock_irqsave(&tsk->sighand->siglock, flags); - tsk->signal->audit_tty = s->enabled != 0; + tsk->signal->audit_tty = s.enabled; + tsk->signal->audit_tty_log_passwd = s.log_passwd; spin_unlock_irqrestore(&tsk->sighand->siglock, flags); break; } From 2ce88dd04276839002a3b31161090d282316610b Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Tue, 30 Apr 2013 10:43:10 -0400 Subject: [PATCH 24/31] audit: do not needlessly take a spinlock in copy_signal current->signal->audit_* can only change from a netlink message from current. Obviously in this case we cannot be handling a netlink message from current. So there is no change these can change under us. No need to take a lock to read them. Signed-off-by: Eric Paris --- drivers/tty/tty_audit.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/tty/tty_audit.c b/drivers/tty/tty_audit.c index 5f3868202183..a03a75163f02 100644 --- a/drivers/tty/tty_audit.c +++ b/drivers/tty/tty_audit.c @@ -134,12 +134,8 @@ void tty_audit_exit(void) */ void tty_audit_fork(struct signal_struct *sig) { - unsigned long flags; - - spin_lock_irqsave(¤t->sighand->siglock, flags); sig->audit_tty = current->signal->audit_tty; sig->audit_tty_log_passwd = current->signal->audit_tty_log_passwd; - spin_unlock_irqrestore(¤t->sighand->siglock, flags); } /** From bee0a224e791cccbc7ecd7faf2d5932942668976 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Tue, 30 Apr 2013 10:46:46 -0400 Subject: [PATCH 25/31] audit: do not needlessly take a lock in tty_audit_exit We were doing spin_lock_irq and spin_unlock_irq. This is STOOPID. If we were in interupt context we were already screwed and called panic() in do_exit(). So the irq stuff is useless. Also, these values can only be changed by receiving a netlink message from current. Since we are in do_exit() clearly we aren't in the syscall sending the netlink message to change these values. Thus, just read them and go with it. Signed-off-by: Eric Paris --- drivers/tty/tty_audit.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/tty/tty_audit.c b/drivers/tty/tty_audit.c index a03a75163f02..a4fdce74f883 100644 --- a/drivers/tty/tty_audit.c +++ b/drivers/tty/tty_audit.c @@ -111,12 +111,9 @@ static void tty_audit_buf_push(struct tty_audit_buf *buf) void tty_audit_exit(void) { struct tty_audit_buf *buf; - unsigned long flags; - spin_lock_irqsave(¤t->sighand->siglock, flags); buf = current->signal->tty_audit_buf; current->signal->tty_audit_buf = NULL; - spin_unlock_irqrestore(¤t->sighand->siglock, flags); if (!buf) return; From 7173c54e3a9deb491a586e7e107375109ee48bcb Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Tue, 30 Apr 2013 11:28:04 -0400 Subject: [PATCH 26/31] audit: use spin_lock in audit_receive_msg to process tty logging This function is called when we receive a netlink message from userspace. We don't need to worry about it coming from irq context or irqs making it re-entrant. Signed-off-by: Eric Paris --- kernel/audit.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/kernel/audit.c b/kernel/audit.c index 998a0d4155cf..d308723d22da 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -805,12 +805,11 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) case AUDIT_TTY_GET: { struct audit_tty_status s; struct task_struct *tsk = current; - unsigned long flags; - spin_lock_irqsave(&tsk->sighand->siglock, flags); + spin_lock(&tsk->sighand->siglock); s.enabled = tsk->signal->audit_tty != 0; s.log_passwd = tsk->signal->audit_tty_log_passwd; - spin_unlock_irqrestore(&tsk->sighand->siglock, flags); + spin_unlock(&tsk->sighand->siglock); audit_send_reply(NETLINK_CB(skb).portid, seq, AUDIT_TTY_GET, 0, 0, &s, sizeof(s)); @@ -819,7 +818,6 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) case AUDIT_TTY_SET: { struct audit_tty_status s; struct task_struct *tsk = current; - unsigned long flags; memset(&s, 0, sizeof(s)); /* guard against past and future API changes */ @@ -828,10 +826,10 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) (s.log_passwd != 0 && s.log_passwd != 1)) return -EINVAL; - spin_lock_irqsave(&tsk->sighand->siglock, flags); + spin_lock(&tsk->sighand->siglock); tsk->signal->audit_tty = s.enabled; tsk->signal->audit_tty_log_passwd = s.log_passwd; - spin_unlock_irqrestore(&tsk->sighand->siglock, flags); + spin_unlock(&tsk->sighand->siglock); break; } default: From b24a30a7305418ff138ff51776fc555ec57c011a Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Tue, 30 Apr 2013 15:30:32 -0400 Subject: [PATCH 27/31] audit: fix event coverage of AUDIT_ANOM_LINK The userspace audit tools didn't like the existing formatting of the AUDIT_ANOM_LINK event. It needed to be expanded to emit an AUDIT_PATH event as well, so this implements the change. The bulk of the patch is moving code out of auditsc.c into audit.c and audit.h for general use. It expands audit_log_name to include an optional "struct path" argument for the simple case of just needing to report a pathname. This also makes audit_log_task_info available when syscall auditing is not enabled, since it is needed in either case for process details. Signed-off-by: Kees Cook Reported-by: Steve Grubb --- include/linux/audit.h | 20 +-- kernel/audit.c | 244 ++++++++++++++++++++++++++++- kernel/audit.h | 157 +++++++++++++++++++ kernel/auditsc.c | 353 +----------------------------------------- 4 files changed, 405 insertions(+), 369 deletions(-) diff --git a/include/linux/audit.h b/include/linux/audit.h index b76bfc8efc25..469d11755e46 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -190,8 +190,6 @@ static inline int audit_get_sessionid(struct task_struct *tsk) return tsk->sessionid; } -extern int audit_log_task_context(struct audit_buffer *ab); -extern void audit_log_task_info(struct audit_buffer *ab, struct task_struct *tsk); extern void __audit_ipc_obj(struct kern_ipc_perm *ipcp); extern void __audit_ipc_set_perm(unsigned long qbytes, uid_t uid, gid_t gid, umode_t mode); extern int __audit_bprm(struct linux_binprm *bprm); @@ -346,13 +344,6 @@ static inline int audit_get_sessionid(struct task_struct *tsk) { return -1; } -static int void audit_log_task_context(struct audit_buffer *ab) -{ - return 0; -} -static inline void audit_log_task_info(struct audit_buffer *ab, - struct task_struct *tsk) -{ } static inline void audit_ipc_obj(struct kern_ipc_perm *ipcp) { } static inline void audit_ipc_set_perm(unsigned long qbytes, uid_t uid, @@ -439,6 +430,10 @@ static inline void audit_log_secctx(struct audit_buffer *ab, u32 secid) { } #endif +extern int audit_log_task_context(struct audit_buffer *ab); +extern void audit_log_task_info(struct audit_buffer *ab, + struct task_struct *tsk); + extern int audit_update_lsm_rules(void); /* Private API (for audit.c only) */ @@ -485,6 +480,13 @@ static inline void audit_log_link_denied(const char *string, { } static inline void audit_log_secctx(struct audit_buffer *ab, u32 secid) { } +static inline int audit_log_task_context(struct audit_buffer *ab) +{ + return 0; +} +static inline void audit_log_task_info(struct audit_buffer *ab, + struct task_struct *tsk) +{ } #define audit_enabled 0 #endif /* CONFIG_AUDIT */ static inline void audit_log_string(struct audit_buffer *ab, const char *buf) diff --git a/kernel/audit.c b/kernel/audit.c index d308723d22da..8cc580316948 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -50,6 +50,7 @@ #include #include #include +#include #include @@ -1393,6 +1394,224 @@ void audit_log_key(struct audit_buffer *ab, char *key) audit_log_format(ab, "(null)"); } +void audit_log_cap(struct audit_buffer *ab, char *prefix, kernel_cap_t *cap) +{ + int i; + + audit_log_format(ab, " %s=", prefix); + CAP_FOR_EACH_U32(i) { + audit_log_format(ab, "%08x", + cap->cap[(_KERNEL_CAPABILITY_U32S-1) - i]); + } +} + +void audit_log_fcaps(struct audit_buffer *ab, struct audit_names *name) +{ + kernel_cap_t *perm = &name->fcap.permitted; + kernel_cap_t *inh = &name->fcap.inheritable; + int log = 0; + + if (!cap_isclear(*perm)) { + audit_log_cap(ab, "cap_fp", perm); + log = 1; + } + if (!cap_isclear(*inh)) { + audit_log_cap(ab, "cap_fi", inh); + log = 1; + } + + if (log) + audit_log_format(ab, " cap_fe=%d cap_fver=%x", + name->fcap.fE, name->fcap_ver); +} + +static inline int audit_copy_fcaps(struct audit_names *name, + const struct dentry *dentry) +{ + struct cpu_vfs_cap_data caps; + int rc; + + if (!dentry) + return 0; + + rc = get_vfs_caps_from_disk(dentry, &caps); + if (rc) + return rc; + + name->fcap.permitted = caps.permitted; + name->fcap.inheritable = caps.inheritable; + name->fcap.fE = !!(caps.magic_etc & VFS_CAP_FLAGS_EFFECTIVE); + name->fcap_ver = (caps.magic_etc & VFS_CAP_REVISION_MASK) >> + VFS_CAP_REVISION_SHIFT; + + return 0; +} + +/* Copy inode data into an audit_names. */ +void audit_copy_inode(struct audit_names *name, const struct dentry *dentry, + const struct inode *inode) +{ + name->ino = inode->i_ino; + name->dev = inode->i_sb->s_dev; + name->mode = inode->i_mode; + name->uid = inode->i_uid; + name->gid = inode->i_gid; + name->rdev = inode->i_rdev; + security_inode_getsecid(inode, &name->osid); + audit_copy_fcaps(name, dentry); +} + +/** + * audit_log_name - produce AUDIT_PATH record from struct audit_names + * @context: audit_context for the task + * @n: audit_names structure with reportable details + * @path: optional path to report instead of audit_names->name + * @record_num: record number to report when handling a list of names + * @call_panic: optional pointer to int that will be updated if secid fails + */ +void audit_log_name(struct audit_context *context, struct audit_names *n, + struct path *path, int record_num, int *call_panic) +{ + struct audit_buffer *ab; + ab = audit_log_start(context, GFP_KERNEL, AUDIT_PATH); + if (!ab) + return; + + audit_log_format(ab, "item=%d", record_num); + + if (path) + audit_log_d_path(ab, " name=", path); + else if (n->name) { + switch (n->name_len) { + case AUDIT_NAME_FULL: + /* log the full path */ + audit_log_format(ab, " name="); + audit_log_untrustedstring(ab, n->name->name); + break; + case 0: + /* name was specified as a relative path and the + * directory component is the cwd */ + audit_log_d_path(ab, " name=", &context->pwd); + break; + default: + /* log the name's directory component */ + audit_log_format(ab, " name="); + audit_log_n_untrustedstring(ab, n->name->name, + n->name_len); + } + } else + audit_log_format(ab, " name=(null)"); + + if (n->ino != (unsigned long)-1) { + audit_log_format(ab, " inode=%lu" + " dev=%02x:%02x mode=%#ho" + " ouid=%u ogid=%u rdev=%02x:%02x", + n->ino, + MAJOR(n->dev), + MINOR(n->dev), + n->mode, + from_kuid(&init_user_ns, n->uid), + from_kgid(&init_user_ns, n->gid), + MAJOR(n->rdev), + MINOR(n->rdev)); + } + if (n->osid != 0) { + char *ctx = NULL; + u32 len; + if (security_secid_to_secctx( + n->osid, &ctx, &len)) { + audit_log_format(ab, " osid=%u", n->osid); + if (call_panic) + *call_panic = 2; + } else { + audit_log_format(ab, " obj=%s", ctx); + security_release_secctx(ctx, len); + } + } + + audit_log_fcaps(ab, n); + audit_log_end(ab); +} + +int audit_log_task_context(struct audit_buffer *ab) +{ + char *ctx = NULL; + unsigned len; + int error; + u32 sid; + + security_task_getsecid(current, &sid); + if (!sid) + return 0; + + error = security_secid_to_secctx(sid, &ctx, &len); + if (error) { + if (error != -EINVAL) + goto error_path; + return 0; + } + + audit_log_format(ab, " subj=%s", ctx); + security_release_secctx(ctx, len); + return 0; + +error_path: + audit_panic("error in audit_log_task_context"); + return error; +} +EXPORT_SYMBOL(audit_log_task_context); + +void audit_log_task_info(struct audit_buffer *ab, struct task_struct *tsk) +{ + const struct cred *cred; + char name[sizeof(tsk->comm)]; + struct mm_struct *mm = tsk->mm; + char *tty; + + if (!ab) + return; + + /* tsk == current */ + cred = current_cred(); + + spin_lock_irq(&tsk->sighand->siglock); + if (tsk->signal && tsk->signal->tty && tsk->signal->tty->name) + tty = tsk->signal->tty->name; + else + tty = "(none)"; + spin_unlock_irq(&tsk->sighand->siglock); + + audit_log_format(ab, + " ppid=%ld pid=%d auid=%u uid=%u gid=%u" + " euid=%u suid=%u fsuid=%u" + " egid=%u sgid=%u fsgid=%u ses=%u tty=%s", + sys_getppid(), + tsk->pid, + from_kuid(&init_user_ns, audit_get_loginuid(tsk)), + from_kuid(&init_user_ns, cred->uid), + from_kgid(&init_user_ns, cred->gid), + from_kuid(&init_user_ns, cred->euid), + from_kuid(&init_user_ns, cred->suid), + from_kuid(&init_user_ns, cred->fsuid), + from_kgid(&init_user_ns, cred->egid), + from_kgid(&init_user_ns, cred->sgid), + from_kgid(&init_user_ns, cred->fsgid), + audit_get_sessionid(tsk), tty); + + get_task_comm(name, tsk); + audit_log_format(ab, " comm="); + audit_log_untrustedstring(ab, name); + + if (mm) { + down_read(&mm->mmap_sem); + if (mm->exe_file) + audit_log_d_path(ab, " exe=", &mm->exe_file->f_path); + up_read(&mm->mmap_sem); + } + audit_log_task_context(ab); +} +EXPORT_SYMBOL(audit_log_task_info); + /** * audit_log_link_denied - report a link restriction denial * @operation: specific link opreation @@ -1401,19 +1620,28 @@ void audit_log_key(struct audit_buffer *ab, char *key) void audit_log_link_denied(const char *operation, struct path *link) { struct audit_buffer *ab; + struct audit_names *name; + name = kzalloc(sizeof(*name), GFP_NOFS); + if (!name) + return; + + /* Generate AUDIT_ANOM_LINK with subject, operation, outcome. */ ab = audit_log_start(current->audit_context, GFP_KERNEL, AUDIT_ANOM_LINK); if (!ab) - return; - audit_log_format(ab, "op=%s action=denied", operation); - audit_log_format(ab, " pid=%d comm=", current->pid); - audit_log_untrustedstring(ab, current->comm); - audit_log_d_path(ab, " path=", link); - audit_log_format(ab, " dev="); - audit_log_untrustedstring(ab, link->dentry->d_inode->i_sb->s_id); - audit_log_format(ab, " ino=%lu", link->dentry->d_inode->i_ino); + goto out; + audit_log_format(ab, "op=%s", operation); + audit_log_task_info(ab, current); + audit_log_format(ab, " res=0"); audit_log_end(ab); + + /* Generate AUDIT_PATH record with object. */ + name->type = AUDIT_TYPE_NORMAL; + audit_copy_inode(name, link->dentry, link->dentry->d_inode); + audit_log_name(current->audit_context, name, link, 0, NULL); +out: + kfree(name); } /** diff --git a/kernel/audit.h b/kernel/audit.h index d06ffc144f81..45c8325de5bb 100644 --- a/kernel/audit.h +++ b/kernel/audit.h @@ -22,6 +22,7 @@ #include #include #include +#include /* 0 = no checking 1 = put_count checking @@ -29,6 +30,11 @@ */ #define AUDIT_DEBUG 0 +/* AUDIT_NAMES is the number of slots we reserve in the audit_context + * for saving names from getname(). If we get more names we will allocate + * a name dynamically and also add those to the list anchored by names_list. */ +#define AUDIT_NAMES 5 + /* At task start time, the audit_state is set in the audit_context using a per-task filter. At syscall entry, the audit_state is augmented by the syscall filter. */ @@ -59,8 +65,159 @@ struct audit_entry { struct audit_krule rule; }; +struct audit_cap_data { + kernel_cap_t permitted; + kernel_cap_t inheritable; + union { + unsigned int fE; /* effective bit of file cap */ + kernel_cap_t effective; /* effective set of process */ + }; +}; + +/* When fs/namei.c:getname() is called, we store the pointer in name and + * we don't let putname() free it (instead we free all of the saved + * pointers at syscall exit time). + * + * Further, in fs/namei.c:path_lookup() we store the inode and device. + */ +struct audit_names { + struct list_head list; /* audit_context->names_list */ + + struct filename *name; + int name_len; /* number of chars to log */ + bool name_put; /* call __putname()? */ + + unsigned long ino; + dev_t dev; + umode_t mode; + kuid_t uid; + kgid_t gid; + dev_t rdev; + u32 osid; + struct audit_cap_data fcap; + unsigned int fcap_ver; + unsigned char type; /* record type */ + /* + * This was an allocated audit_names and not from the array of + * names allocated in the task audit context. Thus this name + * should be freed on syscall exit. + */ + bool should_free; +}; + +/* The per-task audit context. */ +struct audit_context { + int dummy; /* must be the first element */ + int in_syscall; /* 1 if task is in a syscall */ + enum audit_state state, current_state; + unsigned int serial; /* serial number for record */ + int major; /* syscall number */ + struct timespec ctime; /* time of syscall entry */ + unsigned long argv[4]; /* syscall arguments */ + long return_code;/* syscall return code */ + u64 prio; + int return_valid; /* return code is valid */ + /* + * The names_list is the list of all audit_names collected during this + * syscall. The first AUDIT_NAMES entries in the names_list will + * actually be from the preallocated_names array for performance + * reasons. Except during allocation they should never be referenced + * through the preallocated_names array and should only be found/used + * by running the names_list. + */ + struct audit_names preallocated_names[AUDIT_NAMES]; + int name_count; /* total records in names_list */ + struct list_head names_list; /* struct audit_names->list anchor */ + char *filterkey; /* key for rule that triggered record */ + struct path pwd; + struct audit_aux_data *aux; + struct audit_aux_data *aux_pids; + struct sockaddr_storage *sockaddr; + size_t sockaddr_len; + /* Save things to print about task_struct */ + pid_t pid, ppid; + kuid_t uid, euid, suid, fsuid; + kgid_t gid, egid, sgid, fsgid; + unsigned long personality; + int arch; + + pid_t target_pid; + kuid_t target_auid; + kuid_t target_uid; + unsigned int target_sessionid; + u32 target_sid; + char target_comm[TASK_COMM_LEN]; + + struct audit_tree_refs *trees, *first_trees; + struct list_head killed_trees; + int tree_count; + + int type; + union { + struct { + int nargs; + long args[6]; + } socketcall; + struct { + kuid_t uid; + kgid_t gid; + umode_t mode; + u32 osid; + int has_perm; + uid_t perm_uid; + gid_t perm_gid; + umode_t perm_mode; + unsigned long qbytes; + } ipc; + struct { + mqd_t mqdes; + struct mq_attr mqstat; + } mq_getsetattr; + struct { + mqd_t mqdes; + int sigev_signo; + } mq_notify; + struct { + mqd_t mqdes; + size_t msg_len; + unsigned int msg_prio; + struct timespec abs_timeout; + } mq_sendrecv; + struct { + int oflag; + umode_t mode; + struct mq_attr attr; + } mq_open; + struct { + pid_t pid; + struct audit_cap_data cap; + } capset; + struct { + int fd; + int flags; + } mmap; + }; + int fds[2]; + +#if AUDIT_DEBUG + int put_count; + int ino_count; +#endif +}; + #ifdef CONFIG_AUDIT +extern int audit_enabled; extern int audit_ever_enabled; + +extern void audit_copy_inode(struct audit_names *name, + const struct dentry *dentry, + const struct inode *inode); +extern void audit_log_cap(struct audit_buffer *ab, char *prefix, + kernel_cap_t *cap); +extern void audit_log_fcaps(struct audit_buffer *ab, struct audit_names *name); +extern void audit_log_name(struct audit_context *context, + struct audit_names *n, struct path *path, + int record_num, int *call_panic); #endif extern int audit_pid; diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 17e9a260a545..add3086bdb02 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -76,11 +76,6 @@ #define AUDITSC_SUCCESS 1 #define AUDITSC_FAILURE 2 -/* AUDIT_NAMES is the number of slots we reserve in the audit_context - * for saving names from getname(). If we get more names we will allocate - * a name dynamically and also add those to the list anchored by names_list. */ -#define AUDIT_NAMES 5 - /* no execve audit message should be longer than this (userspace limits) */ #define MAX_EXECVE_AUDIT_LEN 7500 @@ -90,44 +85,6 @@ int audit_n_rules; /* determines whether we collect data for signals sent */ int audit_signals; -struct audit_cap_data { - kernel_cap_t permitted; - kernel_cap_t inheritable; - union { - unsigned int fE; /* effective bit of a file capability */ - kernel_cap_t effective; /* effective set of a process */ - }; -}; - -/* When fs/namei.c:getname() is called, we store the pointer in name and - * we don't let putname() free it (instead we free all of the saved - * pointers at syscall exit time). - * - * Further, in fs/namei.c:path_lookup() we store the inode and device. - */ -struct audit_names { - struct list_head list; /* audit_context->names_list */ - struct filename *name; - unsigned long ino; - dev_t dev; - umode_t mode; - kuid_t uid; - kgid_t gid; - dev_t rdev; - u32 osid; - struct audit_cap_data fcap; - unsigned int fcap_ver; - int name_len; /* number of name's characters to log */ - unsigned char type; /* record type */ - bool name_put; /* call __putname() for this name */ - /* - * This was an allocated audit_names and not from the array of - * names allocated in the task audit context. Thus this name - * should be freed on syscall exit - */ - bool should_free; -}; - struct audit_aux_data { struct audit_aux_data *next; int type; @@ -175,106 +132,6 @@ struct audit_tree_refs { struct audit_chunk *c[31]; }; -/* The per-task audit context. */ -struct audit_context { - int dummy; /* must be the first element */ - int in_syscall; /* 1 if task is in a syscall */ - enum audit_state state, current_state; - unsigned int serial; /* serial number for record */ - int major; /* syscall number */ - struct timespec ctime; /* time of syscall entry */ - unsigned long argv[4]; /* syscall arguments */ - long return_code;/* syscall return code */ - u64 prio; - int return_valid; /* return code is valid */ - /* - * The names_list is the list of all audit_names collected during this - * syscall. The first AUDIT_NAMES entries in the names_list will - * actually be from the preallocated_names array for performance - * reasons. Except during allocation they should never be referenced - * through the preallocated_names array and should only be found/used - * by running the names_list. - */ - struct audit_names preallocated_names[AUDIT_NAMES]; - int name_count; /* total records in names_list */ - struct list_head names_list; /* anchor for struct audit_names->list */ - char * filterkey; /* key for rule that triggered record */ - struct path pwd; - struct audit_aux_data *aux; - struct audit_aux_data *aux_pids; - struct sockaddr_storage *sockaddr; - size_t sockaddr_len; - /* Save things to print about task_struct */ - pid_t pid, ppid; - kuid_t uid, euid, suid, fsuid; - kgid_t gid, egid, sgid, fsgid; - unsigned long personality; - int arch; - - pid_t target_pid; - kuid_t target_auid; - kuid_t target_uid; - unsigned int target_sessionid; - u32 target_sid; - char target_comm[TASK_COMM_LEN]; - - struct audit_tree_refs *trees, *first_trees; - struct list_head killed_trees; - int tree_count; - - int type; - union { - struct { - int nargs; - long args[AUDITSC_ARGS]; - } socketcall; - struct { - kuid_t uid; - kgid_t gid; - umode_t mode; - u32 osid; - int has_perm; - uid_t perm_uid; - gid_t perm_gid; - umode_t perm_mode; - unsigned long qbytes; - } ipc; - struct { - mqd_t mqdes; - struct mq_attr mqstat; - } mq_getsetattr; - struct { - mqd_t mqdes; - int sigev_signo; - } mq_notify; - struct { - mqd_t mqdes; - size_t msg_len; - unsigned int msg_prio; - struct timespec abs_timeout; - } mq_sendrecv; - struct { - int oflag; - umode_t mode; - struct mq_attr attr; - } mq_open; - struct { - pid_t pid; - struct audit_cap_data cap; - } capset; - struct { - int fd; - int flags; - } mmap; - }; - int fds[2]; - -#if AUDIT_DEBUG - int put_count; - int ino_count; -#endif -}; - static inline int open_arg(int flags, int mask) { int n = ACC_MODE(flags); @@ -1109,88 +966,6 @@ static inline void audit_free_context(struct audit_context *context) kfree(context); } -int audit_log_task_context(struct audit_buffer *ab) -{ - char *ctx = NULL; - unsigned len; - int error; - u32 sid; - - security_task_getsecid(current, &sid); - if (!sid) - return 0; - - error = security_secid_to_secctx(sid, &ctx, &len); - if (error) { - if (error != -EINVAL) - goto error_path; - return 0; - } - - audit_log_format(ab, " subj=%s", ctx); - security_release_secctx(ctx, len); - return 0; - -error_path: - audit_panic("error in audit_log_task_context"); - return error; -} - -EXPORT_SYMBOL(audit_log_task_context); - -void audit_log_task_info(struct audit_buffer *ab, struct task_struct *tsk) -{ - const struct cred *cred; - char name[sizeof(tsk->comm)]; - struct mm_struct *mm = tsk->mm; - char *tty; - - if (!ab) - return; - - /* tsk == current */ - cred = current_cred(); - - spin_lock_irq(&tsk->sighand->siglock); - if (tsk->signal && tsk->signal->tty) - tty = tsk->signal->tty->name; - else - tty = "(none)"; - spin_unlock_irq(&tsk->sighand->siglock); - - - audit_log_format(ab, - " ppid=%ld pid=%d auid=%u uid=%u gid=%u" - " euid=%u suid=%u fsuid=%u" - " egid=%u sgid=%u fsgid=%u ses=%u tty=%s", - sys_getppid(), - tsk->pid, - from_kuid(&init_user_ns, tsk->loginuid), - from_kuid(&init_user_ns, cred->uid), - from_kgid(&init_user_ns, cred->gid), - from_kuid(&init_user_ns, cred->euid), - from_kuid(&init_user_ns, cred->suid), - from_kuid(&init_user_ns, cred->fsuid), - from_kgid(&init_user_ns, cred->egid), - from_kgid(&init_user_ns, cred->sgid), - from_kgid(&init_user_ns, cred->fsgid), - tsk->sessionid, tty); - - get_task_comm(name, tsk); - audit_log_format(ab, " comm="); - audit_log_untrustedstring(ab, name); - - if (mm) { - down_read(&mm->mmap_sem); - if (mm->exe_file) - audit_log_d_path(ab, " exe=", &mm->exe_file->f_path); - up_read(&mm->mmap_sem); - } - audit_log_task_context(ab); -} - -EXPORT_SYMBOL(audit_log_task_info); - static int audit_log_pid_context(struct audit_context *context, pid_t pid, kuid_t auid, kuid_t uid, unsigned int sessionid, u32 sid, char *comm) @@ -1408,35 +1183,6 @@ static void audit_log_execve_info(struct audit_context *context, kfree(buf); } -static void audit_log_cap(struct audit_buffer *ab, char *prefix, kernel_cap_t *cap) -{ - int i; - - audit_log_format(ab, " %s=", prefix); - CAP_FOR_EACH_U32(i) { - audit_log_format(ab, "%08x", cap->cap[(_KERNEL_CAPABILITY_U32S-1) - i]); - } -} - -static void audit_log_fcaps(struct audit_buffer *ab, struct audit_names *name) -{ - kernel_cap_t *perm = &name->fcap.permitted; - kernel_cap_t *inh = &name->fcap.inheritable; - int log = 0; - - if (!cap_isclear(*perm)) { - audit_log_cap(ab, "cap_fp", perm); - log = 1; - } - if (!cap_isclear(*inh)) { - audit_log_cap(ab, "cap_fi", inh); - log = 1; - } - - if (log) - audit_log_format(ab, " cap_fe=%d cap_fver=%x", name->fcap.fE, name->fcap_ver); -} - static void show_special(struct audit_context *context, int *call_panic) { struct audit_buffer *ab; @@ -1534,68 +1280,6 @@ static void show_special(struct audit_context *context, int *call_panic) audit_log_end(ab); } -static void audit_log_name(struct audit_context *context, struct audit_names *n, - int record_num, int *call_panic) -{ - struct audit_buffer *ab; - ab = audit_log_start(context, GFP_KERNEL, AUDIT_PATH); - if (!ab) - return; /* audit_panic has been called */ - - audit_log_format(ab, "item=%d", record_num); - - if (n->name) { - switch (n->name_len) { - case AUDIT_NAME_FULL: - /* log the full path */ - audit_log_format(ab, " name="); - audit_log_untrustedstring(ab, n->name->name); - break; - case 0: - /* name was specified as a relative path and the - * directory component is the cwd */ - audit_log_d_path(ab, " name=", &context->pwd); - break; - default: - /* log the name's directory component */ - audit_log_format(ab, " name="); - audit_log_n_untrustedstring(ab, n->name->name, - n->name_len); - } - } else - audit_log_format(ab, " name=(null)"); - - if (n->ino != (unsigned long)-1) { - audit_log_format(ab, " inode=%lu" - " dev=%02x:%02x mode=%#ho" - " ouid=%u ogid=%u rdev=%02x:%02x", - n->ino, - MAJOR(n->dev), - MINOR(n->dev), - n->mode, - from_kuid(&init_user_ns, n->uid), - from_kgid(&init_user_ns, n->gid), - MAJOR(n->rdev), - MINOR(n->rdev)); - } - if (n->osid != 0) { - char *ctx = NULL; - u32 len; - if (security_secid_to_secctx( - n->osid, &ctx, &len)) { - audit_log_format(ab, " osid=%u", n->osid); - *call_panic = 2; - } else { - audit_log_format(ab, " obj=%s", ctx); - security_release_secctx(ctx, len); - } - } - - audit_log_fcaps(ab, n); - - audit_log_end(ab); -} - static void audit_log_exit(struct audit_context *context, struct task_struct *tsk) { int i, call_panic = 0; @@ -1713,7 +1397,7 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts i = 0; list_for_each_entry(n, &context->names_list, list) - audit_log_name(context, n, i++, &call_panic); + audit_log_name(context, n, NULL, i++, &call_panic); /* Send end of event record to help user space know we are finished */ ab = audit_log_start(context, GFP_KERNEL, AUDIT_EOE); @@ -2078,41 +1762,6 @@ void audit_putname(struct filename *name) #endif } -static inline int audit_copy_fcaps(struct audit_names *name, const struct dentry *dentry) -{ - struct cpu_vfs_cap_data caps; - int rc; - - if (!dentry) - return 0; - - rc = get_vfs_caps_from_disk(dentry, &caps); - if (rc) - return rc; - - name->fcap.permitted = caps.permitted; - name->fcap.inheritable = caps.inheritable; - name->fcap.fE = !!(caps.magic_etc & VFS_CAP_FLAGS_EFFECTIVE); - name->fcap_ver = (caps.magic_etc & VFS_CAP_REVISION_MASK) >> VFS_CAP_REVISION_SHIFT; - - return 0; -} - - -/* Copy inode data into an audit_names. */ -static void audit_copy_inode(struct audit_names *name, const struct dentry *dentry, - const struct inode *inode) -{ - name->ino = inode->i_ino; - name->dev = inode->i_sb->s_dev; - name->mode = inode->i_mode; - name->uid = inode->i_uid; - name->gid = inode->i_gid; - name->rdev = inode->i_rdev; - security_inode_getsecid(inode, &name->osid); - audit_copy_fcaps(name, dentry); -} - /** * __audit_inode - store the inode and device from a lookup * @name: name being audited From 780a7654cee8d61819512385e778e4827db4bfbc Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Tue, 9 Apr 2013 02:22:10 -0700 Subject: [PATCH 28/31] audit: Make testing for a valid loginuid explicit. audit rule additions containing "-F auid!=4294967295" were failing with EINVAL because of a regression caused by e1760bd. Apparently some userland audit rule sets want to know if loginuid uid has been set and are using a test for auid != 4294967295 to determine that. In practice that is a horrible way to ask if a value has been set, because it relies on subtle implementation details and will break every time the uid implementation in the kernel changes. So add a clean way to test if the audit loginuid has been set, and silently convert the old idiom to the cleaner and more comprehensible new idiom. Cc: # 3.7 Reported-By: Richard Guy Briggs Signed-off-by: "Eric W. Biederman" Tested-by: Richard Guy Briggs Signed-off-by: Eric Paris --- include/linux/audit.h | 5 +++++ include/uapi/linux/audit.h | 1 + kernel/auditfilter.c | 17 +++++++++++++++-- kernel/auditsc.c | 5 ++++- 4 files changed, 25 insertions(+), 3 deletions(-) diff --git a/include/linux/audit.h b/include/linux/audit.h index 469d11755e46..b20b03852f21 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -391,6 +391,11 @@ static inline void audit_ptrace(struct task_struct *t) #define audit_signals 0 #endif /* CONFIG_AUDITSYSCALL */ +static inline bool audit_loginuid_set(struct task_struct *tsk) +{ + return uid_valid(audit_get_loginuid(tsk)); +} + #ifdef CONFIG_AUDIT /* These are defined in audit.c */ /* Public API */ diff --git a/include/uapi/linux/audit.h b/include/uapi/linux/audit.h index c058c24b97ac..75cef3fd97ad 100644 --- a/include/uapi/linux/audit.h +++ b/include/uapi/linux/audit.h @@ -246,6 +246,7 @@ #define AUDIT_OBJ_TYPE 21 #define AUDIT_OBJ_LEV_LOW 22 #define AUDIT_OBJ_LEV_HIGH 23 +#define AUDIT_LOGINUID_SET 24 /* These are ONLY useful when checking * at syscall exit time (AUDIT_AT_EXIT). */ diff --git a/kernel/auditfilter.c b/kernel/auditfilter.c index 478f4602c96b..bc6595fe952e 100644 --- a/kernel/auditfilter.c +++ b/kernel/auditfilter.c @@ -365,7 +365,10 @@ static int audit_field_valid(struct audit_entry *entry, struct audit_field *f) case AUDIT_DIR: case AUDIT_FILTERKEY: break; - /* arch is only allowed to be = or != */ + case AUDIT_LOGINUID_SET: + if ((f->val != 0) && (f->val != 1)) + return -EINVAL; + /* FALL THROUGH */ case AUDIT_ARCH: if (f->op != Audit_not_equal && f->op != Audit_equal) return -EINVAL; @@ -419,17 +422,23 @@ static struct audit_entry *audit_data_to_entry(struct audit_rule_data *data, f->lsm_str = NULL; f->lsm_rule = NULL; + /* Support legacy tests for a valid loginuid */ + if ((f->type == AUDIT_LOGINUID) && (f->val == 4294967295)) { + f->type = AUDIT_LOGINUID_SET; + f->val = 0; + } + err = audit_field_valid(entry, f); if (err) goto exit_free; err = -EINVAL; switch (f->type) { + case AUDIT_LOGINUID: case AUDIT_UID: case AUDIT_EUID: case AUDIT_SUID: case AUDIT_FSUID: - case AUDIT_LOGINUID: case AUDIT_OBJ_UID: f->uid = make_kuid(current_user_ns(), f->val); if (!uid_valid(f->uid)) @@ -1222,6 +1231,10 @@ static int audit_filter_user_rules(struct audit_krule *rule, int type, result = audit_uid_comparator(audit_get_loginuid(current), f->op, f->uid); break; + case AUDIT_LOGINUID_SET: + result = audit_comparator(audit_loginuid_set(current), + f->op, f->val); + break; case AUDIT_MSGTYPE: result = audit_comparator(type, f->op, f->val); break; diff --git a/kernel/auditsc.c b/kernel/auditsc.c index add3086bdb02..3c8a601324a2 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -613,6 +613,9 @@ static int audit_filter_rules(struct task_struct *tsk, if (ctx) result = audit_uid_comparator(tsk->loginuid, f->op, f->uid); break; + case AUDIT_LOGINUID_SET: + result = audit_comparator(audit_loginuid_set(tsk), f->op, f->val); + break; case AUDIT_SUBJ_USER: case AUDIT_SUBJ_ROLE: case AUDIT_SUBJ_TYPE: @@ -1970,7 +1973,7 @@ int audit_set_loginuid(kuid_t loginuid) unsigned int sessionid; #ifdef CONFIG_AUDIT_LOGINUID_IMMUTABLE - if (uid_valid(task->loginuid)) + if (audit_loginuid_set(task)) return -EPERM; #else /* CONFIG_AUDIT_LOGINUID_IMMUTABLE */ if (!capable(CAP_AUDIT_CONTROL)) From 33e2208acfc15ce00d3dd13e839bf6434faa2b04 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Fri, 12 Apr 2013 15:16:32 -0400 Subject: [PATCH 29/31] audit: vfs: fix audit_inode call in O_CREAT case of do_last Jiri reported a regression in auditing of open(..., O_CREAT) syscalls. In older kernels, creating a file with open(..., O_CREAT) created audit_name records that looked like this: type=PATH msg=audit(1360255720.628:64): item=1 name="/abc/foo" inode=138810 dev=fd:00 mode=0100640 ouid=0 ogid=0 rdev=00:00 obj=unconfined_u:object_r:default_t:s0 type=PATH msg=audit(1360255720.628:64): item=0 name="/abc/" inode=138635 dev=fd:00 mode=040750 ouid=0 ogid=0 rdev=00:00 obj=unconfined_u:object_r:default_t:s0 ...in recent kernels though, they look like this: type=PATH msg=audit(1360255402.886:12574): item=2 name=(null) inode=264599 dev=fd:00 mode=0100640 ouid=0 ogid=0 rdev=00:00 obj=unconfined_u:object_r:default_t:s0 type=PATH msg=audit(1360255402.886:12574): item=1 name=(null) inode=264598 dev=fd:00 mode=040750 ouid=0 ogid=0 rdev=00:00 obj=unconfined_u:object_r:default_t:s0 type=PATH msg=audit(1360255402.886:12574): item=0 name="/abc/foo" inode=264598 dev=fd:00 mode=040750 ouid=0 ogid=0 rdev=00:00 obj=unconfined_u:object_r:default_t:s0 Richard bisected to determine that the problems started with commit bfcec708, but the log messages have changed with some later audit-related patches. The problem is that this audit_inode call is passing in the parent of the dentry being opened, but audit_inode is being called with the parent flag false. This causes later audit_inode and audit_inode_child calls to match the wrong entry in the audit_names list. This patch simply sets the flag to properly indicate that this inode represents the parent. With this, the audit_names entries are back to looking like they did before. Cc: # v3.7+ Reported-by: Jiri Jaburek Signed-off-by: Jeff Layton Test By: Richard Guy Briggs Signed-off-by: Eric Paris --- fs/namei.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/namei.c b/fs/namei.c index 43a97ee1d4c8..498ede374dc0 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -2751,7 +2751,7 @@ static int do_last(struct nameidata *nd, struct path *path, if (error) return error; - audit_inode(name, dir, 0); + audit_inode(name, dir, LOOKUP_PARENT); error = -EISDIR; /* trailing slashes? */ if (nd->last.name[nd->last.len]) From 82d8da0d46ae7d3e9089efadb5e8d9841c20a431 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Tue, 7 May 2013 21:24:02 -0400 Subject: [PATCH 30/31] Revert "audit: move kaudit thread start from auditd registration to kaudit init" This reverts commit 6ff5e45985c2fcb97947818f66d1eeaf9d6600b2. Conflicts: kernel/audit.c This patch was starting a kthread for all the time. Since the follow on patches that required it didn't get finished in 3.10 time, we shouldn't ship this change in 3.10. Signed-off-by: Eric Paris --- kernel/audit.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/kernel/audit.c b/kernel/audit.c index 8cc580316948..f9c6506536e6 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -644,6 +644,16 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) if (err) return err; + /* As soon as there's any sign of userspace auditd, + * start kauditd to talk to it */ + if (!kauditd_task) + kauditd_task = kthread_run(kauditd_thread, NULL, "kauditd"); + if (IS_ERR(kauditd_task)) { + err = PTR_ERR(kauditd_task); + kauditd_task = NULL; + return err; + } + seq = nlh->nlmsg_seq; data = nlmsg_data(nlh); @@ -895,10 +905,6 @@ static int __init audit_init(void) else audit_sock->sk_sndtimeo = MAX_SCHEDULE_TIMEOUT; - kauditd_task = kthread_run(kauditd_thread, NULL, "kauditd"); - if (IS_ERR(kauditd_task)) - return PTR_ERR(kauditd_task); - skb_queue_head_init(&audit_skb_queue); skb_queue_head_init(&audit_skb_hold_queue); audit_initialized = AUDIT_INITIALIZED; From 2a0b4be6dd655e24990da1d0811e28b9277f8b12 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Wed, 8 May 2013 00:01:07 -0400 Subject: [PATCH 31/31] audit: fix message spacing printing auid The helper function didn't include a leading space, so it was jammed against the previous text in the audit record. Signed-off-by: Eric Paris --- kernel/audit.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/audit.c b/kernel/audit.c index f9c6506536e6..5c7e62ff4795 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -1388,7 +1388,7 @@ void audit_log_session_info(struct audit_buffer *ab) u32 sessionid = audit_get_sessionid(current); uid_t auid = from_kuid(&init_user_ns, audit_get_loginuid(current)); - audit_log_format(ab, "auid=%u ses=%u\n", auid, sessionid); + audit_log_format(ab, " auid=%u ses=%u\n", auid, sessionid); } void audit_log_key(struct audit_buffer *ab, char *key)