SELinux'u doğrulama

Android, OEM'lerin SELinux uygulamalarını kapsamlı bir şekilde test etmelerini önemle tavsiye eder. Üreticiler SELinux'u uygularken önce yeni politikayı bir test cihazı grubuna uygulamalıdır.

Yeni bir politika uyguladıktan sonra getenforce komutunu vererek SELinux'un cihazda doğru modda çalıştığından emin olun.

Bu komut, global SELinux modunu (Enforcing veya Permissive) yazdırır. Her alanın SELinux modunu belirlemek için ilgili dosyaları incelemeniz veya /platform/system/sepolicy/tools/ içinde bulunan uygun (-p) işaretiyle sepolicy-analyze'nın en son sürümünü çalıştırmanız gerekir.

Okuma isteklerinin reddedilmesi

Hataları kontrol edin. Hatalar, dmesg ve logcat'ye etkinlik günlükleri olarak yönlendirilir ve cihazda yerel olarak görüntülenebilir. Üreticiler, bu cihazlarda dmesg için SELinux çıkışını incelemeli ve herkese açık sürümden önce izin verici modda ayarları hassaslaştırıp zorunlu kılma moduna geçmelidir. SELinux günlük iletileri avc: içerir ve bu nedenle grep ile kolayca bulunabilir. cat /proc/kmsg komutunu çalıştırarak devam eden reddetme günlüklerini veya cat /sys/fs/pstore/console-ramoops komutunu çalıştırarak önceki başlatma işleminden reddetme günlüklerini yakalamak mümkündür.

SELinux hata mesajları, günlüklerin aşırı yüklenmesini önlemek için başlatma işlemi tamamlandıktan sonra sıklık sınırlamasına tabi tutulur. Tüm alakalı mesajları gördüğünüzden emin olmak için adb shell auditctl -r 0 komutunu çalıştırarak bu özelliği devre dışı bırakabilirsiniz.

Bu çıktı sayesinde üreticiler, sistem kullanıcılarının veya bileşenlerinin SELinux politikasını ne zaman ihlal ettiğini kolayca belirleyebilir. Üreticiler daha sonra yazılımda, SELinux politikasında veya her ikisinde de değişiklik yaparak bu kötü davranışı düzeltebilir.

Bu günlük mesajları, özellikle hangi işlemlerin zorunlu kılma modunda başarısız olacağını ve neden başarısız olacağını gösterir. Örnek:

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

Bu çıkışı şu şekilde yorumlayın:

  • Yukarıdaki { connectto }, gerçekleştirilen işlemi gösterir. Sondaki tclass ile birlikte (unix_stream_socket), neyin neye uygulandığı hakkında kabaca bilgi verir. Bu durumda, bir şey bir Unix akış soketine bağlanmaya çalışıyordu.
  • scontext (u:r:shell:s0) simgesi, işlemi hangi bağlamın başlattığını gösterir. Bu durumda, kabuk olarak çalışan bir şeydir.
  • tcontext (u:r:netd:s0), işlemin hedefinin bağlamını gösterir. Bu durumda, bu netd tarafından sahip olunan bir unix_stream_socket'tir.
  • En üstteki comm="ping", ret oluşturulduğu sırada neyin çalıştırıldığı hakkında ek bir ipucu verir. Bu durumda, oldukça iyi bir ipucu verilir.

Başka bir örnek:

adb shell su root dmesg | grep 'avc: '

Ses çıkışı:

<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

Bu ret yanıtındaki temel unsurlar şunlardır:

  • İşlem: Denenen işlem parantez içinde vurgulanır, read write veya setenforce.
  • İşlemi gerçekleştiren: scontext (kaynak bağlamı) girişi, işlemi gerçekleştireni temsil eder. Bu örnekte, rmt_storage arka plan programı işlemi gerçekleştirir.
  • Nesne: tcontext (hedef bağlam) girişi, üzerinde işlem yapılan nesneyi (bu örnekte kmem) temsil eder.
  • Sonuç: tclass (hedef sınıf) girişi, üzerinde işlem yapılan nesnenin türünü gösterir. Bu örnekte chr_file (karakter cihazı) türü kullanılmıştır.

