.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
---|
1 | 2 | /* |
---|
2 | 3 | * Implementation of the kernel access vector cache (AVC). |
---|
3 | 4 | * |
---|
.. | .. |
---|
8 | 9 | * Replaced the avc_lock spinlock by RCU. |
---|
9 | 10 | * |
---|
10 | 11 | * Copyright (C) 2003 Red Hat, Inc., James Morris <jmorris@redhat.com> |
---|
11 | | - * |
---|
12 | | - * This program is free software; you can redistribute it and/or modify |
---|
13 | | - * it under the terms of the GNU General Public License version 2, |
---|
14 | | - * as published by the Free Software Foundation. |
---|
15 | 12 | */ |
---|
16 | 13 | #include <linux/types.h> |
---|
17 | 14 | #include <linux/stddef.h> |
---|
.. | .. |
---|
34 | 31 | #include "avc_ss.h" |
---|
35 | 32 | #include "classmap.h" |
---|
36 | 33 | |
---|
| 34 | +#define CREATE_TRACE_POINTS |
---|
| 35 | +#include <trace/events/avc.h> |
---|
| 36 | + |
---|
37 | 37 | #define AVC_CACHE_SLOTS 512 |
---|
38 | 38 | #define AVC_DEF_CACHE_THRESHOLD 512 |
---|
39 | 39 | #define AVC_CACHE_RECLAIM 16 |
---|
.. | .. |
---|
43 | 43 | #else |
---|
44 | 44 | #define avc_cache_stats_incr(field) do {} while (0) |
---|
45 | 45 | #endif |
---|
| 46 | + |
---|
| 47 | +#undef CREATE_TRACE_POINTS |
---|
| 48 | +#include <trace/hooks/avc.h> |
---|
46 | 49 | |
---|
47 | 50 | struct avc_entry { |
---|
48 | 51 | u32 ssid; |
---|
.. | .. |
---|
127 | 130 | static inline int avc_hash(u32 ssid, u32 tsid, u16 tclass) |
---|
128 | 131 | { |
---|
129 | 132 | return (ssid ^ (tsid<<2) ^ (tclass<<4)) & (AVC_CACHE_SLOTS - 1); |
---|
130 | | -} |
---|
131 | | - |
---|
132 | | -/** |
---|
133 | | - * avc_dump_av - Display an access vector in human-readable form. |
---|
134 | | - * @tclass: target security class |
---|
135 | | - * @av: access vector |
---|
136 | | - */ |
---|
137 | | -static void avc_dump_av(struct audit_buffer *ab, u16 tclass, u32 av) |
---|
138 | | -{ |
---|
139 | | - const char **perms; |
---|
140 | | - int i, perm; |
---|
141 | | - |
---|
142 | | - if (av == 0) { |
---|
143 | | - audit_log_format(ab, " null"); |
---|
144 | | - return; |
---|
145 | | - } |
---|
146 | | - |
---|
147 | | - BUG_ON(!tclass || tclass >= ARRAY_SIZE(secclass_map)); |
---|
148 | | - perms = secclass_map[tclass-1].perms; |
---|
149 | | - |
---|
150 | | - audit_log_format(ab, " {"); |
---|
151 | | - i = 0; |
---|
152 | | - perm = 1; |
---|
153 | | - while (i < (sizeof(av) * 8)) { |
---|
154 | | - if ((perm & av) && perms[i]) { |
---|
155 | | - audit_log_format(ab, " %s", perms[i]); |
---|
156 | | - av &= ~perm; |
---|
157 | | - } |
---|
158 | | - i++; |
---|
159 | | - perm <<= 1; |
---|
160 | | - } |
---|
161 | | - |
---|
162 | | - if (av) |
---|
163 | | - audit_log_format(ab, " 0x%x", av); |
---|
164 | | - |
---|
165 | | - audit_log_format(ab, " }"); |
---|
166 | | -} |
---|
167 | | - |
---|
168 | | -/** |
---|
169 | | - * avc_dump_query - Display a SID pair and a class in human-readable form. |
---|
170 | | - * @ssid: source security identifier |
---|
171 | | - * @tsid: target security identifier |
---|
172 | | - * @tclass: target security class |
---|
173 | | - */ |
---|
174 | | -static void avc_dump_query(struct audit_buffer *ab, struct selinux_state *state, |
---|
175 | | - u32 ssid, u32 tsid, u16 tclass) |
---|
176 | | -{ |
---|
177 | | - int rc; |
---|
178 | | - char *scontext; |
---|
179 | | - u32 scontext_len; |
---|
180 | | - |
---|
181 | | - rc = security_sid_to_context(state, ssid, &scontext, &scontext_len); |
---|
182 | | - if (rc) |
---|
183 | | - audit_log_format(ab, "ssid=%d", ssid); |
---|
184 | | - else { |
---|
185 | | - audit_log_format(ab, "scontext=%s", scontext); |
---|
186 | | - kfree(scontext); |
---|
187 | | - } |
---|
188 | | - |
---|
189 | | - rc = security_sid_to_context(state, tsid, &scontext, &scontext_len); |
---|
190 | | - if (rc) |
---|
191 | | - audit_log_format(ab, " tsid=%d", tsid); |
---|
192 | | - else { |
---|
193 | | - audit_log_format(ab, " tcontext=%s", scontext); |
---|
194 | | - kfree(scontext); |
---|
195 | | - } |
---|
196 | | - |
---|
197 | | - BUG_ON(!tclass || tclass >= ARRAY_SIZE(secclass_map)); |
---|
198 | | - audit_log_format(ab, " tclass=%s", secclass_map[tclass-1].name); |
---|
199 | 133 | } |
---|
200 | 134 | |
---|
201 | 135 | /** |
---|
.. | .. |
---|
510 | 444 | |
---|
511 | 445 | static void avc_node_delete(struct selinux_avc *avc, struct avc_node *node) |
---|
512 | 446 | { |
---|
| 447 | + trace_android_vh_selinux_avc_node_delete(node); |
---|
513 | 448 | hlist_del_rcu(&node->list); |
---|
514 | 449 | call_rcu(&node->rhead, avc_node_free); |
---|
515 | 450 | atomic_dec(&avc->avc_cache.active_nodes); |
---|
.. | .. |
---|
526 | 461 | static void avc_node_replace(struct selinux_avc *avc, |
---|
527 | 462 | struct avc_node *new, struct avc_node *old) |
---|
528 | 463 | { |
---|
| 464 | + trace_android_vh_selinux_avc_node_replace(old, new); |
---|
529 | 465 | hlist_replace_rcu(&old->list, &new->list); |
---|
530 | 466 | call_rcu(&old->rhead, avc_node_free); |
---|
531 | 467 | atomic_dec(&avc->avc_cache.active_nodes); |
---|
.. | .. |
---|
634 | 570 | avc_cache_stats_incr(lookups); |
---|
635 | 571 | node = avc_search_node(avc, ssid, tsid, tclass); |
---|
636 | 572 | |
---|
637 | | - if (node) |
---|
| 573 | + if (node) { |
---|
| 574 | + trace_android_vh_selinux_avc_lookup(node, ssid, tsid, tclass); |
---|
638 | 575 | return node; |
---|
| 576 | + } |
---|
639 | 577 | |
---|
640 | 578 | avc_cache_stats_incr(misses); |
---|
641 | 579 | return NULL; |
---|
.. | .. |
---|
719 | 657 | } |
---|
720 | 658 | } |
---|
721 | 659 | hlist_add_head_rcu(&node->list, head); |
---|
| 660 | + trace_android_vh_selinux_avc_insert(node); |
---|
722 | 661 | found: |
---|
723 | 662 | spin_unlock_irqrestore(lock, flag); |
---|
724 | 663 | return node; |
---|
.. | .. |
---|
733 | 672 | static void avc_audit_pre_callback(struct audit_buffer *ab, void *a) |
---|
734 | 673 | { |
---|
735 | 674 | struct common_audit_data *ad = a; |
---|
736 | | - audit_log_format(ab, "avc: %s ", |
---|
737 | | - ad->selinux_audit_data->denied ? "denied" : "granted"); |
---|
738 | | - avc_dump_av(ab, ad->selinux_audit_data->tclass, |
---|
739 | | - ad->selinux_audit_data->audited); |
---|
740 | | - audit_log_format(ab, " for "); |
---|
| 675 | + struct selinux_audit_data *sad = ad->selinux_audit_data; |
---|
| 676 | + u32 av = sad->audited; |
---|
| 677 | + const char **perms; |
---|
| 678 | + int i, perm; |
---|
| 679 | + |
---|
| 680 | + audit_log_format(ab, "avc: %s ", sad->denied ? "denied" : "granted"); |
---|
| 681 | + |
---|
| 682 | + if (av == 0) { |
---|
| 683 | + audit_log_format(ab, " null"); |
---|
| 684 | + return; |
---|
| 685 | + } |
---|
| 686 | + |
---|
| 687 | + perms = secclass_map[sad->tclass-1].perms; |
---|
| 688 | + |
---|
| 689 | + audit_log_format(ab, " {"); |
---|
| 690 | + i = 0; |
---|
| 691 | + perm = 1; |
---|
| 692 | + while (i < (sizeof(av) * 8)) { |
---|
| 693 | + if ((perm & av) && perms[i]) { |
---|
| 694 | + audit_log_format(ab, " %s", perms[i]); |
---|
| 695 | + av &= ~perm; |
---|
| 696 | + } |
---|
| 697 | + i++; |
---|
| 698 | + perm <<= 1; |
---|
| 699 | + } |
---|
| 700 | + |
---|
| 701 | + if (av) |
---|
| 702 | + audit_log_format(ab, " 0x%x", av); |
---|
| 703 | + |
---|
| 704 | + audit_log_format(ab, " } for "); |
---|
741 | 705 | } |
---|
742 | 706 | |
---|
743 | 707 | /** |
---|
.. | .. |
---|
749 | 713 | static void avc_audit_post_callback(struct audit_buffer *ab, void *a) |
---|
750 | 714 | { |
---|
751 | 715 | struct common_audit_data *ad = a; |
---|
752 | | - audit_log_format(ab, " "); |
---|
753 | | - avc_dump_query(ab, ad->selinux_audit_data->state, |
---|
754 | | - ad->selinux_audit_data->ssid, |
---|
755 | | - ad->selinux_audit_data->tsid, |
---|
756 | | - ad->selinux_audit_data->tclass); |
---|
757 | | - if (ad->selinux_audit_data->denied) { |
---|
758 | | - audit_log_format(ab, " permissive=%u", |
---|
759 | | - ad->selinux_audit_data->result ? 0 : 1); |
---|
| 716 | + struct selinux_audit_data *sad = ad->selinux_audit_data; |
---|
| 717 | + char *scontext = NULL; |
---|
| 718 | + char *tcontext = NULL; |
---|
| 719 | + const char *tclass = NULL; |
---|
| 720 | + u32 scontext_len; |
---|
| 721 | + u32 tcontext_len; |
---|
| 722 | + int rc; |
---|
| 723 | + |
---|
| 724 | + rc = security_sid_to_context(sad->state, sad->ssid, &scontext, |
---|
| 725 | + &scontext_len); |
---|
| 726 | + if (rc) |
---|
| 727 | + audit_log_format(ab, " ssid=%d", sad->ssid); |
---|
| 728 | + else |
---|
| 729 | + audit_log_format(ab, " scontext=%s", scontext); |
---|
| 730 | + |
---|
| 731 | + rc = security_sid_to_context(sad->state, sad->tsid, &tcontext, |
---|
| 732 | + &tcontext_len); |
---|
| 733 | + if (rc) |
---|
| 734 | + audit_log_format(ab, " tsid=%d", sad->tsid); |
---|
| 735 | + else |
---|
| 736 | + audit_log_format(ab, " tcontext=%s", tcontext); |
---|
| 737 | + |
---|
| 738 | + tclass = secclass_map[sad->tclass-1].name; |
---|
| 739 | + audit_log_format(ab, " tclass=%s", tclass); |
---|
| 740 | + |
---|
| 741 | + if (sad->denied) |
---|
| 742 | + audit_log_format(ab, " permissive=%u", sad->result ? 0 : 1); |
---|
| 743 | + |
---|
| 744 | + trace_selinux_audited(sad, scontext, tcontext, tclass); |
---|
| 745 | + kfree(tcontext); |
---|
| 746 | + kfree(scontext); |
---|
| 747 | + |
---|
| 748 | + /* in case of invalid context report also the actual context string */ |
---|
| 749 | + rc = security_sid_to_context_inval(sad->state, sad->ssid, &scontext, |
---|
| 750 | + &scontext_len); |
---|
| 751 | + if (!rc && scontext) { |
---|
| 752 | + if (scontext_len && scontext[scontext_len - 1] == '\0') |
---|
| 753 | + scontext_len--; |
---|
| 754 | + audit_log_format(ab, " srawcon="); |
---|
| 755 | + audit_log_n_untrustedstring(ab, scontext, scontext_len); |
---|
| 756 | + kfree(scontext); |
---|
| 757 | + } |
---|
| 758 | + |
---|
| 759 | + rc = security_sid_to_context_inval(sad->state, sad->tsid, &scontext, |
---|
| 760 | + &scontext_len); |
---|
| 761 | + if (!rc && scontext) { |
---|
| 762 | + if (scontext_len && scontext[scontext_len - 1] == '\0') |
---|
| 763 | + scontext_len--; |
---|
| 764 | + audit_log_format(ab, " trawcon="); |
---|
| 765 | + audit_log_n_untrustedstring(ab, scontext, scontext_len); |
---|
| 766 | + kfree(scontext); |
---|
760 | 767 | } |
---|
761 | 768 | } |
---|
762 | 769 | |
---|
.. | .. |
---|
768 | 775 | { |
---|
769 | 776 | struct common_audit_data stack_data; |
---|
770 | 777 | struct selinux_audit_data sad; |
---|
| 778 | + |
---|
| 779 | + if (WARN_ON(!tclass || tclass >= ARRAY_SIZE(secclass_map))) |
---|
| 780 | + return -EINVAL; |
---|
771 | 781 | |
---|
772 | 782 | if (!a) { |
---|
773 | 783 | a = &stack_data; |
---|
.. | .. |
---|
1052 | 1062 | int rc = 0, rc2; |
---|
1053 | 1063 | |
---|
1054 | 1064 | xp_node = &local_xp_node; |
---|
1055 | | - BUG_ON(!requested); |
---|
| 1065 | + if (WARN_ON(!requested)) |
---|
| 1066 | + return -EACCES; |
---|
1056 | 1067 | |
---|
1057 | 1068 | rcu_read_lock(); |
---|
1058 | 1069 | |
---|
.. | .. |
---|
1142 | 1153 | int rc = 0; |
---|
1143 | 1154 | u32 denied; |
---|
1144 | 1155 | |
---|
1145 | | - BUG_ON(!requested); |
---|
| 1156 | + if (WARN_ON(!requested)) |
---|
| 1157 | + return -EACCES; |
---|
1146 | 1158 | |
---|
1147 | 1159 | rcu_read_lock(); |
---|
1148 | 1160 | |
---|