SELINUX

1. SELinux 来源
 SELinux 即Security-Enhanced Linux,由美国国家安全局(NSA)发起,Secure Computing Corporation (SCC) 和 MITRE 直接参与开发,以及很多研究机构(如犹他大学)一起参与的强制性安全审查机制。该系统最初是作为一款通用访问软件,发布于 2000 年 12 月(代码采用 GPL 许可发布)。在Linux Kernel 2.6 版本后,直接整合进入SELinux,搭建在Linux Security Module(LSM)基础上。目前已经成为最受欢迎、使用最广泛的安全方案。
 
2. SELinux 基本架构与原理
 SELinux 是典型的MAC-Mandatory Access Controls实现,对系统中每个对象都生成一个安全上下文(Security Context),每一个对象访问系统的资源都要进行安全上下文审查。审查的规则包括类型强制检测(type enforcement),多层安全审查(Multi-Level Security),以及基于角色的访问控制(RBAC: Role Based Access Control)。

 SELinux 搭建在Linux Security Module(LSM)基础上,关于 LSM 架构的详细描述请参见文章 “Linux Security Modules: General Security Support for the Linux Kernel”,该文章在 2002 年的 USENIX Security 会议上发表。有完整的实现LSM 的所有hook function。
 SELinux 的整体结构如下图所示:


 
P-1-1-1:整体流程图 

 


 SELinux 包含五个基本组成:

 * 用于处理文件系统的辅助模块,即SELinuxFS。
 * 集成Linux Security Modules 的hooks sets。
 * Security Policy Database。
 * Security Label 验证模块。
 * Access Vector Cache (AVC),访问向量缓存,以便提高验证速度。
 
    基本的访问流程如下图所示:
 
 P-1-1-2:访问流程图
 
 流程如下:
 * 进程通过系统调用(System Call) 访问某个资源,进入Kernel 后,会先做基本的检测,如果异常则直接返回。
 * Linux Kernel DAC 审查,如果异常则直接返回。
 * 调用Linux Kernel Modules 的相关hooks,对接到SELinux 的hooks,进而进行MAC 验证,如果异常则直接返回。
 * 访问真正的系统资源。
 * 返回用户态,将结果反馈。

SELinux and SEAndroid

1. SELinux and SEAndroid
   Android 是建立在标准的Linux Kernel 基础上的,自然也可以开启SELinux。在通用移动平台上,通常很少开启这样的安全服务,Google 为了进一步增强Android 的安全性,经过长期的准备,目前已经在Android 5.0(L) 上有完整地开启SELinux,并对SELinux 进行深入整合形成了SEAndroid。
  
2. SELinux 在Android 上的更新历史
 如下图所描述:
 
 
 P-1-2-1 SELinux 在Android 的更新过程
 * KK 4.4 针对netd, installd, zygote, vold 四个原本具有root 权限的process,以及它们fork 出的子进程启用Enforce 模式。
 * L 版本普遍性开启SELinux Enforce mode。
 * Permissive 模式,只打印audit 异常LOG,不拒绝请求;Enforce 模式,既打印audit 异常LOG,也拒绝请求。

 

 

P-1-2-2 SELinux 在Android 启动模式变化
 

从Android N 版本开始的后续版本,Google都会要求强制性开启SELinux,以保证手机安全。

从Android O 版本后,Google大幅度地增强了SELinux的限制,特别是限制System/Vendor之间的交叉使用,如VNDK的权限控制就主要由selinux来控制。

总体来说,Selinux 在android上的使用过程是一个从具体启用到整体启用、从粗粒度限制到精细控制的过程。

3. SELinux 给Android 带来的影响
   * 严格限制了ROOT 权限,以往ROOT “无法无天” 的情况将得到极大的改善。
   * 通过SELinux保护,降低系统关键进程受攻击的风险,普通进程将没有权限直接连接到系统关键进程。
   * 进一步强化APP的沙箱机制,确保APP难以做出异常行为或者攻击行为。
   * 将改变APP一旦安装权限就已经定死的历史,APP权限动态调整将成为可能。

 

SELinux Mode

1. SELinux Mode
   SELinux 分成两种模式,即Permissve Mode(宽容模式)和Enfocing mode(强制模式)。
   Permissive Mode 只通过Kernel Audit System 记录LOG,但不真正拦截访问。
   Enforcing Mode 在记录LOG 的同时还会真正地拦截访问。
   通常在调试时,我们会启用Permissive Mode,以便尽可能多地发现问题,然后一次修正。在真正量产时使用Enforcing mode来保护系统。

2. 简单确认

C:\Users\mtk71029\Desktop>adb shell
k79v1_64:/ # getenforce
Enforcing    //default enforcing mode
k79v1_64:/ # setenforce 0  //set selinux to permissve mode
k79v1_64:/ # getenforce
Permissive   //permissive mode
k79v1_64:/ # setenforce 1  //set selinux to enforcing mode
k79v1_64:/ # getenforce
Enforcing  //enforcing mode

3. 模式修改

