[PATCH] fix audit oops with invalid operator
[powerpc.git] / kernel / auditsc.c
index 9ebd96f..b1356fc 100644 (file)
@@ -186,6 +186,7 @@ struct audit_context {
        int                 auditable;  /* 1 if record should be written */
        int                 name_count;
        struct audit_names  names[AUDIT_NAMES];
+       char *              filterkey;  /* key for rule that triggered record */
        struct dentry *     pwd;
        struct vfsmount *   pwdmnt;
        struct audit_context *previous; /* For nested syscalls */
@@ -320,11 +321,11 @@ static int audit_filter_rules(struct task_struct *tsk,
                        if (ctx)
                                result = audit_comparator(ctx->loginuid, f->op, f->val);
                        break;
-               case AUDIT_SE_USER:
-               case AUDIT_SE_ROLE:
-               case AUDIT_SE_TYPE:
-               case AUDIT_SE_SEN:
-               case AUDIT_SE_CLR:
+               case AUDIT_SUBJ_USER:
+               case AUDIT_SUBJ_ROLE:
+               case AUDIT_SUBJ_TYPE:
+               case AUDIT_SUBJ_SEN:
+               case AUDIT_SUBJ_CLR:
                        /* NOTE: this may return negative values indicating
                           a temporary error.  We simply treat this as a
                           match for now to avoid losing information that
@@ -341,6 +342,46 @@ static int audit_filter_rules(struct task_struct *tsk,
                                                                  ctx);
                        }
                        break;
+               case AUDIT_OBJ_USER:
+               case AUDIT_OBJ_ROLE:
+               case AUDIT_OBJ_TYPE:
+               case AUDIT_OBJ_LEV_LOW:
+               case AUDIT_OBJ_LEV_HIGH:
+                       /* The above note for AUDIT_SUBJ_USER...AUDIT_SUBJ_CLR
+                          also applies here */
+                       if (f->se_rule) {
+                               /* Find files that match */
+                               if (name) {
+                                       result = selinux_audit_rule_match(
+                                                  name->osid, f->type, f->op,
+                                                  f->se_rule, ctx);
+                               } else if (ctx) {
+                                       for (j = 0; j < ctx->name_count; j++) {
+                                               if (selinux_audit_rule_match(
+                                                     ctx->names[j].osid,
+                                                     f->type, f->op,
+                                                     f->se_rule, ctx)) {
+                                                       ++result;
+                                                       break;
+                                               }
+                                       }
+                               }
+                               /* Find ipc objects that match */
+                               if (ctx) {
+                                       struct audit_aux_data *aux;
+                                       for (aux = ctx->aux; aux;
+                                            aux = aux->next) {
+                                               if (aux->type == AUDIT_IPC) {
+                                                       struct audit_aux_data_ipcctl *axi = (void *)aux;
+                                                       if (selinux_audit_rule_match(axi->osid, f->type, f->op, f->se_rule, ctx)) {
+                                                               ++result;
+                                                               break;
+                                                       }
+                                               }
+                                       }
+                               }
+                       }
+                       break;
                case AUDIT_ARG0:
                case AUDIT_ARG1:
                case AUDIT_ARG2:
@@ -348,11 +389,17 @@ static int audit_filter_rules(struct task_struct *tsk,
                        if (ctx)
                                result = audit_comparator(ctx->argv[f->type-AUDIT_ARG0], f->op, f->val);
                        break;
+               case AUDIT_FILTERKEY:
+                       /* ignore this field for filtering */
+                       result = 1;
+                       break;
                }
 
                if (!result)
                        return 0;
        }
+       if (rule->filterkey)
+               ctx->filterkey = kstrdup(rule->filterkey, GFP_ATOMIC);
        switch (rule->action) {
        case AUDIT_NEVER:    *state = AUDIT_DISABLED;       break;
        case AUDIT_ALWAYS:   *state = AUDIT_RECORD_CONTEXT; break;
@@ -627,6 +674,7 @@ static inline void audit_free_context(struct audit_context *context)
                }
                audit_free_names(context);
                audit_free_aux(context);
+               kfree(context->filterkey);
                kfree(context);
                context  = previous;
        } while (context);
@@ -658,8 +706,7 @@ static void audit_log_task_context(struct audit_buffer *ab)
        return;
 
 error_path:
-       if (ctx)
-               kfree(ctx);
+       kfree(ctx);
        audit_panic("error in audit_log_task_context");
        return;
 }
@@ -736,6 +783,11 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts
                  context->euid, context->suid, context->fsuid,
                  context->egid, context->sgid, context->fsgid, tty);
        audit_log_task_info(ab, tsk);
+       if (context->filterkey) {
+               audit_log_format(ab, " key=");
+               audit_log_untrustedstring(ab, context->filterkey);
+       } else
+               audit_log_format(ab, " key=(null)");
        audit_log_end(ab);
 
        for (aux = context->aux; aux; aux = aux->next) {
@@ -1061,6 +1113,8 @@ void audit_syscall_exit(int valid, long return_code)
        } else {
                audit_free_names(context);
                audit_free_aux(context);
+               kfree(context->filterkey);
+               context->filterkey = NULL;
                tsk->audit_context = context;
        }
 }
