오늘은 안드로이드 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

'개발 이야기' 카테고리의 다른 글
How to fix (would clobber existing tag) (0) | 2023.07.06 |
---|---|
Android kernel gki skip option (0) | 2023.07.04 |
libc : Unable to set property "@vendor property" (0) | 2023.02.23 |
adb devices no permissions issue (0) | 2023.02.10 |
AGI (Android GPU Inspector) (0) | 2023.02.08 |