التحقّق من SELinux

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

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

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

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

تحقَّق من الأخطاء التي يتم توجيهها كسجلات أحداث إلى dmesg وlogcat ويمكن عرضها على الجهاز. على المصنّعين examiningفحص إخراج 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 (جهاز حرفي).

تفريغ حزم المستخدم والنواة

في بعض الحالات، لا تكون المعلومات الواردة في سجلّ الأحداث كافية لتحديد مصدر الرفض. غالبًا ما يكون من المفيد جمع سلسلة الاستدعاء، بما في ذلك kernel و مساحة المستخدم، لفهم سبب الرفض بشكل أفضل.

تحدِّد نواة التشغيل الحديثة نقطة تتبُّع باسم avc:selinux_audited. استخدِم Android simpleperf لتفعيل نقطة التتبُّع هذه وتسجيل سلسلة المكالمات.

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

  • نواة Linux الإصدار 5.10 والإصدارات الأحدث، ولا سيما فروع نواة Android الشائعة 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

سلسلة الاستدعاء أعلاه هي سلسلة استدعاء موحّدة للنواة ومساحة المستخدم. يمنحك ذلك تصوُّرًا أفضل لمسار الرمز البرمجي من خلال بدء التتبّع من مساحة المستخدم وصولاً إلى kernel حيث يحدث الرفض. لمزيد من المعلومات عن simpleperf، يُرجى الاطّلاع على مرجع أوامر Simpleperf القابلة للتنفيذ.

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

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

adb shell setenforce 0

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

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 إمكانية كتابة ذاكرة kernel، ما يشكّل ثغرة أمنية واضحة. غالبًا ما تكون عبارات audit2allow نقطة بدء فقط. بعد استخدام هذه العبارات، قد تحتاج إلى تغيير نطاق المصدر وتصنيف الهدف، بالإضافة إلى دمج وحدات الماكرو المناسبة، للوصول إلى سياسة جيدة. في بعض الأحيان، لا يؤدي الرفض الذي تتم مراجعته إلى إجراء أي تغييرات على السياسة على الإطلاق، بل يجب تعديل التطبيق المعني.