SELinux की पुष्टि करना

Android, OEM को SELinux को पूरी तरह से लागू करने की जांच करने का ज़रूर सुझाव देता है. मैन्युफ़ैक्चरर, SELinux को लागू करते समय, पहले डिवाइसों के टेस्ट पूल पर नई नीति लागू कर सकते हैं.

नई नीति लागू करने के बाद, getenforce कमांड देकर पक्का करें कि डिवाइस पर SELinux सही मोड में चल रहा हो.

इससे ग्लोबल SELinux मोड प्रिंट होता है: लागू करने वाला या अनुमति देने वाला. हर डोमेन के लिए SELinux मोड तय करने के लिए, आपको उससे जुड़ी फ़ाइलों की जांच करनी होगी या /platform/system/sepolicy/tools/ में मौजूद सही (-p) फ़्लैग के साथ sepolicy-analyze का नया वर्शन चलाना होगा.

अनुमति न मिलने की जानकारी

गड़बड़ियों की जांच करें. इन्हें dmesg और logcat पर इवेंट लॉग के तौर पर भेजा जाता है. साथ ही, इन्हें डिवाइस पर स्थानीय तौर पर देखा जा सकता है. मैन्युफ़ैक्चरर को इन डिवाइसों पर, SELinux के आउटपुट को dmesg पर जांचना चाहिए. साथ ही, सार्वजनिक रिलीज़ से पहले, अनुमति वाले मोड में सेटिंग को बेहतर बनाना चाहिए और आखिर में, लागू करने वाले मोड पर स्विच करना चाहिए. SELinux लॉग मैसेज में avc: होता है. इसलिए, इन्हें grep की मदद से आसानी से ढूंढा जा सकता है. cat /proc/kmsg को चलाकर, अनुमति न मिलने के मौजूदा लॉग कैप्चर किए जा सकते हैं. इसके अलावा, cat /sys/fs/pstore/console-ramoops को चलाकर, पिछले बूट से मिले अनुमति न मिलने के लॉग कैप्चर किए जा सकते हैं.

SELinux की गड़बड़ी के मैसेज, बूट होने के बाद सीमित दर से दिखाए जाते हैं, ताकि लॉग में गड़बड़ी के मैसेज काफ़ी न हो जाएं. adb shell auditctl -r 0 को चलाकर, इस सुविधा को बंद किया जा सकता है, ताकि आपको काम के सभी मैसेज दिखें.

इस आउटपुट की मदद से, मैन्युफ़ैक्चरर आसानी से यह पता लगा सकते हैं कि सिस्टम के उपयोगकर्ता या घटक, SELinux नीति का उल्लंघन कब कर रहे हैं. इसके बाद, मैन्युफ़ैक्चरर इस गड़बड़ी को ठीक कर सकते हैं. इसके लिए, वे सॉफ़्टवेयर, SELinux नीति या दोनों में बदलाव कर सकते हैं.

खास तौर पर, इन लॉग मैसेज से पता चलता है कि लागू करने के मोड में कौनसी प्रोसेस काम नहीं करेंगी और क्यों. उदाहरण के लिए:

avc: denied  { connectto } for  pid=2671 comm="ping" path="/dev/socket/dnsproxyd"
scontext=u:r:shell:s0 tcontext=u:r:netd:s0 tclass=unix_stream_socket

इस आउटपुट को इस तरह समझें:

  • ऊपर दिए गए { connectto } से, की जा रही कार्रवाई के बारे में पता चलता है. आखिर में मौजूद tclass (unix_stream_socket) के साथ, इससे आपको यह पता चलता है कि किस डेटा पर कौनसी कार्रवाई की गई. इस मामले में, कोई चीज़ यूनिक्स स्ट्रीम सॉकेट से कनेक्ट करने की कोशिश कर रही थी.
  • scontext (u:r:shell:s0) से पता चलता है कि कार्रवाई किस संदर्भ में शुरू हुई. इस मामले में, यह शेल के तौर पर चल रहा है.
  • tcontext (u:r:netd:s0) से आपको ऐक्शन के टारगेट का कॉन्टेक्स्ट पता चलता है. इस मामले में, यह netd का मालिकाना हक वाला unix_stream_socket है.
  • सबसे ऊपर मौजूद comm="ping" से आपको यह पता चलता है कि अस्वीकार करने का मैसेज जनरेट होने के समय क्या चल रहा था. इस मामले में, यह एक अच्छा संकेत है.

