X-Git-Url: http://ftp.safe.ca/?a=blobdiff_plain;ds=inline;f=kernel%2Fauditsc.c;h=4819f3711973f746207aa15a60b45126546ffc9e;hb=eeb4613436f0f19a38f667ea3078821040559c68;hp=5276b7ef05f1c212a575677b391b080859238584;hpb=5712e88f2b0f626a4857c24128810bbf8ce09537;p=safe%2Fjmp%2Flinux-2.6 diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 5276b7e..4819f37 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -45,7 +45,6 @@ #include #include #include -#include #include #include #include @@ -62,18 +61,14 @@ #include #include #include -#include #include #include #include +#include +#include #include "audit.h" -extern struct list_head audit_filter_list[]; - -/* No syscall auditing will take place unless audit_enabled != 0. */ -extern int audit_enabled; - /* AUDIT_NAMES is the number of slots we reserve in the audit_context * for saving names from getname(). */ #define AUDIT_NAMES 20 @@ -81,12 +76,24 @@ extern int audit_enabled; /* 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; /* determines whether we collect data for signals sent */ int audit_signals; +struct audit_cap_data { + kernel_cap_t permitted; + kernel_cap_t inheritable; + union { + unsigned int fE; /* effective bit of a file capability */ + kernel_cap_t effective; /* effective set of a process */ + }; +}; + /* When fs/namei.c:getname() is called, we store the pointer in name and * we don't let putname() free it (instead we free all of the saved * pointers at syscall exit time). @@ -103,6 +110,8 @@ struct audit_names { gid_t gid; dev_t rdev; u32 osid; + struct audit_cap_data fcap; + unsigned int fcap_ver; }; struct audit_aux_data { @@ -156,7 +165,7 @@ struct audit_aux_data_execve { struct audit_aux_data d; int argc; int envc; - char mem[0]; + struct mm_struct *mm; }; struct audit_aux_data_socketcall { @@ -176,19 +185,36 @@ struct audit_aux_data_fd_pair { int fd[2]; }; -struct audit_aux_data_path { - struct audit_aux_data d; - struct dentry *dentry; - struct vfsmount *mnt; -}; - 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_aux_data_bprm_fcaps { + struct audit_aux_data d; + struct audit_cap_data fcap; + unsigned int fcap_ver; + struct audit_cap_data old_pcap; + struct audit_cap_data new_pcap; +}; + +struct audit_aux_data_capset { + struct audit_aux_data d; + pid_t pid; + struct audit_cap_data cap; +}; + +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 */ @@ -196,7 +222,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 */ @@ -205,8 +230,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; @@ -219,7 +243,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; @@ -238,7 +269,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) && @@ -275,6 +310,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. */ @@ -284,6 +447,7 @@ static int audit_filter_rules(struct task_struct *tsk, struct audit_names *name, enum audit_state *state) { + const struct cred *cred = get_task_cred(tsk); int i, j, need_sid = 1; u32 sid; @@ -303,34 +467,34 @@ static int audit_filter_rules(struct task_struct *tsk, } break; case AUDIT_UID: - result = audit_comparator(tsk->uid, f->op, f->val); + result = audit_comparator(cred->uid, f->op, f->val); break; case AUDIT_EUID: - result = audit_comparator(tsk->euid, f->op, f->val); + result = audit_comparator(cred->euid, f->op, f->val); break; case AUDIT_SUID: - result = audit_comparator(tsk->suid, f->op, f->val); + result = audit_comparator(cred->suid, f->op, f->val); break; case AUDIT_FSUID: - result = audit_comparator(tsk->fsuid, f->op, f->val); + result = audit_comparator(cred->fsuid, f->op, f->val); break; case AUDIT_GID: - result = audit_comparator(tsk->gid, f->op, f->val); + result = audit_comparator(cred->gid, f->op, f->val); break; case AUDIT_EGID: - result = audit_comparator(tsk->egid, f->op, f->val); + result = audit_comparator(cred->egid, f->op, f->val); break; case AUDIT_SGID: - result = audit_comparator(tsk->sgid, f->op, f->val); + result = audit_comparator(cred->sgid, f->op, f->val); break; case AUDIT_FSGID: - result = audit_comparator(tsk->fsgid, f->op, f->val); + result = audit_comparator(cred->fsgid, f->op, f->val); break; case AUDIT_PERS: 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; @@ -389,10 +553,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: @@ -404,14 +572,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; @@ -422,18 +590,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; } @@ -446,7 +614,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; } @@ -469,17 +637,23 @@ 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) + if (!result) { + put_cred(cred); 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; case AUDIT_ALWAYS: *state = AUDIT_RECORD_CONTEXT; break; } + put_cred(cred); return 1; } @@ -588,7 +762,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; @@ -644,12 +835,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) @@ -657,12 +845,6 @@ static inline void audit_free_aux(struct audit_context *context) struct audit_aux_data *aux; while ((aux = context->aux)) { - if (aux->type == AUDIT_AVC_PATH) { - struct audit_aux_data_path *axi = (void *)aux; - dput(axi->dentry); - mntput(axi->mnt); - } - context->aux = aux->next; kfree(aux); } @@ -675,11 +857,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) @@ -706,7 +885,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); @@ -718,11 +897,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; @@ -743,6 +917,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); @@ -759,11 +935,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; @@ -771,7 +947,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: @@ -800,8 +976,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; @@ -812,30 +987,252 @@ 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; } +/* + * 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) +{ + 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; + + /* + * 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; + } + + /* 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); + /* + * 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 (ret) { + WARN_ON(1); + send_sig(SIGKILL, current, 0); + return -1; + } + 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; + } + + /* + * 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, "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_cap(struct audit_buffer *ab, char *prefix, kernel_cap_t *cap) +{ + int i; + + audit_log_format(ab, " %s=", prefix); + CAP_FOR_EACH_U32(i) { + audit_log_format(ab, "%08x", cap->cap[(_KERNEL_CAPABILITY_U32S-1) - i]); + } +} + +static void audit_log_fcaps(struct audit_buffer *ab, struct audit_names *name) +{ + kernel_cap_t *perm = &name->fcap.permitted; + kernel_cap_t *inh = &name->fcap.inheritable; + int log = 0; + + if (!cap_isclear(*perm)) { + audit_log_cap(ab, "cap_fp", perm); + log = 1; + } + if (!cap_isclear(*inh)) { + audit_log_cap(ab, "cap_fi", inh); + log = 1; + } + + if (log) + audit_log_format(ab, " cap_fe=%d cap_fver=%x", name->fcap.fE, name->fcap_ver); +} + static void audit_log_exit(struct audit_context *context, struct task_struct *tsk) { + const struct cred *cred; int i, call_panic = 0; struct audit_buffer *ab; struct audit_aux_data *aux; @@ -845,14 +1242,15 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts context->pid = tsk->pid; if (!context->ppid) context->ppid = sys_getppid(); - context->uid = tsk->uid; - context->gid = tsk->gid; - context->euid = tsk->euid; - context->suid = tsk->suid; - context->fsuid = tsk->fsuid; - context->egid = tsk->egid; - context->sgid = tsk->sgid; - context->fsgid = tsk->fsgid; + cred = current_cred(); + context->uid = cred->uid; + context->gid = cred->gid; + context->euid = cred->euid; + context->suid = cred->suid; + context->fsuid = cred->fsuid; + context->egid = cred->egid; + context->sgid = cred->sgid; + context->fsgid = cred->fsgid; context->personality = tsk->personality; ab = audit_log_start(context, GFP_KERNEL, AUDIT_SYSCALL); @@ -863,22 +1261,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], @@ -886,13 +1284,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) { @@ -949,42 +1347,36 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts case AUDIT_IPC: { struct audit_aux_data_ipcctl *axi = (void *)aux; audit_log_format(ab, - "ouid=%u ogid=%u mode=%x", + "ouid=%u ogid=%u mode=%#o", axi->uid, axi->gid, axi->mode); 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; } case AUDIT_IPC_SET_PERM: { struct audit_aux_data_ipcctl *axi = (void *)aux; audit_log_format(ab, - "qbytes=%lx ouid=%u ogid=%u mode=%x", + "qbytes=%lx ouid=%u ogid=%u mode=%#o", axi->qbytes, axi->uid, axi->gid, axi->mode); break; } case AUDIT_EXECVE: { struct audit_aux_data_execve *axi = (void *)aux; - int i; - const char *p; - for (i = 0, p = axi->mem; i < axi->argc; i++) { - audit_log_format(ab, "a%d=", i); - p = audit_log_untrustedstring(ab, p); - audit_log_format(ab, "\n"); - } + 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++) @@ -995,12 +1387,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); - break; } - - case AUDIT_AVC_PATH: { - struct audit_aux_data_path *axi = (void *)aux; - audit_log_d_path(ab, "path=", axi->dentry, axi->mnt); + audit_log_n_hex(ab, axs->a, axs->len); break; } case AUDIT_FD_PAIR: { @@ -1008,29 +1395,56 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts audit_log_format(ab, "fd0=%d fd1=%d", axs->fd[0], axs->fd[1]); break; } + case AUDIT_BPRM_FCAPS: { + struct audit_aux_data_bprm_fcaps *axs = (void *)aux; + audit_log_format(ab, "fver=%x", axs->fcap_ver); + audit_log_cap(ab, "fp", &axs->fcap.permitted); + audit_log_cap(ab, "fi", &axs->fcap.inheritable); + audit_log_format(ab, " fe=%d", axs->fcap.fE); + audit_log_cap(ab, "old_pp", &axs->old_pcap.permitted); + audit_log_cap(ab, "old_pi", &axs->old_pcap.inheritable); + audit_log_cap(ab, "old_pe", &axs->old_pcap.effective); + audit_log_cap(ab, "new_pp", &axs->new_pcap.permitted); + audit_log_cap(ab, "new_pi", &axs->new_pcap.inheritable); + audit_log_cap(ab, "new_pe", &axs->new_pcap.effective); + break; } + + case AUDIT_CAPSET: { + struct audit_aux_data_capset *axs = (void *)aux; + audit_log_format(ab, "pid=%d", axs->pid); + audit_log_cap(ab, "cap_pi", &axs->cap.inheritable); + audit_log_cap(ab, "cap_pp", &axs->cap.permitted); + audit_log_cap(ab, "cap_pe", &axs->cap.effective); + break; } + } audit_log_end(ab); } 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); } } @@ -1053,14 +1467,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)"); @@ -1081,17 +1494,25 @@ 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_fcaps(ab, n); + 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"); } @@ -1111,8 +1532,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) @@ -1123,7 +1544,6 @@ void audit_free(struct task_struct *tsk) /** * audit_syscall_entry - fill in an audit record at syscall entry - * @tsk: task being audited * @arch: architecture type * @major: major syscall type (function) * @a1: additional syscall register 1 @@ -1147,7 +1567,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 @@ -1211,9 +1632,25 @@ void audit_syscall_entry(int arch, int major, context->ppid = 0; } +void audit_finish_fork(struct task_struct *child) +{ + struct audit_context *ctx = current->audit_context; + struct audit_context *p = child->audit_context; + if (!p || !ctx || !ctx->auditable) + return; + p->arch = ctx->arch; + p->major = ctx->major; + memcpy(p->argv, ctx->argv, sizeof(ctx->argv)); + p->ctime = ctx->ctime; + p->dummy = ctx->dummy; + p->auditable = ctx->auditable; + p->in_syscall = ctx->in_syscall; + p->filterkey = kstrdup(ctx->filterkey, GFP_KERNEL); + p->ppid = current->pid; +} + /** * audit_syscall_exit - deallocate audit context after a system call - * @tsk: task being audited * @valid: success/failure flag * @return_code: syscall return value * @@ -1246,6 +1683,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; @@ -1257,6 +1695,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 @@ -1286,13 +1813,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 @@ -1344,13 +1871,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++; @@ -1360,8 +1887,36 @@ static int audit_inc_name_count(struct audit_context *context, return 0; } + +static inline int audit_copy_fcaps(struct audit_names *name, const struct dentry *dentry) +{ + struct cpu_vfs_cap_data caps; + int rc; + + memset(&name->fcap.permitted, 0, sizeof(kernel_cap_t)); + memset(&name->fcap.inheritable, 0, sizeof(kernel_cap_t)); + name->fcap.fE = 0; + name->fcap_ver = 0; + + if (!dentry) + return 0; + + rc = get_vfs_caps_from_disk(dentry, &caps); + if (rc) + return rc; + + name->fcap.permitted = caps.permitted; + name->fcap.inheritable = caps.inheritable; + name->fcap.fE = !!(caps.magic_etc & VFS_CAP_FLAGS_EFFECTIVE); + name->fcap_ver = (caps.magic_etc & VFS_CAP_REVISION_MASK) >> VFS_CAP_REVISION_SHIFT; + + return 0; +} + + /* Copy inode data into an audit_names. */ -static void audit_copy_inode(struct audit_names *name, const struct inode *inode) +static void audit_copy_inode(struct audit_names *name, const struct dentry *dentry, + const struct inode *inode) { name->ino = inode->i_ino; name->dev = inode->i_sb->s_dev; @@ -1369,20 +1924,22 @@ 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_copy_fcaps(name, dentry); } /** * 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; @@ -1402,13 +1959,14 @@ void __audit_inode(const char *name, const struct inode *inode) idx = context->name_count - 1; context->names[idx].name = NULL; } - audit_copy_inode(&context->names[idx], inode); + handle_path(dentry); + audit_copy_inode(&context->names[idx], dentry, 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 @@ -1419,17 +1977,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; @@ -1460,7 +2021,7 @@ void __audit_inode_child(const char *dname, const struct inode *inode, if (!strcmp(dname, n->name) || !audit_compare_dname_path(dname, n->name, &dirlen)) { if (inode) - audit_copy_inode(n, inode); + audit_copy_inode(n, NULL, inode); else n->ino = (unsigned long)-1; found_child = n->name; @@ -1474,7 +2035,7 @@ add_names: return; idx = context->name_count - 1; context->names[idx].name = NULL; - audit_copy_inode(&context->names[idx], parent); + audit_copy_inode(&context->names[idx], NULL, parent); } if (!found_child) { @@ -1495,11 +2056,12 @@ add_names: } if (inode) - audit_copy_inode(&context->names[idx], inode); + audit_copy_inode(&context->names[idx], NULL, inode); else context->names[idx].ino = (unsigned long)-1; } } +EXPORT_SYMBOL_GPL(__audit_inode_child); /** * auditsc_get_stamp - get local copies of audit_context values @@ -1509,17 +2071,23 @@ add_names: * * Also sets the context as auditable. */ -void auditsc_get_stamp(struct audit_context *ctx, +int auditsc_get_stamp(struct audit_context *ctx, struct timespec *t, unsigned int *serial) { + if (!ctx->in_syscall) + return 0; if (!ctx->serial) ctx->serial = audit_serial(); t->tv_sec = ctx->ctime.tv_sec; t->tv_nsec = ctx->ctime.tv_nsec; *serial = ctx->serial; ctx->auditable = 1; + return 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 @@ -1531,41 +2099,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), + 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 @@ -1787,8 +2343,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; @@ -1828,28 +2383,17 @@ int audit_bprm(struct linux_binprm *bprm) { struct audit_aux_data_execve *ax; struct audit_context *context = current->audit_context; - unsigned long p, next; - void *to; if (likely(!audit_enabled || !context || context->dummy)) return 0; - ax = kmalloc(sizeof(*ax) + PAGE_SIZE * MAX_ARG_PAGES - bprm->p, - GFP_KERNEL); + ax = kmalloc(sizeof(*ax), GFP_KERNEL); if (!ax) return -ENOMEM; ax->argc = bprm->argc; ax->envc = bprm->envc; - for (p = bprm->p, to = ax->mem; p < MAX_ARG_PAGES*PAGE_SIZE; p = next) { - struct page *page = bprm->page[p / PAGE_SIZE]; - void *kaddr = kmap(page); - next = (p + PAGE_SIZE) & ~(PAGE_SIZE - 1); - memcpy(to, kaddr + (p & (PAGE_SIZE - 1)), next - p); - to += next - p; - kunmap(page); - } - + ax->mm = bprm->mm; ax->d.type = AUDIT_EXECVE; ax->d.next = context->aux; context->aux = (void *)ax; @@ -1948,37 +2492,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); -} - -/** - * audit_avc_path - record the granting or denial of permissions - * @dentry: dentry to record - * @mnt: mnt to record - * - * Returns 0 for success or NULL context or < 0 on error. - * - * Called from security/selinux/avc.c::avc_audit() - */ -int audit_avc_path(struct dentry *dentry, struct vfsmount *mnt) -{ - struct audit_aux_data_path *ax; - struct audit_context *context = current->audit_context; - - if (likely(!context)) - return 0; - - ax = kmalloc(sizeof(*ax), GFP_ATOMIC); - if (!ax) - return -ENOMEM; - - ax->dentry = dget(dentry); - ax->mnt = mntget(mnt); - - ax->d.type = AUDIT_AVC_PATH; - ax->d.next = context->aux; - context->aux = (void *)ax; - return 0; + context->target_auid = audit_get_loginuid(t); + context->target_uid = task_uid(t); + context->target_sessionid = audit_get_sessionid(t); + security_task_getsecid(t, &context->target_sid); + memcpy(context->target_comm, t->comm, TASK_COMM_LEN); } /** @@ -1994,28 +2512,30 @@ 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 && - (sig == SIGTERM || sig == SIGHUP || sig == SIGUSR1)) { - audit_sig_pid = tsk->pid; - if (ctx) - audit_sig_uid = ctx->loginuid; - else - audit_sig_uid = tsk->uid; - selinux_get_task_sid(tsk, &audit_sig_sid); - } + uid_t uid = current_uid(), t_uid = task_uid(t); - if (!audit_signals) /* audit_context checked in wrapper */ - return 0; + if (audit_pid && t->tgid == audit_pid) { + if (sig == SIGTERM || sig == SIGHUP || sig == SIGUSR1 || sig == SIGUSR2) { + audit_sig_pid = tsk->pid; + if (tsk->loginuid != -1) + audit_sig_uid = tsk->loginuid; + else + audit_sig_uid = uid; + security_task_getsecid(tsk, &audit_sig_sid); + } + if (!audit_signals || audit_dummy_context()) + return 0; + } /* optimize the common case by putting first signal recipient directly * 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; } @@ -2029,11 +2549,138 @@ int __audit_signal_info(int sig, struct task_struct *t) axp->d.next = ctx->aux_pids; ctx->aux_pids = (void *)axp; } - BUG_ON(axp->pid_count > AUDIT_AUX_PIDS); + 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; } + +/** + * __audit_log_bprm_fcaps - store information about a loading bprm and relevant fcaps + * @bprm: pointer to the bprm being processed + * @new: the proposed new credentials + * @old: the old credentials + * + * Simply check if the proc already has the caps given by the file and if not + * store the priv escalation info for later auditing at the end of the syscall + * + * -Eric + */ +int __audit_log_bprm_fcaps(struct linux_binprm *bprm, + const struct cred *new, const struct cred *old) +{ + struct audit_aux_data_bprm_fcaps *ax; + struct audit_context *context = current->audit_context; + struct cpu_vfs_cap_data vcaps; + struct dentry *dentry; + + ax = kmalloc(sizeof(*ax), GFP_KERNEL); + if (!ax) + return -ENOMEM; + + ax->d.type = AUDIT_BPRM_FCAPS; + ax->d.next = context->aux; + context->aux = (void *)ax; + + dentry = dget(bprm->file->f_dentry); + get_vfs_caps_from_disk(dentry, &vcaps); + dput(dentry); + + ax->fcap.permitted = vcaps.permitted; + ax->fcap.inheritable = vcaps.inheritable; + ax->fcap.fE = !!(vcaps.magic_etc & VFS_CAP_FLAGS_EFFECTIVE); + ax->fcap_ver = (vcaps.magic_etc & VFS_CAP_REVISION_MASK) >> VFS_CAP_REVISION_SHIFT; + + ax->old_pcap.permitted = old->cap_permitted; + ax->old_pcap.inheritable = old->cap_inheritable; + ax->old_pcap.effective = old->cap_effective; + + ax->new_pcap.permitted = new->cap_permitted; + ax->new_pcap.inheritable = new->cap_inheritable; + ax->new_pcap.effective = new->cap_effective; + return 0; +} + +/** + * __audit_log_capset - store information about the arguments to the capset syscall + * @pid: target pid of the capset call + * @new: the new credentials + * @old: the old (current) credentials + * + * Record the aguments userspace sent to sys_capset for later printing by the + * audit system if applicable + */ +int __audit_log_capset(pid_t pid, + const struct cred *new, const struct cred *old) +{ + struct audit_aux_data_capset *ax; + struct audit_context *context = current->audit_context; + + if (likely(!audit_enabled || !context || context->dummy)) + return 0; + + ax = kmalloc(sizeof(*ax), GFP_KERNEL); + if (!ax) + return -ENOMEM; + + ax->d.type = AUDIT_CAPSET; + ax->d.next = context->aux; + context->aux = (void *)ax; + + ax->pid = pid; + ax->cap.effective = new->cap_effective; + ax->cap.inheritable = new->cap_effective; + ax->cap.permitted = new->cap_permitted; + + return 0; +} + +/** + * audit_core_dumps - record information about processes that end abnormally + * @signr: signal value + * + * If a process ends with a core dump, something fishy is going on and we + * should record the event for investigation. + */ +void audit_core_dumps(long signr) +{ + struct audit_buffer *ab; + u32 sid; + uid_t auid = audit_get_loginuid(current), uid; + gid_t gid; + unsigned int sessionid = audit_get_sessionid(current); + + if (!audit_enabled) + return; + + if (signr == SIGQUIT) /* don't care for those */ + return; + + ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_ANOM_ABEND); + current_uid_gid(&uid, &gid); + audit_log_format(ab, "auid=%u uid=%u gid=%u ses=%u", + auid, uid, gid, sessionid); + security_task_getsecid(current, &sid); + if (sid) { + char *ctx = NULL; + u32 len; + + if (security_secid_to_secctx(sid, &ctx, &len)) + audit_log_format(ab, " ssid=%u", sid); + else { + audit_log_format(ab, " subj=%s", ctx); + security_release_secctx(ctx, len); + } + } + audit_log_format(ab, " pid=%d comm=", current->pid); + audit_log_untrustedstring(ab, current->comm); + audit_log_format(ab, " sig=%ld", signr); + audit_log_end(ab); +}