AUDIT: Avoid sleeping function in SElinux AVC audit.
[linux-2.6.git] / kernel / auditsc.c
index 8c45485..8dc5b27 100644 (file)
@@ -1,4 +1,4 @@
-/* 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.
@@ -34,7 +34,8 @@
 #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>
@@ -112,6 +113,23 @@ struct audit_aux_data_ipcctl {
        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 {
@@ -123,7 +141,7 @@ 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];
@@ -135,6 +153,7 @@ struct audit_context {
        uid_t               uid, euid, suid, fsuid;
        gid_t               gid, egid, sgid, fsgid;
        unsigned long       personality;
+       int                 arch;
 
 #if AUDIT_DEBUG
        int                 put_count;
@@ -156,6 +175,8 @@ struct audit_entry {
        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)
@@ -225,7 +246,6 @@ static inline int audit_del_rule(struct audit_rule *rule,
        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)
@@ -250,7 +270,8 @@ 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;
@@ -285,6 +306,8 @@ int audit_receive_filter(int type, int pid, int uid, int seq, void *data)
                        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;
@@ -294,6 +317,8 @@ int audit_receive_filter(int type, int pid, int uid, int seq, void *data)
                        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;
@@ -301,7 +326,6 @@ int audit_receive_filter(int type, int pid, int uid, int seq, void *data)
 
        return err;
 }
-#endif
 
 /* Compare a task_struct with an audit_rule.  Return 1 on match, 0
  * otherwise. */
@@ -348,6 +372,10 @@ static int audit_filter_rules(struct task_struct *tsk,
                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)
@@ -355,7 +383,7 @@ static int audit_filter_rules(struct task_struct *tsk,
                        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) {
@@ -436,7 +464,7 @@ static enum audit_state audit_filter_task(struct task_struct *tsk)
 
 /* 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,
@@ -531,6 +559,11 @@ 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);
        }
@@ -610,19 +643,49 @@ static inline void audit_free_context(struct audit_context *context)
                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=%s", 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;
 
-       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);
        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, " 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"
@@ -639,41 +702,66 @@ static void audit_log_exit(struct audit_context *context)
                  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);
+               aux = context->aux;
+
+               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=%d igid=%d 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);
+                       dput(axi->dentry);
+                       mntput(axi->mnt);
+                       break; }
+
                }
                audit_log_end(ab);
+
+               context->aux = aux->next;
                kfree(aux);
        }
 
        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=%d ogid=%d rdev=%02x:%02x",
                                         context->names[i].ino,
                                         MAJOR(context->names[i].dev),
                                         MINOR(context->names[i].dev),
@@ -701,7 +789,7 @@ void audit_free(struct task_struct *tsk)
 
        /* 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);
@@ -710,7 +798,7 @@ void audit_free(struct task_struct *tsk)
 /* 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
+ * record and this serial number are used by the user-space tools 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.
@@ -744,7 +832,7 @@ static inline unsigned int audit_serial(void)
  * 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)
 {
@@ -798,6 +886,7 @@ void audit_syscall_entry(struct task_struct *tsk, int major,
        if (!audit_enabled)
                return;
 
+       context->arch       = arch;
        context->major      = major;
        context->argv[0]    = a1;
        context->argv[1]    = a2;
@@ -821,13 +910,13 @@ void audit_syscall_entry(struct task_struct *tsk, int major,
  * 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
@@ -835,11 +924,12 @@ void audit_syscall_exit(struct task_struct *tsk, int return_code)
        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;
@@ -952,36 +1042,33 @@ void audit_inode(const char *name, const struct inode *inode)
        context->names[idx].rdev = inode->i_rdev;
 }
 
-void audit_get_stamp(struct audit_context *ctx,
-                    struct timespec *t, int *serial)
+int audit_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;
+               return 1;
        }
+       return 0;
 }
 
-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);
+                               task->pid, task->uid, 
+                               task->audit_context->loginuid, loginuid);
                        audit_log_end(ab);
                }
-               ctx->loginuid = loginuid;
+               task->audit_context->loginuid = loginuid;
        }
        return 0;
 }
@@ -1008,8 +1095,89 @@ int audit_ipc_perms(unsigned long qbytes, uid_t uid, gid_t gid, mode_t mode)
        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;
+               }
+       }
+}
+