Android - Capability的实现

这也是n年前的笔记,重新整理一下。


Linux Capability


关于Linux的capability功能,网上已经有很多文档,这里就简单罗列一下,不细讲了。

进程和文件分别有各自的Capability表示方法。


进程的Capability

cap_effective:
当一个进程要进行某个特权操作时,操作系统会检查cap_effective的对应位是否有效,而不再是检查进程的有效UID是否为0.
例如,如果一个进程要设置系统的时钟,Linux的内核就会检查cap_effective的CAP_SYS_TIME位(第25位)是否有效.

cap_permitted:
表示进程能够使用的能力,在cap_permitted中可以包含cap_effective中没有的能力,这些能力是被进程自己临时放弃的,也可以说cap_effective是cap_permitted的一个子集.

cap_inheritable:
表示能够被当前进程执行的程序继承的能力。

cap_bset:
能力边界集通过sysctl命令导出,用户可以在/proc/sys/kernel/cap-bound中看到系统保留的能力。在默认情况下,能力边界集所有的位都是打开的。

cap_inheritable/ cap_permitted/ cap_effective/ cap_bset保存在struct task_struct-> real_cred中。

struct task_struct {
   ...
    /* process credentials */
    /* objective and real subjective task credentials (COW) */
    const struct cred __rcu *real_cred; 
    /* effective (overridable) subjective task
    const struct cred __rcu *cred;
..
}

struct cred {
    ...

    kernel_cap_t    cap_inheritable; /* caps our children can inherit */
    kernel_cap_t    cap_permitted;  /* caps we're permitted */
    kernel_cap_t    cap_effective;  /* caps we can actually use */
    kernel_cap_t    cap_bset;   /* capability bounding set */
    ...
}

以下系统调用直接操作自己进程的struct task_struct-> real_cred,修改或读取相应的cap成员。

int capget(cap_user_header_t hdrp, cap_user_data_t datap);  
int capset(cap_user_header_t hdrp, const cap_user_data_t datap);

可在/proc/PID/status下察看当前capability:

这里写图片描述

在user space,有专门的cap lib供开发使用。有如下的API可设置进程自己的capability:

cap_init
cap_set_flag
cap_clear
cap_set_proc

文件的Capability

可执行文件也拥有三组能力集,对应于进程的三组能力集,分别称为

cap_effective,
cap_allowed 
cap_forced
(分别简记为fE,fI,fP)

其中,cap_allowed表示程序运行时可从原进程的cap_inheritable中集成的能力集,cap_forced表示运行文件时必须拥有才能完成其服务的能力集,而cap_effective则表示文件开始运行时可以使用的能力。

使用user space的工具修改或读取可执行文件的capability:

setcap:

setcap cap_chown=eip /bin/chown
cap_chown=eip是将chown的能力以cap_effective(e),cap_inheritable(i),cap_permitted(p)三种位图的方式授权给相关的程序文件.

用setcap -r /bin/chown可以删除掉文件的能力。

getcap:

getcap /bin/chown                
得到/bin/chown = cap_chown+eip

对于支持扩展属性的文件系统,文件的capability在扩展属性xattr中的定义如下:

#define XATTR_SECURITY_PREFIX   "security."
#define XATTR_CAPS_SUFFIX "capability"
#define XATTR_NAME_CAPS XATTR_SECURITY_PREFIX XATTR_CAPS_SUFFIX

即扩展属性名是 security.capability。

在Linux层面,提供如下的系统调用访问文件的Cap:
set:

    sys_fsetxattr
    sys_lsetxattr
    sys_setxattr

以上3者的区别见 SETXATTR(2)

get:

    sys_fgetxattr
    sys_lgetxattr
    sys_getxattr

Android 实现


Native Service的Cap

下面以Android进程logd和installd为例来看看native service的实现:

u:r:logd:s0                    logd      476   1     /system/bin/logd

由于logd是由init从init.rc启动,所以需要去除一些不必要的权限,只保留自己需要的:
文件System/core/logd/main.cpp:

drop_privs {
    capdata[CAP_TO_INDEX(CAP_SYSLOG)].permitted = CAP_TO_MASK(CAP_SYSLOG);
    capdata[CAP_TO_INDEX(CAP_AUDIT_CONTROL)].permitted |=  CAP_TO_MASK(CAP_AUDIT_CONTROL);
    capset(&capheader, &capdata[0])
}

可见利用capset重置了Capability。


对于进程installd:

u:r:installd:s0                install   201   1     /system/bin/installd

进入installd进程后,做权限降级处理,只设置自己需要的权限:
framework/native/cmds/installed/installed.c:

drop_privileges {
   capdata[CAP_TO_INDEX(CAP_DAC_OVERRIDE)].permitted |= CAP_TO_MASK(CAP_DAC_OVERRIDE);
   capdata[CAP_TO_INDEX(CAP_CHOWN)].permitted        |= CAP_TO_MASK(CAP_CHOWN);
   capdata[CAP_TO_INDEX(CAP_SETUID)].permitted       |= CAP_TO_MASK(CAP_SETUID);
   capdata[CAP_TO_INDEX(CAP_SETGID)].permitted       |= CAP_TO_MASK(CAP_SETGID);
   capdata[CAP_TO_INDEX(CAP_FOWNER)].permitted       |= CAP_TO_MASK(CAP_FOWNER);
   capset(&capheader, &capdata[0])
}

Android进程的Cap

对Android Server进程和Java进程的Cap处理,是由Zygote进程执行的。
framework/base/core/init/com_android_internal_os_zygote.cpp:

创建普通process时(以BT为例):

com_android_internal_os_Zygote_nativeForkAndSpecialize:

    jlong capabilities = 0;
    if (uid == AID_BLUETOOTH) {
        capabilities |= (1LL << CAP_WAKE_ALARM);
    }
    ...
   SetCapabilities(…)
    --> capset(&capheader, &capdata[0])

创建systemServer process时,传入所需的permittedCapabilities/effectiveCapabilities:

com_android_internal_os_Zygote_nativeForkSystemServer:

forkAndSpecializeCommon
{
    if (pid == 0) {
        SetCapabilities(…)
        --> capset(&capheader, &capdata[0])
    }
}

而所传入的参数如下:
Zygoteinit.cpp:

startSystemServer:
long capabilities = posixCapabilitiesAsBits(
            OsConstants.CAP_BLOCK_SUSPEND,
            OsConstants.CAP_KILL,
            OsConstants.CAP_NET_ADMIN,
            OsConstants.CAP_NET_BIND_SERVICE,
            OsConstants.CAP_NET_BROADCAST,
            OsConstants.CAP_NET_RAW,
            OsConstants.CAP_SYS_MODULE,
            OsConstants.CAP_SYS_NICE,
            OsConstants.CAP_SYS_RESOURCE,
            OsConstants.CAP_SYS_TIME,
            OsConstants.CAP_SYS_TTY_CONFIG
        );

即默认systemserver拥有以上所列的特权。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值