التحقّق من SELinux

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

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

يطبع هذا الأمر وضع SELinux العام: إما Enforcing أو Permissive. لتحديد وضع 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_stream_socket مملوك من خلال 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. استخدِم 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 القابلة للتنفيذ.

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

يمكن إيقاف فرض استخدام 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

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

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

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