एक और उदाहरण:

adb shell su root dmesg | grep 'avc: '

आउटपुट:

<5> type=1400 audit: avc:  denied  { read write } for  pid=177
comm="rmt_storage" name="mem" dev="tmpfs" ino=6004 scontext=u:r:rmt:s0
tcontext=u:object_r:kmem_device:s0 tclass=chr_file

यहां इस अस्वीकार करने की वजह से जुड़ी मुख्य बातें दी गई हैं:

  • कार्रवाई - कोशिश की गई कार्रवाई को ब्रैकेट, read write या setenforce में हाइलाइट किया जाता है.
  • ऐक्टर - scontext (सोर्स कॉन्टेक्स्ट) एंट्री, ऐक्टर को दिखाती है. इस मामले में, rmt_storage डेमन.
  • ऑब्जेक्ट - tcontext (टारगेट कॉन्टेक्स्ट) एंट्री, उस ऑब्जेक्ट को दिखाती है जिस पर कार्रवाई की जा रही है. इस मामले में, kmem.
  • नतीजा - tclass (टारगेट क्लास) एंट्री से पता चलता है कि किस तरह के ऑब्जेक्ट पर कार्रवाई की जा रही है. इस मामले में, chr_file (वर्ण डिवाइस).

उपयोगकर्ता और कर्नेल स्टैक को डंप करना

कुछ मामलों में, इवेंट लॉग में मौजूद जानकारी से, अनुरोध अस्वीकार होने की वजह का पता नहीं चलता. अक्सर, कॉल चेन इकट्ठा करना फ़ायदेमंद होता है. इसमें कर्नेल और यूज़रस्पेस भी शामिल होते हैं. इससे, यह समझने में मदद मिलती है कि अनुमति क्यों अस्वीकार की गई.

हाल ही के कर्नेल, avc:selinux_audited नाम का एक ट्रेसपॉइंट तय करते हैं. इस ट्रेसपॉइंट को चालू करने और कॉलचेन को कैप्चर करने के लिए, Android simpleperf का इस्तेमाल करें.

काम करने वाला कॉन्फ़िगरेशन

  • Linux kernel 5.10 और इसके बाद के वर्शन के साथ काम करता है. खास तौर पर, Android Common Kernel की शाखाएं mainline और android12-5.10 के साथ काम करता है. android12-5.4 शाखा भी काम करती है. simpleperf का इस्तेमाल करके यह पता लगाया जा सकता है कि आपके डिवाइस पर ट्रैसपॉइंट तय किया गया है या नहीं: adb root && adb shell simpleperf list | grep avc:selinux_audited. अन्य कर्नेल वर्शन के लिए, dd81662 और 30969bc को चुना जा सकता है.
  • जिस इवेंट को डीबग किया जा रहा है उसे फिर से चलाया जा सकता है. simpleperf का इस्तेमाल करके, बूट टाइम इवेंट रिकॉर्ड नहीं किए जा सकते. हालांकि, इवेंट को ट्रिगर करने के लिए, अब भी सेवा को फिर से शुरू किया जा सकता है.

कॉल चेन कैप्चर करना

पहला चरण, simpleperf record का इस्तेमाल करके इवेंट रिकॉर्ड करना है:

adb shell -t "cd /data/local/tmp && su root simpleperf record -a -g -e avc:selinux_audited"

इसके बाद, वह इवेंट ट्रिगर होना चाहिए जिसकी वजह से अनुरोध अस्वीकार हुआ था. इसके बाद, रिकॉर्डिंग बंद हो जाएगी. इस उदाहरण में, Ctrl-c का इस्तेमाल करके सैंपल कैप्चर किया जाना चाहिए:

^Csimpleperf I cmd_record.cpp:751] Samples recorded: 1. Samples lost: 0.

