개발 이야기

Validating SELinux

ANDYLION 2023. 2. 23. 14:35
728x90
반응형

오늘은 안드로이드 SELinux에 대해 이야기해보고자 합니다.

Android는 OEM에서 자체 구현한 SELinux를 철저히 테스트할 것을 적극 권장합니다.

Android Platform을 이용하는 제조업체는 SELinux를 구현하려면 먼저 테스트 기기 풀에 새 정책을 적용해야 합니다.

플랫폼에 새 정책을 적용 후 온보드 상태에서 getenforce 명령어를 실행하여 SELinux가 기기에서 제대로 된 모드로 동작하는지 확인 할 수 있습니다.

 

거부 로그 판독

오류 로그는 기본적으로 dmesg logcat 을 이용하여 확인할 수 있습니다.

console:/ # dmesg | grep avc
avc: denied { create } for comm="kdevtmpfs" name="dm-2" scontext=u:r:kernel:s0 tcontext=u:object_r:device:s0 tclass=blk_file permissive=0
avc: denied { create } for comm="kdevtmpfs" name="dm-3" scontext=u:r:kernel:s0 tcontext=u:object_r:device:s0 tclass=blk_file permissive=0
avc: denied { create } for comm="kdevtmpfs" name="dm-4" scontext=u:r:kernel:s0 tcontext=u:object_r:device:s0 tclass=blk_file permissive=0

 

만약 온보드에서 테스트 중간에 진행 중인 거부 로그만 추출하고 싶다면 다음과 같이 수행하면 된다.

$ adb shell cat /proc/kmsg > avc.log

만약 다음과 같이 해서 로그를 추출할 경우

In console,

아래와 같이 avc : denied 가 발생했다고 가정해보자.

[  851.212937] type=1400 audit(1675128630.616:89): avc: denied { read } for comm="android.hardwar" scontext=u:r:hal_memtrack_default:s0 tcontext=u:r:carwatchdogd:s0 tclass=file permissive=0
[  851.216625] audit: audit_lost=35 audit_rate_limit=5 audit_backlog_limit=64
[  851.229797] type=1400 audit(1675128630.620:90): avc: denied { read } for comm="android.hardwar" scontext=u:r:hal_memtrack_default:s0 tcontext=u:r:servicemanager:s0 tclass=file permissive=0
[  851.236449] audit: rate limit exceeded
[  851.257330] type=1400 audit(1675128630.620:91): avc: denied { read } for comm="android.hardwar" scontext=u:r:hal_memtrack_default:s0 tcontext=u:r:hwservicemanager:s0 tclass=file permissive=0
[  851.274553] type=1400 audit(1675128630.620:92): avc: denied { read } for comm="android.hardwar" scontext=u:r:hal_memtrack_default:s0 tcontext=u:r:vndservicemanager:s0 tclass=file permissive=0
[  851.292043] type=1400 audit(1675128630.620:93): avc: denied { read } for comm="android.hardwar" scontext=u:r:hal_memtrack_default:s0 tcontext=u:r:carpowerpolicyd:s0 tclass=file permissive=0

 

In capturing file,

캡처한 파일을 열어보면 다음과 같이 저장된 것을 확인할 수 있다.

# in capture file <avc.log>
<36>[  851.212937] type=1400 audit(1675128630.616:89): avc: denied { read } for comm="android.hardwar" scontext=u:r:hal_memtrack_default:s0 tcontext=u:r:carwatchdogd:s0 tclass=file permissive=0
<4>[  851.216625] audit: audit_lost=35 audit_rate_limit=5 audit_backlog_limit=64
<36>[  851.229797] type=1400 audit(1675128630.620:90): avc: denied { read } for comm="android.hardwar" scontext=u:r:hal_memtrack_default:s0 tcontext=u:r:servicemanager:s0 tclass=file permissive=0
<3>[  851.236449] audit: rate limit exceeded
<36>[  851.257330] type=1400 audit(1675128630.620:91): avc: denied { read } for comm="android.hardwar" scontext=u:r:hal_memtrack_default:s0 tcontext=u:r:hwservicemanager:s0 tclass=file permissive=0
<36>[  851.274553] type=1400 audit(1675128630.620:92): avc: denied { read } for comm="android.hardwar" scontext=u:r:hal_memtrack_default:s0 tcontext=u:r:vndservicemanager:s0 tclass=file permissive=0

 

사실 온보드에서 디버깅할 때 중요한 내용은 kernel panic / reset / reboot 될 경우 어떻게 로그를 추출할 것인가이다.

kernel panic / reset / reboot 이 발생할 경우 추출할 수 있도록 다음과 같이 수행하여 로그를 추출할 수 있다.

cat /sys/fs/pstore/console-ramoops : 이전 부팅에서 남아 있는 거부 로그를 캡처하는 것이 가능합니다.

