最早之前,linux对任务权限分为privileged processes(UID等于0,属于超级用户或者root用户)和unprivileged processes(UID非0)。privileged processes拥有系统的所有权限,而unprivileged processes拥有部分权限(根据进程凭证,比如effective UID, effective GID,and supplementary group list)。
Linux自2.2版本以后,加入了capabilities功能,将这些特权分为不同的种类,比如
(更多介绍:参考文档3中“Capabilities list”介绍)
内核细分的这些权限,不再根据超级用户/普通用户来划分归属,而是可以由用户自由配置(配置还是要超级用户权限滴),即使普通用户也可以做某些之前超级用户才可以执行的任务(比如reboot)。同时capabilities可以细分到线程,支持子进程或者创建线程继承capabilities属性。
每个线程拥有四个capability sets:
(1)Permitted(pP):规定该线程的capabilities能力的全集,即表示线程能够使用的能力的集合。它并不使能线程的capabilities,而是作为一个规定。
(2)Effective(pE):使能该线程的capabilities能力。当线程进行某个特权操作时,系统会检查effective对应位是否有效。如有效,则允许执行。
(3)Inheritable(pI):线程通过execve(2) 执行新命令时,新命令集成的capabilities能力。
(4)bounding set(X):关闭线程的capabilities能力。(此项在capabilities的man文档中并未和上面三个并列,但是在patch中提及,属于capabilitiy sets一员)
举例来说,
某线程设置Permitted:CAP_KILL,CAP_SETFCAP和CAP_SYS_BOOT,那么此线程最大能拥有这些权限。
如果想让它拥有重启系统的权限(CAP_SYS_BOOT),那么Effective设置CAP_SYS_BOOT即可(另外两项可以不设置)。如果想要拥有Permitted定义以外的权限,比如CAP_WAKE_ALARM ,那么Effective会设置失败。
如果希望execve(2)执行新的命令有重启系统权限,那么设置Inheritable:CAP_SYS_BOOT即可。
(更多介绍:参考文档3中“Thread capability sets”介绍)
同时文件也可以拥有capabilities权限,Permitted(fP),Effective(fE)和Inheritable(fI)。
使用execve(2)执行的新命令继承权限遵循:
pP' = (X & fP) | (pI & fI) pI' = pI pE' = (fE ? pP' : 0) X is unchanged
where:
P denotes the value of a thread capability set before the execve(2) P' denotes the value of a thread capability set after the execve(2) F denotes a file capability set cap_bset is the value of the capability bounding set (described below).
解读:
pP’ = (X & fP) | (pI & fI)
execve(2)执行新任务的P’(permitted),是基于fP&X(总集-bounding未使能)与pI & fI(线程和文件属性inheritable取与,inheritable表示子任务应该拥有的capabilities)的取或。
pI’ = pI
execve(2)执行新任务的P’(inheritable),继承原任务的P(inheritable)
pE’ = (fE ? pP’ : 0)
execve(2)执行新任务的P’(effective),根据F(effective)
(文件effective是否设置),设置则继承新任务的P’(permitted),否则无capabilities(即新任务未使能任何权限,但是还是可以根据pP’再调用cap_set使能)。
以上更多介绍,可以见:参考文档3和参考文档4。
1.引入管理混乱。举例来说如果非特权进程设置fE后,它将遵循“secure exec”规则:AT_SECURE被设置后,LD_PRELOAD就不会生效;
2.安全问题。给非特权进程提供特权用户的某些权限,这样增加了黑客攻击的通道。黑客很可能利用capabilities里面漏洞进行提权操作。
3.inheritance是基本无用的。如果你非特权用户或者二进制文件,并且fI未使能时,此时意味着你无法通过execve(2)执行一个帮助程序或者超出权限的命令。同时一般来说,我们希望进程包括创建的子进程拥有同样的权限,此时经常被设置成pP’ = pI。但是这样的话,你又要考虑每个非特权进程的权限问题等等。
linux4.3内核新增了一种capability mask,“ambient”(pA)。pA完成了用户对pI的不足。
pA具有如下特性:
(1)pP和pI未设置的capabilities,pA也不能设置,即pA不能使能超出pP&pI的特权;
(2)当pP或者pI某权限(比如CAP_SYS_BOOT权限)关闭后,pA也随之关闭对应权限,这样确保降低权限时候,子任务可以同时降低;
(3)当使用setresuid将用户从特权用户切换到非特权用户时,pA将被清除(设置SECBIT_NO_SETUID_FIXUP后不会被清除);
权限规则被修改成:
pA' = (file caps or setuid or setgid ? 0 : pA) pP' = (X & fP) | (pI & fI) | pA' pI' = pI pE' = (fE ? pP' : pA') X is unchanged
非特权用户如果有一个capability,那么可以添加到pA。如果这样做,那么它子进程可以在pA,pP和pE中获取这种能力。举例来说,如果你设置pA = CAP_NET_BIND_SERVICE,那么你子进程可以自动连接到low-bumbered ports。
linux对应commit提供了测试用例,ambient_test.c
/*
* Test program for the ambient capabilities. This program spawns a shell
* that allows running processes with a defined set of capabilities.
*
* (C) 2015 Christoph Lameter
* Released under: GPL v3 or later.
*
*
* Compile using:
*
* gcc -o ambient_test ambient_test.o -lcap-ng
*
* This program must have the following capabilities to run properly:
* Permissions for CAP_NET_RAW, CAP_NET_ADMIN, CAP_SYS_NICE
*
* A command to equip the binary with the right caps is:
*
* setcap cap_net_raw,cap_net_admin,cap_sys_nice+p ambient_test
*
*
* To get a shell with additional caps that can be inherited by other processes:
*
* ./ambient_test /bin/bash
*
*
* Verifying that it works:
*
* From the bash spawed by ambient_test run
*
* cat /proc/$$/status
*
* and have a look at the capabilities.
*/
#include
#include
#include
#include
#include
#include
/*
* Definitions from the kernel header files. These are going to be removed
* when the /usr/include files have these defined.
*/
#define PR_CAP_AMBIENT 47
#define PR_CAP_AMBIENT_IS_SET 1
#define PR_CAP_AMBIENT_RAISE 2
#define PR_CAP_AMBIENT_LOWER 3
#define PR_CAP_AMBIENT_CLEAR_ALL 4
static void set_ambient_cap(int cap)
{
int rc;
capng_get_caps_process();
rc = capng_update(CAPNG_ADD, CAPNG_INHERITABLE, cap);
if (rc) {
printf("Cannot add inheritable cap\n");
exit(2);
}
capng_apply(CAPNG_SELECT_CAPS);
/* Note the two 0s at the end. Kernel checks for these */
if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, cap, 0, 0)) {
perror("Cannot set cap");
exit(1);
}
}
int main(int argc, char **argv)
{
int rc;
set_ambient_cap(CAP_NET_RAW);
set_ambient_cap(CAP_NET_ADMIN);
set_ambient_cap(CAP_SYS_NICE);
printf("Ambient_test forking shell\n");
if (execv(argv[1], argv + 1))
perror("Cannot exec");
return 0;
}
编译执行:
#gcc -o ambient_test ambient_test.c -lcap-ng
(-lcap-ng使用时cap-ng.h: No such file or directory错误,请 sudo apt-get install libcap-ng-dev)
#sudo setcap cap_net_raw,cap_net_admin,cap_sys_nice+p ambient_test
#./ambient_test /bin/bash
#cat /proc/$$/status
CapInh: 0000000000803000
CapPrm: 0000000000803000
CapEff: 0000000000803000
CapBnd: 0000003fffffffff
CapAmb: 0000000000803000
CapAmb表示ambient特性,此时execv进入的新的shell环境,pI,pP和pE集成原来的pA。
参考文档:
(1) https://lwn.net/Articles/632520/
(2) https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=58319057b7847667f0c9585b9de0e8932b0fdb08
(3) http://man7.org/linux/man-pages/man7/capabilities.7.html
(4) https://www.cnblogs.com/iamfy/archive/2012/09/20/2694977.html