1 Efuse and Dm-verity
1.1 Efuse - 對bootloader簽名
熔絲檔案:sec.dat
1)燒寫signed bootloader
2)將密匙檔案sec.dat燒寫入efuse
3)重啟設備,設備的bootROM會讀取efuse中的密匙pubk驗證bootloader
4)bootloader驗證通過,啟動,開始AVB驗證流程
1.2 編譯時關閉AVB
@ BoardConfig.mk
BOARD_AVB_ENABLE := false
BOARD_BUILD_DISABLED_VBMETAIMAGE := true
AndroidO之后沒有獨立的recovery.img,boot.img根據cmdline引數來決定mount哪個ramdisk,
如果有skip_initramfs引數,那么mount打包在system.img中的normal ramdisk;否則mount打包在boot.img中的recovery ramdisk,
引數BOARD_BUILD_SYSTEM_ROOT_IMAGE的配置決定是將normal ramdisk打包到boot.img中還是打包到system.img中,
打包到boot.img中:
BOARD_BUILD_SYSTEM_ROOT_IMAGE := false
打包到system.img中:
BOARD_BUILD_SYSTEM_ROOT_IMAGE := true
1.3 AVB驗證流程
需要提前掛載磁區的fstab來自于commandline的androidboot.android_dt_dir
1)bootloader使用內置的OEM pubk驗證vbmeta.img,驗證通過后用vbmeta.img中的boot pubk驗證boot.img,如果驗證通過就啟動boot.img
2)init啟動后,init/fs_mgr使用vbmeta.img中的vendor pubk、system pubk、odm pubk驗證vendor.img、system.img、odm.img,驗證通過就mount,否則不會mount
Figure 1-1 Dm-verity的作業流程
編譯生成Metadata流程:
@ build/core/Makefile
->
@ build/tools/releasetools/build_image.py
->
BuildVerityTree() - 用來生成dm_verity需要的簽名資料
BuildVerityMetadata() - 生成Metadata資料
->
@ system/extras/verity/build_verity_metadata.py
->
build_verity_metadata()
1.4 Disable System Dm-verity
@ device/{ro.boot.hardware}/{ro.board.platform}/fstab.{ro.boot.hardware}
Change
/dev/block/bootdevice/by-name/system /system ext4 ro,barrier=1,discard wait, avb
to
/dev/block/bootdevice/by-name/system /system ext4 ro,barrier=1,discard wait
可以使用內核配置 CONFIG_DM_VERITY_HASH_PREFETCH_MIN_SIZE(默認大小為 128)來啟用 dm-verity 哈希預提取大小,該修改可提升啟動速度,
1.5 AndroidO userdebug版本運行時禁止dm-verity
AndroidO dm-verity disable flag存在于vbmeta.img(keystore磁區)中;而老版本是放置在system.img磁區的dm-verity metadata中,
1)在設定中打開OEM unlocking選項
2)在設定中打開USB debugging選項
3)adb reboot bootloader
4)fastboot flashing unlock和fastboot oem unlock
5)fastboot reboot
6)adb root
7)adb disable-verity
8)adb reboot
9)adb root
10)adb remount
1.6 AndroidO userdebug版本刷機時禁止dm-verity
AndroidO dm-verity disable flag存在于vbmeta.img(keystore磁區)中;而老版本是放置在system.img磁區的dm-verity metadata中,
1)在設定中打開OEM unlocking選項
2)在設定中打開USB debugging選項
3)adb reboot bootloader
4)fastboot flashing unlock和fastboot oem unlock
5)fastboot --disable-verity --disable-verification flash vbmeta vbmeta.img
6)fastboot reboot
7)adb root
8)adb remount
2 Android selinux How-to
2.1 LSM(Linux Security Module)的五種實作
SELinux:Security Enhanced Linux,基于inode,Android當前使用的就是這種
SMACK:Simple Mandatory Access Control Kernel,基于inode
Tomoyo:日本女人名“智代”,日本人實作的代碼,基于path
AppArmor:應用盔甲,AppArmor is installed by default in Ubuntu
Yama:來自梵文,中文名為“閻羅”,只處理ptrace和檔案鏈接
2.2 注釋
avc - Access Vector Cache
avd - Access Vector Decision,訪問向量決策
ssid - 主體的安全標識
tsid - 客體的安全標識
tclass - 客體安全型別
requested - 申請檢查的權限
auditdata - 附加的審計資料,主要產生審計日志,供內核kauditd和用戶auditd保存日志到磁盤中,當權限檢查失敗時,內核會列印審計不通過的log,參考另一篇文章Linux auditd
2.3 編譯時強制打開selinux
BOARD_KERNEL_CMDLINE += androidboot.selinux=enforcing
2.4 查看設備節點的sepolicy權限
ls -alZ /dev/kmsg
2.5 not_full_treble
如果遇到execute_no_trans無法通過編譯neverallow檢查,就需要用到這個宏
2.6 根據avc log自動生成Android Selinux策略
2.6.1 生成policy文本檔案
1)提取所有的avc log
adb shell "cat /proc/kmsg | grep avc" > avc_log.txt
or
adb shell
dmesg | grep avc > /dev/avc_log.txt
adb pull /dev/avc_log.txt .
2)使用audit2allow直接生成policy
sudo apt-get install policycoreutils
audit2allow -i avc_log.txt -o output_pol.te
vi output_pol.te
2.6.2 直接插入到sepolicy檔案中
adb shell
dmesg > /dev/kern_msg.txt
adb pull /dev/kern_msg.txt .
cat kern_msg.log | audit2allow -p out/target/product/<device>/root/sepolicy
2.7 打開關閉Sepolicy
需要android版本是usereng/eng
adb root
adb shell
關閉:
# setenforce 0
打開:
# setenforce 1
2.8 添加到啟動腳本中禁止security
on nonencrypted
# A/B update verifier that marks a successful boot.
exec - root cache -- /system/bin/update_verifier nonencrypted
class_start main
class_start late_start
in late_start script, add [setenforce 0]
2.9 查找用戶空間支持的所有權限字串
用戶空間宏定義
@ sepolicy/global_macros
內核空間權限字串
@ security/selinux/selinuxfs.c
security_load_policy()
->
@ security/selinux/include/classmap.h
struct security_class_mapping secclass_map[]; // 對應tclass,權限bitmap(每個權限占用一個32bit整數的1bit)
2.10 Dump sepolicy DB
@ security/selinux/ss/services.c
#include <linux/moduleparam.h>
static int oem_sepolicy_db_show(char *buffer, const struct kernel_param *kp)
{
unsigned i, j;
struct sidtab_node *cur;
char *context_name = NULL;
u32 length;
i = 0;
while (secclass_map[i].name) i++;
printk(KERN_DEBUG "(tclass_cnt, current_mapping_size, SIDTAB_SIZE) = "
"(%d, %d, %d)\n\n",
i, current_mapping_size, SIDTAB_SIZE);
// step1
if (!current_mapping) {
goto out;
}
for (i = 0; i < current_mapping_size; i++) {
printk("%d (value, num_perms, permission_bitmap) = (%d, %d",
i, current_mapping[i].value, current_mapping[i].num_perms);
if (!current_mapping[i].num_perms) {
printk(")\n");
continue;
} else {
printk(" - ");
}
for (j = 0; j < current_mapping[i].num_perms; j++) {
if (j == (current_mapping[i].num_perms - 1)) {
printk("%x", current_mapping[i].perms[j]);
} else {
printk("%x ", current_mapping[i].perms[j]);
}
}
printk(")\n");
}
// step2
printk("\n");
for (i = 0; i < SIDTAB_SIZE; i++) {
cur = sidtab.htable[i];
while (cur) {
if (context_struct_to_string(&(cur->context),
&context_name, &length) < 0)
continue;
printk("(%d %s)", cur->sid, context_name);
kfree(context_name);
cur = cur->next;
if (cur) {
printk(" ### ");
}
}
printk("\n");
}
out:
// no output log to userspace
return 0;
}
static struct kernel_param_ops oem_sepolicy_db_ops = {
.get = oem_sepolicy_db_show,
};
module_param_cb(oem_sepolicy_db, &oem_sepolicy_db_ops, NULL, 0600);
MODULE_PARM_DESC(oem_sepolicy_db, "show sepolicy db");
3 Abbreviations
avb:Android Verified Boot,用dm-verify驗證system磁區的完整性,用在Android 8.0之后的fstab中
AVC:Access Vector Cache
DAC:Discretionary Access Control,自主訪問控制
FRP:Factory Reset Protection
LSM:Linux Security Module
Appendix
audit2allow python demo for OEM
#
# audit2allow, translate avc log to .te file
# Author: George Tso
#
# usage
# dmesg | grep "avc" > /dev/avc.log
# adb pull /dev/avc.log .
# audit2allow avc.log avc.te
import re
import string
import sys
def write_outfile(outfile, hashmap):
for (hm_key, hm_value) in hashmap.items():
#{
allow_value = ''
hm_value.sort()
if (len(hm_value) == 1):
allow_line = hm_key + ' ' + hm_value[0] + ';' + '\n'
else:
for value in hm_value:
allow_value += ' ' + value
allow_line = hm_key + ' {' + allow_value + ' };' + '\n'
outfile.writelines(allow_line)
#}
def has_proc(line, proc):
e_list = line.split()
for e in e_list:
#{
if (e.find('scontext') > -1):
sub = e.split(':')
if (proc == sub[2]):
return True
else:
return False
#}
return False
def _generate_te(proc_list):
src = ''
tgt = ''
tclass = ''
got_tclass = False
hashmap = {}
got_key = False
repeat = False
outfile = open(sys.argv[2], 'w')
for proc in proc_list:
#{
outfile.writelines('\n\n===============' + proc + '================\n')
file = open(sys.argv[1], 'r')
for line in file.readlines():
#{
line = line.strip()
if not len(line) or line.startswith('#'):
continue
if (has_proc(line, proc) == False):
# not this process, continue
continue
# regular expression to extract {}
perm = re.findall(r'[{](.*?)[}]', line)
#print perm[0].strip()
e_list = line.split()
for e in e_list:
#{
if (e.find('scontext') > -1):
sub = e.split(':')
src = sub[2]
elif (e.find('tcontext') > -1):
sub = e.split(':')
tgt = sub[2]
elif (e.find('tclass') > -1):
sub = e.split('=')
tclass = sub[1]
got_tclass = True
if (got_tclass == True):
got_tclass = False
allow_key = 'allow' + ' ' + src + ' ' + tgt + ':' + tclass.strip()
allow_value = perm[0].lstrip().rstrip()
hm_key = ''
hm_value = []
for (hm_key, hm_value) in hashmap.items():
#{
if (hm_key == allow_key):
got_key = True
break;
#}
if (got_key == True):
got_key = False
for value in hm_value:
#{
if (value == allow_value):
repeat = True
break;
#}
if (repeat == False):
hm_value.append(allow_value)
hashmap[allow_key] = hm_value
repeat = False
else:
hm_value = []
hm_value.append(allow_value)
hashmap[allow_key] = hm_value
#} end of for e in e_list
#} end of for line in file.readlines()
write_outfile(outfile, hashmap)
hashmap.clear()
file.close()
#} for proc in proc_list
outfile.close()
def generate_te():
# STEP 1 - FIND ALL THE PROCESSES
proc_list = []
repeat = False
file = open(sys.argv[1], 'r')
for line in file.readlines():
#{
line = line.strip()
if not len(line) or line.startswith('#'):
continue
e_list = line.split()
for e in e_list:
#{
if (e.find('scontext') > -1):
sub = e.split(':')
for proc in proc_list:
#{
if (proc == sub[2]):
repeat = True;
break;
#}
if (repeat == False):
proc_list.append(sub[2])
repeat = False
break
#}
#}
file.close()
proc_list.sort()
print proc_list
# STEP 2 - GENERATE OUTPUT FILE
_generate_te(proc_list)
if __name__ == '__main__':
if (len(sys.argv) < 3):
print(sys.argv[0] + ' ' + '<input_file.log>' + ' ' + '<output_file.te>')
exit(0)
generate_te()
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/217223.html
標籤:其他
