diff options
author | Stephen Smalley <sds@tycho.nsa.gov> | 2010-01-14 17:28:10 -0500 |
---|---|---|
committer | James Morris <jmorris@namei.org> | 2010-01-17 17:54:26 -0500 |
commit | 19439d05b88dafc4e55d9ffce84ccc27cf8b2bcc (patch) | |
tree | e529e1bbba49f30684c3b88a67df1d62ba3e11b1 /security | |
parent | 8d9525048c74786205b99f3fcd05a839721edfb7 (diff) |
selinux: change the handling of unknown classes
If allow_unknown==deny, SELinux treats an undefined kernel security
class as an error condition rather than as a typical permission denial
and thus does not allow permissions on undefined classes even when in
permissive mode. Change the SELinux logic so that this case is handled
as a typical permission denial, subject to the usual permissive mode and
permissive domain handling.
Also drop the 'requested' argument from security_compute_av() and
helpers as it is a legacy of the original security server interface and
is unused.
Changes:
- Handle permissive domains consistently by moving up the test for a
permissive domain.
- Make security_compute_av_user() consistent with security_compute_av();
the only difference now is that security_compute_av() performs mapping
between the kernel-private class and permission indices and the policy
values. In the userspace case, this mapping is handled by libselinux.
- Moved avd_init inside the policy lock.
Based in part on a patch by Paul Moore <paul.moore@hp.com>.
Reported-by: Andrew Worsley <amworsley@gmail.com>
Signed-off-by: Stephen D. Smalley <sds@tycho.nsa.gov>
Reviewed-by: Paul Moore <paul.moore@hp.com>
Signed-off-by: James Morris <jmorris@namei.org>
Diffstat (limited to 'security')
-rw-r--r-- | security/selinux/avc.c | 5 | ||||
-rw-r--r-- | security/selinux/include/security.h | 10 | ||||
-rw-r--r-- | security/selinux/selinuxfs.c | 7 | ||||
-rw-r--r-- | security/selinux/ss/services.c | 186 |
4 files changed, 88 insertions, 120 deletions
diff --git a/security/selinux/avc.c b/security/selinux/avc.c index f2dde268165a..3ee9b6a8beb6 100644 --- a/security/selinux/avc.c +++ b/security/selinux/avc.c | |||
@@ -746,9 +746,7 @@ int avc_has_perm_noaudit(u32 ssid, u32 tsid, | |||
746 | else | 746 | else |
747 | avd = &avd_entry; | 747 | avd = &avd_entry; |
748 | 748 | ||
749 | rc = security_compute_av(ssid, tsid, tclass, requested, avd); | 749 | security_compute_av(ssid, tsid, tclass, avd); |
750 | if (rc) | ||
751 | goto out; | ||
752 | rcu_read_lock(); | 750 | rcu_read_lock(); |
753 | node = avc_insert(ssid, tsid, tclass, avd); | 751 | node = avc_insert(ssid, tsid, tclass, avd); |
754 | } else { | 752 | } else { |
@@ -770,7 +768,6 @@ int avc_has_perm_noaudit(u32 ssid, u32 tsid, | |||
770 | } | 768 | } |
771 | 769 | ||
772 | rcu_read_unlock(); | 770 | rcu_read_unlock(); |
773 | out: | ||
774 | return rc; | 771 | return rc; |
775 | } | 772 | } |
776 | 773 | ||
diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h index 2553266ad793..022cf067aa3f 100644 --- a/security/selinux/include/security.h +++ b/security/selinux/include/security.h | |||
@@ -96,13 +96,11 @@ struct av_decision { | |||
96 | /* definitions of av_decision.flags */ | 96 | /* definitions of av_decision.flags */ |
97 | #define AVD_FLAGS_PERMISSIVE 0x0001 | 97 | #define AVD_FLAGS_PERMISSIVE 0x0001 |
98 | 98 | ||
99 | int security_compute_av(u32 ssid, u32 tsid, | 99 | void security_compute_av(u32 ssid, u32 tsid, |
100 | u16 tclass, u32 requested, | 100 | u16 tclass, struct av_decision *avd); |
101 | struct av_decision *avd); | ||
102 | 101 | ||
103 | int security_compute_av_user(u32 ssid, u32 tsid, | 102 | void security_compute_av_user(u32 ssid, u32 tsid, |
104 | u16 tclass, u32 requested, | 103 | u16 tclass, struct av_decision *avd); |
105 | struct av_decision *avd); | ||
106 | 104 | ||
107 | int security_transition_sid(u32 ssid, u32 tsid, | 105 | int security_transition_sid(u32 ssid, u32 tsid, |
108 | u16 tclass, u32 *out_sid); | 106 | u16 tclass, u32 *out_sid); |
diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c index fab36fdf2769..b7bb0f5ec07c 100644 --- a/security/selinux/selinuxfs.c +++ b/security/selinux/selinuxfs.c | |||
@@ -494,7 +494,6 @@ static ssize_t sel_write_access(struct file *file, char *buf, size_t size) | |||
494 | char *scon, *tcon; | 494 | char *scon, *tcon; |
495 | u32 ssid, tsid; | 495 | u32 ssid, tsid; |
496 | u16 tclass; | 496 | u16 tclass; |
497 | u32 req; | ||
498 | struct av_decision avd; | 497 | struct av_decision avd; |
499 | ssize_t length; | 498 | ssize_t length; |
500 | 499 | ||
@@ -512,7 +511,7 @@ static ssize_t sel_write_access(struct file *file, char *buf, size_t size) | |||
512 | goto out; | 511 | goto out; |
513 | 512 | ||
514 | length = -EINVAL; | 513 | length = -EINVAL; |
515 | if (sscanf(buf, "%s %s %hu %x", scon, tcon, &tclass, &req) != 4) | 514 | if (sscanf(buf, "%s %s %hu", scon, tcon, &tclass) != 3) |
516 | goto out2; | 515 | goto out2; |
517 | 516 | ||
518 | length = security_context_to_sid(scon, strlen(scon)+1, &ssid); | 517 | length = security_context_to_sid(scon, strlen(scon)+1, &ssid); |
@@ -522,9 +521,7 @@ static ssize_t sel_write_access(struct file *file, char *buf, size_t size) | |||
522 | if (length < 0) | 521 | if (length < 0) |
523 | goto out2; | 522 | goto out2; |
524 | 523 | ||
525 | length = security_compute_av_user(ssid, tsid, tclass, req, &avd); | 524 | security_compute_av_user(ssid, tsid, tclass, &avd); |
526 | if (length < 0) | ||
527 | goto out2; | ||
528 | 525 | ||
529 | length = scnprintf(buf, SIMPLE_TRANSACTION_LIMIT, | 526 | length = scnprintf(buf, SIMPLE_TRANSACTION_LIMIT, |
530 | "%x %x %x %x %u %x", | 527 | "%x %x %x %x %u %x", |
diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index 07ddc81d7b57..9ec24169ccd7 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c | |||
@@ -87,11 +87,10 @@ static u32 latest_granting; | |||
87 | static int context_struct_to_string(struct context *context, char **scontext, | 87 | static int context_struct_to_string(struct context *context, char **scontext, |
88 | u32 *scontext_len); | 88 | u32 *scontext_len); |
89 | 89 | ||
90 | static int context_struct_compute_av(struct context *scontext, | 90 | static void context_struct_compute_av(struct context *scontext, |
91 | struct context *tcontext, | 91 | struct context *tcontext, |
92 | u16 tclass, | 92 | u16 tclass, |
93 | u32 requested, | 93 | struct av_decision *avd); |
94 | struct av_decision *avd); | ||
95 | 94 | ||
96 | struct selinux_mapping { | 95 | struct selinux_mapping { |
97 | u16 value; /* policy value */ | 96 | u16 value; /* policy value */ |
@@ -196,23 +195,6 @@ static u16 unmap_class(u16 tclass) | |||
196 | return tclass; | 195 | return tclass; |
197 | } | 196 | } |
198 | 197 | ||
199 | static u32 unmap_perm(u16 tclass, u32 tperm) | ||
200 | { | ||
201 | if (tclass < current_mapping_size) { | ||
202 | unsigned i; | ||
203 | u32 kperm = 0; | ||
204 | |||
205 | for (i = 0; i < current_mapping[tclass].num_perms; i++) | ||
206 | if (tperm & (1<<i)) { | ||
207 | kperm |= current_mapping[tclass].perms[i]; | ||
208 | tperm &= ~(1<<i); | ||
209 | } | ||
210 | return kperm; | ||
211 | } | ||
212 | |||
213 | return tperm; | ||
214 | } | ||
215 | |||
216 | static void map_decision(u16 tclass, struct av_decision *avd, | 198 | static void map_decision(u16 tclass, struct av_decision *avd, |
217 | int allow_unknown) | 199 | int allow_unknown) |
218 | { | 200 | { |
@@ -532,7 +514,6 @@ out: | |||
532 | static void type_attribute_bounds_av(struct context *scontext, | 514 | static void type_attribute_bounds_av(struct context *scontext, |
533 | struct context *tcontext, | 515 | struct context *tcontext, |
534 | u16 tclass, | 516 | u16 tclass, |
535 | u32 requested, | ||
536 | struct av_decision *avd) | 517 | struct av_decision *avd) |
537 | { | 518 | { |
538 | struct context lo_scontext; | 519 | struct context lo_scontext; |
@@ -553,7 +534,6 @@ static void type_attribute_bounds_av(struct context *scontext, | |||
553 | context_struct_compute_av(&lo_scontext, | 534 | context_struct_compute_av(&lo_scontext, |
554 | tcontext, | 535 | tcontext, |
555 | tclass, | 536 | tclass, |
556 | requested, | ||
557 | &lo_avd); | 537 | &lo_avd); |
558 | if ((lo_avd.allowed & avd->allowed) == avd->allowed) | 538 | if ((lo_avd.allowed & avd->allowed) == avd->allowed) |
559 | return; /* no masked permission */ | 539 | return; /* no masked permission */ |
@@ -569,7 +549,6 @@ static void type_attribute_bounds_av(struct context *scontext, | |||
569 | context_struct_compute_av(scontext, | 549 | context_struct_compute_av(scontext, |
570 | &lo_tcontext, | 550 | &lo_tcontext, |
571 | tclass, | 551 | tclass, |
572 | requested, | ||
573 | &lo_avd); | 552 | &lo_avd); |
574 | if ((lo_avd.allowed & avd->allowed) == avd->allowed) | 553 | if ((lo_avd.allowed & avd->allowed) == avd->allowed) |
575 | return; /* no masked permission */ | 554 | return; /* no masked permission */ |
@@ -586,7 +565,6 @@ static void type_attribute_bounds_av(struct context *scontext, | |||
586 | context_struct_compute_av(&lo_scontext, | 565 | context_struct_compute_av(&lo_scontext, |
587 | &lo_tcontext, | 566 | &lo_tcontext, |
588 | tclass, | 567 | tclass, |
589 | requested, | ||
590 | &lo_avd); | 568 | &lo_avd); |
591 | if ((lo_avd.allowed & avd->allowed) == avd->allowed) | 569 | if ((lo_avd.allowed & avd->allowed) == avd->allowed) |
592 | return; /* no masked permission */ | 570 | return; /* no masked permission */ |
@@ -607,11 +585,10 @@ static void type_attribute_bounds_av(struct context *scontext, | |||
607 | * Compute access vectors based on a context structure pair for | 585 | * Compute access vectors based on a context structure pair for |
608 | * the permissions in a particular class. | 586 | * the permissions in a particular class. |
609 | */ | 587 | */ |
610 | static int context_struct_compute_av(struct context *scontext, | 588 | static void context_struct_compute_av(struct context *scontext, |
611 | struct context *tcontext, | 589 | struct context *tcontext, |
612 | u16 tclass, | 590 | u16 tclass, |
613 | u32 requested, | 591 | struct av_decision *avd) |
614 | struct av_decision *avd) | ||
615 | { | 592 | { |
616 | struct constraint_node *constraint; | 593 | struct constraint_node *constraint; |
617 | struct role_allow *ra; | 594 | struct role_allow *ra; |
@@ -622,19 +599,14 @@ static int context_struct_compute_av(struct context *scontext, | |||
622 | struct ebitmap_node *snode, *tnode; | 599 | struct ebitmap_node *snode, *tnode; |
623 | unsigned int i, j; | 600 | unsigned int i, j; |
624 | 601 | ||
625 | /* | ||
626 | * Initialize the access vectors to the default values. | ||
627 | */ | ||
628 | avd->allowed = 0; | 602 | avd->allowed = 0; |
629 | avd->auditallow = 0; | 603 | avd->auditallow = 0; |
630 | avd->auditdeny = 0xffffffff; | 604 | avd->auditdeny = 0xffffffff; |
631 | avd->seqno = latest_granting; | ||
632 | avd->flags = 0; | ||
633 | 605 | ||
634 | if (unlikely(!tclass || tclass > policydb.p_classes.nprim)) { | 606 | if (unlikely(!tclass || tclass > policydb.p_classes.nprim)) { |
635 | if (printk_ratelimit()) | 607 | if (printk_ratelimit()) |
636 | printk(KERN_WARNING "SELinux: Invalid class %hu\n", tclass); | 608 | printk(KERN_WARNING "SELinux: Invalid class %hu\n", tclass); |
637 | return -EINVAL; | 609 | return; |
638 | } | 610 | } |
639 | 611 | ||
640 | tclass_datum = policydb.class_val_to_struct[tclass - 1]; | 612 | tclass_datum = policydb.class_val_to_struct[tclass - 1]; |
@@ -705,9 +677,7 @@ static int context_struct_compute_av(struct context *scontext, | |||
705 | * permission and notice it to userspace via audit. | 677 | * permission and notice it to userspace via audit. |
706 | */ | 678 | */ |
707 | type_attribute_bounds_av(scontext, tcontext, | 679 | type_attribute_bounds_av(scontext, tcontext, |
708 | tclass, requested, avd); | 680 | tclass, avd); |
709 | |||
710 | return 0; | ||
711 | } | 681 | } |
712 | 682 | ||
713 | static int security_validtrans_handle_fail(struct context *ocontext, | 683 | static int security_validtrans_handle_fail(struct context *ocontext, |
@@ -886,110 +856,116 @@ out: | |||
886 | return rc; | 856 | return rc; |
887 | } | 857 | } |
888 | 858 | ||
889 | 859 | static void avd_init(struct av_decision *avd) | |
890 | static int security_compute_av_core(u32 ssid, | ||
891 | u32 tsid, | ||
892 | u16 tclass, | ||
893 | u32 requested, | ||
894 | struct av_decision *avd) | ||
895 | { | 860 | { |
896 | struct context *scontext = NULL, *tcontext = NULL; | 861 | avd->allowed = 0; |
897 | int rc = 0; | 862 | avd->auditallow = 0; |
898 | 863 | avd->auditdeny = 0xffffffff; | |
899 | scontext = sidtab_search(&sidtab, ssid); | 864 | avd->seqno = latest_granting; |
900 | if (!scontext) { | 865 | avd->flags = 0; |
901 | printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n", | ||
902 | __func__, ssid); | ||
903 | return -EINVAL; | ||
904 | } | ||
905 | tcontext = sidtab_search(&sidtab, tsid); | ||
906 | if (!tcontext) { | ||
907 | printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n", | ||
908 | __func__, tsid); | ||
909 | return -EINVAL; | ||
910 | } | ||
911 | |||
912 | rc = context_struct_compute_av(scontext, tcontext, tclass, | ||
913 | requested, avd); | ||
914 | |||
915 | /* permissive domain? */ | ||
916 | if (ebitmap_get_bit(&policydb.permissive_map, scontext->type)) | ||
917 | avd->flags |= AVD_FLAGS_PERMISSIVE; | ||
918 | |||
919 | return rc; | ||
920 | } | 866 | } |
921 | 867 | ||
868 | |||
922 | /** | 869 | /** |
923 | * security_compute_av - Compute access vector decisions. | 870 | * security_compute_av - Compute access vector decisions. |
924 | * @ssid: source security identifier | 871 | * @ssid: source security identifier |
925 | * @tsid: target security identifier | 872 | * @tsid: target security identifier |
926 | * @tclass: target security class | 873 | * @tclass: target security class |
927 | * @requested: requested permissions | ||
928 | * @avd: access vector decisions | 874 | * @avd: access vector decisions |
929 | * | 875 | * |
930 | * Compute a set of access vector decisions based on the | 876 | * Compute a set of access vector decisions based on the |
931 | * SID pair (@ssid, @tsid) for the permissions in @tclass. | 877 | * SID pair (@ssid, @tsid) for the permissions in @tclass. |
932 | * Return -%EINVAL if any of the parameters are invalid or %0 | ||
933 | * if the access vector decisions were computed successfully. | ||
934 | */ | 878 | */ |
935 | int security_compute_av(u32 ssid, | 879 | void security_compute_av(u32 ssid, |
936 | u32 tsid, | 880 | u32 tsid, |
937 | u16 orig_tclass, | 881 | u16 orig_tclass, |
938 | u32 orig_requested, | 882 | struct av_decision *avd) |
939 | struct av_decision *avd) | ||
940 | { | 883 | { |
941 | u16 tclass; | 884 | u16 tclass; |
942 | u32 requested; | 885 | struct context *scontext = NULL, *tcontext = NULL; |
943 | int rc; | ||
944 | 886 | ||
945 | read_lock(&policy_rwlock); | 887 | read_lock(&policy_rwlock); |
946 | 888 | avd_init(avd); | |
947 | if (!ss_initialized) | 889 | if (!ss_initialized) |
948 | goto allow; | 890 | goto allow; |
949 | 891 | ||
950 | requested = unmap_perm(orig_tclass, orig_requested); | 892 | scontext = sidtab_search(&sidtab, ssid); |
893 | if (!scontext) { | ||
894 | printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n", | ||
895 | __func__, ssid); | ||
896 | goto out; | ||
897 | } | ||
898 | |||
899 | /* permissive domain? */ | ||
900 | if (ebitmap_get_bit(&policydb.permissive_map, scontext->type)) | ||
901 | avd->flags |= AVD_FLAGS_PERMISSIVE; | ||
902 | |||
903 | tcontext = sidtab_search(&sidtab, tsid); | ||
904 | if (!tcontext) { | ||
905 | printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n", | ||
906 | __func__, tsid); | ||
907 | goto out; | ||
908 | } | ||
909 | |||
951 | tclass = unmap_class(orig_tclass); | 910 | tclass = unmap_class(orig_tclass); |
952 | if (unlikely(orig_tclass && !tclass)) { | 911 | if (unlikely(orig_tclass && !tclass)) { |
953 | if (policydb.allow_unknown) | 912 | if (policydb.allow_unknown) |
954 | goto allow; | 913 | goto allow; |
955 | rc = -EINVAL; | ||
956 | goto out; | 914 | goto out; |
957 | } | 915 | } |
958 | rc = security_compute_av_core(ssid, tsid, tclass, requested, avd); | 916 | context_struct_compute_av(scontext, tcontext, tclass, avd); |
959 | map_decision(orig_tclass, avd, policydb.allow_unknown); | 917 | map_decision(orig_tclass, avd, policydb.allow_unknown); |
960 | out: | 918 | out: |
961 | read_unlock(&policy_rwlock); | 919 | read_unlock(&policy_rwlock); |
962 | return rc; | 920 | return; |
963 | allow: | 921 | allow: |
964 | avd->allowed = 0xffffffff; | 922 | avd->allowed = 0xffffffff; |
965 | avd->auditallow = 0; | ||
966 | avd->auditdeny = 0xffffffff; | ||
967 | avd->seqno = latest_granting; | ||
968 | avd->flags = 0; | ||
969 | rc = 0; | ||
970 | goto out; | 923 | goto out; |
971 | } | 924 | } |
972 | 925 | ||
973 | int security_compute_av_user(u32 ssid, | 926 | void security_compute_av_user(u32 ssid, |
974 | u32 tsid, | 927 | u32 tsid, |
975 | u16 tclass, | 928 | u16 tclass, |
976 | u32 requested, | 929 | struct av_decision *avd) |
977 | struct av_decision *avd) | ||
978 | { | 930 | { |
979 | int rc; | 931 | struct context *scontext = NULL, *tcontext = NULL; |
980 | 932 | ||
981 | if (!ss_initialized) { | 933 | read_lock(&policy_rwlock); |
982 | avd->allowed = 0xffffffff; | 934 | avd_init(avd); |
983 | avd->auditallow = 0; | 935 | if (!ss_initialized) |
984 | avd->auditdeny = 0xffffffff; | 936 | goto allow; |
985 | avd->seqno = latest_granting; | 937 | |
986 | return 0; | 938 | scontext = sidtab_search(&sidtab, ssid); |
939 | if (!scontext) { | ||
940 | printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n", | ||
941 | __func__, ssid); | ||
942 | goto out; | ||
987 | } | 943 | } |
988 | 944 | ||
989 | read_lock(&policy_rwlock); | 945 | /* permissive domain? */ |
990 | rc = security_compute_av_core(ssid, tsid, tclass, requested, avd); | 946 | if (ebitmap_get_bit(&policydb.permissive_map, scontext->type)) |
947 | avd->flags |= AVD_FLAGS_PERMISSIVE; | ||
948 | |||
949 | tcontext = sidtab_search(&sidtab, tsid); | ||
950 | if (!tcontext) { | ||
951 | printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n", | ||
952 | __func__, tsid); | ||
953 | goto out; | ||
954 | } | ||
955 | |||
956 | if (unlikely(!tclass)) { | ||
957 | if (policydb.allow_unknown) | ||
958 | goto allow; | ||
959 | goto out; | ||
960 | } | ||
961 | |||
962 | context_struct_compute_av(scontext, tcontext, tclass, avd); | ||
963 | out: | ||
991 | read_unlock(&policy_rwlock); | 964 | read_unlock(&policy_rwlock); |
992 | return rc; | 965 | return; |
966 | allow: | ||
967 | avd->allowed = 0xffffffff; | ||
968 | goto out; | ||
993 | } | 969 | } |
994 | 970 | ||
995 | /* | 971 | /* |