vserver 2.0 rc7
[linux-2.6.git] / kernel / auditsc.c
index 3435b71..37b3ac9 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.
@@ -89,9 +89,30 @@ enum audit_state {
 struct audit_names {
        const char      *name;
        unsigned long   ino;
+       dev_t           dev;
+       umode_t         mode;
+       uid_t           uid;
+       gid_t           gid;
        dev_t           rdev;
 };
 
+struct audit_aux_data {
+       struct audit_aux_data   *next;
+       int                     type;
+};
+
+#define AUDIT_AUX_IPCPERM      0
+
+struct audit_aux_data_ipcctl {
+       struct audit_aux_data   d;
+       struct ipc_perm         p;
+       unsigned long           qbytes;
+       uid_t                   uid;
+       gid_t                   gid;
+       mode_t                  mode;
+};
+
+
 /* The per-task audit context. */
 struct audit_context {
        int                 in_syscall; /* 1 if task is in a syscall */
@@ -102,17 +123,19 @@ 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 audit_context *previous; /* For nested syscalls */
+       struct audit_aux_data *aux;
 
                                /* Save things to print about task_struct */
        pid_t               pid;
        uid_t               uid, euid, suid, fsuid;
        gid_t               gid, egid, sgid, fsgid;
        unsigned long       personality;
+       int                 arch;
 
 #if AUDIT_DEBUG
        int                 put_count;
@@ -228,7 +251,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;
@@ -263,6 +287,7 @@ 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);
                break;
        case AUDIT_DEL:
                flags =((struct audit_rule *)data)->flags;
@@ -272,6 +297,7 @@ 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);
                break;
        default:
                return -EINVAL;
