[PATCH] Audit of POSIX Message Queue Syscalls v.2
[linux-2.6.git] / kernel / auditsc.c
index a300736..14e295a 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copyright 2003-2004 Red Hat Inc., Durham, North Carolina.
  * Copyright 2005 Hewlett-Packard Development Company, L.P.
- * Copyright (C) 2005 IBM Corporation
+ * Copyright (C) 2005, 2006 IBM Corporation
  * All Rights Reserved.
  *
  * This program is free software; you can redistribute it and/or modify
@@ -29,6 +29,9 @@
  * this file -- see entry.S) is based on a GPL'd patch written by
  * okir@suse.de and Copyright 2003 SuSE Linux AG.
  *
+ * POSIX message queue support added by George Wilson <ltcgcw@us.ibm.com>,
+ * 2006.
+ *
  * The support of additional filter rules compares (>, <, >=, <=) was
  * added by Dustin Kirkland <dustin.kirkland@us.ibm.com>, 2005.
  *
@@ -49,6 +52,7 @@
 #include <linux/module.h>
 #include <linux/mount.h>
 #include <linux/socket.h>
+#include <linux/mqueue.h>
 #include <linux/audit.h>
 #include <linux/personality.h>
 #include <linux/time.h>
@@ -59,6 +63,8 @@
 #include <linux/list.h>
 #include <linux/tty.h>
 #include <linux/selinux.h>
+#include <linux/binfmts.h>
+#include <linux/syscalls.h>
 
 #include "audit.h"
 
