Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

selinux: export validatetrans decisions

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>

authored by

Andrew Perepechko and committed by
Paul Moore
f9df6458 f39814f6

+111 -8
+1 -1
security/selinux/include/classmap.h
··· 21 21 { "compute_av", "compute_create", "compute_member", 22 22 "check_context", "load_policy", "compute_relabel", 23 23 "compute_user", "setenforce", "setbool", "setsecparam", 24 - "setcheckreqprot", "read_policy", NULL } }, 24 + "setcheckreqprot", "read_policy", "validate_trans", NULL } }, 25 25 { "process", 26 26 { "fork", "transition", "sigchld", "sigkill", 27 27 "sigstop", "signull", "signal", "ptrace", "getsched", "setsched",
+3
security/selinux/include/security.h
··· 187 187 int security_validate_transition(u32 oldsid, u32 newsid, u32 tasksid, 188 188 u16 tclass); 189 189 190 + int security_validate_transition_user(u32 oldsid, u32 newsid, u32 tasksid, 191 + u16 tclass); 192 + 190 193 int security_bounded_transition(u32 oldsid, u32 newsid); 191 194 192 195 int security_sid_mls_copy(u32 sid, u32 mls_sid, u32 *new_sid);
+80
security/selinux/selinuxfs.c
··· 116 116 SEL_DENY_UNKNOWN, /* export unknown deny handling to userspace */ 117 117 SEL_STATUS, /* export current status using mmap() */ 118 118 SEL_POLICY, /* allow userspace to read the in kernel policy */ 119 + SEL_VALIDATE_TRANS, /* compute validatetrans decision */ 119 120 SEL_INO_NEXT, /* The next inode number to use */ 120 121 }; 121 122 ··· 651 650 static const struct file_operations sel_checkreqprot_ops = { 652 651 .read = sel_read_checkreqprot, 653 652 .write = sel_write_checkreqprot, 653 + .llseek = generic_file_llseek, 654 + }; 655 + 656 + static ssize_t sel_write_validatetrans(struct file *file, 657 + const char __user *buf, 658 + size_t count, loff_t *ppos) 659 + { 660 + char *oldcon = NULL, *newcon = NULL, *taskcon = NULL; 661 + char *req = NULL; 662 + u32 osid, nsid, tsid; 663 + u16 tclass; 664 + int rc; 665 + 666 + rc = task_has_security(current, SECURITY__VALIDATE_TRANS); 667 + if (rc) 668 + goto out; 669 + 670 + rc = -ENOMEM; 671 + if (count >= PAGE_SIZE) 672 + goto out; 673 + 674 + /* No partial writes. */ 675 + rc = -EINVAL; 676 + if (*ppos != 0) 677 + goto out; 678 + 679 + rc = -ENOMEM; 680 + req = kzalloc(count + 1, GFP_KERNEL); 681 + if (!req) 682 + goto out; 683 + 684 + rc = -EFAULT; 685 + if (copy_from_user(req, buf, count)) 686 + goto out; 687 + 688 + rc = -ENOMEM; 689 + oldcon = kzalloc(count + 1, GFP_KERNEL); 690 + if (!oldcon) 691 + goto out; 692 + 693 + newcon = kzalloc(count + 1, GFP_KERNEL); 694 + if (!newcon) 695 + goto out; 696 + 697 + taskcon = kzalloc(count + 1, GFP_KERNEL); 698 + if (!taskcon) 699 + goto out; 700 + 701 + rc = -EINVAL; 702 + if (sscanf(req, "%s %s %hu %s", oldcon, newcon, &tclass, taskcon) != 4) 703 + goto out; 704 + 705 + rc = security_context_str_to_sid(oldcon, &osid, GFP_KERNEL); 706 + if (rc) 707 + goto out; 708 + 709 + rc = security_context_str_to_sid(newcon, &nsid, GFP_KERNEL); 710 + if (rc) 711 + goto out; 712 + 713 + rc = security_context_str_to_sid(taskcon, &tsid, GFP_KERNEL); 714 + if (rc) 715 + goto out; 716 + 717 + rc = security_validate_transition_user(osid, nsid, tsid, tclass); 718 + if (!rc) 719 + rc = count; 720 + out: 721 + kfree(req); 722 + kfree(oldcon); 723 + kfree(newcon); 724 + kfree(taskcon); 725 + return rc; 726 + } 727 + 728 + static const struct file_operations sel_transition_ops = { 729 + .write = sel_write_validatetrans, 654 730 .llseek = generic_file_llseek, 655 731 }; 656 732 ··· 1837 1759 [SEL_DENY_UNKNOWN] = {"deny_unknown", &sel_handle_unknown_ops, S_IRUGO}, 1838 1760 [SEL_STATUS] = {"status", &sel_handle_status_ops, S_IRUGO}, 1839 1761 [SEL_POLICY] = {"policy", &sel_policy_ops, S_IRUGO}, 1762 + [SEL_VALIDATE_TRANS] = {"validatetrans", &sel_transition_ops, 1763 + S_IWUGO}, 1840 1764 /* last one */ {""} 1841 1765 }; 1842 1766 ret = simple_fill_super(sb, SELINUX_MAGIC, selinux_files);
+27 -7
security/selinux/ss/services.c
··· 778 778 return -EPERM; 779 779 } 780 780 781 - int security_validate_transition(u32 oldsid, u32 newsid, u32 tasksid, 782 - u16 orig_tclass) 781 + static int security_compute_validatetrans(u32 oldsid, u32 newsid, u32 tasksid, 782 + u16 orig_tclass, bool user) 783 783 { 784 784 struct context *ocontext; 785 785 struct context *ncontext; ··· 794 794 795 795 read_lock(&policy_rwlock); 796 796 797 - tclass = unmap_class(orig_tclass); 797 + if (!user) 798 + tclass = unmap_class(orig_tclass); 799 + else 800 + tclass = orig_tclass; 798 801 799 802 if (!tclass || tclass > policydb.p_classes.nprim) { 800 - printk(KERN_ERR "SELinux: %s: unrecognized class %d\n", 801 - __func__, tclass); 802 803 rc = -EINVAL; 803 804 goto out; 804 805 } ··· 833 832 while (constraint) { 834 833 if (!constraint_expr_eval(ocontext, ncontext, tcontext, 835 834 constraint->expr)) { 836 - rc = security_validtrans_handle_fail(ocontext, ncontext, 837 - tcontext, tclass); 835 + if (user) 836 + rc = -EPERM; 837 + else 838 + rc = security_validtrans_handle_fail(ocontext, 839 + ncontext, 840 + tcontext, 841 + tclass); 838 842 goto out; 839 843 } 840 844 constraint = constraint->next; ··· 848 842 out: 849 843 read_unlock(&policy_rwlock); 850 844 return rc; 845 + } 846 + 847 + int security_validate_transition_user(u32 oldsid, u32 newsid, u32 tasksid, 848 + u16 tclass) 849 + { 850 + return security_compute_validatetrans(oldsid, newsid, tasksid, 851 + tclass, true); 852 + } 853 + 854 + int security_validate_transition(u32 oldsid, u32 newsid, u32 tasksid, 855 + u16 orig_tclass) 856 + { 857 + return security_compute_validatetrans(oldsid, newsid, tasksid, 858 + orig_tclass, false); 851 859 } 852 860 853 861 /*