안녕하세요. 앤디 라이언입니다.
오늘은 Android SELinux에 대해 이야기하고 싶습니다.
Android는 OEM이 SELinux 구현을 철저하게 테스트할 것을 강력히 권장합니다.
Android 플랫폼을 사용하는 제조업체는 SELinux를 구현하기 전에 테스트 장치 풀에 새 정책을 적용해야 합니다.
새 정책을 플랫폼에 적용한 후 온보딩 상태에서 힘 명령을 실행하여 장치에서 SELinux가 올바른 모드로 실행되고 있는지 확인할 수 있습니다.
거부 로그 읽기
오류 로그는 기본적으로 dmesg 그리고 로그캣 로 확인하실 수 있습니다.
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
이렇게 해서 로그를 추출하면
콘솔에서
이와 같이 avc: 거부됨 그런 일이 일어났다고 가정해 봅시다.
( 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 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
사실 온보드 디버깅의 중요한 내용은 커널 패닉/리셋/재부팅 시 로그를 추출하는 것입니다.
커널 패닉/리셋/재부팅 시 추출할 수 있도록 다음과 같이 로그를 추출할 수 있습니다.
고양이 /sys/fs/pstore/console-ramoops : 이전 부팅에서 남은 거부 로그를 캡처할 수 있습니다.
adb shell auditctl -r(속도) :SELinux 오류 메시지는 부팅이 완료된 후 로그 혼란을 피하기 위해 속도가 제한됩니다.
adb-쉘 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
출력 해석은 다음과 같습니다.
{ 읽다 } : 진행 중인 작업을 나타냅니다.
tclass=file로 무엇을 해야 하는지 알려줍니다.
scontext=hal_memtrack_default 작업을 시작한 컨텍스트를 나타냅니다.
tcontext=autoservice_app 작업 대상의 컨텍스트를 지정합니다.
여기 carservice_app이 소유한 파일입니다.
윗 부분 comm=”안드로이드.하드웨어” 거부 프로토콜이 생성되었을 때 수행된 작업에 대해 자세히 알아보십시오. 이 정보는 여기에서 매우 유용합니다.
이제 이 메시지를 수정하기 위해 무엇을 해야 하는지 봅시다.
# 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
이 거부 프로토콜의 주요 요소는 다음과 같습니다.
현재 작업: { 읽기 쓰기 }
액터(컨텍스트): rmt
객체(tcontext): kmem_device
결과(tclass): chr_file
그런 다음 사용자 모드에서 거부 프로토콜을 제거해 보겠습니다.
# rmt.te
allow rmt kmem_device:chr_file { read write };
# or
allow rmt kmem_device:chr_file rw_file_perms;
사용자/커널 스택 덤프
경우에 따라 이벤트 로그에 포함된 정보가 거부 소스를 확인하기에 불충분합니다.
커널 및 사용자 공간을 포함하여 호출 체인을 수집하면 거부의 원인을 이해하는 데 도움이 되는 경우가 많습니다.
호출 체인 캡처
첫 번째 단계는 다음과 같습니다 간단한 성능 녹음이벤트를 기록합니다.
simpleperf record -a -g -e avc:selinux_audited
공유 모드로 전환
Userdebug 또는 Eng 빌드에서 ADB를 통해 SELinux 적용을 비활성화할 수 있습니다.
먼저 그에게 adb 루트루트로 실행하여 ADB를 전환한 후 다음을 실행하여 SELinux 적용을 비활성화합니다.
$ setenforce 0 # enable permissive, disabled enforcing
# check to switching to permissive
$ getenforce
permissive
또는 장치를 처음으로 부팅할 때 커널 명령줄에서 다음을 실행할 수 있습니다.
또는 bootconfig 실행을 통해:
$ androidboot.selinux=permissive
$ androidboot.selinux=enforcing