@@ -100,6 +106,33 @@ struct audit_aux_data {
 
 #define AUDIT_AUX_IPCPERM      0
 
+struct audit_aux_data_mq_open {
+       struct audit_aux_data   d;
+       int                     oflag;
+       mode_t                  mode;
+       struct mq_attr          attr;
+};
+
+struct audit_aux_data_mq_sendrecv {
+       struct audit_aux_data   d;
+       mqd_t                   mqdes;
+       size_t                  msg_len;
+       unsigned int            msg_prio;
+       struct timespec         abs_timeout;
+};
+
+struct audit_aux_data_mq_notify {
+       struct audit_aux_data   d;
+       mqd_t                   mqdes;
+       struct sigevent         notification;
+};
+
+struct audit_aux_data_mq_getsetattr {
+       struct audit_aux_data   d;
+       mqd_t                   mqdes;
+       struct mq_attr          mqstat;
+};
+
 struct audit_aux_data_ipcctl {
        struct audit_aux_data   d;
        struct ipc_perm         p;
@@ -110,6 +143,13 @@ struct audit_aux_data_ipcctl {
        u32                     osid;
 };
 
+struct audit_aux_data_execve {
+       struct audit_aux_data   d;
+       int argc;
+       int envc;
+       char mem[0];
+};
+
 struct audit_aux_data_socketcall {
        struct audit_aux_data   d;
        int                     nargs;
@@ -148,7 +188,7 @@ struct audit_context {
        struct audit_aux_data *aux;
 
                                /* Save things to print about task_struct */
-       pid_t               pid;
+       pid_t               pid, ppid;
        uid_t               uid, euid, suid, fsuid;
        gid_t               gid, egid, sgid, fsgid;
        unsigned long       personality;
@@ -168,11 +208,9 @@ static int audit_filter_rules(struct task_struct *tsk,
                              struct audit_context *ctx,
                              enum audit_state *state)
 {
-       int i, j;
+       int i, j, need_sid = 1;
        u32 sid;
 
-       selinux_task_ctxid(tsk, &sid);
-
        for (i = 0; i < rule->field_count; i++) {
                struct audit_field *f = &rule->fields[i];
                int result = 0;
@@ -181,6 +219,10 @@ static int audit_filter_rules(struct task_struct *tsk,
                case AUDIT_PID:
                        result = audit_comparator(tsk->pid, f->op, f->val);
                        break;
+               case AUDIT_PPID:
+                       if (ctx)
+                               result = audit_comparator(ctx->ppid, f->op, f->val);
+                       break;
                case AUDIT_UID:
                        result = audit_comparator(tsk->uid, f->op, f->val);
                        break;
@@ -271,11 +313,16 @@ static int audit_filter_rules(struct task_struct *tsk,
                           match for now to avoid losing information that
                           may be wanted.   An error message will also be
                           logged upon error */
-                       if (f->se_rule)
+                       if (f->se_rule) {
+                               if (need_sid) {
+                                       selinux_task_ctxid(tsk, &sid);
+                                       need_sid = 0;
+                               }
                                result = selinux_audit_rule_match(sid, f->type,
                                                                  f->op,
                                                                  f->se_rule,
                                                                  ctx);
+                       }
                        break;
                case AUDIT_ARG0:
                case AUDIT_ARG1:
@@ -291,7 +338,6 @@ static int audit_filter_rules(struct task_struct *tsk,
        }
        switch (rule->action) {
        case AUDIT_NEVER:    *state = AUDIT_DISABLED;       break;
-       case AUDIT_POSSIBLE: *state = AUDIT_BUILD_CONTEXT;  break;
        case AUDIT_ALWAYS:   *state = AUDIT_RECORD_CONTEXT; break;
        }
        return 1;
@@ -368,6 +414,7 @@ static inline struct audit_context *audit_get_context(struct task_struct *tsk,
        }
 
        context->pid = tsk->pid;
+       context->ppid = sys_getppid();  /* sic.  tsk == current in all cases */
        context->uid = tsk->uid;
        context->gid = tsk->gid;
        context->euid = tsk->euid;
@@ -603,7 +650,7 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts
                tty = "(none)";
        audit_log_format(ab,
                  " a0=%lx a1=%lx a2=%lx a3=%lx items=%d"
-                 " pid=%d auid=%u uid=%u gid=%u"
+                 " ppid=%d pid=%d auid=%u uid=%u gid=%u"
                  " euid=%u suid=%u fsuid=%u"
                  " egid=%u sgid=%u fsgid=%u tty=%s",
                  context->argv[0],
@@ -611,6 +658,7 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts
                  context->argv[2],
                  context->argv[3],
                  context->name_count,
+                 context->ppid,
                  context->pid,
                  context->loginuid,
                  context->uid,
@@ -627,11 +675,48 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts
                        continue; /* audit_panic has been called */
 
                switch (aux->type) {
+               case AUDIT_MQ_OPEN: {
+                       struct audit_aux_data_mq_open *axi = (void *)aux;
+                       audit_log_format(ab,
+                               "oflag=0x%x mode=%#o mq_flags=0x%lx mq_maxmsg=%ld "
+                               "mq_msgsize=%ld mq_curmsgs=%ld",
+                               axi->oflag, axi->mode, axi->attr.mq_flags,
+                               axi->attr.mq_maxmsg, axi->attr.mq_msgsize,
+                               axi->attr.mq_curmsgs);
+                       break; }
+
+               case AUDIT_MQ_SENDRECV: {
+                       struct audit_aux_data_mq_sendrecv *axi = (void *)aux;
+                       audit_log_format(ab,
+                               "mqdes=%d msg_len=%zd msg_prio=%u "
+                               "abs_timeout_sec=%ld abs_timeout_nsec=%ld",
+                               axi->mqdes, axi->msg_len, axi->msg_prio,
+                               axi->abs_timeout.tv_sec, axi->abs_timeout.tv_nsec);
+                       break; }
+
+               case AUDIT_MQ_NOTIFY: {
+                       struct audit_aux_data_mq_notify *axi = (void *)aux;
+                       audit_log_format(ab,
+                               "mqdes=%d sigev_signo=%d",
+                               axi->mqdes,
+                               axi->notification.sigev_signo);
+                       break; }
+
+               case AUDIT_MQ_GETSETATTR: {
+                       struct audit_aux_data_mq_getsetattr *axi = (void *)aux;
+                       audit_log_format(ab,
+                               "mqdes=%d mq_flags=0x%lx mq_maxmsg=%ld mq_msgsize=%ld "
+                               "mq_curmsgs=%ld ",
+                               axi->mqdes,
+                               axi->mqstat.mq_flags, axi->mqstat.mq_maxmsg,
+                               axi->mqstat.mq_msgsize, axi->mqstat.mq_curmsgs);
+                       break; }
+
                case AUDIT_IPC: {
                        struct audit_aux_data_ipcctl *axi = (void *)aux;
                        audit_log_format(ab, 
-                                " qbytes=%lx iuid=%u igid=%u mode=%x",
-                                axi->qbytes, axi->uid, axi->gid, axi->mode);
+                                "ouid=%u ogid=%u mode=%x",
+                                axi->uid, axi->gid, axi->mode);
                        if (axi->osid != 0) {
                                char *ctx = NULL;
                                u32 len;
@@ -649,19 +734,18 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts
                case AUDIT_IPC_SET_PERM: {
                        struct audit_aux_data_ipcctl *axi = (void *)aux;
                        audit_log_format(ab,
-                               " new qbytes=%lx new iuid=%u new igid=%u new mode=%x",
+                               "qbytes=%lx ouid=%u ogid=%u mode=%x",
                                axi->qbytes, axi->uid, axi->gid, axi->mode);
-                       if (axi->osid != 0) {
-                               char *ctx = NULL;
-                               u32 len;
-                               if (selinux_ctxid_to_string(
-                                               axi->osid, &ctx, &len)) {
-                                       audit_log_format(ab, " osid=%u",
-                                                       axi->osid);
-                                       call_panic = 1;
-                               } else
-                                       audit_log_format(ab, " obj=%s", ctx);
-                               kfree(ctx);
+                       break; }
+
+               case AUDIT_EXECVE: {
+                       struct audit_aux_data_execve *axi = (void *)aux;
+                       int i;
+                       const char *p;
+                       for (i = 0, p = axi->mem; i < axi->argc; i++) {
+                               audit_log_format(ab, "a%d=", i);
+                               p = audit_log_untrustedstring(ab, p);
+                               audit_log_format(ab, "\n");
                        }
                        break; }
 
@@ -905,11 +989,11 @@ void audit_syscall_exit(int valid, long return_code)
  * Add a name to the list of audit names for this context.
  * Called from fs/namei.c:getname().
  */
-void audit_getname(const char *name)
+void __audit_getname(const char *name)
 {
        struct audit_context *context = current->audit_context;
 
-       if (!context || IS_ERR(name) || !name)
+       if (IS_ERR(name) || !name)
                return;
 
        if (!context->in_syscall) {
@@ -1167,16 +1251,107 @@ uid_t audit_get_loginuid(struct audit_context *ctx)
 }
 
 /**
- * audit_ipc_obj - record audit data for ipc object
- * @ipcp: ipc permissions
+ * __audit_mq_open - record audit data for a POSIX MQ open
+ * @oflag: open flag
+ * @mode: mode bits
+ * @u_attr: queue attributes
  *
  * Returns 0 for success or NULL context or < 0 on error.
  */
-int audit_ipc_obj(struct kern_ipc_perm *ipcp)
+int __audit_mq_open(int oflag, mode_t mode, struct mq_attr __user *u_attr)
 {
-       struct audit_aux_data_ipcctl *ax;
+       struct audit_aux_data_mq_open *ax;
+       struct audit_context *context = current->audit_context;
+
+       if (!audit_enabled)
+               return 0;
+
+       if (likely(!context))
+               return 0;
+
+       ax = kmalloc(sizeof(*ax), GFP_ATOMIC);
+       if (!ax)
+               return -ENOMEM;
+
+       if (u_attr != NULL) {
+               if (copy_from_user(&ax->attr, u_attr, sizeof(ax->attr))) {
+                       kfree(ax);
+                       return -EFAULT;
+               }
+       } else
+               memset(&ax->attr, 0, sizeof(ax->attr));
+
+       ax->oflag = oflag;
+       ax->mode = mode;
+
+       ax->d.type = AUDIT_MQ_OPEN;
+       ax->d.next = context->aux;
+       context->aux = (void *)ax;
+       return 0;
+}
+
+/**
+ * __audit_mq_timedsend - record audit data for a POSIX MQ timed send
+ * @mqdes: MQ descriptor
+ * @msg_len: Message length
+ * @msg_prio: Message priority
+ * @abs_timeout: Message timeout in absolute time
+ *
+ * Returns 0 for success or NULL context or < 0 on error.
+ */
+int __audit_mq_timedsend(mqd_t mqdes, size_t msg_len, unsigned int msg_prio,
+                       const struct timespec __user *u_abs_timeout)
+{
+       struct audit_aux_data_mq_sendrecv *ax;
+       struct audit_context *context = current->audit_context;
+
+       if (!audit_enabled)
+               return 0;
+
+       if (likely(!context))
+               return 0;
+
+       ax = kmalloc(sizeof(*ax), GFP_ATOMIC);
+       if (!ax)
+               return -ENOMEM;
+
+       if (u_abs_timeout != NULL) {
+               if (copy_from_user(&ax->abs_timeout, u_abs_timeout, sizeof(ax->abs_timeout))) {
+                       kfree(ax);
+                       return -EFAULT;
+               }
+       } else
+               memset(&ax->abs_timeout, 0, sizeof(ax->abs_timeout));
+
+       ax->mqdes = mqdes;
+       ax->msg_len = msg_len;
+       ax->msg_prio = msg_prio;
+
+       ax->d.type = AUDIT_MQ_SENDRECV;
+       ax->d.next = context->aux;
+       context->aux = (void *)ax;
+       return 0;
+}
+
+/**
+ * __audit_mq_timedreceive - record audit data for a POSIX MQ timed receive
+ * @mqdes: MQ descriptor
+ * @msg_len: Message length
+ * @msg_prio: Message priority
+ * @abs_timeout: Message timeout in absolute time
+ *
+ * Returns 0 for success or NULL context or < 0 on error.
+ */
+int __audit_mq_timedreceive(mqd_t mqdes, size_t msg_len,
+                               unsigned int __user *u_msg_prio,
+                               const struct timespec __user *u_abs_timeout)
+{
+       struct audit_aux_data_mq_sendrecv *ax;
        struct audit_context *context = current->audit_context;
 
+       if (!audit_enabled)
+               return 0;
+
        if (likely(!context))
                return 0;
 
@@ -1184,6 +1359,116 @@ int audit_ipc_obj(struct kern_ipc_perm *ipcp)
        if (!ax)
                return -ENOMEM;
 
+       if (u_msg_prio != NULL) {
+               if (get_user(ax->msg_prio, u_msg_prio)) {
+                       kfree(ax);
+                       return -EFAULT;
+               }
+       } else
+               ax->msg_prio = 0;
+
+       if (u_abs_timeout != NULL) {
+               if (copy_from_user(&ax->abs_timeout, u_abs_timeout, sizeof(ax->abs_timeout))) {
+                       kfree(ax);
+                       return -EFAULT;
+               }
+       } else
+               memset(&ax->abs_timeout, 0, sizeof(ax->abs_timeout));
+
+       ax->mqdes = mqdes;
+       ax->msg_len = msg_len;
+
+       ax->d.type = AUDIT_MQ_SENDRECV;
+       ax->d.next = context->aux;
+       context->aux = (void *)ax;
+       return 0;
+}
+
+/**
+ * __audit_mq_notify - record audit data for a POSIX MQ notify
+ * @mqdes: MQ descriptor
+ * @u_notification: Notification event
+ *
+ * Returns 0 for success or NULL context or < 0 on error.
+ */
+
+int __audit_mq_notify(mqd_t mqdes, const struct sigevent __user *u_notification)
+{
+       struct audit_aux_data_mq_notify *ax;
+       struct audit_context *context = current->audit_context;
+
+       if (!audit_enabled)
+               return 0;
+
+       if (likely(!context))
+               return 0;
+
+       ax = kmalloc(sizeof(*ax), GFP_ATOMIC);
+       if (!ax)
+               return -ENOMEM;
+
+       if (u_notification != NULL) {
+               if (copy_from_user(&ax->notification, u_notification, sizeof(ax->notification))) {
+                       kfree(ax);
+                       return -EFAULT;
+               }
+       } else
+               memset(&ax->notification, 0, sizeof(ax->notification));
+
+       ax->mqdes = mqdes;
+
+       ax->d.type = AUDIT_MQ_NOTIFY;
+       ax->d.next = context->aux;
+       context->aux = (void *)ax;
+       return 0;
+}
+
+/**
+ * __audit_mq_getsetattr - record audit data for a POSIX MQ get/set attribute
+ * @mqdes: MQ descriptor
+ * @mqstat: MQ flags
+ *
+ * Returns 0 for success or NULL context or < 0 on error.
+ */
+int __audit_mq_getsetattr(mqd_t mqdes, struct mq_attr *mqstat)
+{
+       struct audit_aux_data_mq_getsetattr *ax;
+       struct audit_context *context = current->audit_context;
+
+       if (!audit_enabled)
+               return 0;
+
+       if (likely(!context))
+               return 0;
+
+       ax = kmalloc(sizeof(*ax), GFP_ATOMIC);
+       if (!ax)
+               return -ENOMEM;
+
+       ax->mqdes = mqdes;
+       ax->mqstat = *mqstat;
+
+       ax->d.type = AUDIT_MQ_GETSETATTR;
+       ax->d.next = context->aux;
+       context->aux = (void *)ax;
+       return 0;
+}
+
+/**
+ * audit_ipc_obj - record audit data for ipc object
+ * @ipcp: ipc permissions
+ *
+ * Returns 0 for success or NULL context or < 0 on error.
+ */
+int __audit_ipc_obj(struct kern_ipc_perm *ipcp)
+{
+       struct audit_aux_data_ipcctl *ax;
+       struct audit_context *context = current->audit_context;
+
+       ax = kmalloc(sizeof(*ax), GFP_ATOMIC);
+       if (!ax)
+               return -ENOMEM;
+
        ax->uid = ipcp->uid;
        ax->gid = ipcp->gid;
        ax->mode = ipcp->mode;
@@ -1204,14 +1489,11 @@ int audit_ipc_obj(struct kern_ipc_perm *ipcp)
  *
  * Returns 0 for success or NULL context or < 0 on error.
  */
-int audit_ipc_set_perm(unsigned long qbytes, uid_t uid, gid_t gid, mode_t mode, struct kern_ipc_perm *ipcp)
+int __audit_ipc_set_perm(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_ATOMIC);
        if (!ax)
                return -ENOMEM;
@@ -1220,7 +1502,6 @@ int audit_ipc_set_perm(unsigned long qbytes, uid_t uid, gid_t gid, mode_t mode,
        ax->uid = uid;
        ax->gid = gid;
        ax->mode = mode;
-       selinux_get_ipc_sid(ipcp, &ax->osid);
 
        ax->d.type = AUDIT_IPC_SET_PERM;
        ax->d.next = context->aux;
@@ -1228,6 +1509,39 @@ int audit_ipc_set_perm(unsigned long qbytes, uid_t uid, gid_t gid, mode_t mode,
        return 0;
 }
 
+int audit_bprm(struct linux_binprm *bprm)
+{
+       struct audit_aux_data_execve *ax;
+       struct audit_context *context = current->audit_context;
+       unsigned long p, next;
+       void *to;
+
+       if (likely(!audit_enabled || !context))
+               return 0;
+
+       ax = kmalloc(sizeof(*ax) + PAGE_SIZE * MAX_ARG_PAGES - bprm->p,
+                               GFP_KERNEL);
+       if (!ax)
+               return -ENOMEM;
+
+       ax->argc = bprm->argc;
+       ax->envc = bprm->envc;
+       for (p = bprm->p, to = ax->mem; p < MAX_ARG_PAGES*PAGE_SIZE; p = next) {
+               struct page *page = bprm->page[p / PAGE_SIZE];
+               void *kaddr = kmap(page);
+               next = (p + PAGE_SIZE) & ~(PAGE_SIZE - 1);
+               memcpy(to, kaddr + (p & (PAGE_SIZE - 1)), next - p);
+               to += next - p;
+               kunmap(page);
+       }
+
+       ax->d.type = AUDIT_EXECVE;
+       ax->d.next = context->aux;
+       context->aux = (void *)ax;
+       return 0;
+}
+
+
 /**
  * audit_socketcall - record audit data for sys_socketcall
  * @nargs: number of args
@@ -1322,19 +1636,20 @@ int audit_avc_path(struct dentry *dentry, struct vfsmount *mnt)
  * If the audit subsystem is being terminated, record the task (pid)
  * and uid that is doing that.
  */
-void audit_signal_info(int sig, struct task_struct *t)
+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->tgid == 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;
-               }
+       extern u32 audit_sig_sid;
+
+       if (sig == SIGTERM || sig == SIGHUP || sig == SIGUSR1) {
+               struct task_struct *tsk = current;
+               struct audit_context *ctx = tsk->audit_context;
+               audit_sig_pid = tsk->pid;
+               if (ctx)
+                       audit_sig_uid = ctx->loginuid;
+               else
+                       audit_sig_uid = tsk->uid;
+               selinux_get_task_sid(tsk, &audit_sig_sid);
        }
 }