权限控制
DAC
概念
DAC 即 Discretionary Access control, 自主访问控制, 即系统只提供基本的验证, 完整的访问控制由开发者自己控制。
DAC的简洁造就了它的高效,但是过于简洁也让它的权限划分粒度过大,一旦获得了root权限,几乎就是无所不能。
在CPU日益高涨的今天,性能开销已经不是问题了,权限的细粒度管理更加重要,所以诞生了MAC。
将资源访问者分成三类:Owner、Group、Other 。 将访问权限也分成三类:read、write、execute
进程(访问者)有自己的uid/gid,通过uid/gid 和 文件权限匹配, 系统来确定进程是否可以访问文件。
DAC机制下,每一个用户进程默认都拥有该用户的所有权限。
优缺点
- 因为Root用户是拥有所有权限的,所以DAC对Root用户的限制是无效的。
- 用户进程拥有该用户的所有权限,可以修改/删除该用户的所有文件资源, 难以防止恶意软件。
MAC
MAC 即 Mandatory Access control, 强制性访问控制, 即系统针对每一项访问都进行严格的限制, 具体的限制策略由开发者给出.
优缺点
MAC在DAC的基础上,把行为、规则、判定结果进一步细分。所以它的权限管理粒度更细,但是开销也稍大。
linux 相关
用户组和用户
-rw-rw-r--
首字母含义
- : 文件
d :目录
后面9个字母代表 rw-rw-r--
拥有者
第一个 rw- 是本身 可读可写
第二个 rw- 是同组 可读可写
第三个 r--是其他组 只能读
完整权限 是rwx 可读可写可执行
用户和用户组的概念
一个用户可以分配到多个用户组
一个用户组可以有多个用户
777是怎么来的
0111 读是4 读写是6 读写执行 是7 当都是1的时候
android下用户组
app安装的 是pms 安装的
每个app安装的时候 都是独立的用户组 都有自己的gid和uid 独立且唯一。
从1w开始 最大为19999 最多安装9999个app
root 用户id 是0
PackageManagerService.java
//注册新的appid
optimisticallyRegisterAppId()
//com/android/server/pm/Settings.java
//获取userAppID
mSettings. registerAppIdLPw()
-->
Settings.acquireAndRegisterNewAppIdLPw()
基础权限
pms会扫描 app的main文件,输出到 data/system/packages.list 文件
com.google.android.networkstack.tethering 1073 0 /data/user_de/0/com.google.android.networkstack.tethering network_stack:privapp:targetSdkVersion=29 3002,3003,3007,3006,3005,3004 0 31 1 @system
com.android.cts.priv.ctsshim 10145 0 /data/user/0/com.android.cts.priv.ctsshim default:privapp:targetSdkVersion=28 none 0 31 1 @system
com.google.android.youtube 10114 0 /data/user/0/com.google.android.youtube default:targetSdkVersion=31 3003 0 1524891096 1 @system
....
截取一部分 可以看到 youtube 属于3003用户组
appid 是10114 , 系统应用
com.google.android.youtube 10114 0 /data/user/0/com.google.android.youtube default:targetSdkVersion=31 3003 0 1524891096 1 @system
android 源码目录里面有一个 framwork/base/data/etc/platform.xml 文件
写的是android里面的权限
<?xml version="1.0" encoding="utf-8"?>
<permissions>
<permission name="android.permission.BLUETOOTH_ADMIN" >
<group gid="net_bt_admin" />
</permission>
<permission name="android.permission.BLUETOOTH" >
<group gid="net_bt" />
</permission>
<permission name="android.permission.INTERNET" >
<group gid="inet" />
</permission>
.....省略
</permissions>
INTERNET 对应的是inet
在安装的时候 pms 发现你有这个权限就把你分配到 对应的用户组里面
这样就有了权限,如果注释掉对应权限,对应的用户组也会少对应的用户组
3003 怎么来的
/system/core/include/cutils/android_filesystem_config.h
#define AID_INET3003
#pragma once
#define AID_OEM_RESERVED_START 2900
#define AID_OEM_RESERVED_END 2999
/* The 3000 series are intended for use as supplemental group id's only.
* They indicate special Android capabilities that the kernel is aware of. */
#define AID_NET_BT_ADMIN 3001 /* bluetooth: create any socket */
#define AID_NET_BT 3002 /* bluetooth: create sco, rfcomm or l2cap sockets */
#define AID_INET3003 /* can create AF_INET and AF_INET6 sockets */
#define AID_NET_RAW 3004 /* can create raw INET sockets */
#define AID_NET_ADMIN 3005 /* can configure interfaces and
动态权限
在data/system/packages.xml
android 12以后这个文件被改成2进制了,需要再改回来才能看到。
我们可以看到,在xml文件中的perms标签下有对应权限的声明,以及granted的参数,也就是说下次再进来之后,会检查这个xml文件中我们要申请的这个权限是不是已经获取到了,如果granted = true || flags = 0,那么就不会再弹窗了。
//frameworks/base/services/core/java/com/android/server/pm/Settings.java
//权限写回
writeLPr()
<package name="com.lay.nowinandroid" codePath="/data/app/~~Z3KlFzqUZ3vXhgizpl-R4Q==/com.lay.nowinandroid-NcpT91M9gQfcPkeJJhPoxA==" nativeLibraryPath="/data/app/~~Z3KlFzqUZ3vXhgizpl-R4Q==/com.lay.nowinandroid-NcpT91M9gQfcPkeJJhPoxA==/lib" publicFlags="810073926" privateFlags="-1400893440" ft="188c89ab490" it="188c7e11f01" ut="188c89ab614" version="1" userId="10131">
<sigs count="1" schemeVersion="2">
<cert index="9" key="/>
</sigs>
<perms>
<item name="android.permission.INTERNET" granted="true" flags="0" />
</perms>
<proper-signing-keyset identifier="10" />
</package>
SELinux(基于Te文件)
资料
source.android.google.cn/docs/securi…
上下文
在/system/sepolicy/private/fifile_contexts中声明该执行文件的SEAndroid系统文件的安全上下文
/system/bin/name-server u:object_r:name-server_exec:s0
//代码路径 selinux用户: selinux角色:类型:安全级别
//类型是最重要的
在安全上下文中,只有类型(type)才是最重要的,SELinux用户SELinux角色和安全级别都几乎可以忽略不计的。正因为如此,SEAndroid安全机制又称为是基于TE(Type Enforcement)策略的安全机制。
用户与角色
/system/sepolicy/private/users
user u roles { r } level s0 range s0 - mls_systemhigh;
//声明角色 u 权限是r 级别为s0 可用安全范围 为 s0 到 mls_systemhigh;
在/system/sepolicy/public/roles中声明了SELinux角色r与类型domain关联
role r types domain; //权限r的类型是domain
SeAndroid 只有一个用户u
角色
- r 适用于主题 如进程
- object_r 适用于对象 如文件
r 是进程 objectr 是对象 可执行文件 zygote 就是r
固定格式就为
u:object_r/r:自己的名字:s0
模板 Zygote.te
# zygote
type zygote, domain; //domain 是属性
type zygote_tmpfs, file_type;
type zygote_exec, system_file_type, exec_type, file_type;
类型
文件类型 都是file_type
进程都是domain
安全策略
.te文件就是策略,Type Enforcement
案例讲解
fsbus
- system/sepolicy/prebuilts/api/31.0/private 和 system/sepolicy/private/file_context 下 都新建 name-server.te
type name-server, domain;//定义一个name-server的类型 继承domain
typeattribute name-server coredomain; //给name-server添加 coredomain
typeattribute name-server mlstrustedsubject
//新建一个类型name-server_exec 可执行类型 系统类型 ,文件类型
type name-server_exec, system_file_type,exec_type,file_type;
//权限申请
allow name-server self:tcp_socket { read write getattr getopt setopt shutdown create bind connect name_connect }; allow name-server self:netlink_route_socket {create write read nlmsg_readpriv nlmsg_read}; allow name-server fwmarkd_socket:sock_file {write};
allow name-server port:tcp_socket {name_connect};
allow name-server netd:unix_stream_socket {connectto};
allow name-server self:capability {net_raw};
allow name-server node:tcp_socket {node_bind};
init_daemon_domain(name-server)//会将name-server 和name-server_exec绑定
/**
init _daemon_domain(name-server) 其实相当于调用一个方法
会调用一个宏方法 进行默认的配置 (黄字部分) 将name-server 和 name-server-exec 绑定到一起
可以 看 surfaceflinger.te 里面也进行了调用init_daemon_domain(surfaceflinger)
**
函数位置在 /system/sepolicy/public/te_macros
#####################################
# 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)
')
会调用到 domain_auto_trans() 会将两者绑定到一起
/
-
修改文件file_contexts
//源码路径 可执行的类型 级别s0 可以立即为默认级别 //这里起码后面加exec 也是因为 //domain_auto_trans(init, $1_exec, $1) 参数拼接了exec /system/bin/name-server u:object_r:name-server_exec:s0 -
修改文件netd.te
typeattribute netd coredomain; typeattribute netd domain_deprecated; //#增加============== allow netd name-server:fd {use}; allow netd name-server:tcp_socket {getopt}; allow netd name-server:tcp_socket {setopt}; allow netd name-server:tcp_socket {read write};
权限缺失处理
Audit2allow 工具(Python2)
source.android.google.cn/docs/securi…
//确保自己执行了soruce 和lunch
adb pull /sys/fs/slinux/policy
adb logcat -b enents -d |audit2allow -p policy
AVC阅读
avc: denied { bind } for pid=417 comm="name-server" scontext=u:r:name-server:s0
tcontext=u:r:name-server:s0 tclass=tcp_socket permissive=0
| 说明 | 案例 |
|---|---|
| 缺少什么权限 | {bind} |
| 谁缺少权限 | scontext=u:r:name-server:s0 |
| 对谁缺少权限 | tcontext=u:r:name-server:s0 |
| 什么类型权限 | tclass=tcp_socket permissive=0 |
所以添加
#allow [谁缺少权限] [对谁缺少权限]:[什么类型的权限] [缺少什么权限] #当sconext与tcontext都是自己时候,可以将 [对谁缺少权限]写为:self
allow name-server self:tcp_socket {bind}
allow name-server netd:unix_stream_socket {connectto};
type 和typeattibute 的区别
type有两个作用,定义(声明)并关联某个属性。可以把这两个作用分开,type定义,typeattribute进行关联
#定义httpd_user_content_t,并关联两个属性
type httpd_user_content_t, file_type, httpdcontent;
分成两条语句进行表述:
#定义httpd_user_content_t
type httpd_user_content_t;
#关联属性
typeattribute httpd_user_content_t file_type, httpdcontent;