Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris...
[cascardo/linux.git] / security / selinux / selinuxfs.c
index 73c60ba..732c1c7 100644 (file)
@@ -116,6 +116,7 @@ enum sel_inos {
        SEL_DENY_UNKNOWN, /* export unknown deny handling to userspace */
        SEL_STATUS,     /* export current status using mmap() */
        SEL_POLICY,     /* allow userspace to read the in kernel policy */
+       SEL_VALIDATE_TRANS, /* compute validatetrans decision */
        SEL_INO_NEXT,   /* The next inode number to use */
 };
 
@@ -632,6 +633,83 @@ static const struct file_operations sel_checkreqprot_ops = {
        .llseek         = generic_file_llseek,
 };
 
+static ssize_t sel_write_validatetrans(struct file *file,
+                                       const char __user *buf,
+                                       size_t count, loff_t *ppos)
+{
+       char *oldcon = NULL, *newcon = NULL, *taskcon = NULL;
+       char *req = NULL;
+       u32 osid, nsid, tsid;
+       u16 tclass;
+       int rc;
+
+       rc = task_has_security(current, SECURITY__VALIDATE_TRANS);
+       if (rc)
+               goto out;
+
+       rc = -ENOMEM;
+       if (count >= PAGE_SIZE)
+               goto out;
+
+       /* No partial writes. */
+       rc = -EINVAL;
+       if (*ppos != 0)
+               goto out;
+
+       rc = -ENOMEM;
+       req = kzalloc(count + 1, GFP_KERNEL);
+       if (!req)
+               goto out;
+
+       rc = -EFAULT;
+       if (copy_from_user(req, buf, count))
+               goto out;
+
+       rc = -ENOMEM;
+       oldcon = kzalloc(count + 1, GFP_KERNEL);
+       if (!oldcon)
+               goto out;
+
+       newcon = kzalloc(count + 1, GFP_KERNEL);
+       if (!newcon)
+               goto out;
+
+       taskcon = kzalloc(count + 1, GFP_KERNEL);
+       if (!taskcon)
+               goto out;
+
+       rc = -EINVAL;
+       if (sscanf(req, "%s %s %hu %s", oldcon, newcon, &tclass, taskcon) != 4)
+               goto out;
+
+       rc = security_context_str_to_sid(oldcon, &osid, GFP_KERNEL);
+       if (rc)
+               goto out;
+
+       rc = security_context_str_to_sid(newcon, &nsid, GFP_KERNEL);
+       if (rc)
+               goto out;
+
+       rc = security_context_str_to_sid(taskcon, &tsid, GFP_KERNEL);
+       if (rc)
+               goto out;
+
+       rc = security_validate_transition_user(osid, nsid, tsid, tclass);
+       if (!rc)
+               rc = count;
+out:
+       kfree(req);
+       kfree(oldcon);
+       kfree(newcon);
+       kfree(taskcon);
+       return rc;
+}
+
+static const struct file_operations sel_transition_ops = {
+       .write          = sel_write_validatetrans,
+       .llseek         = generic_file_llseek,
+};
+
 /*
  * Remaining nodes use transaction based IO methods like nfsd/nfsctl.c
  */
@@ -1727,6 +1805,8 @@ static int sel_fill_super(struct super_block *sb, void *data, int silent)
                [SEL_DENY_UNKNOWN] = {"deny_unknown", &sel_handle_unknown_ops, S_IRUGO},
                [SEL_STATUS] = {"status", &sel_handle_status_ops, S_IRUGO},
                [SEL_POLICY] = {"policy", &sel_policy_ops, S_IRUGO},
+               [SEL_VALIDATE_TRANS] = {"validatetrans", &sel_transition_ops,
+                                       S_IWUGO},
                /* last one */ {""}
        };
        ret = simple_fill_super(sb, SELINUX_MAGIC, selinux_files);