Kullanıcı ve çekirdek yığınlarını dökme

Bazı durumlarda, olay günlüğünde yer alan bilgiler, erişim isteğinin reddedilme nedenini belirlemek için yeterli değildir. Reddetme işleminin neden gerçekleştiğini daha iyi anlamak için genellikle çekirdek ve kullanıcı alanı dahil olmak üzere çağrı zincirini toplamak faydalıdır.

Son çekirdekler, avc:selinux_audited adlı bir izleme noktası tanımlar. Bu izleme noktasını etkinleştirmek ve çağrı zincirini yakalamak için Android simpleperf'i kullanın.

Desteklenen yapılandırma

  • Linux çekirdeği >= 5.10, özellikle Android Ortak Çekirdeği dalları mainline ve android12-5.10 desteklenir. android12-5.4 dalı da desteklenir. İzleme noktasının cihazınızda tanımlanıp tanımlanmadığını belirlemek için simpleperf kullanabilirsiniz: adb root && adb shell simpleperf list | grep avc:selinux_audited. Diğer çekirdek sürümleri için dd81662 ve 30969bc taahhütlerini seçebilirsiniz.
  • Hata ayıklamasını yaptığınız etkinliğin yeniden oluşturulabilmesi gerekir. Başlatma süresi etkinlikleri simpleperf kullanılarak desteklenmez. Ancak hizmeti yeniden başlatarak etkinliği tetikleyebilirsiniz.

Çağrı zincirini yakalama

İlk adım, etkinliği simpleperf record kullanarak kaydetmektir:

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

Ardından, reddedilmeye neden olan etkinlik tetiklenmelidir. Ardından kayıt durdurulmalıdır. Bu örnekte, Ctrl-c kullanılarak örnek yakalanmış olmalıdır:

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

Son olarak, yakalanan yığın izlemesini incelemek için simpleperf report kullanılabilir. Örneğin:

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

Yukarıdaki çağrı zinciri, birleşik bir çekirdek ve kullanıcı alanı çağrı zinciridir. İzlemeyi kullanıcı alanından başlayıp ret işleminin gerçekleştiği çekirdeğe kadar götürerek kod akışını daha iyi görmenizi sağlar. simpleperf hakkında daha fazla bilgi için Simpleperf Executable commands reference (Simpleperf Executable komutları referansı) başlıklı makaleyi inceleyin.

İzin vericiye geçiş yapma

SELinux zorunluluğu, userdebug veya eng derlemelerinde adb ile devre dışı bırakılabilir. Bunu yapmak için önce adb root komutunu çalıştırarak ADB'yi root'a geçirin. Ardından, SELinux zorunlu kılmasını devre dışı bırakmak için şu komutu çalıştırın:

adb shell setenforce 0

Veya çekirdek komut satırında (erken cihaz başlatma sırasında):

androidboot.selinux=permissive
androidboot.selinux=enforcing

Alternatif olarak Android 12'de bootconfig üzerinden:

androidboot.selinux=permissive
androidboot.selinux=enforcing

audit2allow aracını kullanma

audit2allow aracı, dmesg retlerini alır ve bunları karşılık gelen SELinux politika ifadelerine dönüştürür. Bu nedenle, SELinux geliştirmeyi büyük ölçüde hızlandırabilir.

Kullanmak için şunu çalıştırın:

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

Bununla birlikte, her olası eklemenin aşırı izinler açısından incelenmesine özen gösterilmelidir. Örneğin, daha önce gösterilen audit2allow the rmt_storage reddiyle ilgili feed'in kullanılması, aşağıdaki önerilen SELinux politikası ifadesiyle sonuçlanır:

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

Bu, rmt'ya çekirdek belleğine yazma olanağı tanır. Bu da büyük bir güvenlik açığıdır. audit2allow ifadeleri genellikle yalnızca bir başlangıç noktasıdır. Bu ifadeleri kullandıktan sonra iyi bir politika oluşturmak için kaynak alanını ve hedefin etiketini değiştirmeniz, ayrıca uygun makroları eklemeniz gerekebilir. Bazen incelenen ret, politika değişikliklerine yol açmamalıdır. Bunun yerine, ihlalde bulunan uygulama değiştirilmelidir.