SELinux 정책파일(.te) 사용법
안녕하세요? 고무다라입니다. 최근 안드로이드 5.0 롤리팝(Lollipop)이 릴리즈 되었죠? 이와 관련하여 SElinux의 정책파일인 .te에 대해 사용법을 찾다가 아래와 같이 정리하게 되었네요. SElinux를 포팅하는게 아니라면 주로 다루게 될 부분이 정책만 조정하는 것이기에, 정책파일 사용법에 대하여 알아보고자 합니다.
SELinux와 유사하게 SEAndroid가 있는데, 이는 Security Enhancements for Android 라고 하며, 안드로이드를 위한 개선된 보안기능이라 할 수 있겠습니다. 즉 SELinux가 확장된 개념이라 할 수 있으며, SEAndroid의 Enforcing Mode는 안드로이드 킷캣(KitKat)에 추가된 주요기능 중의 하나 입니다. 이것이 의미하는 바는 안드로이드의 모든 컴포넌트의 액세스 권한을 SEAndroid의 통제아래 두겠다는 의미라고 할 수 있습니다. 1
|
1. 정책파일의 위치 - Nexus5(hammerhead) 기준 |
먼저 롤리팝의 소스는 아래에서 받으시면 됩니다. 브랜치는 상위꺼나 릴리즈 버전을 받으셔도 될것 같습니다.
Nexus5는 햄머헤드(hammerhead)로 불리며, device/lge/hammerhead/sepolicy/ 에 정책파일이 있습니다. 사용되는 .te 파일 리스트는 BoardConfig.mk 파일에서 보면 아래와 같습니다.
lge/hammerhead/BoardConfig.mk:
...
BOARD_SEPOLICY_DIRS += \device/lge/hammerhead/sepolicy
# The list below is order dependent
BOARD_SEPOLICY_UNION += \
app.te \
bluetooth_loader.te \
bridge.te \
camera.te \
device.te \
domain.te \
file.te \
hostapd.te \
irsc_util.te \
mediaserver.te \
mpdecision.te \
netmgrd.te \
platform_app.te \
qmux.te \
radio.te \
rild.te \
rmt.te \
sensors.te \
ssr.te \
surfaceflinger.te \
system_server.te \
tee.te \
thermald.te \
time.te \
ueventd.te \
vss.te \
wpa.te \
file_contexts \
genfs_contexts \
te_macros
BOARD_SEPOLICY_DIRS 과 BOARD_SEPOLICY_UNION 의 사용은 external/sepolicy에서 사용되고 있습니다. 또한 기본 SELinux 정책 버전은 POLICYVERS ?= 26으로 커널버전 3.0이상을 요구 합니다.
external/sepolicy/Android.mk:
# SELinux policy version.
# Must be <= /selinux/policyvers reported by the Android kernel.
# Must be within the compatibility range reported by checkpolicy -V.
POLICYVERS ?= 26
# Quick edge case error detection for BOARD_SEPOLICY_REPLACE.
# Builds the singular path for each replace file.
sepolicy_replace_paths :=
$(foreach pf, $(BOARD_SEPOLICY_REPLACE), \
$(if $(filter $(pf), $(BOARD_SEPOLICY_UNION)), \
$(error Ambiguous request for sepolicy $(pf). Appears in both \
BOARD_SEPOLICY_REPLACE and BOARD_SEPOLICY_UNION), \
) \
$(eval _paths := $(filter-out $(BOARD_SEPOLICY_IGNORE), \
$(wildcard $(addsuffix /$(pf), $(BOARD_SEPOLICY_DIRS))))) \
$(eval _occurrences := $(words $(_paths))) \
$(if $(filter 0,$(_occurrences)), \
$(error No sepolicy file found for $(pf) in $(BOARD_SEPOLICY_DIRS)), \
) \
$(if $(filter 1, $(_occurrences)), \
$(eval sepolicy_replace_paths += $(_paths)), \
$(error Multiple occurrences of replace file $(pf) in $(_paths)) \
) \
$(if $(filter 0, $(words $(wildcard $(addsuffix /$(pf), $(LOCAL_PATH))))), \
$(error Specified the sepolicy file $(pf) in BOARD_SEPOLICY_REPLACE, \
but none found in $(LOCAL_PATH)), \
) \
)
# Quick edge case error detection for BOARD_SEPOLICY_UNION.
# This ensures that a requested union file exists somewhere
# in one of the listed BOARD_SEPOLICY_DIRS.
$(foreach pf, $(BOARD_SEPOLICY_UNION), \
$(if $(filter 0, $(words $(wildcard $(addsuffix /$(pf), $(BOARD_SEPOLICY_DIRS))))), \
$(error No sepolicy file found for $(pf) in $(BOARD_SEPOLICY_DIRS)), \
) \
)
# Builds paths for all requested policy files w.r.t
# both BOARD_SEPOLICY_REPLACE and BOARD_SEPOLICY_UNION
# product variables.
# $(1): the set of policy name paths to build
build_policy = $(foreach type, $(1), \
$(filter-out $(BOARD_SEPOLICY_IGNORE), \
$(foreach expanded_type, $(notdir $(wildcard $(addsuffix /$(type), $(LOCAL_PATH)))), \
$(if $(filter $(expanded_type), $(BOARD_SEPOLICY_REPLACE)), \
$(wildcard $(addsuffix $(expanded_type), $(sort $(dir $(sepolicy_replace_paths))))), \
$(LOCAL_PATH)/$(expanded_type) \
) \
) \
$(foreach union_policy, $(wildcard $(addsuffix /$(type), $(BOARD_SEPOLICY_DIRS))), \
$(if $(filter $(notdir $(union_policy)), $(BOARD_SEPOLICY_UNION)), \
$(union_policy), \
) \
) \
) \
)
...
여기서 보면 4가지의 변수가 사용되는데 /external/sepolicy/README를 보면, 각각의 의미는 아래와 같습니다.
1. BOARD_SEPOLICY_REPLACE :
2. BOARD_SEPOLICY_UNION :
3. BOARD_SEPOLICY_DIRS :
out/target/product/<device>/etc/sepolicy_intermediates/policy.conf 에서 확인할 수 있습니다.
4. BOARD_SEPOLICY_IGNORE :
예로)
BOARD_SEPOLICY_DIRS += X Y
BOARD_SEPOLICY_REPLACE += A
BOARD_SEPOLICY_IGNORE += X/A
폴더 X, Y가 있고, A의 파일을 치환하면, X/A, Y/A가 되며,
여기서 X/A를 제거하면 결국 Y/A만 남게 됩니다.
관련한 커널 Config 설정값은 아래와 같습니다. 2linario에 적용된 것으로 테스트는 해 보지 않았습니다. 하지만 UBER-L/arch/arm/configs/hammerhead_defconfig 3를 확인해 보니 모두 적용되어 있습니다.
+CONFIG_NF_CONNTRACK_SECMARK=y
+CONFIG_NETFILTER_XT_TARGET_CONNSECMARK=y
+CONFIG_NETFILTER_XT_TARGET_SECMARK=y
+CONFIG_IP_NF_SECURITY=y
+CONFIG_SECURITY=y
+CONFIG_SECURITY_NETWORK=y
+CONFIG_LSM_MMAP_MIN_ADDR=4096
+CONFIG_SECURITY_SELINUX=y
|
2. 초기화 코드 확인 |
system/core/init/init.c의 main() 함수에서 selinux_initialize()의 호출로 SElinux의 초기화가 시작됩니다.
system/core/init/init.c:
{
...
union selinux_callback cb;
cb.func_log = log_callback;
selinux_set_callback(SELINUX_CB_LOG, cb);
cb.func_audit = audit_callback;
selinux_set_callback(SELINUX_CB_AUDIT, cb);
selinux_initialize();
...
return 0;
}
▶selinux_initialize()는 SElinux 관련 함수 초기화를 수행합니다. SElinux 가 disable 되었는지의 여부를 먼저 확인후, 정책파일을 읽은 후, 오류가 있으면 recovery 모드로 안드로이드를 재 부팅 시킵니다. 정상적으로 정책파일이 로드되었으면, SElinux 관련 모든 핸들러들을 초기화 시키고, enforcing 모드인지 확인후 현재 모드로 재 설정을 하게 됩니다.
system/core/init/init.c:
{
if (selinux_is_disabled()) {
return;
}
INFO("loading selinux policy\n");
if (selinux_android_load_policy() < 0) {
ERROR("SELinux: Failed to load policy; rebooting into recovery mode\n");
android_reboot(ANDROID_RB_RESTART2, 0, "recovery");
while (1) { pause(); } // never reached
}
selinux_init_all_handles();
bool is_enforcing = selinux_is_enforcing();
INFO("SELinux: security_setenforce(%d)\n", is_enforcing);
security_setenforce(is_enforcing);
}
▶selinux_is_disabled(void) 함수는 Android.mk의 -DALLOW_DISABLE_SELINUX 의 컴파일러 외부정의에 의하여 설정되며, userdebug, eng 모드일 경우 활성화 됩니다. 활성화 체크 하는 방법은 /sys/fs/selinux 폴더를 체크 하거나, property값중 ro.boot.selinux의 값을 읽어 확인합니다.
system/core/init/init.c:
static bool selinux_is_disabled(void)
{
#ifdef ALLOW_DISABLE_SELINUX
char tmp[PROP_VALUE_MAX];
if (access("/sys/fs/selinux", F_OK) != 0) {
/* SELinux is not compiled into the kernel, or has been disabled
* via the kernel command line "selinux=0".
*/
return true;
}
if ((property_get("ro.boot.selinux", tmp) != 0) && (strcmp(tmp, "disabled") == 0)) {
/* SELinux is compiled into the kernel, but we've been told to disable it. */
return true;
}
#endif
return false;
}
system/core/init/Android.mk:
...
ifneq (,$(filter userdebug eng,$(TARGET_BUILD_VARIANT)))LOCAL_CFLAGS += -DALLOW_LOCAL_PROP_OVERRIDE=1 -DALLOW_DISABLE_SELINUX=1
endif
...
▶selinux_android_load_policy()은 external/libselinux/src/android.c에 라이브러리 형태로 위치 하고 있으며, 내부적으로는 set_selinuxmnt() 호출 후 selinux_android_load_policy_helper() 를 호출합니다.
external/libselinux/src/android.c:
int selinux_android_load_policy(void)
{
const char *mnt = SELINUXMNT;
int rc;
rc = mount(SELINUXFS, mnt, SELINUXFS, 0, NULL);
if (rc < 0) {
if (errno == ENODEV) {
/* SELinux not enabled in kernel */
return -1;
}
if (errno == ENOENT) {
/* Fall back to legacy mountpoint. */
mnt = OLDSELINUXMNT;
rc = mkdir(mnt, 0755);
if (rc == -1 && errno != EEXIST) {
selinux_log(SELINUX_ERROR,"SELinux: Could not mkdir: %s\n",
strerror(errno));
return -1;
}
rc = mount(SELINUXFS, mnt, SELINUXFS, 0, NULL);
}
}
if (rc < 0) {
selinux_log(SELINUX_ERROR,"SELinux: Could not mount selinuxfs: %s\n",
strerror(errno));
return -1;
}
set_selinuxmnt(mnt);
return selinux_android_load_policy_helper(false);
}
▶selinux_android_load_policy_helper()은 정책 로딩을 위한 메모리 맵핑을 하고, security_load_policy() 호출에 의해 정책이 로드 된다.
external/libselinux/src/android.c:
{
...
map = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
...
rc = security_load_policy(map, sb.st_size);
...
return 0;
}
▶security_load_policy()는 selinux_mnt 폴더의 load 파일을 열어 정책파일을 기록한다.
/external/libselinux/src/policy.h:
/external/libselinux/src/load_policy.c:
int security_load_policy(void *data, size_t len)
{
...
snprintf(path, sizeof path, "%s/load", selinux_mnt);
fd = open(path, O_RDWR);
if (fd < 0)
return -1;
ret = write(fd, data, len);
...
return 0;
}
▶selinux_init_all_handles는 sehandle와 sehandle_prop 핸들을 설정하며, 관련한 파일은 아래와 같다.
/external/libselinux/src/android.c:
#define POLICY_BASE_VERSION "/selinux_version"
▶selinux_is_enforcing()은 enforcing이 설정되었는지 체크 하며, 기본값은 enforcing 모드이다. 확인 방법은 ro.boot.selinux의 값을 읽고, 설정이 안되었으면 enforcing 모드라 가정하며, permissive 모드이면 fasle를 반환한다. 그외 나머지 값은 enforcing 모드라고 가정한다.
system/core/init/init.c:
{
#ifdef ALLOW_DISABLE_SELINUX
char tmp[PROP_VALUE_MAX];
if (property_get("ro.boot.selinux", tmp) == 0) {
/* Property is not set. Assume enforcing */
return true;
}
if (strcmp(tmp, "permissive") == 0) {
/* SELinux is in the kernel, but we've been told to go into permissive mode */
return false;
}
if (strcmp(tmp, "enforcing") != 0) {
ERROR("SELinux: Unknown value of ro.boot.selinux. Got: \"%s\". Assuming enforcing.\n", tmp);
}
#endif
return true;
}
▶security_setenforce() 호출을 마지막으로 selinux_mnt의 enforce 파일에 enforcing 모드값을 설정하고 초기화를 완료한다.
external/libselinux/src/setenforce.c:
{
...
snprintf(path, sizeof path, "%s/enforce", selinux_mnt);
fd = open(path, O_RDWR);
if (fd < 0)
return -1;
snprintf(buf, sizeof buf, "%d", value);
ret = write(fd, buf, strlen(buf));
...
return 0;
}
|
3. 정책파일 .te 의 사용법 |
그럼 sepolicy에 있는 .te 파일에 정책설정하는 방법에 대하여 알아보겠습니다. 사용법에 관한 내용은 아래 NSA 사이트를 참고하였으며, TE 설정파일의 예를 보면 아래와 같습니다. 4
Table 7. TE Configuration Files
Filename | Description |
---|---|
tunables/*.tun | Defines policy tunables for customization. |
attrib.te | Defines type attributes. |
macros/program/*.te | Defines macros for specific program domains. |
macros/*.te | Defines commonly used macros. |
types/*.te | Defines general types. |
domains/user.te | Defines unprivileged user domains. |
domains/admin.te | Defines administrator domains. |
domains/misc/*.te | Defines miscellaneous domains not associated with a particular program. |
domains/program/*.te | Defines domains for specific programs. |
domains/program/unused/*.te | Optional domains for further programs. |
assert.te | Defines assertions on the TE configuration. |
이를 기준으로 햄머헤드의 BOARD_SEPOLICY_UNION 설정파일중 몇개를 확인해 보도록 하겠습니다.
attrib.te 파일은 attributes을 정의한 것으로 되어 있으나, android-5.0.0_r2
에는 attrib.te 대신 external/sepolicy/attributes의 파일이 있습니다. dev_type의 속성은 주로 device.te 파일에, file_type, data_file_type의 속성은 file.te에 사용되고 있습니다. device.te는 디바이스와 관련된 설정이며, file.te는 파일과 관련된 설정이라 할 수 있습니다.
external/sepolicy/attributes:
# Attribute declarations
#
# All types used for devices.
attribute dev_type;
# All types used for processes.
attribute domain;
# All types used for filesystems.
attribute fs_type;
# All types used for context= mounts.
attribute contextmount_type;
# All types used for files that can exist on a labeled fs.
# Do not use for pseudo file types.
attribute file_type;
# All types used for domain entry points.
attribute exec_type;
# All types used for /data files.
attribute data_file_type;
# All types use for sysfs files.
attribute sysfs_type;
...
카메라와 관련된 정책파일인 camera.te를 보면 크게 type, allow, type_transition, macros 4가지를 사용하고 있음을 볼수 있습니다.
1. type :
type camera_exec, exec_type, file_type;
타입은 말 그대로 형태를 선언하는 것으로 주로 사용하는 카메라 타입은 프로세스 형태의 도메인과 카메라 실행타입은 실행형 타입과 파일형 타입의 특성을 가진것으로 선언되어 있습니다.
2. allow :
카메라 타입은 자기 자신의 프로세스를 메모리상에 실행가능하게 허락하는 설정입니다. execmem은 external/sepolicy/access_vector에 class process 내에 선언되어 있습니다.
3. type_transition :
타입변환은 camera 타입의 system_data_file:sock을 camera_socket의 "cam_socekt1"으로 변환하는 설정입니다.
4. macro :
unix_socket_connet의 경우 여러 .te 파일에서 사용되고 있으며, 정의된 곳은 te_macros에 정의 되어 있으며, 아래와 같습니다. 매크로는 define으로 시작하여 선언되며, ( )로 안에 구성을 하게 됩니다.
external/sepolicy/te_macros:
#####################################
# unix_socket_connect(clientdomain, socket, serverdomain)
# Allow a local socket connection from clientdomain via
# socket to serverdomain.
define(`unix_socket_connect', `
allow $1 $2_socket:sock_file write;
allow $1 $3:unix_stream_socket connectto;
')
...
device/lge/hammerhead/sepolicy/camera.te:
type camera, domain;
type camera_exec, exec_type, file_type;
# Started by init
init_daemon_domain(camera)
allow camera self:process execmem;
# Interact with other media devices
allow camera camera_device:dir search;
allow camera { gpu_device video_device camera_device }:chr_file rw_file_perms;
allow camera { surfaceflinger mediaserver }:fd use;
# Create front and back camera sockets (/data/cam_socket[12])
type_transition camera system_data_file:sock_file camera_socket "cam_socket1";
type_transition camera system_data_file:sock_file camera_socket "cam_socket2";
allow camera camera_socket:sock_file { create unlink };
allow camera system_data_file:dir w_dir_perms;
allow camera system_data_file:sock_file unlink;
type_transition camera system_data_file:file camera_data_file "fdAlbum";
allow camera camera_data_file:file create_file_perms;
# Connect to sensor socket (/data/app/sensor_ctl_socket)
allow camera apk_data_file:dir r_dir_perms;
unix_socket_connect(camera, sensors, sensors)
allow camera sensors_socket:sock_file read;
allow camera sensors_device:chr_file rw_file_perms;
# Read camera files from persist filesystem
allow camera persist_file:dir search;
r_dir_file(camera, persist_camera_file)
컨텍스트의 확인은 ls -Z로 확인할 수 있으며, 아래의 file_context에 대하여 살펴보면, 크게 형태가 u:object_r:type_s#의 형태로 되어 있음을 볼 수 있으며, 각 의미하는 바는 아래와 같다.
- u : user를 뜻하며 object를 기본적으로 가지고 있다.
- r : role을 뜻함
- type : 정책 타입을 말하며, 예로 device type, process type, file system type, network type, IPC type 등이 있다.
- s : security Level(MLS의 확장기능)로 보안 레벨을 의미한다.
/device/lge/hammerhead/sepolicy/file_contexts:
/dev/kgsl-3d0 u:object_r:gpu_device:s0
/dev/kgsl u:object_r:gpu_device:s0
# Bluetooth
/dev/ttyHS99 u:object_r:hci_attach_dev:s0
# nfc
/dev/bcm2079x u:object_r:nfc_device:s0
# Used by keystore to access trustzone
/dev/qseecom u:object_r:tee_device:s0
# GPS
/dev/gss u:object_r:sensors_device:s0
...
마지막으로 파일 및 폴더에 대한 권한은 external/sepolicy/global_macros에서 찾아 볼 수 있으며, 3가지 일반적인 그룹으로 나뉘어 있으며, object class, permission, socket 그룹으로 나뉘어 있습니다.
external/sepolicy/global_macros:
# Common groupings of object classes.
#
define(`capability_class_set', `{ capability capability2 }')
define(`devfile_class_set', `{ chr_file blk_file }')
define(`notdevfile_class_set', `{ file lnk_file sock_file fifo_file }')
define(`file_class_set', `{ devfile_class_set notdevfile_class_set }')
define(`dir_file_class_set', `{ dir file_class_set }')
define(`socket_class_set', `{ socket tcp_socket udp_socket rawip_socket netlink_socket packet_socket key_socket unix_stream_socket unix_dgram_socket appletalk_socket netlink_route_socket netlink_firewall_socket netlink_tcpdiag_socket netlink_nflog_socket netlink_xfrm_socket netlink_selinux_socket netlink_audit_socket netlink_ip6fw_socket netlink_dnrt_socket netlink_kobject_uevent_socket tun_socket }')
define(`dgram_socket_class_set', `{ udp_socket unix_dgram_socket }')
define(`stream_socket_class_set', `{ tcp_socket unix_stream_socket }')
define(`unpriv_socket_class_set', `{ tcp_socket udp_socket unix_stream_socket unix_dgram_socket }')
define(`ipc_class_set', `{ sem msgq shm ipc }')
#####################################
# Common groupings of permissions.
#
define(`x_file_perms', `{ getattr execute execute_no_trans }')
define(`r_file_perms', `{ getattr open read ioctl lock }')
define(`w_file_perms', `{ open append write }')
define(`rx_file_perms', `{ r_file_perms x_file_perms }')
define(`ra_file_perms', `{ r_file_perms append }')
define(`rw_file_perms', `{ r_file_perms w_file_perms }')
define(`rwx_file_perms', `{ rw_file_perms x_file_perms }')
define(`link_file_perms', `{ getattr link unlink rename }')
define(`create_file_perms', `{ create setattr rw_file_perms link_file_perms }')
define(`r_dir_perms', `{ open getattr read search ioctl }')
define(`w_dir_perms', `{ open search write add_name remove_name }')
define(`ra_dir_perms', `{ r_dir_perms add_name write }')
define(`rw_dir_perms', `{ r_dir_perms w_dir_perms }')
define(`create_dir_perms', `{ create reparent rmdir setattr rw_dir_perms link_file_perms }')
define(`r_ipc_perms', `{ getattr read associate unix_read }')
define(`w_ipc_perms', `{ write unix_write }')
define(`rw_ipc_perms', `{ r_ipc_perms w_ipc_perms }')
define(`create_ipc_perms', `{ create setattr destroy rw_ipc_perms }')
#####################################
# Common socket permission sets.
define(`rw_socket_perms', `{ ioctl read getattr write setattr append bind connect getopt setopt shutdown }')
define(`create_socket_perms', `{ create rw_socket_perms }')
define(`rw_stream_socket_perms', `{ rw_socket_perms listen accept }')
define(`create_stream_socket_perms', `{ create rw_stream_socket_perms }')
이상으로 .te 파일에 대하여 정리 해 보았습니다. 정책변경에 대한 .te 파일의 설정 및 개념정립에 도움이 되었으면 합니다.
끝.
- http://www.codeproject.com/Articles/806904/Android-Security-Customization-with-SEAndroid [본문으로]
- http://lists.linaro.org/pipermail/linaro-dev/2014-June/017279.html [본문으로]
- https://github.com/Cl3Kener/UBER-L/blob/master/arch/arm/configs/hammerhead_defconfig [본문으로]
- https://www.nsa.gov/research/_files/selinux/papers/policy2/t1.shtml [본문으로]
'프로그램 > 안드로이드' 카테고리의 다른 글
안드로이드 롤리팝 퀵 스타트 가이드 (0) | 2014.11.19 |
---|---|
안드로이드 킷캣과 롤리팝 버전 비교 및 모바일 운영체제 정리 (0) | 2014.11.11 |
abd shell command 백그라운드 실행 (0) | 2014.09.17 |
몽키 스크립트 (0) | 2013.06.27 |
댓글