selinux: export validatetrans decisions
authorAndrew Perepechko <anserper@ya.ru>
Thu, 24 Dec 2015 16:09:41 +0000 (11:09 -0500)
committerPaul Moore <pmoore@redhat.com>
Thu, 24 Dec 2015 16:09:41 +0000 (11:09 -0500)
Make validatetrans decisions available through selinuxfs.
"/validatetrans" is added to selinuxfs for this purpose.
This functionality is needed by file system servers
implemented in userspace or kernelspace without the VFS
layer.

Writing "$oldcontext $newcontext $tclass $taskcontext"
to /validatetrans is expected to return 0 if the transition
is allowed and -EPERM otherwise.

Signed-off-by: Andrew Perepechko <anserper@ya.ru>
CC: andrew.perepechko@seagate.com
Acked-by: Stephen Smalley <sds@tycho.nsa.gov>
Signed-off-by: Paul Moore <pmoore@redhat.com>
security/selinux/include/classmap.h
security/selinux/include/security.h
security/selinux/selinuxfs.c
security/selinux/ss/services.c

index 5a4eef59aeff97676e8b2d2ef94bc45ca9315201..ef83c4b85a3305cb2a2f7bc0d474260721565ef3 100644 (file)
@@ -21,7 +21,7 @@ struct security_class_mapping secclass_map[] = {
          { "compute_av", "compute_create", "compute_member",
            "check_context", "load_policy", "compute_relabel",
            "compute_user", "setenforce", "setbool", "setsecparam",
-           "setcheckreqprot", "read_policy", NULL } },
+           "setcheckreqprot", "read_policy", "validate_trans", NULL } },
        { "process",
          { "fork", "transition", "sigchld", "sigkill",
            "sigstop", "signull", "signal", "ptrace", "getsched", "setsched",
index 223e9fd15d6651f16a776fcc19fee232ae802129..38feb55d531a3086a89d261c961ff54e58230d85 100644 (file)
@@ -187,6 +187,9 @@ int security_node_sid(u16 domain, void *addr, u32 addrlen,
 int security_validate_transition(u32 oldsid, u32 newsid, u32 tasksid,
                                 u16 tclass);
 
+int security_validate_transition_user(u32 oldsid, u32 newsid, u32 tasksid,
+                                     u16 tclass);
+
 int security_bounded_transition(u32 oldsid, u32 newsid);
 
 int security_sid_mls_copy(u32 sid, u32 mls_sid, u32 *new_sid);
index c02da25d7b631992aa7841ca98250b08b48d8477..0dc407dac3b9791c96d7c4b1ef4f7c57eedc7870 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 */
 };
 
@@ -653,6 +654,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
  */
@@ -1759,6 +1837,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);
index ebb5eb3c318c789922da4851b1b0507f28e7f831..ebda97333f1b707c9e05cba9278c1c9693492d0d 100644 (file)
@@ -778,8 +778,8 @@ out:
        return -EPERM;
 }
 
-int security_validate_transition(u32 oldsid, u32 newsid, u32 tasksid,
-                                u16 orig_tclass)
+static int security_compute_validatetrans(u32 oldsid, u32 newsid, u32 tasksid,
+                                         u16 orig_tclass, bool user)
 {
        struct context *ocontext;
        struct context *ncontext;
@@ -794,11 +794,12 @@ int security_validate_transition(u32 oldsid, u32 newsid, u32 tasksid,
 
        read_lock(&policy_rwlock);
 
-       tclass = unmap_class(orig_tclass);
+       if (!user)
+               tclass = unmap_class(orig_tclass);
+       else
+               tclass = orig_tclass;
 
        if (!tclass || tclass > policydb.p_classes.nprim) {
-               printk(KERN_ERR "SELinux: %s:  unrecognized class %d\n",
-                       __func__, tclass);
                rc = -EINVAL;
                goto out;
        }
@@ -832,8 +833,13 @@ int security_validate_transition(u32 oldsid, u32 newsid, u32 tasksid,
        while (constraint) {
                if (!constraint_expr_eval(ocontext, ncontext, tcontext,
                                          constraint->expr)) {
-                       rc = security_validtrans_handle_fail(ocontext, ncontext,
-                                                            tcontext, tclass);
+                       if (user)
+                               rc = -EPERM;
+                       else
+                               rc = security_validtrans_handle_fail(ocontext,
+                                                                    ncontext,
+                                                                    tcontext,
+                                                                    tclass);
                        goto out;
                }
                constraint = constraint->next;
@@ -844,6 +850,20 @@ out:
        return rc;
 }
 
+int security_validate_transition_user(u32 oldsid, u32 newsid, u32 tasksid,
+                                       u16 tclass)
+{
+       return security_compute_validatetrans(oldsid, newsid, tasksid,
+                                               tclass, true);
+}
+
+int security_validate_transition(u32 oldsid, u32 newsid, u32 tasksid,
+                                u16 orig_tclass)
+{
+       return security_compute_validatetrans(oldsid, newsid, tasksid,
+                                               orig_tclass, false);
+}
+
 /*
  * security_bounded_transition - check whether the given
  * transition is directed to bounded, or not.