| .. | .. |
|---|
| 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 | |
|---|