@@ -326,6 +352,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)
@@ -333,12 +363,12 @@ 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) {
                                for (j = 0; j < ctx->name_count; j++) {
-                                       if (MAJOR(ctx->names[j].rdev)==value) {
+                                       if (MAJOR(ctx->names[j].dev)==value) {
                                                ++result;
                                                break;
                                        }
@@ -348,7 +378,7 @@ static int audit_filter_rules(struct task_struct *tsk,
                case AUDIT_DEVMINOR:
                        if (ctx) {
                                for (j = 0; j < ctx->name_count; j++) {
-                                       if (MINOR(ctx->names[j].rdev)==value) {
+                                       if (MINOR(ctx->names[j].dev)==value) {
                                                ++result;
                                                break;
                                        }
@@ -504,6 +534,16 @@ static inline void audit_free_names(struct audit_context *context)
        context->name_count = 0;
 }
 
+static inline void audit_free_aux(struct audit_context *context)
+{
+       struct audit_aux_data *aux;
+
+       while ((aux = context->aux)) {
+               context->aux = aux->next;
+               kfree(aux);
+       }
+}
+
 static inline void audit_zero_context(struct audit_context *context,
                                      enum audit_state state)
 {
@@ -570,6 +610,7 @@ static inline void audit_free_context(struct audit_context *context)
                               context->name_count, count);
                }
                audit_free_names(context);
+               audit_free_aux(context);
                kfree(context);
                context  = previous;
        } while (context);
@@ -577,6 +618,33 @@ 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;
@@ -588,8 +656,11 @@ static void audit_log_exit(struct audit_context *context)
        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"
@@ -606,22 +677,49 @@ 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);
+               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: {
+                       struct audit_aux_data_ipcctl *axi = (void *)aux;
+                       audit_log_format(ab, 
+                                        " qbytes=%lx uid=%d gid=%d mode=%x",
+                                        axi->qbytes, axi->uid, axi->gid, axi->mode);
+                       }
+               }
+               audit_log_end(ab);
+               kfree(aux);
+       }
+
        for (i = 0; i < context->name_count; i++) {
                ab = audit_log_start(context);
                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",
-                                        context->names[i].ino);
-               /* FIXME: should use format_dev_t, but ab structure is
-                * opaque. */
-               if (context->names[i].rdev != -1)
-                       audit_log_format(ab, " dev=%02x:%02x",
+                       audit_log_format(ab, " inode=%lu dev=%02x:%02x mode=%#o"
+                                            " uid=%d gid=%d rdev=%02x:%02x",
+                                        context->names[i].ino,
+                                        MAJOR(context->names[i].dev),
+                                        MINOR(context->names[i].dev),
+                                        context->names[i].mode,
+                                        context->names[i].uid,
+                                        context->names[i].gid,
                                         MAJOR(context->names[i].rdev),
                                         MINOR(context->names[i].rdev));
                audit_log_end(ab);
@@ -686,7 +784,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)
 {
@@ -740,6 +838,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;
@@ -763,13 +862,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
@@ -782,6 +881,7 @@ void audit_syscall_exit(struct task_struct *tsk, int return_code)
 
        context->in_syscall = 0;
        context->auditable  = 0;
+
        if (context->previous) {
                struct audit_context *new_context = context->previous;
                context->previous  = NULL;
@@ -789,6 +889,7 @@ void audit_syscall_exit(struct task_struct *tsk, int return_code)
                tsk->audit_context = new_context;
        } else {
                audit_free_names(context);
+               audit_free_aux(context);
                audit_zero_context(context, context->state);
                tsk->audit_context = context;
        }
@@ -800,7 +901,9 @@ void audit_getname(const char *name)
 {
        struct audit_context *context = current->audit_context;
 
-       BUG_ON(!context);
+       if (!context || IS_ERR(name) || !name)
+               return;
+
        if (!context->in_syscall) {
 #if AUDIT_DEBUG == 2
                printk(KERN_ERR "%s:%d(:%d): ignoring getname(%p)\n",
@@ -812,7 +915,6 @@ void audit_getname(const char *name)
        BUG_ON(context->name_count >= AUDIT_NAMES);
        context->names[context->name_count].name = name;
        context->names[context->name_count].ino  = (unsigned long)-1;
-       context->names[context->name_count].rdev = -1;
        ++context->name_count;
 }
 
@@ -855,11 +957,10 @@ void audit_putname(const char *name)
        }
 #endif
 }
-EXPORT_SYMBOL(audit_putname);
 
 /* Store the inode and device from a lookup.  Called from
  * fs/namei.c:path_lookup(). */
-void audit_inode(const char *name, unsigned long ino, dev_t rdev)
+void audit_inode(const char *name, const struct inode *inode)
 {
        int idx;
        struct audit_context *context = current->audit_context;
@@ -885,12 +986,16 @@ void audit_inode(const char *name, unsigned long ino, dev_t rdev)
                ++context->ino_count;
 #endif
        }
-       context->names[idx].ino  = ino;
-       context->names[idx].rdev = rdev;
+       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 audit_get_stamp(struct audit_context *ctx,
-                    struct timespec *t, int *serial)
+                    struct timespec *t, unsigned int *serial)
 {
        if (ctx) {
                t->tv_sec  = ctx->ctime.tv_sec;
@@ -905,20 +1010,21 @@ void audit_get_stamp(struct audit_context *ctx,
 
 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);
                if (ab) {
                        audit_log_format(ab, "login pid=%d uid=%u "
                                "old loginuid=%u new loginuid=%u",
-                               ctx->pid, ctx->uid, ctx->loginuid, loginuid);
+                               task->pid, task->uid, 
+                               task->audit_context->loginuid, loginuid);
                        audit_set_type(ab, AUDIT_LOGIN);
                        audit_log_end(ab);
                }
-               ctx->loginuid = loginuid;
+               task->audit_context->loginuid = loginuid;
        }
        return 0;
 }
@@ -927,3 +1033,26 @@ uid_t audit_get_loginuid(struct audit_context *ctx)
 {
        return ctx ? ctx->loginuid : -1;
 }
+
+int audit_ipc_perms(unsigned long qbytes, uid_t uid, gid_t gid, mode_t mode)
+{
+       struct audit_aux_data_ipcctl *ax;
+       struct audit_context *context = current->audit_context;
+
+       if (likely(!context))
+               return 0;
+
+       ax = kmalloc(sizeof(*ax), GFP_KERNEL);
+       if (!ax)
+               return -ENOMEM;
+
+       ax->qbytes = qbytes;
+       ax->uid = uid;
+       ax->gid = gid;
+       ax->mode = mode;
+
+       ax->d.type = AUDIT_AUX_IPCPERM;
+       ax->d.next = context->aux;
+       context->aux = (void *)ax;
+       return 0;
+}