perf: Fix double start/stop in x86_pmu_start()
[linux.git] / kernel / auditsc.c
index 7aaeb38b262abe2dd0e2ed8ab2c87352e2dd07e2..af1de0f34eaed8dbf3dfb0057cd5c200da70b47a 100644 (file)
@@ -463,6 +463,134 @@ static int match_tree_refs(struct audit_context *ctx, struct audit_tree *tree)
        return 0;
 }
 
+static int audit_compare_id(uid_t uid1,
+                           struct audit_names *name,
+                           unsigned long name_offset,
+                           struct audit_field *f,
+                           struct audit_context *ctx)
+{
+       struct audit_names *n;
+       unsigned long addr;
+       uid_t uid2;
+       int rc;
+
+       BUILD_BUG_ON(sizeof(uid_t) != sizeof(gid_t));
+
+       if (name) {
+               addr = (unsigned long)name;
+               addr += name_offset;
+
+               uid2 = *(uid_t *)addr;
+               rc = audit_comparator(uid1, f->op, uid2);
+               if (rc)
+                       return rc;
+       }
+
+       if (ctx) {
+               list_for_each_entry(n, &ctx->names_list, list) {
+                       addr = (unsigned long)n;
+                       addr += name_offset;
+
+                       uid2 = *(uid_t *)addr;
+
+                       rc = audit_comparator(uid1, f->op, uid2);
+                       if (rc)
+                               return rc;
+               }
+       }
+       return 0;
+}
+
+static int audit_field_compare(struct task_struct *tsk,
+                              const struct cred *cred,
+                              struct audit_field *f,
+                              struct audit_context *ctx,
+                              struct audit_names *name)
+{
+       switch (f->val) {
+       /* process to file object comparisons */
+       case AUDIT_COMPARE_UID_TO_OBJ_UID:
+               return audit_compare_id(cred->uid,
+                                       name, offsetof(struct audit_names, uid),
+                                       f, ctx);
+       case AUDIT_COMPARE_GID_TO_OBJ_GID:
+               return audit_compare_id(cred->gid,
+                                       name, offsetof(struct audit_names, gid),
+                                       f, ctx);
+       case AUDIT_COMPARE_EUID_TO_OBJ_UID:
+               return audit_compare_id(cred->euid,
+                                       name, offsetof(struct audit_names, uid),
+                                       f, ctx);
+       case AUDIT_COMPARE_EGID_TO_OBJ_GID:
+               return audit_compare_id(cred->egid,
+                                       name, offsetof(struct audit_names, gid),
+                                       f, ctx);
+       case AUDIT_COMPARE_AUID_TO_OBJ_UID:
+               return audit_compare_id(tsk->loginuid,
+                                       name, offsetof(struct audit_names, uid),
+                                       f, ctx);
+       case AUDIT_COMPARE_SUID_TO_OBJ_UID:
+               return audit_compare_id(cred->suid,
+                                       name, offsetof(struct audit_names, uid),
+                                       f, ctx);
+       case AUDIT_COMPARE_SGID_TO_OBJ_GID:
+               return audit_compare_id(cred->sgid,
+                                       name, offsetof(struct audit_names, gid),
+                                       f, ctx);
+       case AUDIT_COMPARE_FSUID_TO_OBJ_UID:
+               return audit_compare_id(cred->fsuid,
+                                       name, offsetof(struct audit_names, uid),
+                                       f, ctx);
+       case AUDIT_COMPARE_FSGID_TO_OBJ_GID:
+               return audit_compare_id(cred->fsgid,
+                                       name, offsetof(struct audit_names, gid),
+                                       f, ctx);
+       /* uid comparisons */
+       case AUDIT_COMPARE_UID_TO_AUID:
+               return audit_comparator(cred->uid, f->op, tsk->loginuid);
+       case AUDIT_COMPARE_UID_TO_EUID:
+               return audit_comparator(cred->uid, f->op, cred->euid);
+       case AUDIT_COMPARE_UID_TO_SUID:
+               return audit_comparator(cred->uid, f->op, cred->suid);
+       case AUDIT_COMPARE_UID_TO_FSUID:
+               return audit_comparator(cred->uid, f->op, cred->fsuid);
+       /* auid comparisons */
+       case AUDIT_COMPARE_AUID_TO_EUID:
+               return audit_comparator(tsk->loginuid, f->op, cred->euid);
+       case AUDIT_COMPARE_AUID_TO_SUID:
+               return audit_comparator(tsk->loginuid, f->op, cred->suid);
+       case AUDIT_COMPARE_AUID_TO_FSUID:
+               return audit_comparator(tsk->loginuid, f->op, cred->fsuid);
+       /* euid comparisons */
+       case AUDIT_COMPARE_EUID_TO_SUID:
+               return audit_comparator(cred->euid, f->op, cred->suid);
+       case AUDIT_COMPARE_EUID_TO_FSUID:
+               return audit_comparator(cred->euid, f->op, cred->fsuid);
+       /* suid comparisons */
+       case AUDIT_COMPARE_SUID_TO_FSUID:
+               return audit_comparator(cred->suid, f->op, cred->fsuid);
+       /* gid comparisons */
+       case AUDIT_COMPARE_GID_TO_EGID:
+               return audit_comparator(cred->gid, f->op, cred->egid);
+       case AUDIT_COMPARE_GID_TO_SGID:
+               return audit_comparator(cred->gid, f->op, cred->sgid);
+       case AUDIT_COMPARE_GID_TO_FSGID:
+               return audit_comparator(cred->gid, f->op, cred->fsgid);
+       /* egid comparisons */
+       case AUDIT_COMPARE_EGID_TO_SGID:
+               return audit_comparator(cred->egid, f->op, cred->sgid);
+       case AUDIT_COMPARE_EGID_TO_FSGID:
+               return audit_comparator(cred->egid, f->op, cred->fsgid);
+       /* sgid comparison */
+       case AUDIT_COMPARE_SGID_TO_FSGID:
+               return audit_comparator(cred->sgid, f->op, cred->fsgid);
+       default:
+               WARN(1, "Missing AUDIT_COMPARE define.  Report as a bug\n");
+               return 0;
+       }
+       return 0;
+}
+
 /* Determine if any context name data matches a rule's watch data */
 /* Compare a task_struct with an audit_rule.  Return 1 on match, 0
  * otherwise.
@@ -586,6 +714,30 @@ static int audit_filter_rules(struct task_struct *tsk,
                                }
                        }
                        break;
+               case AUDIT_OBJ_UID:
+                       if (name) {
+                               result = audit_comparator(name->uid, f->op, f->val);
+                       } else if (ctx) {
+                               list_for_each_entry(n, &ctx->names_list, list) {
+                                       if (audit_comparator(n->uid, f->op, f->val)) {
+                                               ++result;
+                                               break;
+                                       }
+                               }
+                       }
+                       break;
+               case AUDIT_OBJ_GID:
+                       if (name) {
+                               result = audit_comparator(name->gid, f->op, f->val);
+                       } else if (ctx) {
+                               list_for_each_entry(n, &ctx->names_list, list) {
+                                       if (audit_comparator(n->gid, f->op, f->val)) {
+                                               ++result;
+                                               break;
+                                       }
+                               }
+                       }
+                       break;
                case AUDIT_WATCH:
                        if (name)
                                result = audit_watch_compare(rule->watch, name->ino, name->dev);
@@ -669,8 +821,10 @@ static int audit_filter_rules(struct task_struct *tsk,
                case AUDIT_FILETYPE:
                        result = audit_match_filetype(ctx, f->val);
                        break;
+               case AUDIT_FIELD_COMPARE:
+                       result = audit_field_compare(tsk, cred, f, ctx, name);
+                       break;
                }
-
                if (!result)
                        return 0;
        }
@@ -1017,7 +1171,7 @@ static void audit_log_task_info(struct audit_buffer *ab, struct task_struct *tsk
                while (vma) {
                        if ((vma->vm_flags & VM_EXECUTABLE) &&
                            vma->vm_file) {
-                               audit_log_d_path(ab, "exe=",
+                               audit_log_d_path(ab, " exe=",
                                                 &vma->vm_file->f_path);
                                break;
                        }
@@ -1208,8 +1362,8 @@ static void audit_log_execve_info(struct audit_context *context,
                                  struct audit_buffer **ab,
                                  struct audit_aux_data_execve *axi)
 {
-       int i;
-       size_t len, len_sent = 0;
+       int i, len;
+       size_t len_sent = 0;
        const char __user *p;
        char *buf;
 
@@ -1386,7 +1540,7 @@ static void audit_log_name(struct audit_context *context, struct audit_names *n,
                case 0:
                        /* name was specified as a relative path and the
                         * directory component is the cwd */
