From 0c11b9428f619ab377c92eff2f160a834a6585dd Mon Sep 17 00:00:00 2001 From: Al Viro Date: Thu, 10 Jan 2008 04:20:52 -0500 Subject: [PATCH] switch audit_get_loginuid() to task_struct * all callers pass something->audit_context Signed-off-by: Al Viro --- include/linux/audit.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/audit.h b/include/linux/audit.h index bdd6f5de5fc4..f63117fab305 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -409,7 +409,7 @@ extern unsigned int audit_serial(void); extern void auditsc_get_stamp(struct audit_context *ctx, struct timespec *t, unsigned int *serial); extern int audit_set_loginuid(struct task_struct *task, uid_t loginuid); -extern uid_t audit_get_loginuid(struct audit_context *ctx); +extern uid_t audit_get_loginuid(struct task_struct *task); extern void audit_log_task_context(struct audit_buffer *ab); extern int __audit_ipc_obj(struct kern_ipc_perm *ipcp); extern int __audit_ipc_set_perm(unsigned long qbytes, uid_t uid, gid_t gid, mode_t mode); @@ -488,7 +488,7 @@ extern int audit_signals; #define audit_inode_child(d,i,p) do { ; } while (0) #define audit_core_dumps(i) do { ; } while (0) #define auditsc_get_stamp(c,t,s) do { BUG(); } while (0) -#define audit_get_loginuid(c) ({ -1; }) +#define audit_get_loginuid(t) (-1) #define audit_log_task_context(b) do { ; } while (0) #define audit_ipc_obj(i) ({ 0; }) #define audit_ipc_set_perm(q,u,g,m) ({ 0; }) -- cgit v1.2.3 From bfef93a5d1fb5654fe2025276c55e202d10b5255 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Thu, 10 Jan 2008 04:53:18 -0500 Subject: [PATCH] get rid of loginuid races Keeping loginuid in audit_context is racy and results in messier code. Taken to task_struct, out of the way of ->audit_context changes. Signed-off-by: Al Viro --- include/linux/audit.h | 2 +- include/linux/init_task.h | 7 ++++++ include/linux/sched.h | 3 +++ kernel/auditsc.c | 56 +++++++++++++---------------------------------- 4 files changed, 26 insertions(+), 42 deletions(-) (limited to 'include/linux') diff --git a/include/linux/audit.h b/include/linux/audit.h index f63117fab305..d7c6a12f4d1c 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -409,7 +409,7 @@ extern unsigned int audit_serial(void); extern void auditsc_get_stamp(struct audit_context *ctx, struct timespec *t, unsigned int *serial); extern int audit_set_loginuid(struct task_struct *task, uid_t loginuid); -extern uid_t audit_get_loginuid(struct task_struct *task); +#define audit_get_loginuid(t) ((t)->loginuid) extern void audit_log_task_context(struct audit_buffer *ab); extern int __audit_ipc_obj(struct kern_ipc_perm *ipcp); extern int __audit_ipc_set_perm(unsigned long qbytes, uid_t uid, gid_t gid, mode_t mode); diff --git a/include/linux/init_task.h b/include/linux/init_task.h index e6b3f7080679..ea3e9efd7396 100644 --- a/include/linux/init_task.h +++ b/include/linux/init_task.h @@ -114,6 +114,12 @@ extern struct group_info init_groups; .pid = &init_struct_pid, \ } +#ifdef CONFIG_AUDITSYSCALL +#define INIT_IDS \ + .loginuid = -1, +#else +#define INIT_IDS +#endif /* * INIT_TASK is used to set up the first task table, touch at * your own risk!. Base=0, limit=0x1fffff (=2MB) @@ -173,6 +179,7 @@ extern struct group_info init_groups; [PIDTYPE_SID] = INIT_PID_LINK(PIDTYPE_SID), \ }, \ .dirties = INIT_PROP_LOCAL_SINGLE(dirties), \ + INIT_IDS \ INIT_TRACE_IRQFLAGS \ INIT_LOCKDEP \ } diff --git a/include/linux/sched.h b/include/linux/sched.h index 6c333579d9da..5e2730389089 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1139,6 +1139,9 @@ struct task_struct { void *security; #endif struct audit_context *audit_context; +#ifdef CONFIG_AUDITSYSCALL + uid_t loginuid; +#endif seccomp_t seccomp; /* Thread group tracking */ diff --git a/kernel/auditsc.c b/kernel/auditsc.c index bd4e0a2443fb..c95173a194bf 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -192,7 +192,6 @@ struct audit_context { enum audit_state state; unsigned int serial; /* serial number for record */ struct timespec ctime; /* time of syscall entry */ - uid_t loginuid; /* login uid (identity) */ int major; /* syscall number */ unsigned long argv[4]; /* syscall arguments */ int return_valid; /* return code is valid */ @@ -506,7 +505,7 @@ static int audit_filter_rules(struct task_struct *tsk, case AUDIT_LOGINUID: result = 0; if (ctx) - result = audit_comparator(ctx->loginuid, f->op, f->val); + result = audit_comparator(tsk->loginuid, f->op, f->val); break; case AUDIT_SUBJ_USER: case AUDIT_SUBJ_ROLE: @@ -783,11 +782,8 @@ static inline void audit_free_aux(struct audit_context *context) static inline void audit_zero_context(struct audit_context *context, enum audit_state state) { - uid_t loginuid = context->loginuid; - memset(context, 0, sizeof(*context)); context->state = state; - context->loginuid = loginuid; } static inline struct audit_context *audit_alloc_context(enum audit_state state) @@ -826,11 +822,6 @@ int audit_alloc(struct task_struct *tsk) return -ENOMEM; } - /* Preserve login uid */ - context->loginuid = -1; - if (current->audit_context) - context->loginuid = current->audit_context->loginuid; - tsk->audit_context = context; set_tsk_thread_flag(tsk, TIF_SYSCALL_AUDIT); return 0; @@ -1047,7 +1038,7 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts context->name_count, context->ppid, context->pid, - context->loginuid, + tsk->loginuid, context->uid, context->gid, context->euid, context->suid, context->fsuid, @@ -1779,39 +1770,22 @@ int audit_set_loginuid(struct task_struct *task, uid_t loginuid) { struct audit_context *context = task->audit_context; - if (context) { - /* Only log if audit is enabled */ - if (context->in_syscall) { - struct audit_buffer *ab; - - ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_LOGIN); - if (ab) { - audit_log_format(ab, "login pid=%d uid=%u " - "old auid=%u new auid=%u", - task->pid, task->uid, - context->loginuid, loginuid); - audit_log_end(ab); - } + if (context && context->in_syscall) { + struct audit_buffer *ab; + + ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_LOGIN); + if (ab) { + audit_log_format(ab, "login pid=%d uid=%u " + "old auid=%u new auid=%u", + task->pid, task->uid, + task->loginuid, loginuid); + audit_log_end(ab); } - context->loginuid = loginuid; } + task->loginuid = loginuid; return 0; } -/** - * audit_get_loginuid - get the loginuid for an audit_context - * @ctx: the audit_context - * - * Returns the context's loginuid or -1 if @ctx is NULL. - */ -uid_t audit_get_loginuid(struct task_struct *task) -{ - struct audit_context *ctx = task->audit_context; - return ctx ? ctx->loginuid : -1; -} - -EXPORT_SYMBOL(audit_get_loginuid); - /** * __audit_mq_open - record audit data for a POSIX MQ open * @oflag: open flag @@ -2217,8 +2191,8 @@ int __audit_signal_info(int sig, struct task_struct *t) if (audit_pid && t->tgid == audit_pid) { if (sig == SIGTERM || sig == SIGHUP || sig == SIGUSR1) { audit_sig_pid = tsk->pid; - if (ctx) - audit_sig_uid = ctx->loginuid; + if (tsk->loginuid != -1) + audit_sig_uid = tsk->loginuid; else audit_sig_uid = tsk->uid; selinux_get_task_sid(tsk, &audit_sig_sid); -- cgit v1.2.3 From 4746ec5b01ed07205a91e4f7ed9de9d70f371407 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Tue, 8 Jan 2008 10:06:53 -0500 Subject: [AUDIT] add session id to audit messages In order to correlate audit records to an individual login add a session id. This is incremented every time a user logs in and is included in almost all messages which currently output the auid. The field is labeled ses= or oses= Signed-off-by: Eric Paris --- drivers/char/tty_audit.c | 18 +++++++++++++----- include/linux/audit.h | 2 ++ include/linux/init_task.h | 3 ++- include/linux/sched.h | 1 + kernel/auditsc.c | 36 +++++++++++++++++++++++++++--------- net/core/dev.c | 5 +++-- security/selinux/selinuxfs.c | 17 ++++++++++------- security/selinux/ss/services.c | 5 +++-- 8 files changed, 61 insertions(+), 26 deletions(-) (limited to 'include/linux') diff --git a/drivers/char/tty_audit.c b/drivers/char/tty_audit.c index c590fc45b2fd..bacded0eefab 100644 --- a/drivers/char/tty_audit.c +++ b/drivers/char/tty_audit.c @@ -73,6 +73,7 @@ static void tty_audit_buf_put(struct tty_audit_buf *buf) * @tsk with @loginuid. @buf->mutex must be locked. */ static void tty_audit_buf_push(struct task_struct *tsk, uid_t loginuid, + unsigned int sessionid, struct tty_audit_buf *buf) { struct audit_buffer *ab; @@ -85,9 +86,9 @@ static void tty_audit_buf_push(struct task_struct *tsk, uid_t loginuid, if (ab) { char name[sizeof(tsk->comm)]; - audit_log_format(ab, "tty pid=%u uid=%u auid=%u major=%d " - "minor=%d comm=", tsk->pid, tsk->uid, - loginuid, buf->major, buf->minor); + audit_log_format(ab, "tty pid=%u uid=%u auid=%u ses=%u " + "major=%d minor=%d comm=", tsk->pid, tsk->uid, + loginuid, sessionid, buf->major, buf->minor); get_task_comm(name, tsk); audit_log_untrustedstring(ab, name); audit_log_format(ab, " data="); @@ -105,7 +106,9 @@ static void tty_audit_buf_push(struct task_struct *tsk, uid_t loginuid, */ static void tty_audit_buf_push_current(struct tty_audit_buf *buf) { - tty_audit_buf_push(current, audit_get_loginuid(current), buf); + uid_t auid = audit_get_loginuid(current); + unsigned int sessionid = audit_get_sessionid(current); + tty_audit_buf_push(current, auid, sessionid, buf); } /** @@ -151,6 +154,11 @@ void tty_audit_fork(struct signal_struct *sig) void tty_audit_push_task(struct task_struct *tsk, uid_t loginuid) { struct tty_audit_buf *buf; + /* FIXME I think this is correct. Check against netlink once that is + * I really need to read this code more closely. But that's for + * another patch. + */ + unsigned int sessionid = audit_get_sessionid(tsk); spin_lock_irq(&tsk->sighand->siglock); buf = tsk->signal->tty_audit_buf; @@ -161,7 +169,7 @@ void tty_audit_push_task(struct task_struct *tsk, uid_t loginuid) return; mutex_lock(&buf->mutex); - tty_audit_buf_push(tsk, loginuid, buf); + tty_audit_buf_push(tsk, loginuid, sessionid, buf); mutex_unlock(&buf->mutex); tty_audit_buf_put(buf); diff --git a/include/linux/audit.h b/include/linux/audit.h index d7c6a12f4d1c..52f1b12505a9 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -410,6 +410,7 @@ extern void auditsc_get_stamp(struct audit_context *ctx, struct timespec *t, unsigned int *serial); extern int audit_set_loginuid(struct task_struct *task, uid_t loginuid); #define audit_get_loginuid(t) ((t)->loginuid) +#define audit_get_sessionid(t) ((t)->sessionid) extern void audit_log_task_context(struct audit_buffer *ab); extern int __audit_ipc_obj(struct kern_ipc_perm *ipcp); extern int __audit_ipc_set_perm(unsigned long qbytes, uid_t uid, gid_t gid, mode_t mode); @@ -489,6 +490,7 @@ extern int audit_signals; #define audit_core_dumps(i) do { ; } while (0) #define auditsc_get_stamp(c,t,s) do { BUG(); } while (0) #define audit_get_loginuid(t) (-1) +#define audit_get_sessionid(t) (-1) #define audit_log_task_context(b) do { ; } while (0) #define audit_ipc_obj(i) ({ 0; }) #define audit_ipc_set_perm(q,u,g,m) ({ 0; }) diff --git a/include/linux/init_task.h b/include/linux/init_task.h index ea3e9efd7396..f42663eaf655 100644 --- a/include/linux/init_task.h +++ b/include/linux/init_task.h @@ -116,7 +116,8 @@ extern struct group_info init_groups; #ifdef CONFIG_AUDITSYSCALL #define INIT_IDS \ - .loginuid = -1, + .loginuid = -1, \ + .sessionid = -1, #else #define INIT_IDS #endif diff --git a/include/linux/sched.h b/include/linux/sched.h index 5e2730389089..af6947e69b40 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1141,6 +1141,7 @@ struct task_struct { struct audit_context *audit_context; #ifdef CONFIG_AUDITSYSCALL uid_t loginuid; + unsigned int sessionid; #endif seccomp_t seccomp; diff --git a/kernel/auditsc.c b/kernel/auditsc.c index a222e73fec74..4e67abb02904 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -178,6 +178,7 @@ struct audit_aux_data_pids { pid_t target_pid[AUDIT_AUX_PIDS]; uid_t target_auid[AUDIT_AUX_PIDS]; uid_t target_uid[AUDIT_AUX_PIDS]; + unsigned int target_sessionid[AUDIT_AUX_PIDS]; u32 target_sid[AUDIT_AUX_PIDS]; char target_comm[AUDIT_AUX_PIDS][TASK_COMM_LEN]; int pid_count; @@ -219,6 +220,7 @@ struct audit_context { pid_t target_pid; uid_t target_auid; uid_t target_uid; + unsigned int target_sessionid; u32 target_sid; char target_comm[TASK_COMM_LEN]; @@ -936,7 +938,8 @@ static void audit_log_task_info(struct audit_buffer *ab, struct task_struct *tsk } static int audit_log_pid_context(struct audit_context *context, pid_t pid, - uid_t auid, uid_t uid, u32 sid, char *comm) + uid_t auid, uid_t uid, unsigned int sessionid, + u32 sid, char *comm) { struct audit_buffer *ab; char *s = NULL; @@ -947,7 +950,8 @@ static int audit_log_pid_context(struct audit_context *context, pid_t pid, if (!ab) return 1; - audit_log_format(ab, "opid=%d oauid=%d ouid=%d", pid, auid, uid); + audit_log_format(ab, "opid=%d oauid=%d ouid=%d oses=%d", pid, auid, + uid, sessionid); if (selinux_sid_to_string(sid, &s, &len)) { audit_log_format(ab, " obj=(none)"); rc = 1; @@ -1056,7 +1060,7 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts " a0=%lx a1=%lx a2=%lx a3=%lx items=%d" " ppid=%d pid=%d auid=%u uid=%u gid=%u" " euid=%u suid=%u fsuid=%u" - " egid=%u sgid=%u fsgid=%u tty=%s", + " egid=%u sgid=%u fsgid=%u tty=%s ses=%u", context->argv[0], context->argv[1], context->argv[2], @@ -1068,7 +1072,8 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts context->uid, context->gid, context->euid, context->suid, context->fsuid, - context->egid, context->sgid, context->fsgid, tty); + context->egid, context->sgid, context->fsgid, tty, + tsk->sessionid); mutex_unlock(&tty_mutex); @@ -1187,6 +1192,7 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts if (audit_log_pid_context(context, axs->target_pid[i], axs->target_auid[i], axs->target_uid[i], + axs->target_sessionid[i], axs->target_sid[i], axs->target_comm[i])) call_panic = 1; @@ -1195,6 +1201,7 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts if (context->target_pid && audit_log_pid_context(context, context->target_pid, context->target_auid, context->target_uid, + context->target_sessionid, context->target_sid, context->target_comm)) call_panic = 1; @@ -1787,6 +1794,9 @@ void auditsc_get_stamp(struct audit_context *ctx, ctx->auditable = 1; } +/* global counter which is incremented every time something logs in */ +static atomic_t session_id = ATOMIC_INIT(0); + /** * audit_set_loginuid - set a task's audit_context loginuid * @task: task whose audit context is being modified @@ -1798,6 +1808,7 @@ void auditsc_get_stamp(struct audit_context *ctx, */ int audit_set_loginuid(struct task_struct *task, uid_t loginuid) { + unsigned int sessionid = atomic_inc_return(&session_id); struct audit_context *context = task->audit_context; if (context && context->in_syscall) { @@ -1806,12 +1817,15 @@ int audit_set_loginuid(struct task_struct *task, uid_t loginuid) ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_LOGIN); if (ab) { audit_log_format(ab, "login pid=%d uid=%u " - "old auid=%u new auid=%u", + "old auid=%u new auid=%u" + " old ses=%u new ses=%u", task->pid, task->uid, - task->loginuid, loginuid); + task->loginuid, loginuid, + task->sessionid, sessionid); audit_log_end(ab); } } + task->sessionid = sessionid; task->loginuid = loginuid; return 0; } @@ -2200,6 +2214,7 @@ void __audit_ptrace(struct task_struct *t) context->target_pid = t->pid; context->target_auid = audit_get_loginuid(t); context->target_uid = t->uid; + context->target_sessionid = audit_get_sessionid(t); selinux_get_task_sid(t, &context->target_sid); memcpy(context->target_comm, t->comm, TASK_COMM_LEN); } @@ -2240,6 +2255,7 @@ int __audit_signal_info(int sig, struct task_struct *t) ctx->target_pid = t->tgid; ctx->target_auid = audit_get_loginuid(t); ctx->target_uid = t->uid; + ctx->target_sessionid = audit_get_sessionid(t); selinux_get_task_sid(t, &ctx->target_sid); memcpy(ctx->target_comm, t->comm, TASK_COMM_LEN); return 0; @@ -2260,6 +2276,7 @@ int __audit_signal_info(int sig, struct task_struct *t) axp->target_pid[axp->pid_count] = t->tgid; axp->target_auid[axp->pid_count] = audit_get_loginuid(t); axp->target_uid[axp->pid_count] = t->uid; + axp->target_sessionid[axp->pid_count] = audit_get_sessionid(t); selinux_get_task_sid(t, &axp->target_sid[axp->pid_count]); memcpy(axp->target_comm[axp->pid_count], t->comm, TASK_COMM_LEN); axp->pid_count++; @@ -2278,6 +2295,8 @@ void audit_core_dumps(long signr) { struct audit_buffer *ab; u32 sid; + uid_t auid = audit_get_loginuid(current); + unsigned int sessionid = audit_get_sessionid(current); if (!audit_enabled) return; @@ -2286,9 +2305,8 @@ void audit_core_dumps(long signr) return; ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_ANOM_ABEND); - audit_log_format(ab, "auid=%u uid=%u gid=%u", - audit_get_loginuid(current), - current->uid, current->gid); + audit_log_format(ab, "auid=%u uid=%u gid=%u ses=%u", + auid, current->uid, current->gid, sessionid); selinux_get_task_sid(current, &sid); if (sid) { char *ctx = NULL; diff --git a/net/core/dev.c b/net/core/dev.c index c0b69b3bb041..ba075a9dcecb 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -2754,10 +2754,11 @@ static void __dev_set_promiscuity(struct net_device *dev, int inc) "left"); audit_log(current->audit_context, GFP_ATOMIC, AUDIT_ANOM_PROMISCUOUS, - "dev=%s prom=%d old_prom=%d auid=%u", + "dev=%s prom=%d old_prom=%d auid=%u ses=%u", dev->name, (dev->flags & IFF_PROMISC), (old_flags & IFF_PROMISC), - audit_get_loginuid(current)); + audit_get_loginuid(current), + audit_get_sessionid(current)); if (dev->change_rx_flags) dev->change_rx_flags(dev, IFF_PROMISC); diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c index bee969432979..0341567665b3 100644 --- a/security/selinux/selinuxfs.c +++ b/security/selinux/selinuxfs.c @@ -172,9 +172,10 @@ static ssize_t sel_write_enforce(struct file * file, const char __user * buf, if (length) goto out; audit_log(current->audit_context, GFP_KERNEL, AUDIT_MAC_STATUS, - "enforcing=%d old_enforcing=%d auid=%u", new_value, - selinux_enforcing, - audit_get_loginuid(current)); + "enforcing=%d old_enforcing=%d auid=%u ses=%u", + new_value, selinux_enforcing, + audit_get_loginuid(current), + audit_get_sessionid(current)); selinux_enforcing = new_value; if (selinux_enforcing) avc_ss_reset(0); @@ -243,8 +244,9 @@ static ssize_t sel_write_disable(struct file * file, const char __user * buf, if (length < 0) goto out; audit_log(current->audit_context, GFP_KERNEL, AUDIT_MAC_STATUS, - "selinux=0 auid=%u", - audit_get_loginuid(current)); + "selinux=0 auid=%u ses=%u", + audit_get_loginuid(current), + audit_get_sessionid(current)); } length = count; @@ -356,8 +358,9 @@ out1: (security_get_allow_unknown() ? "allow" : "deny"))); audit_log(current->audit_context, GFP_KERNEL, AUDIT_MAC_POLICY_LOAD, - "policy loaded auid=%u", - audit_get_loginuid(current)); + "policy loaded auid=%u ses=%u", + audit_get_loginuid(current), + audit_get_sessionid(current)); out: mutex_unlock(&sel_mutex); vfree(data); diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index 819a6f91e801..fced6bccee76 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -1905,11 +1905,12 @@ int security_set_bools(int len, int *values) if (!!values[i] != policydb.bool_val_to_struct[i]->state) { audit_log(current->audit_context, GFP_ATOMIC, AUDIT_MAC_CONFIG_CHANGE, - "bool=%s val=%d old_val=%d auid=%u", + "bool=%s val=%d old_val=%d auid=%u ses=%u", policydb.p_bool_val_to_name[i], !!values[i], policydb.bool_val_to_struct[i]->state, - audit_get_loginuid(current)); + audit_get_loginuid(current), + audit_get_sessionid(current)); } if (values[i]) { policydb.bool_val_to_struct[i]->state = 1; -- cgit v1.2.3 From c0641f28dcbecb6dc34a4fd003a9947fcd080696 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Mon, 7 Jan 2008 13:49:15 -0500 Subject: [AUDIT] Add End of Event record This patch adds an end of event record type. It will be sent by the kernel as the last record when a multi-record event is triggered. This will aid realtime analysis programs since they will now reliably know they have the last record to complete an event. The audit daemon filters this and will not write it to disk. Signed-off-by: Steve Grubb Signed-off-by: Eric Paris --- include/linux/audit.h | 1 + kernel/auditsc.c | 5 +++++ 2 files changed, 6 insertions(+) (limited to 'include/linux') diff --git a/include/linux/audit.h b/include/linux/audit.h index 52f1b12505a9..4d1c2f2fcc1b 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -98,6 +98,7 @@ #define AUDIT_FD_PAIR 1317 /* audit record for pipe/socketpair */ #define AUDIT_OBJ_PID 1318 /* ptrace target */ #define AUDIT_TTY 1319 /* Input on an administrative TTY */ +#define AUDIT_EOE 1320 /* End of multi-record event */ #define AUDIT_AVC 1400 /* SE Linux avc denial or grant */ #define AUDIT_SELINUX_ERR 1401 /* Internal SE Linux Errors */ diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 4e67abb02904..6e5de767bad1 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -1270,6 +1270,11 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts audit_log_end(ab); } + + /* Send end of event record to help user space know we are finished */ + ab = audit_log_start(context, GFP_KERNEL, AUDIT_EOE); + if (ab) + audit_log_end(ab); if (call_panic) audit_panic("error converting sid to string"); } -- cgit v1.2.3 From de6bbd1d30e5912620d25dd15e3f180ac7f9fcef Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Mon, 7 Jan 2008 14:31:58 -0500 Subject: [AUDIT] break large execve argument logging into smaller messages execve arguments can be quite large. There is no limit on the number of arguments and a 4G limit on the size of an argument. this patch prints those aruguments in bite sized pieces. a userspace size limitation of 8k was discovered so this keeps messages around 7.5k single arguments larger than 7.5k in length are split into multiple records and can be identified as aX[Y]= Signed-off-by: Eric Paris --- Documentation/filesystems/proc.txt | 7 -- include/linux/audit.h | 6 +- kernel/audit.c | 37 ++++--- kernel/auditsc.c | 205 +++++++++++++++++++++++++++++-------- kernel/sysctl.c | 11 -- 5 files changed, 192 insertions(+), 74 deletions(-) (limited to 'include/linux') diff --git a/Documentation/filesystems/proc.txt b/Documentation/filesystems/proc.txt index 11fe51c036bf..194c8f351320 100644 --- a/Documentation/filesystems/proc.txt +++ b/Documentation/filesystems/proc.txt @@ -1134,13 +1134,6 @@ check the amount of free space (value is in seconds). Default settings are: 4, resume it if we have a value of 3 or more percent; consider information about the amount of free space valid for 30 seconds -audit_argv_kb -------------- - -The file contains a single value denoting the limit on the argv array size -for execve (in KiB). This limit is only applied when system call auditing for -execve is enabled, otherwise the value is ignored. - ctrl-alt-del ------------ diff --git a/include/linux/audit.h b/include/linux/audit.h index 4d1c2f2fcc1b..97153027207a 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -525,9 +525,11 @@ extern void audit_log_end(struct audit_buffer *ab); extern void audit_log_hex(struct audit_buffer *ab, const unsigned char *buf, size_t len); -extern const char * audit_log_untrustedstring(struct audit_buffer *ab, +extern int audit_string_contains_control(const char *string, + size_t len); +extern void audit_log_untrustedstring(struct audit_buffer *ab, const char *string); -extern const char * audit_log_n_untrustedstring(struct audit_buffer *ab, +extern void audit_log_n_untrustedstring(struct audit_buffer *ab, size_t n, const char *string); extern void audit_log_d_path(struct audit_buffer *ab, diff --git a/kernel/audit.c b/kernel/audit.c index cf6698289426..26ff925e13f2 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -1349,6 +1349,21 @@ static void audit_log_n_string(struct audit_buffer *ab, size_t slen, skb_put(skb, slen + 2); /* don't include null terminator */ } +/** + * audit_string_contains_control - does a string need to be logged in hex + * @string - string to be checked + * @len - max length of the string to check + */ +int audit_string_contains_control(const char *string, size_t len) +{ + const unsigned char *p; + for (p = string; p < (const unsigned char *)string + len && *p; p++) { + if (*p == '"' || *p < 0x21 || *p > 0x7f) + return 1; + } + return 0; +} + /** * audit_log_n_untrustedstring - log a string that may contain random characters * @ab: audit_buffer @@ -1363,19 +1378,13 @@ static void audit_log_n_string(struct audit_buffer *ab, size_t slen, * The caller specifies the number of characters in the string to log, which may * or may not be the entire string. */ -const char *audit_log_n_untrustedstring(struct audit_buffer *ab, size_t len, - const char *string) +void audit_log_n_untrustedstring(struct audit_buffer *ab, size_t len, + const char *string) { - const unsigned char *p; - - for (p = string; p < (const unsigned char *)string + len && *p; p++) { - if (*p == '"' || *p < 0x21 || *p > 0x7f) { - audit_log_hex(ab, string, len); - return string + len + 1; - } - } - audit_log_n_string(ab, len, string); - return p + 1; + if (audit_string_contains_control(string, len)) + audit_log_hex(ab, string, len); + else + audit_log_n_string(ab, len, string); } /** @@ -1386,9 +1395,9 @@ const char *audit_log_n_untrustedstring(struct audit_buffer *ab, size_t len, * Same as audit_log_n_untrustedstring(), except that strlen is used to * determine string length. */ -const char *audit_log_untrustedstring(struct audit_buffer *ab, const char *string) +void audit_log_untrustedstring(struct audit_buffer *ab, const char *string) { - return audit_log_n_untrustedstring(ab, strlen(string), string); + audit_log_n_untrustedstring(ab, strlen(string), string); } /* This is a helper-function to print the escaped d_path */ diff --git a/kernel/auditsc.c b/kernel/auditsc.c index aaaca8a13bbe..6e03322e155b 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -78,6 +78,9 @@ extern struct list_head audit_filter_list[]; /* Indicates that audit should log the full pathname. */ #define AUDIT_NAME_FULL -1 +/* no execve audit message should be longer than this (userspace limits) */ +#define MAX_EXECVE_AUDIT_LEN 7500 + /* number of audit rules */ int audit_n_rules; @@ -965,55 +968,187 @@ static int audit_log_pid_context(struct audit_context *context, pid_t pid, return rc; } -static void audit_log_execve_info(struct audit_buffer *ab, - struct audit_aux_data_execve *axi) +/* + * to_send and len_sent accounting are very loose estimates. We aren't + * really worried about a hard cap to MAX_EXECVE_AUDIT_LEN so much as being + * within about 500 bytes (next page boundry) + * + * why snprintf? an int is up to 12 digits long. if we just assumed when + * logging that a[%d]= was going to be 16 characters long we would be wasting + * space in every audit message. In one 7500 byte message we can log up to + * about 1000 min size arguments. That comes down to about 50% waste of space + * if we didn't do the snprintf to find out how long arg_num_len was. + */ +static int audit_log_single_execve_arg(struct audit_context *context, + struct audit_buffer **ab, + int arg_num, + size_t *len_sent, + const char __user *p, + char *buf) { - int i; - long len, ret; - const char __user *p; - char *buf; + char arg_num_len_buf[12]; + const char __user *tmp_p = p; + /* how many digits are in arg_num? 3 is the length of a=\n */ + size_t arg_num_len = snprintf(arg_num_len_buf, 12, "%d", arg_num) + 3; + size_t len, len_left, to_send; + size_t max_execve_audit_len = MAX_EXECVE_AUDIT_LEN; + unsigned int i, has_cntl = 0, too_long = 0; + int ret; + + /* strnlen_user includes the null we don't want to send */ + len_left = len = strnlen_user(p, MAX_ARG_STRLEN) - 1; - if (axi->mm != current->mm) - return; /* execve failed, no additional info */ - - p = (const char __user *)axi->mm->arg_start; + /* + * We just created this mm, if we can't find the strings + * we just copied into it something is _very_ wrong. Similar + * for strings that are too long, we should not have created + * any. + */ + if (unlikely((len = -1) || len > MAX_ARG_STRLEN - 1)) { + WARN_ON(1); + send_sig(SIGKILL, current, 0); + } - for (i = 0; i < axi->argc; i++, p += len) { - len = strnlen_user(p, MAX_ARG_STRLEN); + /* walk the whole argument looking for non-ascii chars */ + do { + if (len_left > MAX_EXECVE_AUDIT_LEN) + to_send = MAX_EXECVE_AUDIT_LEN; + else + to_send = len_left; + ret = copy_from_user(buf, tmp_p, to_send); /* - * We just created this mm, if we can't find the strings - * we just copied into it something is _very_ wrong. Similar - * for strings that are too long, we should not have created - * any. + * There is no reason for this copy to be short. We just + * copied them here, and the mm hasn't been exposed to user- + * space yet. */ - if (!len || len > MAX_ARG_STRLEN) { + if (ret) { WARN_ON(1); send_sig(SIGKILL, current, 0); } - - buf = kmalloc(len, GFP_KERNEL); - if (!buf) { - audit_panic("out of memory for argv string\n"); + buf[to_send] = '\0'; + has_cntl = audit_string_contains_control(buf, to_send); + if (has_cntl) { + /* + * hex messages get logged as 2 bytes, so we can only + * send half as much in each message + */ + max_execve_audit_len = MAX_EXECVE_AUDIT_LEN / 2; break; } + len_left -= to_send; + tmp_p += to_send; + } while (len_left > 0); + + len_left = len; + + if (len > max_execve_audit_len) + too_long = 1; + + /* rewalk the argument actually logging the message */ + for (i = 0; len_left > 0; i++) { + int room_left; + + if (len_left > max_execve_audit_len) + to_send = max_execve_audit_len; + else + to_send = len_left; + + /* do we have space left to send this argument in this ab? */ + room_left = MAX_EXECVE_AUDIT_LEN - arg_num_len - *len_sent; + if (has_cntl) + room_left -= (to_send * 2); + else + room_left -= to_send; + if (room_left < 0) { + *len_sent = 0; + audit_log_end(*ab); + *ab = audit_log_start(context, GFP_KERNEL, AUDIT_EXECVE); + if (!*ab) + return 0; + } - ret = copy_from_user(buf, p, len); /* - * There is no reason for this copy to be short. We just - * copied them here, and the mm hasn't been exposed to user- - * space yet. + * first record needs to say how long the original string was + * so we can be sure nothing was lost. + */ + if ((i == 0) && (too_long)) + audit_log_format(*ab, "a%d_len=%ld ", arg_num, + has_cntl ? 2*len : len); + + /* + * normally arguments are small enough to fit and we already + * filled buf above when we checked for control characters + * so don't bother with another copy_from_user */ + if (len >= max_execve_audit_len) + ret = copy_from_user(buf, p, to_send); + else + ret = 0; if (ret) { WARN_ON(1); send_sig(SIGKILL, current, 0); } + buf[to_send] = '\0'; + + /* actually log it */ + audit_log_format(*ab, "a%d", arg_num); + if (too_long) + audit_log_format(*ab, "[%d]", i); + audit_log_format(*ab, "="); + if (has_cntl) + audit_log_hex(*ab, buf, to_send); + else + audit_log_format(*ab, "\"%s\"", buf); + audit_log_format(*ab, "\n"); + + p += to_send; + len_left -= to_send; + *len_sent += arg_num_len; + if (has_cntl) + *len_sent += to_send * 2; + else + *len_sent += to_send; + } + /* include the null we didn't log */ + return len + 1; +} + +static void audit_log_execve_info(struct audit_context *context, + struct audit_buffer **ab, + struct audit_aux_data_execve *axi) +{ + int i; + size_t len, len_sent = 0; + const char __user *p; + char *buf; - audit_log_format(ab, "a%d=", i); - audit_log_untrustedstring(ab, buf); - audit_log_format(ab, "\n"); + if (axi->mm != current->mm) + return; /* execve failed, no additional info */ + + p = (const char __user *)axi->mm->arg_start; - kfree(buf); + audit_log_format(*ab, "argc=%d ", axi->argc); + + /* + * we need some kernel buffer to hold the userspace args. Just + * allocate one big one rather than allocating one of the right size + * for every single argument inside audit_log_single_execve_arg() + * should be <8k allocation so should be pretty safe. + */ + buf = kmalloc(MAX_EXECVE_AUDIT_LEN + 1, GFP_KERNEL); + if (!buf) { + audit_panic("out of memory for argv string\n"); + return; } + + for (i = 0; i < axi->argc; i++) { + len = audit_log_single_execve_arg(context, ab, i, + &len_sent, p, buf); + if (len <= 0) + break; + p += len; + } + kfree(buf); } static void audit_log_exit(struct audit_context *context, struct task_struct *tsk) @@ -1157,7 +1292,7 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts case AUDIT_EXECVE: { struct audit_aux_data_execve *axi = (void *)aux; - audit_log_execve_info(ab, axi); + audit_log_execve_info(context, &ab, axi); break; } case AUDIT_SOCKETCALL: { @@ -2094,8 +2229,6 @@ int __audit_ipc_set_perm(unsigned long qbytes, uid_t uid, gid_t gid, mode_t mode return 0; } -int audit_argv_kb = 32; - int audit_bprm(struct linux_binprm *bprm) { struct audit_aux_data_execve *ax; @@ -2104,14 +2237,6 @@ int audit_bprm(struct linux_binprm *bprm) if (likely(!audit_enabled || !context || context->dummy)) return 0; - /* - * Even though the stack code doesn't limit the arg+env size any more, - * the audit code requires that _all_ arguments be logged in a single - * netlink skb. Hence cap it :-( - */ - if (bprm->argv_len > (audit_argv_kb << 10)) - return -E2BIG; - ax = kmalloc(sizeof(*ax), GFP_KERNEL); if (!ax) return -ENOMEM; diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 357b68ba23ec..7cb1ac3e6fff 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -81,7 +81,6 @@ extern int percpu_pagelist_fraction; extern int compat_log; extern int maps_protect; extern int sysctl_stat_interval; -extern int audit_argv_kb; extern int latencytop_enabled; /* Constants used for minimum and maximum */ @@ -390,16 +389,6 @@ static struct ctl_table kern_table[] = { .mode = 0644, .proc_handler = &proc_dointvec, }, -#ifdef CONFIG_AUDITSYSCALL - { - .ctl_name = CTL_UNNUMBERED, - .procname = "audit_argv_kb", - .data = &audit_argv_kb, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = &proc_dointvec, - }, -#endif { .ctl_name = KERN_CORE_PATTERN, .procname = "core_pattern", -- cgit v1.2.3