-/* auditsc.c -- System-call auditing support -*- linux-c -*-
+/* auditsc.c -- System-call auditing support
* Handles all system-call specific auditing features.
*
* Copyright 2003-2004 Red Hat Inc., Durham, North Carolina.
#include <asm/types.h>
#include <linux/mm.h>
#include <linux/module.h>
-
+#include <linux/mount.h>
+#include <linux/socket.h>
#include <linux/audit.h>
#include <linux/personality.h>
#include <linux/time.h>
mode_t mode;
};
+struct audit_aux_data_socketcall {
+ struct audit_aux_data d;
+ int nargs;
+ unsigned long args[0];
+};
+
+struct audit_aux_data_sockaddr {
+ struct audit_aux_data d;
+ int len;
+ char a[0];
+};
+
+struct audit_aux_data_path {
+ struct audit_aux_data d;
+ struct dentry *dentry;
+ struct vfsmount *mnt;
+};
/* The per-task audit context. */
struct audit_context {
int major; /* syscall number */
unsigned long argv[4]; /* syscall arguments */
int return_valid; /* return code is valid */
- int return_code;/* syscall return code */
+ long return_code;/* syscall return code */
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;
uid_t uid, euid, suid, fsuid;
gid_t gid, egid, sgid, fsgid;
unsigned long personality;
+ int arch;
#if AUDIT_DEBUG
int put_count;
struct audit_rule rule;
};
+extern int audit_pid;
+
/* Check to see if two rules are identical. It is called from
* audit_del_rule during AUDIT_DEL. */
static int audit_compare_rule(struct audit_rule *a, struct audit_rule *b)
return -EFAULT; /* No matching rule */
}
-#ifdef CONFIG_NET
/* Copy rule from user-space to kernel-space. Called during
* AUDIT_ADD. */
static int audit_copy_rule(struct audit_rule *d, struct audit_rule *s)
return 0;
}
-int audit_receive_filter(int type, int pid, int uid, int seq, void *data)
+int audit_receive_filter(int type, int pid, int uid, int seq, void *data,
+ uid_t loginuid)
{
u32 flags;
struct audit_entry *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);
break;
case AUDIT_DEL:
flags =((struct audit_rule *)data)->flags;
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);
break;
default:
return -EINVAL;
return err;
}
-#endif
/* Compare a task_struct with an audit_rule. Return 1 on match, 0
* otherwise. */
case AUDIT_PERS:
result = (tsk->personality == value);
break;
+ case AUDIT_ARCH:
+ if (ctx)
+ result = (ctx->arch == value);
+ break;
case AUDIT_EXIT:
if (ctx && ctx->return_valid)
break;
case AUDIT_SUCCESS:
if (ctx && ctx->return_valid)
- result = (ctx->return_code >= 0);
+ result = (ctx->return_valid == AUDITSC_SUCCESS);
break;
case AUDIT_DEVMAJOR:
if (ctx) {
/* At syscall entry and exit time, this filter is called if the
* audit_state is not low enough that auditing cannot take place, but is
- * also not high enough that we already know we have to write and audit
+ * also not high enough that we already know we have to write an audit
* record (i.e., the state is AUDIT_SETUP_CONTEXT or AUDIT_BUILD_CONTEXT).
*/
static enum audit_state audit_filter_syscall(struct task_struct *tsk,
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 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);
}
printk(KERN_ERR "audit: freed %d contexts\n", count);
}
+static void audit_log_task_info(struct audit_buffer *ab)
+{
+ char name[sizeof(current->comm)];
+ struct mm_struct *mm = current->mm;
+ struct vm_area_struct *vma;
+
+ get_task_comm(name, current);
+ audit_log_format(ab, " comm=");
+ audit_log_untrustedstring(ab, name);
+
+ if (!mm)
+ return;
+
+ down_read(&mm->mmap_sem);
+ vma = mm->mmap;
+ while (vma) {
+ if ((vma->vm_flags & VM_EXECUTABLE) &&
+ vma->vm_file) {
+ audit_log_d_path(ab, "exe=",
+ vma->vm_file->f_dentry,
+ vma->vm_file->f_vfsmnt);
+ break;
+ }
+ vma = vma->vm_next;
+ }
+ up_read(&mm->mmap_sem);
+}
+
static void audit_log_exit(struct audit_context *context)
{
int i;
struct audit_buffer *ab;
+ struct audit_aux_data *aux;
- ab = audit_log_start(context);
+ ab = audit_log_start(context, 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);
if (context->return_valid)
- audit_log_format(ab, " exit=%d", context->return_code);
+ audit_log_format(ab, " success=%s exit=%ld",
+ (context->return_valid==AUDITSC_SUCCESS)?"yes":"no",
+ context->return_code);
audit_log_format(ab,
" a0=%lx a1=%lx a2=%lx a3=%lx items=%d"
- " pid=%d loginuid=%d uid=%d gid=%d"
- " euid=%d suid=%d fsuid=%d"
- " egid=%d sgid=%d fsgid=%d",
+ " pid=%d auid=%u uid=%u gid=%u"
+ " euid=%u suid=%u fsuid=%u"
+ " egid=%u sgid=%u fsgid=%u",
context->argv[0],
context->argv[1],
context->argv[2],
context->gid,
context->euid, context->suid, context->fsuid,
context->egid, context->sgid, context->fsgid);
+ audit_log_task_info(ab);
audit_log_end(ab);
- while (context->aux) {
- struct audit_aux_data *aux;
- ab = audit_log_start(context);
+ for (aux = context->aux; aux; aux = aux->next) {
+
+ ab = audit_log_start(context, aux->type);
if (!ab)
continue; /* audit_panic has been called */
- aux = context->aux;
- context->aux = aux->next;
-
- audit_log_format(ab, "auxitem=%d", aux->type);
switch (aux->type) {
- case AUDIT_AUX_IPCPERM: {
+ case AUDIT_IPC: {
struct audit_aux_data_ipcctl *axi = (void *)aux;
audit_log_format(ab,
- " qbytes=%lx uid=%d gid=%d mode=%x",
+ " qbytes=%lx iuid=%u igid=%u mode=%x",
axi->qbytes, axi->uid, axi->gid, axi->mode);
- }
+ 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; i<axs->nargs; i++)
+ audit_log_format(ab, " a%d=%lx", i, axs->args[i]);
+ break; }
+
+ case AUDIT_SOCKADDR: {
+ 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);
+ break; }
+
}
audit_log_end(ab);
- kfree(aux);
}
+ if (context->pwd && context->pwdmnt) {
+ ab = audit_log_start(context, 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);
+ ab = audit_log_start(context, 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=%s",
- context->names[i].name);
+ if (context->names[i].name) {
+ audit_log_format(ab, " name=");
+ audit_log_untrustedstring(ab, context->names[i].name);
+ }
if (context->names[i].ino != (unsigned long)-1)
audit_log_format(ab, " inode=%lu dev=%02x:%02x mode=%#o"
- " uid=%d gid=%d rdev=%02x:%02x",
+ " ouid=%u ogid=%u rdev=%02x:%02x",
context->names[i].ino,
MAJOR(context->names[i].dev),
MINOR(context->names[i].dev),
/* 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)
+ if (context->in_syscall && context->auditable && context->pid != audit_pid)
audit_log_exit(context);
audit_free_context(context);
}
-/* Compute a serial number for the audit record. Audit records are
- * written to user-space as soon as they are generated, so a complete
- * audit record may be written in several pieces. The timestamp of the
- * record and this serial number are used by the user-space daemon to
- * determine which pieces belong to the same audit record. The
- * (timestamp,serial) tuple is unique for each syscall and is live from
- * syscall entry to syscall exit.
- *
- * Atomic values are only guaranteed to be 24-bit, so we count down.
- *
- * NOTE: Another possibility is to store the formatted records off the
- * audit context (for those records that have a context), and emit them
- * all at syscall exit. However, this could delay the reporting of
- * significant errors until syscall exit (or never, if the system
- * halts). */
-static inline unsigned int audit_serial(void)
-{
- static atomic_t serial = ATOMIC_INIT(0xffffff);
- unsigned int a, b;
-
- do {
- a = atomic_read(&serial);
- if (atomic_dec_and_test(&serial))
- atomic_set(&serial, 0xffffff);
- b = atomic_read(&serial);
- } while (b != a - 1);
-
- return 0xffffff - b;
-}
-
/* Fill in audit context at syscall entry. This only happens if the
* audit context was created when the task was created and the state or
* filters demand the audit context be built. If the state from the
* then the record will be written at syscall exit time (otherwise, it
* will only be written if another part of the kernel requests that it
* be written). */
-void audit_syscall_entry(struct task_struct *tsk, int major,
+void audit_syscall_entry(struct task_struct *tsk, int arch, int major,
unsigned long a1, unsigned long a2,
unsigned long a3, unsigned long a4)
{
if (!audit_enabled)
return;
+ context->arch = arch;
context->major = major;
context->argv[0] = a1;
context->argv[1] = a2;
* 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(struct task_struct *tsk, int return_code)
+void audit_syscall_exit(struct task_struct *tsk, int valid, long return_code)
{
struct audit_context *context;
get_task_struct(tsk);
task_lock(tsk);
- context = audit_get_context(tsk, 1, return_code);
+ context = audit_get_context(tsk, valid, return_code);
task_unlock(tsk);
/* Not having a context here is ok, since the parent may have
if (likely(!context))
return;
- if (context->in_syscall && context->auditable)
+ if (context->in_syscall && context->auditable && context->pid != audit_pid)
audit_log_exit(context);
context->in_syscall = 0;
context->auditable = 0;
+
if (context->previous) {
struct audit_context *new_context = context->previous;
context->previous = NULL;
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
context->names[idx].rdev = inode->i_rdev;
}
-void audit_get_stamp(struct audit_context *ctx,
- struct timespec *t, int *serial)
+void auditsc_get_stamp(struct audit_context *ctx,
+ struct timespec *t, unsigned int *serial)
{
- if (ctx) {
- t->tv_sec = ctx->ctime.tv_sec;
- t->tv_nsec = ctx->ctime.tv_nsec;
- *serial = ctx->serial;
- ctx->auditable = 1;
- } else {
- *t = CURRENT_TIME;
- *serial = 0;
- }
+ t->tv_sec = ctx->ctime.tv_sec;
+ t->tv_nsec = ctx->ctime.tv_nsec;
+ *serial = ctx->serial;
+ ctx->auditable = 1;
}
-extern int audit_set_type(struct audit_buffer *ab, int type);
-
-int audit_set_loginuid(struct audit_context *ctx, uid_t loginuid)
+int audit_set_loginuid(struct task_struct *task, uid_t loginuid)
{
- if (ctx) {
+ if (task->audit_context) {
struct audit_buffer *ab;
- ab = audit_log_start(NULL);
+ ab = audit_log_start(NULL, AUDIT_LOGIN);
if (ab) {
audit_log_format(ab, "login pid=%d uid=%u "
- "old loginuid=%u new loginuid=%u",
- ctx->pid, ctx->uid, ctx->loginuid, loginuid);
- audit_set_type(ab, AUDIT_LOGIN);
+ "old auid=%u new auid=%u",
+ task->pid, task->uid,
+ task->audit_context->loginuid, loginuid);
audit_log_end(ab);
}
- ctx->loginuid = loginuid;
+ task->audit_context->loginuid = loginuid;
}
return 0;
}
ax->gid = gid;
ax->mode = mode;
- ax->d.type = AUDIT_AUX_IPCPERM;
+ ax->d.type = AUDIT_IPC;
+ ax->d.next = context->aux;
+ context->aux = (void *)ax;
+ return 0;
+}
+
+int audit_socketcall(int nargs, unsigned long *args)
+{
+ struct audit_aux_data_socketcall *ax;
+ struct audit_context *context = current->audit_context;
+
+ if (likely(!context))
+ return 0;
+
+ ax = kmalloc(sizeof(*ax) + nargs * sizeof(unsigned long), GFP_KERNEL);
+ if (!ax)
+ return -ENOMEM;
+
+ ax->nargs = nargs;
+ memcpy(ax->args, args, nargs * sizeof(unsigned long));
+
+ ax->d.type = AUDIT_SOCKETCALL;
+ ax->d.next = context->aux;
+ context->aux = (void *)ax;
+ return 0;
+}
+
+int audit_sockaddr(int len, void *a)
+{
+ struct audit_aux_data_sockaddr *ax;
+ struct audit_context *context = current->audit_context;
+
+ if (likely(!context))
+ return 0;
+
+ ax = kmalloc(sizeof(*ax) + len, GFP_KERNEL);
+ if (!ax)
+ return -ENOMEM;
+
+ ax->len = len;
+ memcpy(ax->a, a, len);
+
+ ax->d.type = AUDIT_SOCKADDR;
ax->d.next = context->aux;
context->aux = (void *)ax;
return 0;
}
+
+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;
+}
+
+void audit_signal_info(int sig, struct task_struct *t)
+{
+ extern pid_t audit_sig_pid;
+ extern uid_t audit_sig_uid;
+
+ if (unlikely(audit_pid && t->pid == audit_pid)) {
+ if (sig == SIGTERM || sig == SIGHUP) {
+ struct audit_context *ctx = current->audit_context;
+ audit_sig_pid = current->pid;
+ if (ctx)
+ audit_sig_uid = ctx->loginuid;
+ else
+ audit_sig_uid = current->uid;
+ }
+ }
+}
+