-                       audit_log_d_path(ab, "name=", &context->pwd);
+                       audit_log_d_path(ab, " name=", &context->pwd);
                        break;
                default:
                        /* log the name's directory component */
@@ -1571,7 +1725,7 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts
        if (context->pwd.dentry && context->pwd.mnt) {
                ab = audit_log_start(context, GFP_KERNEL, AUDIT_CWD);
                if (ab) {
-                       audit_log_d_path(ab, "cwd=", &context->pwd);
+                       audit_log_d_path(ab, " cwd=", &context->pwd);
                        audit_log_end(ab);
                }
        }
@@ -1707,33 +1861,14 @@ void __audit_syscall_entry(int arch, int major,
        context->ppid       = 0;
 }
 
-void audit_finish_fork(struct task_struct *child)
-{
-       struct audit_context *ctx = current->audit_context;
-       struct audit_context *p = child->audit_context;
-       if (!p || !ctx)
-               return;
-       if (!ctx->in_syscall || ctx->current_state != AUDIT_RECORD_CONTEXT)
-               return;
-       p->arch = ctx->arch;
-       p->major = ctx->major;
-       memcpy(p->argv, ctx->argv, sizeof(ctx->argv));
-       p->ctime = ctx->ctime;
-       p->dummy = ctx->dummy;
-       p->in_syscall = ctx->in_syscall;
-       p->filterkey = kstrdup(ctx->filterkey, GFP_KERNEL);
-       p->ppid = current->pid;
-       p->prio = ctx->prio;
-       p->current_state = ctx->current_state;
-}
-
 /**
  * audit_syscall_exit - deallocate audit context after a system call
- * @pt_regs: syscall registers
+ * @success: success value of the syscall
+ * @return_code: return value of the syscall
  *
  * Tear down after system call.  If the audit context has been marked as
  * auditable (either because of the AUDIT_RECORD_CONTEXT state from
- * filtering, or because some other part of the kernel write an audit
+ * filtering, or because some other part of the kernel wrote an audit
  * message), then write out the syscall information.  In call cases,
  * free the names stored from getname().
  */
