📄 auditsc.c
字号:
if (unlikely(!context)) return; /* * This happens only on certain architectures that make system * calls in kernel_thread via the entry.S interface, instead of * with direct calls. (If you are porting to a new * architecture, hitting this condition can indicate that you * got the _exit/_leave calls backward in entry.S.) * * i386 no * x86_64 no * ppc64 yes (see arch/powerpc/platforms/iseries/misc.S) * * This also happens with vm86 emulation in a non-nested manner * (entries without exits), so this case must be caught. */ if (context->in_syscall) { struct audit_context *newctx;#if AUDIT_DEBUG printk(KERN_ERR "audit(:%d) pid=%d in syscall=%d;" " entering syscall=%d\n", context->serial, tsk->pid, context->major, major);#endif newctx = audit_alloc_context(context->state); if (newctx) { newctx->previous = context; context = newctx; tsk->audit_context = newctx; } else { /* If we can't alloc a new context, the best we * can do is to leak memory (any pending putname * will be lost). The only other alternative is * to abandon auditing. */ audit_zero_context(context, context->state); } } BUG_ON(context->in_syscall || context->name_count); if (!audit_enabled) return; context->arch = arch; context->major = major; context->argv[0] = a1; context->argv[1] = a2; context->argv[2] = a3; context->argv[3] = a4; state = context->state; context->dummy = !audit_n_rules; if (!context->dummy && (state == AUDIT_SETUP_CONTEXT || state == AUDIT_BUILD_CONTEXT)) state = audit_filter_syscall(tsk, context, &audit_filter_list[AUDIT_FILTER_ENTRY]); if (likely(state == AUDIT_DISABLED)) return; context->serial = 0; context->ctime = CURRENT_TIME; context->in_syscall = 1; context->auditable = !!(state == AUDIT_RECORD_CONTEXT); context->ppid = 0;}/** * audit_syscall_exit - deallocate audit context after a system call * @tsk: task being audited * @valid: success/failure flag * @return_code: syscall return value * * Tear down after system call. If the audit context has been marked as * auditable (either because of the AUDIT_RECORD_CONTEXT state from * filtering, or because some other part of the kernel write an audit * message), then write out the syscall information. In call cases, * free the names stored from getname(). */void audit_syscall_exit(int valid, long return_code){ struct task_struct *tsk = current; struct audit_context *context; context = audit_get_context(tsk, valid, return_code); if (likely(!context)) return; if (context->in_syscall && context->auditable) audit_log_exit(context, tsk); context->in_syscall = 0; context->auditable = 0; if (context->previous) { struct audit_context *new_context = context->previous; context->previous = NULL; audit_free_context(context); 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; context->target_pid = 0; context->target_sid = 0; kfree(context->filterkey); context->filterkey = NULL; tsk->audit_context = context; }}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 * * Add a name to the list of audit names for this context. * Called from fs/namei.c:getname(). */void __audit_getname(const char *name){ struct audit_context *context = current->audit_context; if (IS_ERR(name) || !name) return; if (!context->in_syscall) {#if AUDIT_DEBUG == 2 printk(KERN_ERR "%s:%d(:%d): ignoring getname(%p)\n", __FILE__, __LINE__, context->serial, name); dump_stack();#endif return; } BUG_ON(context->name_count >= AUDIT_NAMES); context->names[context->name_count].name = name; context->names[context->name_count].name_len = AUDIT_NAME_FULL; context->names[context->name_count].name_put = 1; context->names[context->name_count].ino = (unsigned long)-1; context->names[context->name_count].osid = 0; ++context->name_count; if (!context->pwd.dentry) { read_lock(¤t->fs->lock); context->pwd = current->fs->pwd; path_get(¤t->fs->pwd); read_unlock(¤t->fs->lock); }}/* audit_putname - intercept a putname request * @name: name to intercept and delay for putname * * If we have stored the name from getname in the audit context, * then we delay the putname until syscall exit. * Called from include/linux/fs.h:putname(). */void audit_putname(const char *name){ struct audit_context *context = current->audit_context; BUG_ON(!context); if (!context->in_syscall) {#if AUDIT_DEBUG == 2 printk(KERN_ERR "%s:%d(:%d): __putname(%p)\n", __FILE__, __LINE__, context->serial, name); if (context->name_count) { int i; for (i = 0; i < context->name_count; i++) printk(KERN_ERR "name[%d] = %p = %s\n", i, context->names[i].name, context->names[i].name ?: "(null)"); }#endif __putname(name); }#if AUDIT_DEBUG else { ++context->put_count; if (context->put_count > context->name_count) { printk(KERN_ERR "%s:%d(:%d): major=%d" " in_syscall=%d putname(%p) name_count=%d" " put_count=%d\n", __FILE__, __LINE__, context->serial, context->major, context->in_syscall, name, context->name_count, context->put_count); dump_stack(); } }#endif}static int audit_inc_name_count(struct audit_context *context, const struct inode *inode){ if (context->name_count >= AUDIT_NAMES) { if (inode) printk(KERN_DEBUG "name_count maxed, losing inode data: " "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\n"); return 1; } context->name_count++;#if AUDIT_DEBUG context->ino_count++;#endif return 0;}/* Copy inode data into an audit_names. */static void audit_copy_inode(struct audit_names *name, const struct inode *inode){ name->ino = inode->i_ino; name->dev = inode->i_sb->s_dev; name->mode = inode->i_mode; name->uid = inode->i_uid; name->gid = inode->i_gid; name->rdev = inode->i_rdev; security_inode_getsecid(inode, &name->osid);}/** * audit_inode - store the inode and device from a lookup * @name: name being audited * @dentry: dentry being audited * * Called from fs/namei.c:path_lookup(). */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; if (context->name_count && context->names[context->name_count-1].name && context->names[context->name_count-1].name == name) idx = context->name_count - 1; else if (context->name_count > 1 && context->names[context->name_count-2].name && context->names[context->name_count-2].name == name) idx = context->name_count - 2; else { /* FIXME: how much do we care about inodes that have no * associated name? */ if (audit_inc_name_count(context, inode)) return; 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 * @dentry: dentry being audited * @parent: inode of dentry parent * * For syscalls that create or remove filesystem objects, audit_inode * can only collect information for the filesystem object's parent. * This call updates the audit context with the child's information. * Syscalls that create a new filesystem object must be hooked after * the object is created. Syscalls that remove a filesystem object * must be hooked prior, in order to capture the target inode during * unsuccessful attempts. */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; /* parent is more likely, look for it first */ for (idx = 0; idx < context->name_count; idx++) { struct audit_names *n = &context->names[idx]; if (!n->name) continue; if (n->ino == parent->i_ino && !audit_compare_dname_path(dname, n->name, &dirlen)) { n->name_len = dirlen; /* update parent data in place */ found_parent = n->name; goto add_names; } } /* no matching parent, look for matching child */ for (idx = 0; idx < context->name_count; idx++) { struct audit_names *n = &context->names[idx]; if (!n->name) continue; /* strcmp() is the more likely scenario */ if (!strcmp(dname, n->name) || !audit_compare_dname_path(dname, n->name, &dirlen)) { if (inode) audit_copy_inode(n, inode); else n->ino = (unsigned long)-1; found_child = n->name; goto add_names; } }add_names: if (!found_parent) { if (audit_inc_name_count(context, parent)) return; idx = context->name_count - 1; context->names[idx].name = NULL; audit_copy_inode(&context->names[idx], parent); } if (!found_child) { if (audit_inc_name_count(context, inode)) return; idx = context->name_count - 1; /* Re-use the name belonging to the slot for a matching parent * directory. All names for this context are relinquished in * audit_free_names() */ if (found_parent) { context->names[idx].name = found_parent; context->names[idx].name_len = AUDIT_NAME_FULL; /* don't call __putname() */ context->names[idx].name_put = 0; } else { context->names[idx].name = NULL; } if (inode) audit_copy_inode(&context->names[idx], 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 * @ctx: audit_context for the task * @t: timespec to store time recorded in the audit_context * @serial: serial value that is recorded in the audit_context * * Also sets the context as auditable. */void auditsc_get_stamp(struct audit_context *ctx, struct timespec *t, unsigned int *serial){ 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;}/* 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 * @loginuid: loginuid value * * Returns 0. * * Called (set) from fs/proc/base.c::proc_loginuid_write(). */int audit_set_loginuid(struct task_struct *task, uid_t loginuid){ unsigned int sessionid = atomic_inc_return(&session_id); struct audit_context *context = task->audit_context; if (context && context->in_syscall) { 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"
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -