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 | |
| 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>
| -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 | /* |