@@ -1909,9 +2044,6 @@ void __audit_getname(const char *name)
        struct audit_context *context = current->audit_context;
        struct audit_names *n;
 
-       if (IS_ERR(name) || !name)
-               return;
-
        if (!context->in_syscall) {
 #if AUDIT_DEBUG == 2
                printk(KERN_ERR "%s:%d(:%d): ignoring getname(%p)\n",
@@ -2159,19 +2291,28 @@ int auditsc_get_stamp(struct audit_context *ctx,
 static atomic_t session_id = ATOMIC_INIT(0);
 
 /**
- * audit_set_loginuid - set a task's audit_context loginuid
- * @task: task whose audit context is being modified
+ * audit_set_loginuid - set current task's audit_context loginuid
  * @loginuid: loginuid value
  *
  * Returns 0.
  *
  * Called (set) from fs/proc/base.c::proc_loginuid_write().
  */
-int audit_set_loginuid(struct task_struct *task, uid_t loginuid)
+int audit_set_loginuid(uid_t loginuid)
 {
-       unsigned int sessionid = atomic_inc_return(&session_id);
+       struct task_struct *task = current;
        struct audit_context *context = task->audit_context;
+       unsigned int sessionid;
+
+#ifdef CONFIG_AUDIT_LOGINUID_IMMUTABLE
+       if (task->loginuid != -1)
+               return -EPERM;
+#else /* CONFIG_AUDIT_LOGINUID_IMMUTABLE */
+       if (!capable(CAP_AUDIT_CONTROL))
+               return -EPERM;
+#endif  /* CONFIG_AUDIT_LOGINUID_IMMUTABLE */
 
+       sessionid = atomic_inc_return(&session_id);
        if (context && context->in_syscall) {
                struct audit_buffer *ab;