天天看点

capabilities: ambient capabilities说明

linux capability介绍

最早之前,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功能,将这些特权分为不同的种类,比如

  • CAP_KILL:拥有kill进程的权限
  • CAP_SETFCAP:拥有配置文件capabulities属性的权限
  • CAP_SYS_BOOT:运行reboot和kexec_load的权限
  • CAP_SYS_MODULE:模块相关操作的权限

(更多介绍:参考文档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。

内核capabilities机制存在的问题

1.引入管理混乱。举例来说如果非特权进程设置fE后,它将遵循“secure exec”规则:AT_SECURE被设置后,LD_PRELOAD就不会生效;

2.安全问题。给非特权进程提供特权用户的某些权限,这样增加了黑客攻击的通道。黑客很可能利用capabilities里面漏洞进行提权操作。

3.inheritance是基本无用的。如果你非特权用户或者二进制文件,并且fI未使能时,此时意味着你无法通过execve(2)执行一个帮助程序或者超出权限的命令。同时一般来说,我们希望进程包括创建的子进程拥有同样的权限,此时经常被设置成pP’ = pI。但是这样的话,你又要考虑每个非特权进程的权限问题等等。

ambient capabilities

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)  Christoph Lameter <[email protected]>
 * 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 <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <cap-ng.h>
#include <sys/prctl.h>
#include <linux/capability.h>

/*
 * 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();
    }
    capng_apply(CAPNG_SELECT_CAPS);

    /* Note the two s at the end. Kernel checks for these */
    if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, cap, , )) {
        perror("Cannot set cap");
        exit();
    }
}

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[], argv + ))
        perror("Cannot exec");

    return ;
}
           

编译执行:

#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: 
CapPrm: 
CapEff: 
CapBnd: fffffffff
CapAmb:   
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

capabilities: ambient capabilities说明

继续阅读