#include <linux/audit.h>
#include <linux/personality.h>
#include <linux/time.h>
+#include <linux/kthread.h>
#include <asm/unistd.h>
/* 0 = no checking
uid_t uid;
gid_t gid;
dev_t rdev;
+ unsigned flags;
};
struct audit_aux_data {
int auditable; /* 1 if record should be written */
int name_count;
struct audit_names names[AUDIT_NAMES];
+ struct dentry * pwd;
+ struct vfsmount * pwdmnt;
struct audit_context *previous; /* For nested syscalls */
struct audit_aux_data *aux;
/* There are three lists of rules -- one to search at task creation
* time, one to search at syscall entry time, and another to search at
* syscall exit time. */
-static LIST_HEAD(audit_tsklist);
-static LIST_HEAD(audit_entlist);
-static LIST_HEAD(audit_extlist);
+static struct list_head audit_filter_list[AUDIT_NR_FILTERS] = {
+ LIST_HEAD_INIT(audit_filter_list[0]),
+ LIST_HEAD_INIT(audit_filter_list[1]),
+ LIST_HEAD_INIT(audit_filter_list[2]),
+ LIST_HEAD_INIT(audit_filter_list[3]),
+ LIST_HEAD_INIT(audit_filter_list[4]),
+#if AUDIT_NR_FILTERS != 5
+#error Fix audit_filter_list initialiser
+#endif
+};
struct audit_entry {
struct list_head list;
/* Note that audit_add_rule and audit_del_rule are called via
* audit_receive() in audit.c, and are protected by
* audit_netlink_sem. */
-static inline int audit_add_rule(struct audit_entry *entry,
- struct list_head *list)
+static inline void audit_add_rule(struct audit_entry *entry,
+ struct list_head *list)
{
- if (entry->rule.flags & AUDIT_PREPEND) {
- entry->rule.flags &= ~AUDIT_PREPEND;
+ if (entry->rule.flags & AUDIT_FILTER_PREPEND) {
+ entry->rule.flags &= ~AUDIT_FILTER_PREPEND;
list_add_rcu(&entry->list, list);
} else {
list_add_tail_rcu(&entry->list, list);
}
- return 0;
}
static void audit_free_rule(struct rcu_head *head)
return 0;
}
}
- return -EFAULT; /* No matching rule */
+ return -ENOENT; /* No matching rule */
}
/* Copy rule from user-space to kernel-space. Called during
return -1;
if (s->field_count < 0 || s->field_count > AUDIT_MAX_FIELDS)
return -1;
+ if ((s->flags & ~AUDIT_FILTER_PREPEND) >= AUDIT_NR_FILTERS)
+ return -1;
d->flags = s->flags;
d->action = s->action;
return 0;
}
+static int audit_list_rules(void *_dest)
+{
+ int pid, seq;
+ int *dest = _dest;
+ struct audit_entry *entry;
+ int i;
+
+ pid = dest[0];
+ seq = dest[1];
+ kfree(dest);
+
+ down(&audit_netlink_sem);
+
+ /* The *_rcu iterators not needed here because we are
+ always called with audit_netlink_sem held. */
+ for (i=0; i<AUDIT_NR_FILTERS; i++) {
+ list_for_each_entry(entry, &audit_filter_list[i], list)
+ audit_send_reply(pid, seq, AUDIT_LIST, 0, 1,
+ &entry->rule, sizeof(entry->rule));
+ }
+ audit_send_reply(pid, seq, AUDIT_LIST, 1, 1, NULL, 0);
+
+ up(&audit_netlink_sem);
+ return 0;
+}
+
int audit_receive_filter(int type, int pid, int uid, int seq, void *data,
uid_t loginuid)
{
- u32 flags;
struct audit_entry *entry;
+ struct task_struct *tsk;
+ int *dest;
int err = 0;
+ unsigned listnr;
switch (type) {
case AUDIT_LIST:
- /* The *_rcu iterators not needed here because we are
- always called with audit_netlink_sem held. */
- list_for_each_entry(entry, &audit_tsklist, list)
- audit_send_reply(pid, seq, AUDIT_LIST, 0, 1,
- &entry->rule, sizeof(entry->rule));
- list_for_each_entry(entry, &audit_entlist, list)
- audit_send_reply(pid, seq, AUDIT_LIST, 0, 1,
- &entry->rule, sizeof(entry->rule));
- list_for_each_entry(entry, &audit_extlist, list)
- audit_send_reply(pid, seq, AUDIT_LIST, 0, 1,
- &entry->rule, sizeof(entry->rule));
- audit_send_reply(pid, seq, AUDIT_LIST, 1, 1, NULL, 0);
+ /* We can't just spew out the rules here because we might fill
+ * the available socket buffer space and deadlock waiting for
+ * auditctl to read from it... which isn't ever going to
+ * happen if we're actually running in the context of auditctl
+ * trying to _send_ the stuff */
+
+ dest = kmalloc(2 * sizeof(int), GFP_KERNEL);
+ if (!dest)
+ return -ENOMEM;
+ dest[0] = pid;
+ dest[1] = seq;
+
+ tsk = kthread_run(audit_list_rules, dest, "audit_list_rules");
+ if (IS_ERR(tsk)) {
+ kfree(dest);
+ err = PTR_ERR(tsk);
+ }
break;
case AUDIT_ADD:
if (!(entry = kmalloc(sizeof(*entry), GFP_KERNEL)))
kfree(entry);
return -EINVAL;
}
- flags = entry->rule.flags;
- if (!err && (flags & AUDIT_PER_TASK))
- err = audit_add_rule(entry, &audit_tsklist);
- if (!err && (flags & AUDIT_AT_ENTRY))
- err = audit_add_rule(entry, &audit_entlist);
- if (!err && (flags & AUDIT_AT_EXIT))
- err = audit_add_rule(entry, &audit_extlist);
- audit_log(NULL, AUDIT_CONFIG_CHANGE,
- "auid %u added an audit rule\n", loginuid);
+ listnr = entry->rule.flags & ~AUDIT_FILTER_PREPEND;
+ audit_add_rule(entry, &audit_filter_list[listnr]);
+ audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE,
+ "auid=%u added an audit rule\n", loginuid);
break;
case AUDIT_DEL:
- flags =((struct audit_rule *)data)->flags;
- if (!err && (flags & AUDIT_PER_TASK))
- err = audit_del_rule(data, &audit_tsklist);
- if (!err && (flags & AUDIT_AT_ENTRY))
- err = audit_del_rule(data, &audit_entlist);
- if (!err && (flags & AUDIT_AT_EXIT))
- err = audit_del_rule(data, &audit_extlist);
- audit_log(NULL, AUDIT_CONFIG_CHANGE,
- "auid %u removed an audit rule\n", loginuid);
+ listnr =((struct audit_rule *)data)->flags & ~AUDIT_FILTER_PREPEND;
+ if (listnr >= AUDIT_NR_FILTERS)
+ return -EINVAL;
+
+ err = audit_del_rule(data, &audit_filter_list[listnr]);
+ if (!err)
+ audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE,
+ "auid=%u removed an audit rule\n", loginuid);
break;
default:
return -EINVAL;
enum audit_state state;
rcu_read_lock();
- list_for_each_entry_rcu(e, &audit_tsklist, list) {
+ list_for_each_entry_rcu(e, &audit_filter_list[AUDIT_FILTER_TASK], list) {
if (audit_filter_rules(tsk, &e->rule, NULL, &state)) {
rcu_read_unlock();
return state;
int word = AUDIT_WORD(ctx->major);
int bit = AUDIT_BIT(ctx->major);
+ if (audit_pid && ctx->pid == audit_pid)
+ return AUDIT_DISABLED;
+
rcu_read_lock();
list_for_each_entry_rcu(e, list, list) {
if ((e->rule.mask[word] & bit) == bit
return AUDIT_BUILD_CONTEXT;
}
+int audit_filter_user(int pid, int type)
+{
+ struct task_struct *tsk;
+ struct audit_entry *e;
+ enum audit_state state;
+ int ret = 1;
+
+ read_lock(&tasklist_lock);
+ tsk = find_task_by_pid(pid);
+ if (tsk)
+ get_task_struct(tsk);
+ read_unlock(&tasklist_lock);
+
+ if (!tsk)
+ return -ESRCH;
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(e, &audit_filter_list[AUDIT_FILTER_USER], list) {
+ if (audit_filter_rules(tsk, &e->rule, NULL, &state)) {
+ if (state == AUDIT_DISABLED)
+ ret = 0;
+ break;
+ }
+ }
+ rcu_read_unlock();
+ put_task_struct(tsk);
+
+ return ret; /* Audit by default */
+
+}
+
/* This should be called with task_lock() held. */
static inline struct audit_context *audit_get_context(struct task_struct *tsk,
int return_valid,
context->return_valid = return_valid;
context->return_code = return_code;
- if (context->in_syscall && !context->auditable) {
+ if (context->in_syscall && !context->auditable && tsk->pid != audit_pid) {
enum audit_state state;
- state = audit_filter_syscall(tsk, context, &audit_extlist);
+ state = audit_filter_syscall(tsk, context, &audit_filter_list[AUDIT_FILTER_EXIT]);
if (state == AUDIT_RECORD_CONTEXT)
context->auditable = 1;
}
if (context->names[i].name)
__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;
}
static inline void audit_free_aux(struct audit_context *context)
struct vm_area_struct *vma;
get_task_comm(name, current);
- audit_log_format(ab, " comm=%s", name);
+ audit_log_format(ab, " comm=");
+ audit_log_untrustedstring(ab, name);
if (!mm)
return;
{
int i;
struct audit_buffer *ab;
+ struct audit_aux_data *aux;
- ab = audit_log_start(context, AUDIT_SYSCALL);
+ ab = audit_log_start(context, GFP_KERNEL, AUDIT_SYSCALL);
if (!ab)
return; /* audit_panic has been called */
- audit_log_format(ab, "syscall=%d", context->major);
+ audit_log_format(ab, "arch=%x syscall=%d",
+ context->arch, context->major);
if (context->personality != PER_LINUX)
audit_log_format(ab, " per=%lx", context->personality);
- audit_log_format(ab, " arch=%x", context->arch);
if (context->return_valid)
audit_log_format(ab, " success=%s exit=%ld",
(context->return_valid==AUDITSC_SUCCESS)?"yes":"no",
context->egid, context->sgid, context->fsgid);
audit_log_task_info(ab);
audit_log_end(ab);
- while (context->aux) {
- struct audit_aux_data *aux;
- aux = context->aux;
+ for (aux = context->aux; aux; aux = aux->next) {
- ab = audit_log_start(context, aux->type);
+ ab = audit_log_start(context, GFP_KERNEL, aux->type);
if (!ab)
continue; /* audit_panic has been called */
case AUDIT_AVC_PATH: {
struct audit_aux_data_path *axi = (void *)aux;
audit_log_d_path(ab, "path=", axi->dentry, axi->mnt);
- dput(axi->dentry);
- mntput(axi->mnt);
break; }
}
audit_log_end(ab);
-
- context->aux = aux->next;
- kfree(aux);
}
+ if (context->pwd && context->pwdmnt) {
+ ab = audit_log_start(context, GFP_KERNEL, AUDIT_CWD);
+ if (ab) {
+ audit_log_d_path(ab, "cwd=", context->pwd, context->pwdmnt);
+ audit_log_end(ab);
+ }
+ }
for (i = 0; i < context->name_count; i++) {
- ab = audit_log_start(context, AUDIT_PATH);
+ ab = audit_log_start(context, GFP_KERNEL, AUDIT_PATH);
if (!ab)
continue; /* audit_panic has been called */
+
audit_log_format(ab, "item=%d", i);
if (context->names[i].name) {
audit_log_format(ab, " name=");
audit_log_untrustedstring(ab, context->names[i].name);
}
+ audit_log_format(ab, " flags=%x\n", context->names[i].flags);
+
if (context->names[i].ino != (unsigned long)-1)
audit_log_format(ab, " inode=%lu dev=%02x:%02x mode=%#o"
" ouid=%u ogid=%u rdev=%02x:%02x",
/* Check for system calls that do not go through the exit
* function (e.g., exit_group), then free context block. */
- if (context->in_syscall && context->auditable && context->pid != audit_pid)
+ if (context->in_syscall && context->auditable)
audit_log_exit(context);
audit_free_context(context);
state = context->state;
if (state == AUDIT_SETUP_CONTEXT || state == AUDIT_BUILD_CONTEXT)
- state = audit_filter_syscall(tsk, context, &audit_entlist);
+ state = audit_filter_syscall(tsk, context, &audit_filter_list[AUDIT_FILTER_ENTRY]);
if (likely(state == AUDIT_DISABLED))
return;
if (likely(!context))
return;
- if (context->in_syscall && context->auditable && context->pid != audit_pid)
+ if (context->in_syscall && context->auditable)
audit_log_exit(context);
context->in_syscall = 0;
context->names[context->name_count].name = name;
context->names[context->name_count].ino = (unsigned long)-1;
++context->name_count;
+ if (!context->pwd) {
+ read_lock(¤t->fs->lock);
+ context->pwd = dget(current->fs->pwd);
+ context->pwdmnt = mntget(current->fs->pwdmnt);
+ read_unlock(¤t->fs->lock);
+ }
+
}
/* Intercept a putname request. Called from
/* Store the inode and device from a lookup. 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 inode *inode, unsigned flags)
{
int idx;
struct audit_context *context = current->audit_context;
++context->ino_count;
#endif
}
- context->names[idx].ino = inode->i_ino;
- context->names[idx].dev = inode->i_sb->s_dev;
- context->names[idx].mode = inode->i_mode;
- context->names[idx].uid = inode->i_uid;
- context->names[idx].gid = inode->i_gid;
- context->names[idx].rdev = inode->i_rdev;
+ context->names[idx].flags = flags;
+ context->names[idx].ino = inode->i_ino;
+ context->names[idx].dev = inode->i_sb->s_dev;
+ context->names[idx].mode = inode->i_mode;
+ context->names[idx].uid = inode->i_uid;
+ context->names[idx].gid = inode->i_gid;
+ context->names[idx].rdev = inode->i_rdev;
}
void auditsc_get_stamp(struct audit_context *ctx,
if (task->audit_context) {
struct audit_buffer *ab;
- ab = audit_log_start(NULL, AUDIT_LOGIN);
+ 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",