Android Framework之 权限管理 SeLinux理解

1,012 阅读7分钟

权限控制

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 
​
动态权限

资料 :juejin.cn/post/724551…

在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

wertherzhang.com/seandroid%E…

案例讲解

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 [谁缺少权限] [对谁缺少权限]:[什么类型的权限] [缺少什么权限] #当sconexttcontext都是自己时候,可以将 [对谁缺少权限]写为: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;