A. 如何在 adbd 服务起来之后,将 SElinux 切到 permissive 模式(1.不适用于Android R 及后续版本的user load中;2.重启后失效):
adb root
adb shell setenforce 0    //将 SElinux 切到 permissive 模式
(adb shell setenforce 1  //将 SElinux 切回 enforcing 模式)

B. 如何在 boot 阶段,将 SElinux 默认模式切到 permissive 模式:
步骤1:

(使用lk的平台:)  /vendor/mediatek/proprietary/bootable/bootloader/lk/platform/mtxxxx/rules.mk

  或

(使用lk2的平台:)/vendor/mediatek/proprietary/bootable/bootloader/lk2/platform/mediatek/mtxxxx/rules.mk

中,将:
# choose one of following value -> 1: disabled/ 2: permissive /3: enforcing
SELINUX_STATUS := 3
修改为:
# choose one of following value -> 1: disabled/ 2: permissive /3: enforcing
SELINUX_STATUS := 2

步骤2(只在 user load 中需要这一步操作):
/system/core/init/selinux.cpp
bool IsEnforcing() {
return false; //强制将 SELinux 切到 permissive 模式。
if (ALLOW_PERMISSIVE_SELINUX) {
return StatusFromCmdline() == SELINUX_ENFORCING;
}
return true;
}

步骤3(只在 Android R 及后续版本的 user load 中需要这一步操作) :

对于kernel-4.1x版本:
  在alps/kernel-x.xx/arch/armxx/configs/xxxx_defconfig (注意不是 xxxx_debug_defconfig,debug_defconfig 是eng load 的)中,设置:
  CONFIG_SECURITY_SELINUX_DEVELOP=y
  例如:
  alps/kernel-4.14/arch/arm64/configs/k69v1_64_tee_cts_defconfig

对于Android S及后续版本中的kernel-5.10及后续版本的GKI2.0平台(如:MT6983/MT6895/MT6879)的mgk defconfig中,设置:
  CONFIG_SECURITY_SELINUX_DEVELOP=y

  例如:

  alps/kernel-5.10/arch/arm64/configs/mgk_64_k510_defconfig

步骤4(只在 Android S及后续版本中的kernel-5.10及后续版本的GKI2.0平台(如:MT6983/MT6895/MT6879)中需要这一步操作):

  禁用prebuilt boot.img替换:
  在device/mediatek/vendor/common/BoardConfig-vext.mk中,将

  MTK_USE_PREBUILT_BOOTIMAGE := yes
  修改为:

  MTK_USE_PREBUILT_BOOTIMAGE := no

DAC and MAC

1. DAC and MAC
 DAC 即 Discretionary Access control,自主访问控制,即系统只提供基本的验证,完整的访问控制由开发者自己控制。
 MAC 即 Mandatory Access control,强制性访问控制,即系统针对每一项访问都进行严格限制,具体的限制策略由开发者给出。


2. Linux DAC
   Linux DAC 采用了一种非常简单的策略,将资源访问者分成三类,分别是Owner, Group, Other; 资源针对这三类访问者设置不同的访问权限。而访问权限又分成 read, write, execute。
   访问者通常是进程,有自己的uid/gid,通过uid/gid 和 文件权限匹配,来确定是否可以访问。
   将Root权限根据不同的应用场景划分成许多的Root Capabilities,其中如果有CAP_DAC_OVERRIDE 这项的话,可以直接绕过Linux DAC 限制。
  
   Linux DAC 有明显的不足,其中一个重要点就是Root 权限 “无法无天”,几乎可以做任意事情,一旦入侵者拿到root 权限,就已经完全掌控了系统。另外,每一个进程默认都拿到对应这个用户的所有权限,可以改动/删除这个用户的所有文件资源。很明显,这个难以防止恶意软件。


3. Linux MAC
   Linux MAC 针对DAC 的不足,要求系统对每一项访问进行检查,每访问一个文件资源都需要进行针对性的验证。而这个针对性的验证,是根据已经定义好了的策略进行的。在Linux Kernel中,所有的MAC机制都搭建在Linux Security Modules (LSM) 基础上,包括:SELinux、Apparmor、Smack 和 TOMOYO Linux等。目前SELinux已经成了事实上的行业标准。
  
   相对于Linux DAC,MAC 可以明显弥补DAC 的缺陷。一方面,限制了Root 权限,即使你有root 权限,如果无法通过MAC 验证,那么一样地无法真正执行相关的操作。另外,对每一项权限进行了更加完整的细化,可限制用户对资源的访问行为。

Core SELinux Components and Security Context

1. Core SELinux Components
 SELinux 的核心组件可以参考下图:

  P-2-1-1 SELinux 核心组件
 

 

 

 * Subject 通常是指触发访问行为的对象,在Linux 里面通常是一个进程(Process)。
 * Object Manager 即对象访问管理器,通过它可以知道Subject 需要访问哪些资源,并且触发验证机制。
 * Security Server 即安全服务器,用来验证某个Subject 是否可以真正地访问某个Object,而这个验证机制是基于定义好的Security Policy的。
 * Security Policy 即一种描述SELinux Policy的语言。
 * Access Vector Cache (AVC) 即访问缓存,用来记录以往的访问验证情况,以便快速处理,提高效率。
 
2. Security Context
 SELinux 给Linux 的所有对象都分配一个安全上下文(Security Context),描述成一个标准的字符串。
 
 两种类型:
 * Subject 主体,linux通常以进程为单位。
 * Object  访问对象,linux 通常以文件为单位。

 标准格式:
 user:role:type[:range]
 * User: 用户,并非Linux UID。
 * Role:  角色,一个user可以属于多个role,不同的role具有不同的权限。它是SELinux中一种比较高层次,更方便的权限管理思路,即Role Based Access Control(基于角色的访问控制,简称为RBAC,SELinux 不推荐使用)。
 * Type: Subject或者Object的类型。MAC的基础管理思路其实不是针对上面的RBAC,而是所谓的Type Enforcement Access Control(简称TEAC,一般用TE表示)。对进程来说,Type就是Domain。
 * Range: Multi-Level Security(MLS)的级别。MLS将系统的进程和文件进行了分级,不同级别的资源需要对应级别的进程才能访问。

 

  P-2-2-2 对象类型和权限项关系

 每一个对象都有一个Class,比如一支文件,它是File Class 类型,每个Class 类型都会根据实际情况定义权限类型项,如: read, write, exec, ioctl, append 等等。

 Subject 即前面提到的主体,它能够产生访问行为,Linux 里面通常是一个process,但process 本身也可能是一个Object,比如另外一个进程对它发送Signal,比如对它进行Ptrace 操作。
 
3. 查看Security Context
 * 查看进程的Security Context 用:ps -A -Z 或者cat proc/PID/attr/current,查看当前进程可用id,如:
 
 
 P-2-2-3 ps 查询进程Security Context

  

 

* 查看文件的Security Context 用:ls -Z

P-2-2-4 查看文件Security Context

Type Enforcement Access Control

1. SELinux supports two forms of MAC
 * Type Enforcement (TE)
 顾名思义,Type Enforcement 是根据Security Label 中的 type 进行权限审查,审查 subject type 对 object type 的某个class 类型中某种permission 是否具有访问权限,是目前使用最为广泛的MAC 审查机制,简单易用。

 * Multi-Level Security (MLS)
 多层安全机制,是基于Bell-La Padula (BLP) 模型,将Subject 和 Object 定义成多层次的安全等级,不同安全等级之间有相关的访问约束,常见的访问约束是 "no write down" 和 "no read up"。它是根据Security Label 里面的最后一个字段label 进行确认的。
 
 目前在Android 中,重点启用了Type Enforcement 机制,Multi-Level Security (MLS) 虽然有定义,但没有深入使用,这里暂时略过。
 
2. Type Enforcement Access Control
 

* 控制语句格式:
   rule_name  source_type  target_type:class perm_set;
 * rule:控制类型,分成两方面 allow 以及 audit。
 * source_type:也叫subject,通常是domain。
 * target_type:代表请求的资源。
 * class:代表请求的资源的类型。

 * perm_set:代表对资源访问的操作。
 
3. Rule of Type Enforcement
 如下表所示:
 
 
 P-2-3-1 TEAC-Rule
 
   * allow:赋予某项权限。
   * auditallow:audit含义就是记录某项操作。默认SELinux只记录那些权限检查失败的操作。auditallow则使得权限检查成功的操作也被记录。注意,auditallow只是允许记录,它和赋予权限没关系。赋予权限必须且                         只能使用allow语句。
   * dontaudit:对那些权限检查失败的操作不做记录。
   * neverallow:在编译阶段检查安全策略文件中是否有违反该项规则的allow语句。

 

4. 简单的案例
   下面一段是截取的init.te 中的一部分:
 # Create /data/property and files within it.
 allow init property_data_file:dir create_dir_perms;
 allow init property_data_file:file create_file_perms;

 # Set any property.
 allow init property_type:property_service set;

 # Run "ifup lo" to bring up the localhost interface
 allow init self:udp_socket { create ioctl };

 # Set scheduling info for psi monitor thread.
 # TODO: delete or revise this line b/131761776
 allow init kernel:process { getsched setsched };

 # The init domain is only entered via an exec based transition from the
 # kernel domain, never via setcon().
 neverallow domain init:process dyntransition;
 neverallow { domain -kernel} init:process transition;
 neverallow init { file_type fs_type -init_exec }:file entrypoint;

Domain and Object Transitions

1. Domain Transitions
 在SELinux 中,我们通常称一个进程是一个domain,一个进程fork 另外一个进程并执行(exec) 一个执行档时,我们往往会涉及到domain 的切换。比如init 进程,SELinux 给予了它很大的权限。而对于init拉起的服务,我们要限制这个服务的权限。于是就涉及到从一个domain 切换到另外一个domain,不然默认就使用init 进程的domain。


 在SELinux 里面有专门的一条语法:
  type_transition statement.

 在准备切换前我们先要确保有相关的权限操作:
 *. The source domain has permission to transition into the target domain.
  源domain 必须有权限切换到这个目标domain。


 *. The application binary file needs to be executable in the source domain.
  源doamin 必须要有执行这个执行档的权限。


 *. The application binary file needs an entry point into the target domain.
  这个执行档必须是目标domain 的入口(Entry Point)。

 如下面的demo, init 拉起apache 并且切换到 apache 的domain。

 #首先,你得让init_t域中的进程能够执行type为apache_exec_t的文件:
 allow init_t apache_exec_t:file {read getattr execute};

 #然后,你还得告诉SELinux,允许init_t做DT切换以进入apache_t域:
 allow init_t apache_t:process transition;

 #最后,你还得告诉SELinux,切换入口(对应为entrypoint权限)为执行apache_exec_t类型 的文件:
 allow apache_t apache_exec_t:file entrypoint;

 #Domain Transition
 type_transition init_t apache_exec_t:process apache_t;

Google 为了方便操作,定义了专门的 transition 宏,方便大家操作,可以参考如:

system/sepolicy/public/te_macros 文件中的:

 #####################################
# domain_trans(olddomain, type, newdomain)
# Allow a transition from olddomain to newdomain
# upon executing a file labeled with type.
# This only allows the transition; it does not
# cause it to occur automatically - use domain_auto_trans
# if that is what you want.
#
define(`domain_trans', `
# Old domain may exec the file and transition to the new domain.
allow $1 $2:file { getattr open read execute map };
allow $1 $3:process transition;
# New domain is entered by executing the file.
allow $3 $2:file { entrypoint open read execute getattr map };
# New domain can send SIGCHLD to its caller.
ifelse($1, `init', `', `allow $3 $1:process sigchld;')
# Enable AT_SECURE, i.e. libc secure mode.
dontaudit $1 $3:process noatsecure;
# XXX dontaudit candidate but requires further study.
allow $1 $3:process { siginh rlimitinh };
')

#####################################
# domain_auto_trans(olddomain, type, newdomain)
# Automatically transition from olddomain to newdomain
# upon executing a file labeled with type.
#
define(`domain_auto_trans', `
# Allow the necessary permissions.
domain_trans($1,$2,$3)
# Make the transition occur by default.
type_transition $1 $2:process $3;
')

#####################################
# init_daemon_domain(domain)
# Set up a transition from init to the daemon domain
# upon executing its binary.
define(`init_daemon_domain', `
domain_auto_trans(init, $1_exec, $1)
')

建议尽量使用宏定义,让配置更简洁。

 
2. Target Transitions
 一个进程在一个目录下创建文件时,默认是沿用父目录的Security Context,如果要设置成特定的Label,就必须进行Object Transitions。

 同样是使用:
 type_transition statement.
 
 对应的必须有两个前提条件:
 * The source domain needs permission to add file entries into the directory
  这个process 必须有在这个目录下添加文件的权限。
 
 * The source domain needs permission to create file entries
  这个process 必须有在这个目录下创建以这个Security Context 为Label 的文件的权限。
  
 下面是一个demo,ext_gateway_t 这个domain 在类型为in_queue_t 的目录下,创建类型为 in_file_t 的文件。
  
 #首先,你得让ext_gateway_t 对in_queue_t 目录具备访问权限:
 allow ext_gateway_t in_queue_t:dir { write search add_name };

 #然后,你还得告诉SELinux,允许ext_gateway_t  访问in_file_t的文件:
 allow ext_gateway_t in_file_t:file { write create getattr };

 #Object Transition
 type_transition ext_gateway_t in_queue_t:file in_file_t;

 结果如下图:
 
 
 P-2-4-1 对象domain 切换结果

 

同样Google 也为此定义了专门的target transition 宏,以方便大家。

system/sepolicy/public/te_macros 文件中的:

#####################################
# file_type_trans(domain, dir_type, file_type)
# Allow domain to create a file labeled file_type in a
# directory labeled dir_type.
# This only allows the transition; it does not
# cause it to occur automatically - use file_type_auto_trans
# if that is what you want.
#
define(`file_type_trans', `
# Allow the domain to add entries to the directory.
allow $1 $2:dir ra_dir_perms;
# Allow the domain to create the file.
allow $1 $3:notdevfile_class_set create_file_perms;
allow $1 $3:dir create_dir_perms;
')

#####################################
# file_type_auto_trans(domain, dir_type, file_type)
# Automatically label new files with file_type when
# they are created by domain in directories labeled dir_type.
#
define(`file_type_auto_trans', `
# Allow the necessary permissions.
file_type_trans($1, $2, $3)
# Make the transition occur by default.
type_transition $1 $2:dir $3;
type_transition $1 $2:notdevfile_class_set $3;
')

SEAndroid API

1. SELinux File System
 SELinux 在Kernel 中实现,统一对外的接口都集中在SELinux File System 当中,即通过读写相关文件的方式来达成。
 SELinux File System 在比较新的Linux Kernel 中被mount 在/sys/fs/selinux。老的版本就直接是/selinuxfs。
 具体每一个文件的说明,请参考:
 The SELinux Notebook: 2.19.2.3 SELinux Filesystem
 
2. SELinux Lib
 所有SELinux Lib 几乎都是对selinuxfs 的封装,具体的API 可以参考:
 The SELinux Notebook: 9. APPENDIX B - LIBSELINUX LIBRARY FUNCTIONS
 
 在android 上,Google 有进一步对这些API 进行封装,具体都在/external/libselinux 下面,下面对一些重要的API 进行说明。(参考: NB SEforAndroid 1 - SELinux Wiki)
     selinux_android_setcontext

        Sets the correct domain context when launching applications using setcon(3). Information contained in the seapp_contexts file is used to compute the correct context.
        It is called by frameworks/base/core/jni/com_android_internal_os_Zygote.cpp when forking a new process and the system/core/run-as/run-as.c utility.

    selinux_android_setfilecon

        Sets the correct context on application directory / files using setfilecon(3). Information contained in the seapp_contexts file is used to compute the correct context.
        The function is used by the package installer within frameworks/native/cmds/installd/commands.c via the package install() and make_user_data() functions.

    selinux_android_restorecon
    selinux_android_restorecon_pkgdir

        Basically these functions are used to label files and directories based on entries from the file_contexts and/or seapp_contexts files. They call a common handler (selinux_android_restorecon_common()) that will then relabel the requested directories and files. It will also handle recursive labeling of directories and files should a new app, file_contexts or seapp_contexts be installed (see the Checking File Labels section for further information).
        The selinux_android_restorecon function is used by:

            frameworks/native/cmds/installd/installd.c when installing a new app.
            frameworks/base/core/jni/android_os_SELinux.cpp for the Java native_restorecon method.
            frameworks/native/cmds/dumpstate/utils.c when dumping Dalvik and stack traces to ensure correct label.

        The selinux_android_restorecon_pkgdir function is used by:

            frameworks/native/cmds/installd/commands.c for the package restorecon_data() and make_user_data() functions.

    selinux_android_seapp_context_reload

        Loads the seapp_contexts file for frameworks/native/cmds/installd/installd.c when the package installer is loaded.

    selinux_android_load_policy

        Mounts the SELinux filesystem if SELinux is enabled and then calls selinux_android_reload_policy to load the policy into the kernel. Used by system/core/init/init.c to initialise SELinux.

    selinux_android_reload_policy

        Reloads the policy into the kernel. Used by system/core/init/init.c selinux_reload_policy() to reload policy after setting the selinux.reload_policy property.

    selinux_android_use_data_policy

        Used by system/core/init/init.c to decide which policy directory to load the property_contexts file from.

    There is also a new labeling service for selabel_lookup(3) to query the Android property_contexts and service_contexts files.
    Various Android services will also call (not a complete list):

        selinux_status_updated(3), is_selinux_enabled(3), to check whether anything changed within the SELinux environment (e.g. updated configuration files).
        selinux_check_access(3) to check if the source context has access premission for the class on the target context.
        selinux_label_open(3), selabel_lookup(3), selinux_android_file_context_handle, selinux_android_prop_context_handle, setfilecon(3), setfscreatecon(3) to manage file labeling.
        selinux_lookup_best_match called by system/core/init/devices.c when ueventd creates a device node as it may also create one or more symlinks (for block and PCI devices). Therefore a "best match" look-up for a device node is based on its real path, plus any links that may have been created

3. SELinux Java API
 Google 进一步将部分重要的SELinxu API 包装成了Java API。对应的代码是:
 Java API: frameworks/base/core/java/android/os/SELinux.java
 JNI Code: frameworks/base/core/jni/android_os_SELinux.cpp

 对应的API 描述如(参考: NB SEforAndroid 1 - SELinux Wiki):
 boolean isSELinuxEnabled()

    Determine whether SELinux is enabled or disabled.

    Return true if SELinux is enabled.

 boolean isSELinuxEnforced()

    Determine whether SELinux is permissive or enforcing.

    Returns true if SELinux is enforcing.

 boolean setSELinuxEnforce(boolean value)

    Set whether SELinux is in permissive or enforcing modes.

    value of true sets SELinux to enforcing mode.

    Returns true if the desired mode was set.

 boolean setFSCreateContext(String context)

    Sets the security context for newly created file objects.

    context is the security context to set.

    Returns true if the operation succeeded.

 boolean setFileContext(String path, String context)

    Change the security context of an existing file object.

    path represents the path of file object to relabel.

    context is the new security context to set.

    Returns true if the operation succeeded.

 String getFileContext(String path)

    Get the security context of a file object.

    path the pathname of the file object.

    Returns the requested security context or null.

 String getPeerContext(FileDescriptor fd)

    Get the security context of a peer socket.

    FileDescriptor is the file descriptor class of the peer socket.

    Returns the peer socket security context or null.

 String getContext()

    Gets the security context of the current process.

    Returns the current process security context or null.

 String getPidContext(int pid)

    Gets the security context of a given process id.

    pid an int representing the process id to check.

    Returns the security context of the given pid or null.

 String[] getBooleanNames()

    Gets a list of the SELinux boolean names.

    Return an array of strings containing the SELinux boolean names.

 boolean getBooleanValue(String name)

    Gets the value for the given SELinux boolean name.

    name is the name of the SELinux boolean.

    Returns true or false indicating whether the SELinux boolean is set or not.

 boolean setBooleanValue(String name, boolean value)

    Sets the value for the given SELinux boolean name. Note that this will be set the boolean permanently across reboots.

    name is the name of the SELinux boolean.

    value is the new value of the SELinux boolean.

    Returns true if the operation succeeded.

 boolean checkSELinuxAccess(String scon, String tcon, String tclass, String perm)

    Check permissions between two security contexts.

    scon is the source or subject security context.

    tcon is the target or object security context.

    tclass is the object security class name.

    perm is the permission name.

    Returns true if permission was granted.

 boolean native_restorecon(String pathname)

    Restores a file to its default SELinux security context. If the system is not compiled with SELinux, then true is automatically returned. If SELinux is compiled in, but disabled, then true is returned.

    pathname is the pathname of the file to be relabeled.

    Returns true if the relabeling succeeded.

 boolean restorecon(String pathname)

    Restores a file to its default SELinux security context. If the system is not compiled with SELinux, then true is automatically returned. If SELinux is compiled in, but disabled, then true is returned.

    pathname is the pathname of the file to be relabeled.

    Returns true if the relabeling succeeded.

    exception NullPointerException if the pathname is a null object.

 boolean restorecon(File file)

    Restores a file to its default SELinux security context. If the system is not compiled with SELinux, then true is automatically returned. If SELinux is compiled in, but disabled, then true is returned.

    file is the file object representing the path to be relabeled.

    Returns true if the relabeling succeeded.

    exception NullPointerException if the file is a null object.

SEAndroid Commands

1. SELinux Commands

参考(NB SEforAndroid 1 - SELinux Wiki):

CommandComment
chconChange security context of file:
chcon context path
getenforceReturns the current enforcing mode.
getenforce
getseboolReturns SELinux boolean value(s):
getsebool [-a | boolean_name]
idIf SELinux is enabled then the security context is automatically displayed.
load_policyLoad new policy into kernel:
load_policy policy-file
lsSupports -Z option to display security context.
psSupports -Z option to display security context.
restoreconRestore file default security context as defined in the file_contexts or seapp_contexts files. The options are: D - data files, F - Force reset, n - do not change, R/r - Recursive change, v - Show changes.
restorecon [-DFnrRv] pathname
runconRun command in specified security context:
runcon context program args...
setenforceModify the SELinux enforcing mode:
setenforce [enforcing|permissive|1|0]
setseboolSet SELinux boolean to a value (note that the cmd does not set the boolean across reboots):
setsebool boolean_name [1|true|on|0|false|off]

2. Init.rc Commands

seclabel <securitycontext>

service option: Change to security context before exec'ing this service. Primarily for use by services run from the rootfs, e.g. ueventd, adbd. Services on the system partition can instead use policy defined transitions based on their file security context. If not specified and no transition is defined in policy, defaults to the init context.

restorecon <path>

action command: Restore the file named by <path> to the security context specified in the file_contexts configuration. Not required for directories created by the init.rc as these are automatically labeled correctly by init.

restorecon_recursive <path> [ <path> ]*

action command: Recursively restore the directory tree named by <path> to the security context specified in the file_contexts configuration. Do NOT use this with paths leading to shell-writable or app-writable directories, e.g. /data/local/tmp, /data/data or any prefix thereof.

See the Checking File Labels section for further details.

setcon <securitycontext>

action command: Set the current process security context to the specified string. This is typically only used from early-init to set the init context before any other process is started (see init.rc example above).

setenforce 0|1

action command: Set the SELinux system-wide enforcing status. 0 is permissive (i.e. log but do not deny), 1 is enforcing.

setsebool <name> <value>

action command: Set SELinux boolean <name> to <value>.

<value> may be 1|true|on or 0|false|off

 

SEAndroid Policy Files

1. SELinux Policy Path
 两个路径:
 Google Original:alps/system/sepolicy/ (通常不建议修改)
 MTK:alps/device/mediatek/sepolicy/
 所有的Policy 最终采用Union(合并) 的方式编译在一起。

 需要注意的是,从android m1 (6.1) 后,MTK sepolicy 进行了分层划分,以便根据不同的项目、平台、客户需求进行配置,消除sepolicy 冗余,降低安全风险。
/device/mediatek/sepolicy 是ALL platform 都需要。
/device/mediatek/[platform]/sepolicy 则是某个具体的平台。
/device/[customer]/[project]/sepolicy 目前没有默认配置,客户可根据需要在BroadConfig.mk 中配置BOARD_SEPOLICY_DIRS 以新增目录。

Basic 版本             导入 /device/mediatek/common|[platfrom]/sepolicy/basic
Bsp 版本                导入/device/mediatek/common|[platfrom]/sepolicy/basic|bsp
TK 版本(一般情况)    导入/device/mediatek/common|[platfrom]/sepolicy/basic|bsp|full

注:MTK SEPolicy路径在android O 版本后,进行了大改,一定要参考后续 SELinux Update in O(8.0 & 8.1) 章节。

2. SELinux Policy Files

 system/sepolicy/*
 access_vectors, security_classes
  :SE for Android classes and permissions.
 initial_sids, initial_sids_contexts,
  :Initial_sid
 fs_use, genfs_contexts, port_contexts
  : file system label
 users, roles
  : only user (u) and role (r) used by the policy
 mls
  : Contains the constraints applied to the defined classes and permissions.
 global_macros, mls_macro, te_marcos
  : Some macros use to policy define…
 attributes
  : Contains the attribute names that will be used to group type identifiers defined by the policy.
 policy_capabilities
  : Contains the policy capabilities enabled for the kernel policy
 *.te
  : Contains all type enforcement policy for process and resources
 file_contexts
  : contains default file contexts for setting the filesystem as standard SELinux
 property_contexts
  : contains default contexts to be applied to Android property services
 service_contexts
  : Contains default contexts for Android services
 seapp_contexts
  : contains information to allow domain or file contexts to be computed based on parameters
 mac_permissions.xml
  : contains information to allow apk install permissions and mappings to selinux domain
 selinux-network.sh
  :If using iptables, then the information may be configured in this file as part of the build.
 

 3. SELinux Files in Phone
 SELinux Policy 手机上关键性的配置文件包括:
 xxx_sepolicy.cil:SELinux Policy 编译后的sepolicy 文件;
 xxx_file_contexts:系统文件以及device 所对应的security context;
 xxx_seapp_contexts:zygote 设置app 的security context 的配置项;
 xxx_service_contexts:service 所绑定的security context;
 xxx_property_contexts:property 所绑定的security context;
 xxx_mac_permissions.xml:定义app 的security context 类型。

 更详细说明请参看 "SELinux Update in O(8.0 & 8.1) -> 审查最终结果/确认更新" 章节。

Understand SEAndroid Policy

1. Security Classes and Permissions
系统中针对不同的“文件” 类型(Class),如普通的file、socket、SELinux 使用的security、对每个process 参数的process 等定义相关的class。而每一个class 都有相应的permissions。 比如file 就有 read, write, create, getattr, setattr, lock, ioctl 等等。比如process 就有fork, sigchld, sigkill, ptrace, getpgid, setpgid 等等。
SEAndroid 在 system/sepolicy/private/access_vectors 中定义了相关的class,以及所具有的Permissions。同时在security_classes 中声明了这些classes。
 
 比如file:
 system/sepolicy/private/access_vectors
 #
 # Define a common prefix for file access vectors.
 #

 common file
 {
  ioctl
  read
  write
  create
  getattr
  setattr
  lock
  relabelfrom
  relabelto
  append

  map
  unlink
  link
  rename
  execute
  quotaon
  mounton

  audit_access

  open

  execmod

  watch

  watch_mount

  watch_sb

  watch_with_perm

  watch_reads
 }
 ......

 class file
 inherits file
 {
  execute_no_trans
  entrypoint
 }

 system/sepolicy/private/security_classes
 class file


 需要注意的是,这些定义和 Kernel 中相关API 是强相关的,普通用户严禁修改。
 
2. SE for Android Classes and Permissions
 除了Kernel 默认定义的Class/Permissions 之外,Google 还为SEAndroid 定义了几个 UserSpace 会使用到的Class/Permissions,它们是:

binder class - This is a kernel object to manage the Binder IPC service.
PermissionDescription (4 unique permissions)
callPerform a binder IPC to a given target process (can A call B?).
impersonatePerform a binder IPC on behalf of another process (can A impersonate B on an IPC?).

Not currently used in policy but kernel (selinux/hooks.c) checks permission in selinux_binder_transaction call.

set_context_mgrRegister self as the Binder Context Manager aka servicemanager (global name service). Can A set the context manager to B, where normally A == B.

See policy module servicemanager.te.

transferTransfer a binder reference to another process (can A transfer a binder reference to B?).

zygote class - This is a userspace object to manage the Android application loader. See Java SELinux.checkSELinuxAccess() in frameworks/base/core/java/com/android/internal/os/ZygoteConnection.java
PermissionDescription (4 unique permissions)
specifyidsPeer may specify uid’s or gid’s.
specifyrlimitsPeer may specify rlimits.
specifyinvokewithPeer may specify --invoke-with to launch Zygote with a wrapper command.
specifyseinfoSpecify a seinfo string for use in determining the app security label.
property_service class - This is a userspace object to manage the Android Property Service. See check_mac_perms() in system/core/init/property_service.c
PermissionDescription (1 unique permission)
setSet a property.

service_manager class - This is a userspace object to manage Android services. See check_mac_perms() in frameworks/native/cmds/servicemanager/service_manager.c
PermissionDescription (3 unique permission)
addAdd a service.
findFind a service.
listList services.


 

keystore_key class - This is a userspace object to manage the Android keystore (see system/security/keystore/keystore.cpp).
PermissionDescription (16 unique permissions)
testTest if keystore okay.
getGet key.
insertInsert/update key.
deleteDelete key.
existCheck if key exists.
sawSearch for matching string.
resetReset keystore.
passwordGenerate new keystore password.
lockLock keystore.
unlockUnlock keystore.
zeroCheck if keystore empty.
signSign data.
verifyVerify data.
grantAdd or remove access.
duplicateDuplicate the key.
clear_uidClear keys for this uid.


 

rd class - This is a userspace object to allow file dumps (see system/core/debuggerd/debuggerd.cpp).
PermissionDescription (2 unique permissions)
dump_tombstoneWrite tombstone file.
dump_backtraceWrite backtrace file.

drmservice class - This is a userspace object to allow finer access control of the Digital Rights Management services (see frameworks/av/drm/drmserver/DrmManagerService.cpp).
PermissionDescription (8 unique permissions)
consumeRightsConsume rights for content.
setPlaybackStatusSet the playback state.
openDecryptSessionOpen the DRM session for the requested DRM plugin.
closeDecryptSessionClose DRM session.
initializeDecrypSessionInitialise the decrypt resources.
decryptDecrypt data stream.
finalizeDecryptUnitRelease DRM resources.
preadRead the data stream.


 
3. Multi-Level Security and Multi-Category
 Google 目前虽然已经定义好了MLS/MCS 的框架,但并没有真正使用。相关的policy code 主要定义在mls,以及 mls_macros 当中。
 比如:
 # Process read operations: No read up unless trusted.
 mlsconstrain process { getsched getsession getpgid getcap getattr ptrace share }
    (l1 dom l2 or t1 == mlstrustedsubject);

 # Process write operations:  No write down unless trusted.
 mlsconstrain process { sigkill sigstop signal setsched setpgid setcap setrlimit ptrace share }
    (l1 domby l2 or t1 == mlstrustedsubject);
 
 但目前mls_num_sens=1, mls_num_cats=1024; 所有process 的level 都是0 并且,所有的process 都还没有真正使用level 属性,所以目前我们可以不关注MLS/MCS。

4. Attribute and Type
 这个是SELinux 的重点之一,每一个文件都有唯一的Security Context,而目前版本type 是Security Context 中最重要的栏目。每一个文件都对应一个type,而每一个type 都对应有一个或多个Attribute。
 所有常见的Attribute 定义在:

 system/sepolicy/public/attrubites

 如:
 # 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;
 
 Type 的定义就比较分散:
 * 普通文件 type 定义在 file.te。

 如:
 type labeledfs, fs_type;
 type pipefs, fs_type;
 type sockfs, fs_type;
 type rootfs, fs_type;
 type proc, fs_type;
 ......
 # File types
 type unlabeled, file_type;
 # Default type for anything under /system.
 type system_file, file_type;
 # Default type for anything under /data.
 type system_data_file, file_type, data_file_type;
 # /data/.layout_version or other installd-created files that
 # are created in a system_data_file directory.
 type install_data_file, file_type, data_file_type;
 
 * 设备文件 type 定义在 device.te。

 如:
 # Device types
 type device, dev_type, fs_type;
 type alarm_device, dev_type, mlstrustedobject;
 type adb_device, dev_type;
 type ashmem_device, dev_type, mlstrustedobject;
 type audio_device, dev_type;
 type binder_device, dev_type, mlstrustedobject;
 type block_device, dev_type;
 type camera_device, dev_type;
 type dm_device, dev_type;
 
 * 执行档文件,如果需要domain 切换,通常直接定义在以这个执行档文件命令的扩展名为te 的文件中。
   会定义执行档类型,以及这个process 的domain。

 如vold.te:
 # volume manager
 type vold, domain;
 type vold_exec, exec_type, file_type, system_file_type;

 init_daemon_domain(vold)

5. SELinux User and Role

 Google 使用了下面的两个语句来定义user 和 role:
 user u roles { r } level s0 range s0 - mls_systemhigh;    #(s0 - s0:c0.c1023)
 role r;
 role r types domain;
 也就是说,目前android 只定义了一个user u,一个role r,file system 使用object_r,user u 只有一个role r,mls 的low_level 是s0,high level 是s0:c0.c1023。因为android 只有一个role r,所以并没有进行角色访问约束(RBAC)。

6. File system labels
 系统中的所有Process 都使用domain attribute。而系统中的文件,则默认是根据不同的文件系统类型来定义。
 对于存储型的file system。比如ext2/3/4, yaffs2 等支持 fs_use_xattr, fs_use_task or fs_use_trans 操作,默认标签是labelfs,参考fs_use:
 # Label inodes via getxattr.
 fs_use_xattr yaffs2 u:object_r:labeledfs:s0;
 fs_use_xattr jffs2 u:object_r:labeledfs:s0;
 fs_use_xattr ext2 u:object_r:labeledfs:s0;
 fs_use_xattr ext3 u:object_r:labeledfs:s0;
 fs_use_xattr ext4 u:object_r:labeledfs:s0;
 fs_use_xattr xfs u:object_r:labeledfs:s0;
 fs_use_xattr btrfs u:object_r:labeledfs:s0;
 fs_use_xattr f2fs u:object_r:labeledfs:s0;
 
 对于非真实存储型的file system 并且无法支持 fs_use_xattr, fs_use_task or fs_use_trans,比如proc 此类需要使用genfscon 来定义,参考 genfs_contexts:
 # selinuxfs booleans can be individually labeled.
 genfscon selinuxfs / u:object_r:selinuxfs:s0
 genfscon cgroup / u:object_r:cgroup:s0
 # sysfs labels can be set by userspace.
 genfscon sysfs / u:object_r:sysfs:s0
 genfscon inotifyfs / u:object_r:inotify:s0
 genfscon vfat / u:object_r:vfat:s0
 genfscon debugfs / u:object_r:debugfs:s0
 genfscon fuse / u:object_r:fuse:s0
 genfscon pstore / u:object_r:pstorefs:s0
 genfscon functionfs / u:object_r:functionfs:s0
 genfscon usbfs / u:object_r:usbfs:s0

7. File labels
 目前统一全部定义在file_contexts 当中。这个文件也是经常会修改的。比如:
 ##########################
 # Devices
 #
 /dev(/.*)?  u:object_r:device:s0
 /dev/akm8973.*  u:object_r:sensors_device:s0
 /dev/accelerometer u:object_r:sensors_device:s0
 /dev/adf[0-9]*  u:object_r:graphics_device:s0
 /dev/adf-interface[0-9]*\.[0-9]* u:object_r:graphics_device:s0
 /dev/adf-overlay-engine[0-9]*\.[0-9]* u:object_r:graphics_device:s0
 /dev/alarm  u:object_r:alarm_device:s0
 /dev/android_adb.* u:object_r:adb_device:s0
  
 #############################
 # System files
 #
 /system(/.*)?  u:object_r:system_file:s0
 /system/bin/sh  -- u:object_r:shell_exec:s0
 /system/bin/run-as -- u:object_r:runas_exec:s0
 /system/bin/bootanimation u:object_r:bootanim_exec:s0
 /system/bin/app_process32 u:object_r:zygote_exec:s0
 /system/bin/app_process64 u:object_r:zygote_exec:s0
 /system/bin/servicemanager u:object_r:servicemanager_exec:s0
 /system/bin/surfaceflinger u:object_r:surfaceflinger_exec:s0
 
 #############################
 # Data files
 #
 /data(/.*)?  u:object_r:system_data_file:s0
 /data/.layout_version  u:object_r:install_data_file:s0
 /data/backup(/.*)?  u:object_r:backup_data_file:s0
 /data/secure/backup(/.*)? u:object_r:backup_data_file:s0
 /data/security(/.*)? u:object_r:security_file:s0
 /data/system/ndebugsocket u:object_r:system_ndebug_socket:s0
 /data/drm(/.*)?  u:object_r:drm_data_file:s0
 
 ###########################################
 # Root
 /   u:object_r:rootfs:s0

 # Data files
 /adb_keys  u:object_r:adb_keys_file:s0
 /default\.prop  u:object_r:rootfs:s0
 /fstab\..*  u:object_r:rootfs:s0
 /init\..*  u:object_r:rootfs:s0
 /res(/.*)?  u:object_r:rootfs:s0
 /ueventd\..*  u:object_r:rootfs:s0

8. Type Enforce
 类型审查,通常涉及到:
   * domain 类型定义
   * 执行档 入口定义
   * SELinux 模式设置
   * domain 切换
   * 类型访问规则
 
 下面我们就以mediaserver 这个进程的定义来解析,对应文件是 mediaserver.te。
 * domain 类型定义:
 type mediaserver, domain;
 
 * 执行档 入口定义:
 type mediaserver_exec, system_file_type, exec_type, file_type;
 
 * SELinux 模式设置:
 默认都是 enforcing mode,Google 严禁process在正式版本中使用permissive mode。设置成permissive mode 通常都是为了debug 需要,在debug 完成后都需要设置成 enforcing mode。
 
 * domain 切换:
 init_daemon_domain(mediaserver)
 这是一个复杂的宏,简单来说就是当init fork 子进程执行mediaserver_exec 这个类型的执行档时,其domain 从init 切换到mediaserver。
 这个宏展开是:
 #####################################
 # init_daemon_domain(domain)
 # Set up a transition from init to the daemon domain
 # upon executing its binary.
 define(`init_daemon_domain', `
 domain_auto_trans(init, $1_exec, $1)
 tmpfs_domain($1)
 ')
 
 #####################################
 # domain_auto_trans(olddomain, type, newdomain)
 # Automatically transition from olddomain to newdomain
 # upon executing a file labeled with type.
 #
 define(`domain_auto_trans', `
 # Allow the necessary permissions.
 domain_trans($1,$2,$3)
 # Make the transition occur by default.
 type_transition $1 $2:process $3;
 ')
 
 define(`domain_trans', `
 # Old domain may exec the file and transition to the new domain.
 allow $1 $2:file { getattr open read execute };
 allow $1 $3:process transition;
 # New domain is entered by executing the file.
 allow $3 $2:file { entrypoint open read execute getattr };
 # New domain can send SIGCHLD to its caller.
 allow $3 $1:process sigchld;
 # Enable AT_SECURE, i.e. libc secure mode.
 dontaudit $1 $3:process noatsecure;
 # XXX dontaudit candidate but requires further study.
 allow $1 $3:process { siginh rlimitinh };
 ')
 
 这类宏还有很多,可以参考te_macros。
 
 * 类型访问规则
 #允许mediaserver 读取sdcard_type 类型的目录和文件:

 r_dir_file(mediaserver, sdcard_type)

 #允许mediaserver使用binder服务和发起binder call:
 binder_use(mediaserver)
 binder_call(mediaserver, binderservicedomain)
 binder_call(mediaserver, appdomain)
 binder_service(mediaserver)

 # 允许mediaserver 做其他的一些操作:
 allow mediaserver media_data_file:dir create_dir_perms;
 allow mediaserver media_data_file:file create_file_perms;
 allow mediaserver { sdcard_type fuse }:file write;
 allow mediaserver gpu_device:chr_file rw_file_perms;
 allow mediaserver video_device:dir r_dir_perms;
 ......

SELinux Policy limited by Google

从一开始,Google 就已经通过neverallow 语法以及 CTS 测试对selinux policy 进行了初步限制。

1. 维持system/sepolicy 与Google AOSP一致,尽量不要修改。特别是不要去除任何的neverallow 语句。

2. 严禁修改untrusted_app.te,放开untrusted_app 的权限。

3. 系统关键进程启动长时间运行的process,必须进行domain 切换。比如netd, installd, vold, zygote 等。

4. Native thread 严禁使用kernel 标签,而kernel thread 除init 外,都应当是kernel 标签。

5. 在user build 中不能存在如su, recovery, init_shell 标签的进程。

6. 在user build 中所有的process 都必须是Enforce Mode。

在后续的版本更新中,Google 导入了大批量的限制性 neverallow 语法,特别是从O 版本开始针对System/Vendor 的分离,进行了大量针对性的限制,具体可以参考Google 在/system/sepolicy 中所设定的neverallow 语句。 

Add new service started by init

▪情景:定义一个init 启动的service -- demo_service,对应的执行档为/system/bin/demo。

▪(1). 在/device/mediatke/sepolicy 下创建一个demo.te(例如:Android T中的/device/mediatke/sepolicy/base/private/demo.te)

▪(2). 在demo.te中定义demo 类型,init 启动service 时类型转换:

•type  demo, domain, coredomain;

•type  demo_exec, system_file_type, exec_type, file_type;

•init_daemon_domain(demo)

▪(3). 在file_contexts中绑定执行档:

•/system/bin/demo  u:object_r:demo_exec:s0

▪(4). 根据demo 需要访问的文件以及设备,在demo.te中定义其它的权限。

注: Android O 版本后, 需要根据service 本身是放在vendor 还是 system 选择不同的存放sepolicy 存放位置。

Set System Property

情景:一个native service demo,需要设置一个自定义的system property -- demo.setting.case1。

(1). 在property.te中定义system property类型:
 system_internal_prop(system_demo_prop)
 typeattribute system_demo_prop extended_core_property_type;

(2). 在property_contexts中绑定system property 类型:
 demo.   u:object_r:system_demo_prop:s0

(3). 在demo.te 中新增设置权限,可以使用宏set_prop:
set_prop(demo, system_demo_prop) 

(4). 如果其他的domain 里面需要读取,可以使用宏get_prop:
get_prop(xxxx, system_demo_prop)

Use Binder and Add System Service

▪情景:  一个native service demo,创建了一个binder service demo,并对外提供服务。

▪(1). 在service.te 中定义service 类型:

•type  demo_service, service_manager_type;

▪(2).  在service_contexts中绑定service:

•demo  u:object_r:demo_service:s0

▪(3). 在demo.te中声明binder 权限:

•binder_use(demo)

•binder_call(demo, binderservicedomain)

•biner_service(demo)

▪(4). 在demo.te中允许demo 这个进程添加demo 这个service:

•allow  demo demo_service:service_manager add;

Access system device

▪情景:一个native service 需要访问一个专属的char device -- /dev/demo。

▪(1).在device.te中定义device类型:

•type demo_device, dev_type;

▪(2). 在file_contexts中绑定demo device:

•/dev/demo u:object_r:demo_device:s0

▪(3). 在demo.te中授权demo使用demo device的权限:

• allow demo demo_device:chr_file rw_file_perms;

Use Local Socket

▪情景:  一个native service 通过init 创建一个socket 并绑定在 /dev/socket/demo,并且允许某些process 访问。

▪(1). 在file.te中定义socket的类型:

•type demo_socket, file_type;

▪(2). 在file_contexts中绑定socket 的类型:

•/dev/socket/demo_socket u:object_r:demo_socket:s0

▪(3). 允许所有app process访问:

•# allow app connectto & write

•unix_socket_connect(appdomain, demo, demo)

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值