Android selinux知识点总结

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()

猜你喜欢

转载自blog.csdn.net/zoosenpin/article/details/73580947