@@ -1145,14 +1199,18 @@ void audit_putname(const char *name)
 #endif
 }
 
-static void audit_inode_context(int idx, const struct inode *inode)
+/* Copy inode data into an audit_names. */
+static void audit_copy_inode(struct audit_names *name, const struct inode *inode)
 {
-       struct audit_context *context = current->audit_context;
-
-       selinux_get_inode_sid(inode, &context->names[idx].osid);
+       name->ino   = inode->i_ino;
+       name->dev   = inode->i_sb->s_dev;
+       name->mode  = inode->i_mode;
+       name->uid   = inode->i_uid;
+       name->gid   = inode->i_gid;
+       name->rdev  = inode->i_rdev;
+       selinux_get_inode_sid(inode, &name->osid);
 }
 
-
 /**
  * audit_inode - store the inode and device from a lookup
  * @name: name being audited
@@ -1186,20 +1244,14 @@ void __audit_inode(const char *name, const struct inode *inode)
                ++context->ino_count;
 #endif
        }
-       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;
-       audit_inode_context(idx, inode);
+       audit_copy_inode(&context->names[idx], inode);
 }
 
 /**
  * audit_inode_child - collect inode info for created/removed objects
  * @dname: inode's dentry name
  * @inode: inode being audited
- * @pino: inode number of dentry parent
+ * @parent: inode of dentry parent
  *
  * For syscalls that create or remove filesystem objects, audit_inode
  * can only collect information for the filesystem object's parent.
@@ -1210,7 +1262,7 @@ void __audit_inode(const char *name, const struct inode *inode)
  * unsuccessful attempts.
  */
 void __audit_inode_child(const char *dname, const struct inode *inode,
-                        unsigned long pino)
+                        const struct inode *parent)
 {
        int idx;
        struct audit_context *context = current->audit_context;
@@ -1224,7 +1276,7 @@ void __audit_inode_child(const char *dname, const struct inode *inode,
        if (!dname)
                goto update_context;
        for (idx = 0; idx < context->name_count; idx++)
-               if (context->names[idx].ino == pino) {
+               if (context->names[idx].ino == parent->i_ino) {
                        const char *name = context->names[idx].name;
 
                        if (!name)
@@ -1248,16 +1300,47 @@ update_context:
        context->names[idx].name_len = AUDIT_NAME_FULL;
        context->names[idx].name_put = 0;       /* don't call __putname() */
 
-       if (inode) {
-               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;
-               audit_inode_context(idx, inode);
-       } else
-               context->names[idx].ino   = (unsigned long)-1;
+       if (!inode)
+               context->names[idx].ino = (unsigned long)-1;
+       else
+               audit_copy_inode(&context->names[idx], inode);
+
+       /* A parent was not found in audit_names, so copy the inode data for the
+        * provided parent. */
+       if (!found_name) {
+               idx = context->name_count++;
+#if AUDIT_DEBUG
+               context->ino_count++;
+#endif
+               audit_copy_inode(&context->names[idx], parent);
+       }
+}
+
+/**
+ * audit_inode_update - update inode info for last collected name
+ * @inode: inode being audited
+ *
+ * When open() is called on an existing object with the O_CREAT flag, the inode
+ * data audit initially collects is incorrect.  This additional hook ensures
+ * audit has the inode data for the actual object to be opened.
+ */
+void __audit_inode_update(const struct inode *inode)
+{
+       struct audit_context *context = current->audit_context;
+       int idx;
+
+       if (!context->in_syscall || !inode)
+               return;
+
+       if (context->name_count == 0) {
+               context->name_count++;
+#if AUDIT_DEBUG
+               context->ino_count++;
+#endif
+       }
+       idx = context->name_count - 1;
+
+       audit_copy_inode(&context->names[idx], inode);
 }
 
 /**
@@ -1367,7 +1450,7 @@ int __audit_mq_open(int oflag, mode_t mode, struct mq_attr __user *u_attr)
  * @mqdes: MQ descriptor
  * @msg_len: Message length
  * @msg_prio: Message priority
- * @abs_timeout: Message timeout in absolute time
+ * @u_abs_timeout: Message timeout in absolute time
  *
  * Returns 0 for success or NULL context or < 0 on error.
  */
@@ -1409,8 +1492,8 @@ int __audit_mq_timedsend(mqd_t mqdes, size_t msg_len, unsigned int msg_prio,
  * __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
+ * @u_msg_prio: Message priority
+ * @u_abs_timeout: Message timeout in absolute time
  *
  * Returns 0 for success or NULL context or < 0 on error.
  */
@@ -1558,7 +1641,6 @@ int __audit_ipc_obj(struct kern_ipc_perm *ipcp)
  * @uid: msgq user id
  * @gid: msgq group id
  * @mode: msgq mode (permissions)
- * @ipcp: in-kernel IPC permissions
  *
  * Returns 0 for success or NULL context or < 0 on error.
  */