adb shell auditctl -r (rate) :SELinux 오류 메시지에는 부팅이 완료된 후 로그를 지나치게 채우지 않도록 속도 제한이 적용됩니다.

adb shell auditctl -r 0 : 이 제한을 해제할 수 있습니다.

SELinux 로그 메세지는 avc : 를 포함하므로 grep 명령어를 사용하여 거부 로그만 추출할 수 있습니다.

예를 들어,

console:/ # dmesg | grep avc
[    7.368515] audit: type=1400 audit(1675127786.772:5): avc:  denied  { create } for  pid=29 comm="kdevtmpfs" name="loop16" scontext=u:r:kernel:s0 tcontext=u:object_r:device:s0 tclass=blk_file permissive=0
[    7.404421] audit: type=1400 audit(1675127786.808:6): avc:  denied  { create } for  pid=29 comm="kdevtmpfs" name="loop17" scontext=u:r:kernel:s0 tcontext=u:object_r:device:s0 tclass=blk_file permissive=0
[    7.432445] audit: type=1400 audit(1675127786.836:7): avc:  denied  { create } for  pid=29 comm="kdevtmpfs" name="loop18" scontext=u:r:kernel:s0 tcontext=u:object_r:device:s0 tclass=blk_file permissive=0
[    7.464428] audit: type=1400 audit(1675127786.868:8): avc:  denied  { create } for  pid=29 comm="kdevtmpfs" name="loop19" scontext=u:r:kernel:s0 tcontext=u:object_r:device:s0 tclass=blk_file permissive=0

제조업체는 출력된 결과를 이용하여 시스템 사용자나 구성요소가 SELinux 정책을 위반했을 때 쉽게 식별할 수 있습니다. 그런 다음 소프트웨어와 SELinux 정책 중 하나 또는 둘 다를 변경하여 잘못된 동작을 바로잡을 수 있습니다.

특히 이러한 로그 메시지는 시행 모드에서 어떤 프로세스가 실패할지와 그 원인을 보여 줍니다. 

예를 들면 다음과 같습니다.

avc: denied { read } for comm="android.hardwar" 
scontext=u:r:hal_memtrack_default:s0 
tcontext=u:r:carservice_app:s0 tclass=file permissive=0

출력 내용의 해석은 다음과 같습니다.

{ read } : 실행 중인 작업을 나타냅니다. tclass=file와 함께 어떤 작업을 수행한지 알려줍니다.

scontext=hal_memtrack_default 는 어떤 컨텍스트가 작업을 시작했는지 알려줍니다.

tcontext=carservice_app 는 작업 대상의 컨텍스트를 알려줍니다. 여기서는 carservice_app이 소유한 file입니다.

윗부분의 comm="android.hardwar" 에서는 거부 로그가 생성되었을 때 실행 중이었던 작업에 관해 추가적으로 알아볼 수 있습니다. 여기서 이 정보는 매우 유용합니다.

그럼 이 메세지를 해결하기 위해서 필요한 작업은 무엇인지 확인해보겠습니다.

# In hal_memtrack_default.te

allow hal_memtrack_default carservice_app:file { read };
# or
allow hal_memtrack_default carservice_app:file r_file_perms;

 

두 번째 이슈를 살펴보겠습니다.

<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 }

행위자 (scontext) : rmt

객체 (tcontext) : kmem_device

결과 (tclass) : chr_file

그럼 user mode에서 거부 로그를 제거해보겠습니다.

# rmt.te

allow rmt kmem_device:chr_file { read write };
# or
allow rmt kmem_device:chr_file rw_file_perms;

 

User / Kernel stack dump

경우에 따라 이벤트 로그에 포함된 정보만으로는 거부 출처를 파악하기에 충분하지 않습니다. 커널 및 사용자 공간을 비롯한 호출 체인을 수집하면 거부가 발생한 원인을 이해하는 데 유용한 경우가 많습니다.

호출체인 캡처

첫 번째 단계는 다음과 같이 simpleperf record를 사용하여 이벤트를 기록하는 것입니다.

simpleperf record -a -g -e avc:selinux_audited

 

 

허용 모드로 전환

SELinux 시행은 userdebug 또는 eng 빌드에서 ADB를 통해 사용 중지할 수 있습니다. 이렇게 하려면 먼저 adb root를 실행하여 ADB를 루트로 전환합니다. 그런 다음 SELinux 시행을 사용 중지하려면 다음을 실행합니다.

$ setenforce 0 # enable permissive, disabled enforcing

# check to switching to permissive
$ getenforce
permissive

또는 초기에 기기를 불러오는 동안 커널 명령줄에서 다음을 실행합니다. 또는  bootconfig를 통해 다음을 실행합니다.

$ androidboot.selinux=permissive
$ androidboot.selinux=enforcing

 

728x90
반응형
SMALL