التحقّق من SELinux

تشجّع Android بشدة الشركات المصنّعة للأجهزة الأصلية على اختبار عمليات تنفيذ SELinux بدقة. عندما تنفّذ الشركات المصنّعة SELinux، عليها تطبيق السياسة الجديدة على مجموعة اختبارية من الأجهزة أولاً.

بعد تطبيق سياسة جديدة، تأكَّد من أنّ SELinux يعمل في الوضع الصحيح على الجهاز عن طريق تنفيذ الأمر getenforce.

يطبع هذا الأمر وضع SELinux العام: إما "فرض" أو "متساهل". لتحديد وضع SELinux لكل نطاق، عليك فحص الملفات المقابلة أو تشغيل أحدث إصدار من sepolicy-analyze باستخدام العلامة المناسبة (-p)، والموجودة في /platform/system/sepolicy/tools/.

قراءة عمليات الرفض

ابحث عن الأخطاء التي يتم توجيهها كسجلات أحداث إلى 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)، يوضّح لك ذلك بشكلٍ تقريبي ما تم تنفيذه وما تم تنفيذه عليه. في هذه الحالة، كان هناك إجراء يحاول الاتصال بمقبس تدفق Unix.
  • يخبرك scontext (u:r:shell:s0) السياق الذي بدأ الإجراء. في هذه الحالة، يكون هذا الإجراء شيئًا يتم تشغيله كصدفة.
  • يخبرك tcontext (u:r:netd:s0) سياق هدف الإجراء. في هذه الحالة، يكون ذلك مقبس تدفق Unix مملوكًا لـ netd.
  • يمنحك 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. استخدِم simpleperf من Android لتفعيل نقطة التتبُّع هذه وتسجيل سلسلة طلبات الإجراءات.

الإعدادات المتوافقة

  • يتوافق النظام مع Linux kernel >= 5.10، وخاصةً الفروع الرئيسية mainline و android12-5.10 من Android Common Kernel. يتوافق النظام أيضًا مع الفرع 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 القابلة للتنفيذ

التبديل إلى الوضع المتساهل

يمكن إيقاف فرض SELinux باستخدام adb على إصدارات userdebug أو eng. لإجراء ذلك، عليك أولاً تبديل ADB إلى الجذر عن طريق تشغيل adb root. بعد ذلك، لإيقاف فرض SELinux، شغِّل:

adb shell setenforce 0

أو في سطر أوامر النواة (أثناء عملية تشغيل الجهاز المبكرة):

androidboot.selinux=permissive
androidboot.selinux=enforcing

أو من خلال bootconfig في Android 12:

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

ومع ذلك، يجب توخي الحذر لفحص كل إضافة محتملة بحثًا عن الأذونات المفرطة. على سبيل المثال، يؤدي إدخال رفض rmt_storage الذي تم عرضه سابقًا إلى audit2allow إلى عبارة سياسة SELinux المقترَحة التالية:

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

سيمنح ذلك rmt إمكانية كتابة ذاكرة النواة، ما يمثّل ثغرة أمنية صارخة. غالبًا ما تكون عبارات audit2allow مجرد نقطة بداية. بعد استخدام هذه العبارات، قد تحتاج إلى تغيير نطاق المصدر وتصنيف الهدف، بالإضافة إلى دمج وحدات ماكرو مناسبة، للوصول إلى سياسة جيدة. في بعض الأحيان، يجب ألا يؤدي الرفض الذي يتم فحصه إلى أي تغييرات في السياسة على الإطلاق، بل يجب تغيير التطبيق المخالف.