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 --- kernel/auditsc.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'kernel/auditsc.c') diff --git a/kernel/auditsc.c b/kernel/auditsc.c index bce9ecdb7712..bd4e0a2443fb 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -1804,8 +1804,9 @@ int audit_set_loginuid(struct task_struct *task, uid_t loginuid) * * Returns the context's loginuid or -1 if @ctx is NULL. */ -uid_t audit_get_loginuid(struct audit_context *ctx) +uid_t audit_get_loginuid(struct task_struct *task) { + struct audit_context *ctx = task->audit_context; return ctx ? ctx->loginuid : -1; } @@ -2273,7 +2274,7 @@ void audit_core_dumps(long signr) ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_ANOM_ABEND); audit_log_format(ab, "auid=%u uid=%u gid=%u", - audit_get_loginuid(current->audit_context), + audit_get_loginuid(current), current->uid, current->gid); selinux_get_task_sid(current, &sid); if (sid) { -- cgit v1.2.2 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 --- kernel/auditsc.c | 56 +++++++++++++++----------------------------------------- 1 file changed, 15 insertions(+), 41 deletions(-) (limited to 'kernel/auditsc.c') 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.2 From f701b75ed5ffb6820efe530d1a3abcc6fc4678ad Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Mon, 7 Jan 2008 13:34:51 -0500 Subject: [AUDIT] return EINTR not ERESTART* The syscall exit code will change ERESTART* kernel internal return codes to EINTR if it does not restart the syscall. Since we collect the audit info before that point we should fix those in the audit log as well. Signed-off-by: Eric Paris --- kernel/auditsc.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) (limited to 'kernel/auditsc.c') diff --git a/kernel/auditsc.c b/kernel/auditsc.c index c95173a194bf..ce8c957201ef 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -701,7 +701,24 @@ static inline struct audit_context *audit_get_context(struct task_struct *tsk, if (likely(!context)) return NULL; context->return_valid = return_valid; - context->return_code = return_code; + + /* + * we need to fix up the return code in the audit logs if the actual + * return codes are later going to be fixed up by the arch specific + * signal handlers + * + * This is actually a test for: + * (rc == ERESTARTSYS ) || (rc == ERESTARTNOINTR) || + * (rc == ERESTARTNOHAND) || (rc == ERESTART_RESTARTBLOCK) + * + * but is faster than a bunch of || + */ + if (unlikely(return_code <= -ERESTARTSYS) && + (return_code >= -ERESTART_RESTARTBLOCK) && + (return_code != -ENOIOCTLCMD)) + context->return_code = -EINTR; + else + context->return_code = return_code; if (context->in_syscall && !context->dummy && !context->auditable) { enum audit_state state; -- cgit v1.2.2 From c2a7780efe37d01bdb3facc85a94663e6d67d4a8 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Mon, 7 Jan 2008 13:40:17 -0500 Subject: [AUDIT] collect uid, loginuid, and comm in OBJ_PID records Add uid, loginuid, and comm collection to OBJ_PID records. This just gives users a little more information about the task that received a signal. pid is rather meaningless after the fact, and even though comm isn't great we can't collect exe reasonably on this code path for performance reasons. Signed-off-by: Eric Paris --- kernel/auditsc.c | 32 +++++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) (limited to 'kernel/auditsc.c') diff --git a/kernel/auditsc.c b/kernel/auditsc.c index ce8c957201ef..a222e73fec74 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -176,7 +176,10 @@ struct audit_aux_data_fd_pair { struct audit_aux_data_pids { struct audit_aux_data d; pid_t target_pid[AUDIT_AUX_PIDS]; + uid_t target_auid[AUDIT_AUX_PIDS]; + uid_t target_uid[AUDIT_AUX_PIDS]; u32 target_sid[AUDIT_AUX_PIDS]; + char target_comm[AUDIT_AUX_PIDS][TASK_COMM_LEN]; int pid_count; }; @@ -214,7 +217,10 @@ struct audit_context { int arch; pid_t target_pid; + uid_t target_auid; + uid_t target_uid; u32 target_sid; + char target_comm[TASK_COMM_LEN]; struct audit_tree_refs *trees, *first_trees; int tree_count; @@ -930,7 +936,7 @@ 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, - u32 sid) + uid_t auid, uid_t uid, u32 sid, char *comm) { struct audit_buffer *ab; char *s = NULL; @@ -941,11 +947,14 @@ 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); if (selinux_sid_to_string(sid, &s, &len)) { - audit_log_format(ab, "opid=%d obj=(none)", pid); + audit_log_format(ab, " obj=(none)"); rc = 1; } else - audit_log_format(ab, "opid=%d obj=%s", pid, s); + audit_log_format(ab, " obj=%s", s); + audit_log_format(ab, " ocomm="); + audit_log_untrustedstring(ab, comm); audit_log_end(ab); kfree(s); @@ -1176,13 +1185,17 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts for (i = 0; i < axs->pid_count; i++) if (audit_log_pid_context(context, axs->target_pid[i], - axs->target_sid[i])) + axs->target_auid[i], + axs->target_uid[i], + axs->target_sid[i], + axs->target_comm[i])) call_panic = 1; } if (context->target_pid && audit_log_pid_context(context, context->target_pid, - context->target_sid)) + context->target_auid, context->target_uid, + context->target_sid, context->target_comm)) call_panic = 1; if (context->pwd && context->pwdmnt) { @@ -2185,7 +2198,10 @@ void __audit_ptrace(struct task_struct *t) struct audit_context *context = current->audit_context; context->target_pid = t->pid; + context->target_auid = audit_get_loginuid(t); + context->target_uid = t->uid; selinux_get_task_sid(t, &context->target_sid); + memcpy(context->target_comm, t->comm, TASK_COMM_LEN); } /** @@ -2222,7 +2238,10 @@ int __audit_signal_info(int sig, struct task_struct *t) * in audit_context */ if (!ctx->target_pid) { ctx->target_pid = t->tgid; + ctx->target_auid = audit_get_loginuid(t); + ctx->target_uid = t->uid; selinux_get_task_sid(t, &ctx->target_sid); + memcpy(ctx->target_comm, t->comm, TASK_COMM_LEN); return 0; } @@ -2239,7 +2258,10 @@ int __audit_signal_info(int sig, struct task_struct *t) BUG_ON(axp->pid_count >= AUDIT_AUX_PIDS); 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; 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++; return 0; -- cgit v1.2.2 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 --- kernel/auditsc.c | 36 +++++++++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 9 deletions(-) (limited to 'kernel/auditsc.c') 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; -- cgit v1.2.2 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 --- kernel/auditsc.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'kernel/auditsc.c') 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.2 From 6246ccab99093a562044596dd868213caa0b2b4c Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Mon, 7 Jan 2008 14:01:18 -0500 Subject: [AUDIT] do not panic on exclude messages in audit_log_pid_context() If we fail to get an ab in audit_log_pid_context this may be due to an exclude rule rather than a memory allocation failure. If it was due to a memory allocation failue we would have already paniced and no need to do it again. Signed-off-by: Eric Paris --- kernel/auditsc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'kernel/auditsc.c') diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 6e5de767bad1..aaaca8a13bbe 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -948,7 +948,7 @@ static int audit_log_pid_context(struct audit_context *context, pid_t pid, ab = audit_log_start(context, GFP_KERNEL, AUDIT_OBJ_PID); if (!ab) - return 1; + return rc; audit_log_format(ab, "opid=%d oauid=%d ouid=%d oses=%d", pid, auid, uid, sessionid); -- cgit v1.2.2 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 --- kernel/auditsc.c | 205 ++++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 165 insertions(+), 40 deletions(-) (limited to 'kernel/auditsc.c') 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; -- cgit v1.2.2 From b593d384efcff7bdf6beb1bc1bc69927977aee26 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Tue, 8 Jan 2008 17:38:31 -0500 Subject: [AUDIT] create context if auditing was ever enabled Disabling audit at runtime by auditctl doesn't mean that we can stop allocating contexts for new processes; we don't want to miss them when that sucker is reenabled. (based on work from Al Viro in the RHEL kernel series) Signed-off-by: Eric Paris --- kernel/auditsc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'kernel/auditsc.c') diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 6e03322e155b..1c06ecf38d7b 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -70,6 +70,7 @@ #include "audit.h" extern struct list_head audit_filter_list[]; +extern int audit_ever_enabled; /* AUDIT_NAMES is the number of slots we reserve in the audit_context * for saving names from getname(). */ @@ -838,7 +839,7 @@ int audit_alloc(struct task_struct *tsk) struct audit_context *context; enum audit_state state; - if (likely(!audit_enabled)) + if (likely(!audit_ever_enabled)) return 0; /* Return if not auditing. */ state = audit_filter_task(tsk); -- cgit v1.2.2