AUDIT: Avoid sleeping function in SElinux AVC audit.
[linux-2.6.git] / kernel / auditsc.c
index 680bb92..8dc5b27 100644 (file)
@@ -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 {
@@ -157,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)
@@ -286,7 +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, "auid %u added an audit rule\n", loginuid);
+               audit_log(NULL, AUDIT_CONFIG_CHANGE, 
+                               "auid %u added an audit rule\n", loginuid);
                break;
        case AUDIT_DEL:
                flags =((struct audit_rule *)data)->flags;
@@ -296,7 +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, "auid %u removed an audit rule\n", loginuid);
+               audit_log(NULL, AUDIT_CONFIG_CHANGE,
+                               "auid %u removed an audit rule\n", loginuid);
                break;
        default:
                return -EINVAL;
@@ -442,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,
@@ -537,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);
        }
@@ -648,7 +675,7 @@ 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);
@@ -680,28 +707,51 @@ static void audit_log_exit(struct audit_context *context)
        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);
@@ -711,7 +761,7 @@ static void audit_log_exit(struct audit_context *context)
                }
                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),
@@ -739,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);
@@ -748,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.
@@ -874,7 +924,7 @@ void audit_syscall_exit(struct task_struct *tsk, int valid, long 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;
@@ -992,7 +1042,7 @@ 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,
+int audit_get_stamp(struct audit_context *ctx,
                     struct timespec *t, unsigned int *serial)
 {
        if (ctx) {
@@ -1000,26 +1050,22 @@ void audit_get_stamp(struct audit_context *ctx,
                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 task_struct *task, uid_t loginuid)
 {
        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",
                                task->pid, task->uid, 
                                task->audit_context->loginuid, loginuid);
-                       audit_set_type(ab, AUDIT_LOGIN);
                        audit_log_end(ab);
                }
                task->audit_context->loginuid = loginuid;
@@ -1049,7 +1095,70 @@ 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;
@@ -1059,7 +1168,6 @@ void audit_signal_info(int sig, struct task_struct *t)
 {
        extern pid_t audit_sig_pid;
        extern uid_t audit_sig_uid;
-       extern int audit_pid;
 
        if (unlikely(audit_pid && t->pid == audit_pid)) {
                if (sig == SIGTERM || sig == SIGHUP) {