आखिर में, कैप्चर किए गए स्टैक ट्रेस की जांच करने के लिए, simpleperf report का इस्तेमाल किया जा सकता है. उदाहरण के लिए:

adb shell -t "cd /data/local/tmp && su root simpleperf report -g --full-callgraph"
[...]
Children  Self     Command  Pid   Tid   Shared Object                                   Symbol
100.00%   0.00%    dmesg    3318  3318  /apex/com.android.runtime/lib64/bionic/libc.so  __libc_init
       |
       -- __libc_init
          |
           -- main
              toybox_main
              toy_exec_which
              dmesg_main
              klogctl
              entry_SYSCALL_64_after_hwframe
              do_syscall_64
              __x64_sys_syslog
              do_syslog
              selinux_syslog
              slow_avc_audit
              common_lsm_audit
              avc_audit_post_callback
              avc_audit_post_callback

ऊपर दी गई कॉल चेन, यूनिफ़ाइड कर्नेल और यूज़रस्पेस कॉल चेन है. यह आपको कोड फ़्लो के बारे में बेहतर जानकारी देता है. इसके लिए, यह ट्रैक को यूज़रस्पेस से लेकर उस कर्नेल तक शुरू करता है जहां अनुमति अस्वीकार की जाती है. simpleperf के बारे में ज़्यादा जानकारी के लिए, Simpleperf के रन किए जा सकने वाले कमांड का रेफ़रंस देखें

अनुमति देने वाले मोड पर स्विच करना

userdebug या eng बिल्ड पर, adb की मदद से SELinux को बंद किया जा सकता है. ऐसा करने के लिए, पहले adb root चलाकर, ADB को रूट पर स्विच करें. इसके बाद, SELinux को लागू करने की सुविधा बंद करने के लिए, यह कमांड चलाएं:

adb shell setenforce 0

इसके अलावा, डिवाइस के चालू होने के दौरान, कर्नेल कमांड लाइन में भी यह जानकारी देखी जा सकती है:

androidboot.selinux=permissive
androidboot.selinux=enforcing

इसके अलावा, Android 12 में bootconfig की मदद से भी ऐसा किया जा सकता है:

androidboot.selinux=permissive
androidboot.selinux=enforcing

audit2allow का इस्तेमाल करना

audit2allow टूल, dmesg के अस्वीकार किए गए अनुरोधों को लेकर, उनसे मिलते-जुलते SELinux नीति स्टेटमेंट में बदलाव करता है. इसलिए, इससे SELinux के डेवलपमेंट में काफ़ी तेज़ी आ सकती है.

इसका इस्तेमाल करने के लिए, यह चलाएं:

adb pull /sys/fs/selinux/policy
adb logcat -b events -d | audit2allow -p policy

इसके बावजूद, हर संभावित जोड़ की जांच करके यह देखना ज़रूरी है कि उसमें ज़रूरत से ज़्यादा अनुमतियां तो नहीं मांगी गई हैं. उदाहरण के लिए, audit2allow को पहले दिखाए गए rmt_storage के नतीजे के तौर पर फ़ीड करने पर, SELinux नीति का यह सुझाव मिलता है:

#============= shell ==============
allow shell kernel:security setenforce;
#============= rmt ==============
allow rmt kmem_device:chr_file { read write };

इससे rmt को कर्नेल मेमोरी में लिखने की अनुमति मिल जाएगी, जो सुरक्षा के लिहाज़ से एक बड़ा खतरा है. अक्सर, audit2allow स्टेटमेंट सिर्फ़ शुरुआत करने के लिए होते हैं. इन स्टेटमेंट का इस्तेमाल करने के बाद, आपको टारगेट के सोर्स डोमेन और लेबल को बदलना पड़ सकता है. साथ ही, सही नीति बनाने के लिए, सही मैक्रो भी शामिल करने पड़ सकते हैं. कभी-कभी, अस्वीकार किए गए ऐप्लिकेशन की समीक्षा करने पर, नीति में कोई बदलाव नहीं करना पड़ता. इसके बजाय, उल्लंघन करने वाले ऐप्लिकेशन में बदलाव करना पड़ता है.