X-Git-Url: http://ftp.safe.ca/?a=blobdiff_plain;f=kernel%2Fauditsc.c;h=cf5bc2f5f9c3e527ac2ea72ec019d156be72ecb6;hb=dbda4c0b97b18fd59b3964548361b4f92357f730;hp=938e60a61882008d732bcdf83377fc29d3fa8368;hpb=a9022e9cb9e919e31d5bc15fcef5c7186740645e;p=safe%2Fjmp%2Flinux-2.6 diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 938e60a..cf5bc2f 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -61,15 +61,13 @@ #include #include #include -#include #include #include #include +#include #include "audit.h" -extern struct list_head audit_filter_list[]; - /* AUDIT_NAMES is the number of slots we reserve in the audit_context * for saving names from getname(). */ #define AUDIT_NAMES 20 @@ -77,6 +75,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; @@ -175,10 +176,19 @@ 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]; + 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; }; +struct audit_tree_refs { + struct audit_tree_refs *next; + struct audit_chunk *c[31]; +}; + /* The per-task audit context. */ struct audit_context { int dummy; /* must be the first element */ @@ -186,7 +196,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 */ @@ -195,8 +204,7 @@ struct audit_context { int name_count; struct audit_names names[AUDIT_NAMES]; char * filterkey; /* key for rule that triggered record */ - struct dentry * pwd; - struct vfsmount * pwdmnt; + struct path pwd; struct audit_context *previous; /* For nested syscalls */ struct audit_aux_data *aux; struct audit_aux_data *aux_pids; @@ -209,7 +217,14 @@ struct audit_context { int arch; 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]; + + struct audit_tree_refs *trees, *first_trees; + int tree_count; #if AUDIT_DEBUG int put_count; @@ -228,7 +243,11 @@ static inline int open_arg(int flags, int mask) static int audit_match_perm(struct audit_context *ctx, int mask) { - unsigned n = ctx->major; + unsigned n; + if (unlikely(!ctx)) + return 0; + n = ctx->major; + switch (audit_classify_syscall(ctx->arch, n)) { case 0: /* native */ if ((mask & AUDIT_PERM_WRITE) && @@ -265,6 +284,134 @@ static int audit_match_perm(struct audit_context *ctx, int mask) } } +static int audit_match_filetype(struct audit_context *ctx, int which) +{ + unsigned index = which & ~S_IFMT; + mode_t mode = which & S_IFMT; + + if (unlikely(!ctx)) + return 0; + + if (index >= ctx->name_count) + return 0; + if (ctx->names[index].ino == -1) + return 0; + if ((ctx->names[index].mode ^ mode) & S_IFMT) + return 0; + return 1; +} + +/* + * We keep a linked list of fixed-sized (31 pointer) arrays of audit_chunk *; + * ->first_trees points to its beginning, ->trees - to the current end of data. + * ->tree_count is the number of free entries in array pointed to by ->trees. + * Original condition is (NULL, NULL, 0); as soon as it grows we never revert to NULL, + * "empty" becomes (p, p, 31) afterwards. We don't shrink the list (and seriously, + * it's going to remain 1-element for almost any setup) until we free context itself. + * References in it _are_ dropped - at the same time we free/drop aux stuff. + */ + +#ifdef CONFIG_AUDIT_TREE +static int put_tree_ref(struct audit_context *ctx, struct audit_chunk *chunk) +{ + struct audit_tree_refs *p = ctx->trees; + int left = ctx->tree_count; + if (likely(left)) { + p->c[--left] = chunk; + ctx->tree_count = left; + return 1; + } + if (!p) + return 0; + p = p->next; + if (p) { + p->c[30] = chunk; + ctx->trees = p; + ctx->tree_count = 30; + return 1; + } + return 0; +} + +static int grow_tree_refs(struct audit_context *ctx) +{ + struct audit_tree_refs *p = ctx->trees; + ctx->trees = kzalloc(sizeof(struct audit_tree_refs), GFP_KERNEL); + if (!ctx->trees) { + ctx->trees = p; + return 0; + } + if (p) + p->next = ctx->trees; + else + ctx->first_trees = ctx->trees; + ctx->tree_count = 31; + return 1; +} +#endif + +static void unroll_tree_refs(struct audit_context *ctx, + struct audit_tree_refs *p, int count) +{ +#ifdef CONFIG_AUDIT_TREE + struct audit_tree_refs *q; + int n; + if (!p) { + /* we started with empty chain */ + p = ctx->first_trees; + count = 31; + /* if the very first allocation has failed, nothing to do */ + if (!p) + return; + } + n = count; + for (q = p; q != ctx->trees; q = q->next, n = 31) { + while (n--) { + audit_put_chunk(q->c[n]); + q->c[n] = NULL; + } + } + while (n-- > ctx->tree_count) { + audit_put_chunk(q->c[n]); + q->c[n] = NULL; + } + ctx->trees = p; + ctx->tree_count = count; +#endif +} + +static void free_tree_refs(struct audit_context *ctx) +{ + struct audit_tree_refs *p, *q; + for (p = ctx->first_trees; p; p = q) { + q = p->next; + kfree(p); + } +} + +static int match_tree_refs(struct audit_context *ctx, struct audit_tree *tree) +{ +#ifdef CONFIG_AUDIT_TREE + struct audit_tree_refs *p; + int n; + if (!tree) + return 0; + /* full ones */ + for (p = ctx->first_trees; p != ctx->trees; p = p->next) { + for (n = 0; n < 31; n++) + if (audit_tree_match(p->c[n], tree)) + return 1; + } + /* partial */ + if (p) { + for (n = ctx->tree_count; n < 31; n++) + if (audit_tree_match(p->c[n], tree)) + return 1; + } +#endif + return 0; +} + /* Determine if any context name data matches a rule's watch data */ /* Compare a task_struct with an audit_rule. Return 1 on match, 0 * otherwise. */ @@ -320,7 +467,7 @@ static int audit_filter_rules(struct task_struct *tsk, result = audit_comparator(tsk->personality, f->op, f->val); break; case AUDIT_ARCH: - if (ctx) + if (ctx) result = audit_comparator(ctx->arch, f->op, f->val); break; @@ -379,10 +526,14 @@ static int audit_filter_rules(struct task_struct *tsk, result = (name->dev == rule->watch->dev && name->ino == rule->watch->ino); break; + case AUDIT_DIR: + if (ctx) + result = match_tree_refs(ctx, rule->tree); + break; 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: @@ -394,14 +545,14 @@ static int audit_filter_rules(struct task_struct *tsk, match for now to avoid losing information that may be wanted. An error message will also be logged upon error */ - if (f->se_rule) { + if (f->lsm_rule) { if (need_sid) { - selinux_get_task_sid(tsk, &sid); + security_task_getsecid(tsk, &sid); need_sid = 0; } - result = selinux_audit_rule_match(sid, f->type, + result = security_audit_rule_match(sid, f->type, f->op, - f->se_rule, + f->lsm_rule, ctx); } break; @@ -412,18 +563,18 @@ static int audit_filter_rules(struct task_struct *tsk, case AUDIT_OBJ_LEV_HIGH: /* The above note for AUDIT_SUBJ_USER...AUDIT_SUBJ_CLR also applies here */ - if (f->se_rule) { + if (f->lsm_rule) { /* Find files that match */ if (name) { - result = selinux_audit_rule_match( + result = security_audit_rule_match( name->osid, f->type, f->op, - f->se_rule, ctx); + f->lsm_rule, ctx); } else if (ctx) { for (j = 0; j < ctx->name_count; j++) { - if (selinux_audit_rule_match( + if (security_audit_rule_match( ctx->names[j].osid, f->type, f->op, - f->se_rule, ctx)) { + f->lsm_rule, ctx)) { ++result; break; } @@ -436,7 +587,7 @@ static int audit_filter_rules(struct task_struct *tsk, aux = aux->next) { if (aux->type == AUDIT_IPC) { struct audit_aux_data_ipcctl *axi = (void *)aux; - if (selinux_audit_rule_match(axi->osid, f->type, f->op, f->se_rule, ctx)) { + if (security_audit_rule_match(axi->osid, f->type, f->op, f->lsm_rule, ctx)) { ++result; break; } @@ -459,12 +610,15 @@ static int audit_filter_rules(struct task_struct *tsk, case AUDIT_PERM: result = audit_match_perm(ctx, f->val); break; + case AUDIT_FILETYPE: + result = audit_match_filetype(ctx, f->val); + break; } if (!result) return 0; } - if (rule->filterkey) + if (rule->filterkey && ctx) ctx->filterkey = kstrdup(rule->filterkey, GFP_ATOMIC); switch (rule->action) { case AUDIT_NEVER: *state = AUDIT_DISABLED; break; @@ -578,7 +732,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; @@ -634,12 +805,9 @@ static inline void audit_free_names(struct audit_context *context) __putname(context->names[i].name); } context->name_count = 0; - if (context->pwd) - dput(context->pwd); - if (context->pwdmnt) - mntput(context->pwdmnt); - context->pwd = NULL; - context->pwdmnt = NULL; + path_put(&context->pwd); + context->pwd.dentry = NULL; + context->pwd.mnt = NULL; } static inline void audit_free_aux(struct audit_context *context) @@ -659,11 +827,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) @@ -690,7 +855,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); @@ -702,11 +867,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; @@ -727,6 +887,8 @@ static inline void audit_free_context(struct audit_context *context) context->name_count, count); } audit_free_names(context); + unroll_tree_refs(context, NULL, 0); + free_tree_refs(context); audit_free_aux(context); kfree(context->filterkey); kfree(context); @@ -743,11 +905,11 @@ void audit_log_task_context(struct audit_buffer *ab) int error; u32 sid; - selinux_get_task_sid(current, &sid); + security_task_getsecid(current, &sid); if (!sid) return; - error = selinux_sid_to_string(sid, &ctx, &len); + error = security_secid_to_secctx(sid, &ctx, &len); if (error) { if (error != -EINVAL) goto error_path; @@ -755,7 +917,7 @@ void audit_log_task_context(struct audit_buffer *ab) } audit_log_format(ab, " subj=%s", ctx); - kfree(ctx); + security_release_secctx(ctx, len); return; error_path: @@ -784,8 +946,7 @@ static void audit_log_task_info(struct audit_buffer *ab, struct task_struct *tsk if ((vma->vm_flags & VM_EXECUTABLE) && vma->vm_file) { audit_log_d_path(ab, "exe=", - vma->vm_file->f_path.dentry, - vma->vm_file->f_path.mnt); + &vma->vm_file->f_path); break; } vma = vma->vm_next; @@ -796,77 +957,218 @@ 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, unsigned int sessionid, + u32 sid, char *comm) { struct audit_buffer *ab; - char *s = NULL; + char *ctx = NULL; u32 len; int rc = 0; ab = audit_log_start(context, GFP_KERNEL, AUDIT_OBJ_PID); if (!ab) - return 1; + return rc; - if (selinux_sid_to_string(sid, &s, &len)) { - audit_log_format(ab, "opid=%d obj=(none)", pid); + audit_log_format(ab, "opid=%d oauid=%d ouid=%d oses=%d", pid, auid, + uid, sessionid); + if (security_secid_to_secctx(sid, &ctx, &len)) { + audit_log_format(ab, " obj=(none)"); rc = 1; - } else - audit_log_format(ab, "opid=%d obj=%s", pid, s); + } else { + audit_log_format(ab, " obj=%s", ctx); + security_release_secctx(ctx, len); + } + audit_log_format(ab, " ocomm="); + audit_log_untrustedstring(ab, comm); audit_log_end(ab); - kfree(s); 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; - - if (axi->mm != current->mm) - return; /* execve failed, no additional info */ + 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; - 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); + return -1; + } - 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); + return -1; } - - 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=%zu ", 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); + return -1; } + 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_n_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; + + if (axi->mm != current->mm) + return; /* execve failed, no additional info */ + + p = (const char __user *)axi->mm->arg_start; - audit_log_format(ab, "a%d=", i); - audit_log_untrustedstring(ab, buf); - audit_log_format(ab, "\n"); + audit_log_format(*ab, "argc=%d ", axi->argc); - kfree(buf); + /* + * 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) @@ -898,22 +1200,22 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts if (context->personality != PER_LINUX) audit_log_format(ab, " per=%lx", context->personality); if (context->return_valid) - audit_log_format(ab, " success=%s exit=%ld", + audit_log_format(ab, " success=%s exit=%ld", (context->return_valid==AUDITSC_SUCCESS)?"yes":"no", context->return_code); - mutex_lock(&tty_mutex); - read_lock(&tasklist_lock); + spin_lock_irq(&tsk->sighand->siglock); if (tsk->signal && tsk->signal->tty && tsk->signal->tty->name) tty = tsk->signal->tty->name; else tty = "(none)"; - read_unlock(&tasklist_lock); + spin_unlock_irq(&tsk->sighand->siglock); + audit_log_format(ab, " 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], @@ -921,13 +1223,13 @@ 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, - context->egid, context->sgid, context->fsgid, tty); + context->egid, context->sgid, context->fsgid, tty, + tsk->sessionid); - mutex_unlock(&tty_mutex); audit_log_task_info(ab, tsk); if (context->filterkey) { @@ -989,14 +1291,15 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts if (axi->osid != 0) { char *ctx = NULL; u32 len; - if (selinux_sid_to_string( + if (security_secid_to_secctx( axi->osid, &ctx, &len)) { audit_log_format(ab, " osid=%u", axi->osid); call_panic = 1; - } else + } else { audit_log_format(ab, " obj=%s", ctx); - kfree(ctx); + security_release_secctx(ctx, len); + } } break; } @@ -1009,11 +1312,10 @@ 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: { - int i; struct audit_aux_data_socketcall *axs = (void *)aux; audit_log_format(ab, "nargs=%d", axs->nargs); for (i=0; inargs; i++) @@ -1024,7 +1326,7 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts struct audit_aux_data_sockaddr *axs = (void *)aux; audit_log_format(ab, "saddr="); - audit_log_hex(ab, axs->a, axs->len); + audit_log_n_hex(ab, axs->a, axs->len); break; } case AUDIT_FD_PAIR: { @@ -1038,23 +1340,28 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts for (aux = context->aux_pids; aux; aux = aux->next) { struct audit_aux_data_pids *axs = (void *)aux; - int i; 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_sessionid[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_sessionid, + context->target_sid, context->target_comm)) call_panic = 1; - if (context->pwd && context->pwdmnt) { + if (context->pwd.dentry && context->pwd.mnt) { ab = audit_log_start(context, GFP_KERNEL, AUDIT_CWD); if (ab) { - audit_log_d_path(ab, "cwd=", context->pwd, context->pwdmnt); + audit_log_d_path(ab, "cwd=", &context->pwd); audit_log_end(ab); } } @@ -1077,14 +1384,13 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts case 0: /* name was specified as a relative path and the * directory component is the cwd */ - audit_log_d_path(ab, " name=", context->pwd, - context->pwdmnt); + 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_len, - n->name); + audit_log_n_untrustedstring(ab, n->name, + n->name_len); } } else audit_log_format(ab, " name=(null)"); @@ -1105,17 +1411,23 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts if (n->osid != 0) { char *ctx = NULL; u32 len; - if (selinux_sid_to_string( + if (security_secid_to_secctx( n->osid, &ctx, &len)) { audit_log_format(ab, " osid=%u", n->osid); call_panic = 2; - } else + } else { audit_log_format(ab, " obj=%s", ctx); - kfree(ctx); + security_release_secctx(ctx, len); + } } 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"); } @@ -1135,8 +1447,8 @@ void audit_free(struct task_struct *tsk) return; /* Check for system calls that do not go through the exit - * function (e.g., exit_group), then free context block. - * We use GFP_ATOMIC here because we might be doing this + * function (e.g., exit_group), then free context block. + * We use GFP_ATOMIC here because we might be doing this * in the context of the idle thread */ /* that can happen only if we are called from do_exit() */ if (context->in_syscall && context->auditable) @@ -1171,7 +1483,8 @@ void audit_syscall_entry(int arch, int major, struct audit_context *context = tsk->audit_context; enum audit_state state; - BUG_ON(!context); + if (unlikely(!context)) + return; /* * This happens only on certain architectures that make system @@ -1270,6 +1583,7 @@ void audit_syscall_exit(int valid, long return_code) tsk->audit_context = new_context; } else { audit_free_names(context); + unroll_tree_refs(context, NULL, 0); audit_free_aux(context); context->aux = NULL; context->aux_pids = NULL; @@ -1281,6 +1595,95 @@ void audit_syscall_exit(int valid, long return_code) } } +static inline void handle_one(const struct inode *inode) +{ +#ifdef CONFIG_AUDIT_TREE + struct audit_context *context; + struct audit_tree_refs *p; + struct audit_chunk *chunk; + int count; + if (likely(list_empty(&inode->inotify_watches))) + return; + context = current->audit_context; + p = context->trees; + count = context->tree_count; + rcu_read_lock(); + chunk = audit_tree_lookup(inode); + rcu_read_unlock(); + if (!chunk) + return; + if (likely(put_tree_ref(context, chunk))) + return; + if (unlikely(!grow_tree_refs(context))) { + printk(KERN_WARNING "out of memory, audit has lost a tree reference\n"); + audit_set_auditable(context); + audit_put_chunk(chunk); + unroll_tree_refs(context, p, count); + return; + } + put_tree_ref(context, chunk); +#endif +} + +static void handle_path(const struct dentry *dentry) +{ +#ifdef CONFIG_AUDIT_TREE + struct audit_context *context; + struct audit_tree_refs *p; + const struct dentry *d, *parent; + struct audit_chunk *drop; + unsigned long seq; + int count; + + context = current->audit_context; + p = context->trees; + count = context->tree_count; +retry: + drop = NULL; + d = dentry; + rcu_read_lock(); + seq = read_seqbegin(&rename_lock); + for(;;) { + struct inode *inode = d->d_inode; + if (inode && unlikely(!list_empty(&inode->inotify_watches))) { + struct audit_chunk *chunk; + chunk = audit_tree_lookup(inode); + if (chunk) { + if (unlikely(!put_tree_ref(context, chunk))) { + drop = chunk; + break; + } + } + } + parent = d->d_parent; + if (parent == d) + break; + d = parent; + } + if (unlikely(read_seqretry(&rename_lock, seq) || drop)) { /* in this order */ + rcu_read_unlock(); + if (!drop) { + /* just a race with rename */ + unroll_tree_refs(context, p, count); + goto retry; + } + audit_put_chunk(drop); + if (grow_tree_refs(context)) { + /* OK, got more space */ + unroll_tree_refs(context, p, count); + goto retry; + } + /* too bad */ + printk(KERN_WARNING + "out of memory, audit has lost a tree reference\n"); + unroll_tree_refs(context, p, count); + audit_set_auditable(context); + return; + } + rcu_read_unlock(); +#endif +} + /** * audit_getname - add a name to the list * @name: name to add @@ -1310,13 +1713,13 @@ void __audit_getname(const char *name) context->names[context->name_count].ino = (unsigned long)-1; context->names[context->name_count].osid = 0; ++context->name_count; - if (!context->pwd) { + if (!context->pwd.dentry) { read_lock(¤t->fs->lock); - context->pwd = dget(current->fs->pwd); - context->pwdmnt = mntget(current->fs->pwdmnt); + context->pwd = current->fs->pwd; + path_get(¤t->fs->pwd); read_unlock(¤t->fs->lock); } - + } /* audit_putname - intercept a putname request @@ -1368,13 +1771,13 @@ static int audit_inc_name_count(struct audit_context *context, if (context->name_count >= AUDIT_NAMES) { if (inode) printk(KERN_DEBUG "name_count maxed, losing inode data: " - "dev=%02x:%02x, inode=%lu", + "dev=%02x:%02x, inode=%lu\n", MAJOR(inode->i_sb->s_dev), MINOR(inode->i_sb->s_dev), inode->i_ino); else - printk(KERN_DEBUG "name_count maxed, losing inode data"); + printk(KERN_DEBUG "name_count maxed, losing inode data\n"); return 1; } context->name_count++; @@ -1393,20 +1796,21 @@ static void audit_copy_inode(struct audit_names *name, const struct inode *inode name->uid = inode->i_uid; name->gid = inode->i_gid; name->rdev = inode->i_rdev; - selinux_get_inode_sid(inode, &name->osid); + security_inode_getsecid(inode, &name->osid); } /** * audit_inode - store the inode and device from a lookup * @name: name being audited - * @inode: inode being audited + * @dentry: dentry being audited * * Called from fs/namei.c:path_lookup(). */ -void __audit_inode(const char *name, const struct inode *inode) +void __audit_inode(const char *name, const struct dentry *dentry) { int idx; struct audit_context *context = current->audit_context; + const struct inode *inode = dentry->d_inode; if (!context->in_syscall) return; @@ -1426,13 +1830,14 @@ void __audit_inode(const char *name, const struct inode *inode) idx = context->name_count - 1; context->names[idx].name = NULL; } + handle_path(dentry); audit_copy_inode(&context->names[idx], inode); } /** * audit_inode_child - collect inode info for created/removed objects * @dname: inode's dentry name - * @inode: inode being audited + * @dentry: dentry being audited * @parent: inode of dentry parent * * For syscalls that create or remove filesystem objects, audit_inode @@ -1443,17 +1848,20 @@ void __audit_inode(const char *name, const struct inode *inode) * must be hooked prior, in order to capture the target inode during * unsuccessful attempts. */ -void __audit_inode_child(const char *dname, const struct inode *inode, +void __audit_inode_child(const char *dname, const struct dentry *dentry, const struct inode *parent) { int idx; struct audit_context *context = current->audit_context; const char *found_parent = NULL, *found_child = NULL; + const struct inode *inode = dentry->d_inode; int dirlen = 0; if (!context->in_syscall) return; + if (inode) + handle_one(inode); /* determine matching parent */ if (!dname) goto add_names; @@ -1545,6 +1953,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 @@ -1556,41 +1967,29 @@ 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) { - /* 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" + " old ses=%u new ses=%u", + task->pid, task->uid, + task->loginuid, loginuid, + task->sessionid, sessionid); + audit_log_end(ab); } - context->loginuid = loginuid; } + task->sessionid = sessionid; + 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 audit_context *ctx) -{ - return ctx ? ctx->loginuid : -1; -} - -EXPORT_SYMBOL(audit_get_loginuid); - -/** * __audit_mq_open - record audit data for a POSIX MQ open * @oflag: open flag * @mode: mode bits @@ -1812,8 +2211,7 @@ int __audit_ipc_obj(struct kern_ipc_perm *ipcp) ax->uid = ipcp->uid; ax->gid = ipcp->gid; ax->mode = ipcp->mode; - selinux_get_ipc_sid(ipcp, &ax->osid); - + security_ipc_getsecid(ipcp, &ax->osid); ax->d.type = AUDIT_IPC; ax->d.next = context->aux; context->aux = (void *)ax; @@ -1849,8 +2247,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; @@ -1859,14 +2255,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; @@ -1972,7 +2360,11 @@ void __audit_ptrace(struct task_struct *t) struct audit_context *context = current->audit_context; context->target_pid = t->pid; - selinux_get_task_sid(t, &context->target_sid); + context->target_auid = audit_get_loginuid(t); + context->target_uid = t->uid; + context->target_sessionid = audit_get_sessionid(t); + security_task_getsecid(t, &context->target_sid); + memcpy(context->target_comm, t->comm, TASK_COMM_LEN); } /** @@ -1988,18 +2380,15 @@ int __audit_signal_info(int sig, struct task_struct *t) struct audit_aux_data_pids *axp; struct task_struct *tsk = current; struct audit_context *ctx = tsk->audit_context; - extern pid_t audit_sig_pid; - extern uid_t audit_sig_uid; - extern u32 audit_sig_sid; if (audit_pid && t->tgid == audit_pid) { - if (sig == SIGTERM || sig == SIGHUP || sig == SIGUSR1) { + if (sig == SIGTERM || sig == SIGHUP || sig == SIGUSR1 || sig == SIGUSR2) { 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); + security_task_getsecid(tsk, &audit_sig_sid); } if (!audit_signals || audit_dummy_context()) return 0; @@ -2009,7 +2398,11 @@ int __audit_signal_info(int sig, struct task_struct *t) * in audit_context */ if (!ctx->target_pid) { ctx->target_pid = t->tgid; - selinux_get_task_sid(t, &ctx->target_sid); + ctx->target_auid = audit_get_loginuid(t); + ctx->target_uid = t->uid; + ctx->target_sessionid = audit_get_sessionid(t); + security_task_getsecid(t, &ctx->target_sid); + memcpy(ctx->target_comm, t->comm, TASK_COMM_LEN); return 0; } @@ -2026,7 +2419,11 @@ 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; - selinux_get_task_sid(t, &axp->target_sid[axp->pid_count]); + 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); + security_task_getsecid(t, &axp->target_sid[axp->pid_count]); + memcpy(axp->target_comm[axp->pid_count], t->comm, TASK_COMM_LEN); axp->pid_count++; return 0; @@ -2043,6 +2440,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; @@ -2051,19 +2450,19 @@ 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->audit_context), - current->uid, current->gid); - selinux_get_task_sid(current, &sid); + audit_log_format(ab, "auid=%u uid=%u gid=%u ses=%u", + auid, current->uid, current->gid, sessionid); + security_task_getsecid(current, &sid); if (sid) { char *ctx = NULL; u32 len; - if (selinux_sid_to_string(sid, &ctx, &len)) + if (security_secid_to_secctx(sid, &ctx, &len)) audit_log_format(ab, " ssid=%u", sid); - else + else { audit_log_format(ab, " subj=%s", ctx); - kfree(ctx); + security_release_secctx(ctx, len); + } } audit_log_format(ab, " pid=%d comm=", current->pid); audit_log_untrustedstring(ab, current->comm);