/* flags stating the success for a syscall */ #define AUDITSC_INVALID 0 #define AUDITSC_SUCCESS 1 #define AUDITSC_FAILURE 2
/* no execve audit message should be longer than this (userspace limits),
* see the note near the top of audit_log_execve_info() about this value */ #define MAX_EXECVE_AUDIT_LEN 7500
/* max length to print of cmdline/proctitle value during audit */ #define MAX_PROCTITLE_AUDIT_LEN 128
/* number of audit rules */ int audit_n_rules;
/* determines whether we collect data for signals sent */ int audit_signals;
struct audit_aux_data { struct audit_aux_data *next; int type;
};
/* Number of target pids per aux struct. */ #define AUDIT_AUX_PIDS 16
staticvoid unroll_tree_refs(struct audit_context *ctx, struct audit_tree_refs *p, int count)
{ 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;
}
if (!tree) return0; /* 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)) return1;
} /* partial */ if (p) { for (n = ctx->tree_count; n < 31; n++) if (audit_tree_match(p->c[n], tree)) return1;
} return0;
}
if (name) {
rc = audit_gid_comparator(gid, f->op, name->gid); if (rc) return rc;
}
if (ctx) {
list_for_each_entry(n, &ctx->names_list, list) {
rc = audit_gid_comparator(gid, f->op, n->gid); if (rc) return rc;
}
} return0;
}
staticint audit_field_compare(struct task_struct *tsk, conststruct cred *cred, struct audit_field *f, struct audit_context *ctx, struct audit_names *name)
{ switch (f->val) { /* process to file object comparisons */ case AUDIT_COMPARE_UID_TO_OBJ_UID: return audit_compare_uid(cred->uid, name, f, ctx); case AUDIT_COMPARE_GID_TO_OBJ_GID: return audit_compare_gid(cred->gid, name, f, ctx); case AUDIT_COMPARE_EUID_TO_OBJ_UID: return audit_compare_uid(cred->euid, name, f, ctx); case AUDIT_COMPARE_EGID_TO_OBJ_GID: return audit_compare_gid(cred->egid, name, f, ctx); case AUDIT_COMPARE_AUID_TO_OBJ_UID: return audit_compare_uid(audit_get_loginuid(tsk), name, f, ctx); case AUDIT_COMPARE_SUID_TO_OBJ_UID: return audit_compare_uid(cred->suid, name, f, ctx); case AUDIT_COMPARE_SGID_TO_OBJ_GID: return audit_compare_gid(cred->sgid, name, f, ctx); case AUDIT_COMPARE_FSUID_TO_OBJ_UID: return audit_compare_uid(cred->fsuid, name, f, ctx); case AUDIT_COMPARE_FSGID_TO_OBJ_GID: return audit_compare_gid(cred->fsgid, name, f, ctx); /* uid comparisons */ case AUDIT_COMPARE_UID_TO_AUID: return audit_uid_comparator(cred->uid, f->op,
audit_get_loginuid(tsk)); case AUDIT_COMPARE_UID_TO_EUID: return audit_uid_comparator(cred->uid, f->op, cred->euid); case AUDIT_COMPARE_UID_TO_SUID: return audit_uid_comparator(cred->uid, f->op, cred->suid); case AUDIT_COMPARE_UID_TO_FSUID: return audit_uid_comparator(cred->uid, f->op, cred->fsuid); /* auid comparisons */ case AUDIT_COMPARE_AUID_TO_EUID: return audit_uid_comparator(audit_get_loginuid(tsk), f->op,
cred->euid); case AUDIT_COMPARE_AUID_TO_SUID: return audit_uid_comparator(audit_get_loginuid(tsk), f->op,
cred->suid); case AUDIT_COMPARE_AUID_TO_FSUID: return audit_uid_comparator(audit_get_loginuid(tsk), f->op,
cred->fsuid); /* euid comparisons */ case AUDIT_COMPARE_EUID_TO_SUID: return audit_uid_comparator(cred->euid, f->op, cred->suid); case AUDIT_COMPARE_EUID_TO_FSUID: return audit_uid_comparator(cred->euid, f->op, cred->fsuid); /* suid comparisons */ case AUDIT_COMPARE_SUID_TO_FSUID: return audit_uid_comparator(cred->suid, f->op, cred->fsuid); /* gid comparisons */ case AUDIT_COMPARE_GID_TO_EGID: return audit_gid_comparator(cred->gid, f->op, cred->egid); case AUDIT_COMPARE_GID_TO_SGID: return audit_gid_comparator(cred->gid, f->op, cred->sgid); case AUDIT_COMPARE_GID_TO_FSGID: return audit_gid_comparator(cred->gid, f->op, cred->fsgid); /* egid comparisons */ case AUDIT_COMPARE_EGID_TO_SGID: return audit_gid_comparator(cred->egid, f->op, cred->sgid); case AUDIT_COMPARE_EGID_TO_FSGID: return audit_gid_comparator(cred->egid, f->op, cred->fsgid); /* sgid comparison */ case AUDIT_COMPARE_SGID_TO_FSGID: return audit_gid_comparator(cred->sgid, f->op, cred->fsgid); default:
WARN(1, "Missing AUDIT_COMPARE define. Report as a bug\n"); return0;
} return0;
}
/* 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. * *Iftask_creationistrue,thisisanexplicitindicationthatweare *filteringataskruleattaskcreationtime.Thisandtsk==currentare *theonlysituationswheretsk->credmaybeaccessedwithoutanrcureadlock.
*/ staticint audit_filter_rules(struct task_struct *tsk, struct audit_krule *rule, struct audit_context *ctx, struct audit_names *name, enum audit_state *state, bool task_creation)
{ conststruct cred *cred; int i, need_sid = 1; struct lsm_prop prop = { }; unsignedint sessionid;
if (ctx && rule->prio <= ctx->prio) return0;
cred = rcu_dereference_check(tsk->cred, tsk == current || task_creation);
for (i = 0; i < rule->field_count; i++) { struct audit_field *f = &rule->fields[i]; struct audit_names *n; int result = 0;
pid_t pid;
switch (f->type) { case AUDIT_PID:
pid = task_tgid_nr(tsk);
result = audit_comparator(pid, f->op, f->val); break; case AUDIT_PPID: if (ctx) { if (!ctx->ppid)
ctx->ppid = task_ppid_nr(tsk);
result = audit_comparator(ctx->ppid, f->op, f->val);
} break; case AUDIT_EXE:
result = audit_exe_compare(tsk, rule->exe); if (f->op == Audit_not_equal)
result = !result; break; case AUDIT_UID:
result = audit_uid_comparator(cred->uid, f->op, f->uid); break; case AUDIT_EUID:
result = audit_uid_comparator(cred->euid, f->op, f->uid); break; case AUDIT_SUID:
result = audit_uid_comparator(cred->suid, f->op, f->uid); break; case AUDIT_FSUID:
result = audit_uid_comparator(cred->fsuid, f->op, f->uid); break; case AUDIT_GID:
result = audit_gid_comparator(cred->gid, f->op, f->gid); if (f->op == Audit_equal) { if (!result)
result = groups_search(cred->group_info, f->gid);
} elseif (f->op == Audit_not_equal) { if (result)
result = !groups_search(cred->group_info, f->gid);
} break; case AUDIT_EGID:
result = audit_gid_comparator(cred->egid, f->op, f->gid); if (f->op == Audit_equal) { if (!result)
result = groups_search(cred->group_info, f->gid);
} elseif (f->op == Audit_not_equal) { if (result)
result = !groups_search(cred->group_info, f->gid);
} break; case AUDIT_SGID:
result = audit_gid_comparator(cred->sgid, f->op, f->gid); break; case AUDIT_FSGID:
result = audit_gid_comparator(cred->fsgid, f->op, f->gid); break; case AUDIT_SESSIONID:
sessionid = audit_get_sessionid(tsk);
result = audit_comparator(sessionid, f->op, f->val); break; case AUDIT_PERS:
result = audit_comparator(tsk->personality, f->op, f->val); break; case AUDIT_ARCH: if (ctx)
result = audit_comparator(ctx->arch, f->op, f->val); break;
case AUDIT_EXIT: if (ctx && ctx->return_valid != AUDITSC_INVALID)
result = audit_comparator(ctx->return_code, f->op, f->val); break; case AUDIT_SUCCESS: if (ctx && ctx->return_valid != AUDITSC_INVALID) { if (f->val)
result = audit_comparator(ctx->return_valid, f->op, AUDITSC_SUCCESS); else
result = audit_comparator(ctx->return_valid, f->op, AUDITSC_FAILURE);
} break; case AUDIT_DEVMAJOR: if (name) { if (audit_comparator(MAJOR(name->dev), f->op, f->val) ||
audit_comparator(MAJOR(name->rdev), f->op, f->val))
++result;
} elseif (ctx) {
list_for_each_entry(n, &ctx->names_list, list) { if (audit_comparator(MAJOR(n->dev), f->op, f->val) ||
audit_comparator(MAJOR(n->rdev), f->op, f->val)) {
++result; break;
}
}
} break; case AUDIT_DEVMINOR: if (name) { if (audit_comparator(MINOR(name->dev), f->op, f->val) ||
audit_comparator(MINOR(name->rdev), f->op, f->val))
++result;
} elseif (ctx) {
list_for_each_entry(n, &ctx->names_list, list) { if (audit_comparator(MINOR(n->dev), f->op, f->val) ||
audit_comparator(MINOR(n->rdev), f->op, f->val)) {
++result; break;
}
}
} break; case AUDIT_INODE: if (name)
result = audit_comparator(name->ino, f->op, f->val); elseif (ctx) {
list_for_each_entry(n, &ctx->names_list, list) { if (audit_comparator(n->ino, f->op, f->val)) {
++result; break;
}
}
} break; case AUDIT_OBJ_UID: if (name) {
result = audit_uid_comparator(name->uid, f->op, f->uid);
} elseif (ctx) {
list_for_each_entry(n, &ctx->names_list, list) { if (audit_uid_comparator(n->uid, f->op, f->uid)) {
++result; break;
}
}
} break; case AUDIT_OBJ_GID: if (name) {
result = audit_gid_comparator(name->gid, f->op, f->gid);
} elseif (ctx) {
list_for_each_entry(n, &ctx->names_list, list) { if (audit_gid_comparator(n->gid, f->op, f->gid)) {
++result; break;
}
}
} break; case AUDIT_WATCH: if (name) {
result = audit_watch_compare(rule->watch,
name->ino,
name->dev); if (f->op == Audit_not_equal)
result = !result;
} break; case AUDIT_DIR: if (ctx) {
result = match_tree_refs(ctx, rule->tree); if (f->op == Audit_not_equal)
result = !result;
} break; case AUDIT_LOGINUID:
result = audit_uid_comparator(audit_get_loginuid(tsk),
f->op, f->uid); break; case AUDIT_LOGINUID_SET:
result = audit_comparator(audit_loginuid_set(tsk), f->op, f->val); break; case AUDIT_SADDR_FAM: if (ctx && ctx->sockaddr)
result = audit_comparator(ctx->sockaddr->ss_family,
f->op, f->val); break; case AUDIT_SUBJ_USER: case AUDIT_SUBJ_ROLE: case AUDIT_SUBJ_TYPE: case AUDIT_SUBJ_SEN: case AUDIT_SUBJ_CLR: /* NOTE: this may return negative values indicating atemporaryerror.Wesimplytreatthisasa matchfornowtoavoidlosinginformationthat maybewanted.Anerrormessagewillalsobe
logged upon error */ if (f->lsm_rule) { if (need_sid) { /* @tsk should always be equal to *@currentwiththeexceptionof *fork()/copy_process()inwhichcase *thenew@tskcredsarestilladup *of@current'scredssowecanstill *use *security_current_getlsmprop_subj() *hereeventhoughitalwaysrefs *@current'screds
*/
security_current_getlsmprop_subj(&prop);
need_sid = 0;
}
result = security_audit_rule_match(&prop,
f->type,
f->op,
f->lsm_rule);
} break; case AUDIT_OBJ_USER: case AUDIT_OBJ_ROLE: case AUDIT_OBJ_TYPE: case AUDIT_OBJ_LEV_LOW: case AUDIT_OBJ_LEV_HIGH: /* The above note for AUDIT_SUBJ_USER...AUDIT_SUBJ_CLR
also applies here */ if (f->lsm_rule) { /* Find files that match */ if (name) {
result = security_audit_rule_match(
&name->oprop,
f->type,
f->op,
f->lsm_rule);
} elseif (ctx) {
list_for_each_entry(n, &ctx->names_list, list) { if (security_audit_rule_match(
&n->oprop,
f->type,
f->op,
f->lsm_rule)) {
++result; break;
}
}
} /* Find ipc objects that match */ if (!ctx || ctx->type != AUDIT_IPC) break; if (security_audit_rule_match(&ctx->ipc.oprop,
f->type, f->op,
f->lsm_rule))
++result;
} break; case AUDIT_ARG0: case AUDIT_ARG1: case AUDIT_ARG2: case AUDIT_ARG3: if (ctx)
result = audit_comparator(ctx->argv[f->type-AUDIT_ARG0], f->op, f->val); break; case AUDIT_FILTERKEY: /* ignore this field for filtering */
result = 1; break; case AUDIT_PERM:
result = audit_match_perm(ctx, f->val); if (f->op == Audit_not_equal)
result = !result; break; case AUDIT_FILETYPE:
result = audit_match_filetype(ctx, f->val); if (f->op == Audit_not_equal)
result = !result; break; case AUDIT_FIELD_COMPARE:
result = audit_field_compare(tsk, cred, f, ctx, name); break;
} if (!result) return0;
}
if (ctx) { if (rule->filterkey) {
kfree(ctx->filterkey);
ctx->filterkey = kstrdup(rule->filterkey, GFP_ATOMIC);
}
ctx->prio = rule->prio;
} switch (rule->action) { case AUDIT_NEVER:
*state = AUDIT_STATE_DISABLED; break; case AUDIT_ALWAYS:
*state = AUDIT_STATE_RECORD; break;
} return1;
}
/* At process creation time, we can determine if system-call auditing is *completelydisabledforthistask.Sinceweonlyhavethetask *structureatthispoint,wecanonlycheckuidandgid.
*/ staticenum audit_state audit_filter_task(struct task_struct *tsk, char **key)
{ struct audit_entry *e; enum audit_state state;
/* At syscall exit time, this filter is called if the audit_state is *notlowenoughthatauditingcannottakeplace,butisalsonot *highenoughthatwealreadyknowwehavetowriteanauditrecord *(i.e.,thestateisAUDIT_STATE_BUILD).
*/ staticvoid audit_filter_syscall(struct task_struct *tsk, struct audit_context *ctx)
{ if (auditd_test_task(tsk)) return;
return __audit_filter_op(tsk, ctx, list, n, ctx->major);
}
/* At syscall exit time, this filter is called if any audit_names have been *collectedduringsyscallprocessing.Weonlycheckrulesinsublistsathash *bucketsapplicabletotheinodenumbersinaudit_names. *Regardingaudit_state,samerulesapplyasforaudit_filter_syscall().
*/ void audit_filter_inodes(struct task_struct *tsk, struct audit_context *ctx)
{ struct audit_names *n;
if (auditd_test_task(tsk)) return;
rcu_read_lock();
list_for_each_entry(n, &ctx->names_list, list) { if (audit_filter_inode_name(tsk, n, ctx)) break;
}
rcu_read_unlock();
}
staticinlinevoid audit_free_context(struct audit_context *context)
{ /* resetting is extra work, but it is likely just noise */
audit_reset_context(context);
audit_proctitle_free(context);
free_tree_refs(context);
kfree(context->filterkey);
kfree(context);
}
staticvoid audit_log_execve_info(struct audit_context *context, struct audit_buffer **ab)
{ long len_max; long len_rem; long len_full; long len_buf; long len_abuf = 0; long len_tmp; bool require_data; bool encode; unsignedint iter; unsignedint arg; char *buf_head; char *buf; constchar __user *p = (constchar __user *)current->mm->arg_start;
/* NOTE: this buffer needs to be large enough to hold all the non-arg *dataweputintheauditrecordforthisargument(seethe
* code below) ... at this point in time 96 is plenty */ char abuf[96];
/* NOTE: we set MAX_EXECVE_AUDIT_LEN to a rather arbitrary limit, the *currentvalueof7500isnotasimportantasthefactthatit *islessthan8k,asettingof7500givesusplentyofwiggle
* room if we go over a little bit in the logging below */
WARN_ON_ONCE(MAX_EXECVE_AUDIT_LEN > 7500);
len_max = MAX_EXECVE_AUDIT_LEN;
/* scratch buffer to hold the userspace args */
buf_head = kmalloc(MAX_EXECVE_AUDIT_LEN + 1, GFP_KERNEL); if (!buf_head) {
audit_panic("out of memory for argv string"); return;
}
buf = buf_head;
len_rem = len_max;
len_buf = 0;
len_full = 0;
require_data = true;
encode = false;
iter = 0;
arg = 0; do { /* NOTE: we don't ever want to trust this value for anything *serious,buttheauditrecordformatinsistswe *provideanargumentlengthforreallylongarguments, *e.g.>MAX_EXECVE_AUDIT_LEN,sowehavenochoicebut *tousestrncpy_from_user()toobtainthisvaluefor *recordinginthelog,althoughwedon'tuseit
* anywhere here to avoid a double-fetch problem */ if (len_full == 0)
len_full = strnlen_user(p, MAX_ARG_STRLEN) - 1;
/* read more data from userspace */ if (require_data) { /* can we make more room in the buffer? */ if (buf != buf_head) {
memmove(buf_head, buf, len_buf);
buf = buf_head;
}
/* fetch as much as we can of the argument */
len_tmp = strncpy_from_user(&buf_head[len_buf], p,
len_max - len_buf); if (len_tmp == -EFAULT) { /* unable to copy from userspace */
send_sig(SIGKILL, current, 0); goto out;
} elseif (len_tmp == (len_max - len_buf)) { /* buffer is not large enough */
require_data = true; /* NOTE: if we are going to span multiple *buffersforcetheencodingsowestand *achanceatasanelen_fullvalueand
* consistent record encoding */
encode = true;
len_full = len_full * 2;
p += len_tmp;
} else {
require_data = false; if (!encode)
encode = audit_string_contains_control(
buf, len_tmp); /* try to use a trusted value for len_full */ if (len_full < len_max)
len_full = (encode ?
len_tmp * 2 : len_tmp);
p += len_tmp + 1;
}
len_buf += len_tmp;
buf_head[len_buf] = '\0';
/* length of the buffer in the audit record? */
len_abuf = (encode ? len_buf * 2 : len_buf + 2);
}
/* write as much as we can to the audit log */ if (len_buf >= 0) { /* NOTE: some magic numbers here - basically if we *can'tfitareasonableamountofdataintothe *existingauditbuffer,flushitandstartwith
* a new buffer */ if ((sizeof(abuf) + 8) > len_rem) {
len_rem = len_max;
audit_log_end(*ab);
*ab = audit_log_start(context,
GFP_KERNEL, AUDIT_EXECVE); if (!*ab) goto out;
}
/* log the arg in the audit record */
audit_log_format(*ab, "%s", abuf);
len_rem -= len_tmp;
len_tmp = len_buf; if (encode) { if (len_abuf > len_rem)
len_tmp = len_rem / 2; /* encoding */
audit_log_n_hex(*ab, buf, len_tmp);
len_rem -= len_tmp * 2;
len_abuf -= len_tmp * 2;
} else { if (len_abuf > len_rem)
len_tmp = len_rem - 2; /* quotes */
audit_log_n_string(*ab, buf, len_tmp);
len_rem -= len_tmp + 2; /* don't subtract the "2" because we still need
* to add quotes to the remaining string */
len_abuf -= len_tmp;
}
len_buf -= len_tmp;
buf += len_tmp;
}
/* ready to move to the next argument? */ if ((len_buf == 0) && !require_data) {
arg++;
iter = 0;
len_full = 0;
require_data = true;
encode = false;
}
} while (arg < context->execve.argc);
/* NOTE: the caller handles the final audit_log_end() call */
break; case AUDIT_TIME_ADJNTPVAL: case AUDIT_TIME_INJOFFSET: /* this call deviates from the rest, eating the buffer */
audit_log_time(context, &ab); break;
}
audit_log_end(ab);
}
staticinlineint audit_proctitle_rtrim(char *proctitle, int len)
{ char *end = proctitle + len - 1;
while (end > proctitle && !isprint(*end))
end--;
/* catch the case where proctitle is only 1 non-print character */
len = end - proctitle + 1;
len -= isprint(proctitle[len-1]) == 0; return len;
}
if (context->type)
show_special(context, &call_panic);
if (context->fds[0] >= 0) {
ab = audit_log_start(context, GFP_KERNEL, AUDIT_FD_PAIR); if (ab) {
audit_log_format(ab, "fd0=%d fd1=%d",
context->fds[0], context->fds[1]);
audit_log_end(ab);
}
}
if (context->sockaddr_len) {
ab = audit_log_start(context, GFP_KERNEL, AUDIT_SOCKADDR); if (ab) {
audit_log_format(ab, "saddr=");
audit_log_n_hex(ab, (void *)context->sockaddr,
context->sockaddr_len);
audit_log_end(ab);
}
}
for (aux = context->aux_pids; aux; aux = aux->next) { struct audit_aux_data_pids *axs = (void *)aux;
for (i = 0; i < axs->pid_count; i++) if (audit_log_pid_context(context, axs->target_pid[i],
axs->target_auid[i],
axs->target_uid[i],
axs->target_sessionid[i],
&axs->target_ref[i],
axs->target_comm[i]))
call_panic = 1;
}
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);
audit_log_end(ab);
}
}
i = 0;
list_for_each_entry(n, &context->names_list, list) { if (n->hidden) continue;
audit_log_name(context, n, NULL, i++, &call_panic);
}
if (context->context == AUDIT_CTX_SYSCALL)
audit_log_proctitle();
/* 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 in audit_log_exit()");
}
/* this may generate CONFIG_CHANGE records */ if (!list_empty(&context->killed_trees))
audit_kill_trees(context);
/* We are called either by do_exit() or the fork() error handling code; *intheformercasetsk==currentandinthelattertskisa *randomtask_structthatdoesn'thaveanymeaningfuldatawe *needtologviaaudit_log_exit().
*/ if (tsk == current && !context->dummy) {
context->return_valid = AUDITSC_INVALID;
context->return_code = 0; if (context->context == AUDIT_CTX_SYSCALL) {
audit_filter_syscall(tsk, context);
audit_filter_inodes(tsk, context); if (context->current_state == AUDIT_STATE_RECORD)
audit_log_exit();
} elseif (context->context == AUDIT_CTX_URING) { /* TODO: verify this case is real and valid */
audit_filter_uring(tsk, context);
audit_filter_inodes(tsk, context); if (context->current_state == AUDIT_STATE_RECORD)
audit_log_uring(context);
}
}
/* this may generate CONFIG_CHANGE records */ if (!list_empty(&ctx->killed_trees))
audit_kill_trees(ctx);
/* run through both filters to ensure we set the filterkey properly */
audit_filter_uring(current, ctx);
audit_filter_inodes(current, ctx); if (ctx->current_state != AUDIT_STATE_RECORD) goto out;
audit_log_exit();
if (!context || context->dummy ||
context->context != AUDIT_CTX_SYSCALL) goto out;
/* this may generate CONFIG_CHANGE records */ if (!list_empty(&context->killed_trees))
audit_kill_trees(context);
audit_return_fixup(context, success, return_code); /* run through both filters to ensure we set the filterkey properly */
audit_filter_syscall(current, context);
audit_filter_inodes(current, context); if (context->current_state != AUDIT_STATE_RECORD) goto out;
if (likely(!inode->i_fsnotify_marks)) return;
context = 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))) {
pr_warn("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);
}
context = 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_backing_inode(d);
if (inode && unlikely(inode->i_fsnotify_marks)) { 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 */
pr_warn("out of memory, audit has lost a tree reference\n");
unroll_tree_refs(context, p, count);
audit_set_auditable(context); return;
}
rcu_read_unlock();
}
/* *Ifwehaveapointertoanaudit_namesentryalready,thenwecan *justuseitdirectlyifthetypeiscorrect.
*/
n = name->aname; if (n) { if (parent) { if (n->type == AUDIT_TYPE_PARENT ||
n->type == AUDIT_TYPE_UNKNOWN) goto out;
} else { if (n->type != AUDIT_TYPE_PARENT) goto out;
}
}
list_for_each_entry_reverse(n, &context->names_list, list) { if (n->ino) { /* valid inode number, use that for the comparison */ if (n->ino != inode->i_ino ||
n->dev != inode->i_sb->s_dev) continue;
} elseif (n->name) { /* inode number has not been set, check the name */ if (strcmp(n->name->name, name->name)) continue;
} else /* no inode and no name (?!) ... this is odd ... */ continue;
/* match the correct record type */ if (parent) { if (n->type == AUDIT_TYPE_PARENT ||
n->type == AUDIT_TYPE_UNKNOWN) goto out;
} else { if (n->type != AUDIT_TYPE_PARENT) goto out;
}
}
out_alloc: /* unable to find an entry with both a matching name and type */
n = audit_alloc_name(context, AUDIT_TYPE_UNKNOWN); if (!n) return; if (name) {
n->name = name;
refname(name);
}
/* is there a matching child entry? */
list_for_each_entry(n, &context->names_list, list) { /* can only match entries that have a name */ if (!n->name ||
(n->type != type && n->type != AUDIT_TYPE_UNKNOWN)) continue;
if (!found_parent) { /* create a new, "anonymous" parent record */
n = audit_alloc_name(context, AUDIT_TYPE_PARENT); if (!n) return;
audit_copy_inode(n, NULL, parent, 0);
}
if (!found_child) {
found_child = audit_alloc_name(context, type); if (!found_child) return;
/* Re-use the name belonging to the slot for a matching parent *directory.Allnamesforthiscontextarerelinquishedin
* audit_free_names() */ if (found_parent) {
found_child->name = found_parent->name;
found_child->name_len = AUDIT_NAME_FULL;
refname(found_child->name);
}
}
if (!audit_signals || audit_dummy_context()) return0;
/* optimize the common case by putting first signal recipient directly
* in audit_context */ if (!ctx->target_pid) {
ctx->target_pid = task_tgid_nr(t);
ctx->target_auid = audit_get_loginuid(t);
ctx->target_uid = t_uid;
ctx->target_sessionid = audit_get_sessionid(t);
strscpy(ctx->target_comm, t->comm);
security_task_getlsmprop_obj(t, &ctx->target_ref); return0;
}
axp = (void *)ctx->aux_pids; if (!axp || axp->pid_count == AUDIT_AUX_PIDS) {
axp = kzalloc(sizeof(*axp), GFP_ATOMIC); if (!axp) return -ENOMEM;
context->module.name = kstrdup(name, GFP_KERNEL); if (!context->module.name)
audit_log_lost("out of memory in __audit_log_kern_module");
context->type = AUDIT_KERN_MODULE;
}
/* only set type if not already set by NTP */ if (!context->type)
context->type = AUDIT_TIME_INJOFFSET;
memcpy(&context->time.tk_injoffset, &offset, sizeof(offset));
}
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.64Bemerkung:
(vorverarbeitet am 2026-09-29)
¤