process_capabilities
capabilities概述
传统的UNIX系统将进程分为两类:
- 
provileged特权进程:effective user ID是0的进程,内核对此类进程跳过所有权限检查,即有权限执行任何操作。 - 
unprivileged非特权进程:effective user ID不是0的进程,内核对此类进程进行全面权限检查,根据effective UID, effective GID, supplementary grop list决定是否有权限执行某项操作。 
传统UNIX对于普通进程执行特权操作就必须使进程的effective user ID为0(通过set-user-ID),如此进程就拥有了所有特权,若进程执行非法或意外操作将导致严重破坏。为了安全Linux-kernel-2.2后将特权(以前属于effective user ID = 0的)拆分为不同的细小单元。一个单元只拥有一部分特权。这些单元就是capabilities,其实capabilities是属于线程的特性,也就是可以给线程分配不同的capability。
capabilites 列表
| capability 名称 | 描述 | 
|---|---|
| CAP_AUDIT_CONTROL | 启用和禁用内核审计;改变审计过滤规则;检索审计状态和过滤规则 | 
| CAP_AUDIT_READ | 允许通过 multicast netlink 套接字读取审计日志 | 
| CAP_AUDIT_WRITE | 将记录写入内核审计日志 | 
| CAP_BLOCK_SUSPEND | 使用可以阻止系统挂起的特性 | 
| CAP_CHOWN | 修改文件所有者的权限 | 
| CAP_DAC_OVERRIDE | 忽略文件的 DAC 访问限制 | 
| CAP_DAC_READ_SEARCH | 忽略文件读及目录搜索的 DAC 访问限制 | 
| CAP_FOWNER | 忽略文件属主 ID 必须和进程用户 ID 相匹配的限制 | 
| CAP_FSETID | 允许设置文件的 setuid 位 | 
| CAP_IPC_LOCK | 允许锁定共享内存片段 | 
| CAP_IPC_OWNER | 忽略 IPC 所有权检查 | 
| CAP_KILL | 允许对不属于自己的进程发送信号 | 
| CAP_LEASE | 允许修改文件锁的 FL_LEASE 标志 | 
| CAP_LINUX_IMMUTABLE | 允许修改文件的 IMMUTABLE 和 APPEND 属性标志 | 
| CAP_MAC_ADMIN | 允许 MAC 配置或状态更改 | 
| CAP_MAC_OVERRIDE | 忽略文件的 DAC 访问限制 | 
| CAP_MKNOD | 允许使用 mknod() 系统调用 | 
| CAP_NET_ADMIN | 允许执行网络管理任务 | 
| CAP_NET_BIND_SERVICE | 允许绑定到小于 1024 的端口 | 
| CAP_NET_BROADCAST | 允许网络广播和多播访问 | 
| CAP_NET_RAW | 允许使用原始套接字 | 
| CAP_SETGID | 允许改变进程的 GID | 
| CAP_SETFCAP | 允许为文件设置任意的 capabilities | 
| CAP_SETPCAP | 参考 capabilities man page | 
| CAP_SETUID | 允许改变进程的 UID | 
| CAP_SYS_ADMIN | 允许执行系统管理任务,如加载或卸载文件系统、设置磁盘配额等 | 
| CAP_SYS_BOOT | 允许重新启动系统 | 
| CAP_SYS_CHROOT | 允许使用 chroot() 系统调用 | 
| CAP_SYS_MODULE | 允许插入和删除内核模块 | 
| CAP_SYS_NICE | 允许提升优先级及设置其他进程的优先级 | 
| CAP_SYS_PACCT | 允许执行进程的 BSD 式审计 | 
| CAP_SYS_PTRACE | 允许跟踪任何进程 | 
| CAP_SYS_RAWIO | 允许直接访问 /devport、/dev/mem、/dev/kmem 及原始块设备 | 
| CAP_SYS_RESOURCE | 忽略资源限制 | 
| CAP_SYS_TIME | 允许改变系统时钟 | 
| CAP_SYS_TTY_CONFIG | 允许配置 TTY 设备 | 
| CAP_SYSLOG | 允许使用 syslog() 系统调用 | 
| CAP_WAKE_ALARM | 允许触发一些能唤醒系统的东西(比如 CLOCK_BOOTTIME_ALARM 计时器) | 
相关系统调用
cap_t cap_get_proc() // 获取当前进程的capalibities
cap_t cap_get_pid(pid_t pid) // 获取指定进程的capalibities
cap_t cap_from_text(char *buf_p) // 将文本转化为capablibities
char *cap_to_text(cap_t caps, ssize_t *length_p) // 将capalibities转为文本
int cap_from_name(char *name, int *cap_p) // 将字符串表示的capability转为对应的数字
char *cap_to_name(int cap) // 将cap的数字转为文本
int cap_get_flag(cap_t cap_p, cap_value_t cap, cap_flag_t flag, cap_flag_value_t *value_p); // 设置能力,配合cap_set_proc()使用
int cap_set_proc(cap_t cap_p) //设置当前进程的能力
cap_t cap_init() // creates a capability state
int cap_free() // 释放capability state内存
获取、修改文件capability的命令
获取文件capabilities
$ getcap /usr/bin/ping
/usr/bin/ping = cap_net_admin,cap_net_raw+p
设置文件capabilities
setcap 'cap_net_admin+ep cap_net_raw+ei' /tmp/cap/sleep
执行exec()之类的系统调用,capabilities变化如下:
ambient capability set 是Linux 4.3后才有
Linux 2.6.25, the bounding set was a system-wide attribute shared by all threads.
P'(ambient)     = (file is privileged) ? 0 : P(ambient)
P'(permitted)   = (P(inheritable) & F(inheritable)) | (F(permitted) & P(bounding)) | P'(ambient)
P'(effective)   = F(effective) ? P'(permitted) : P'(ambient)
P'(inheritable) = P(inheritable)
P'(bounding)    = P(bounding)
执行fork()等创建进程的系统调用,capabilities继承父进程的
示例
提前安装好相关依赖库
yum -y install libcap-devel libcap-ng-devel
打印当前进程的capabilities
#include <sys/capability.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main(void) {
    pid_t pid;
    cap_t cap;
    cap_value_t cap_list[CAP_LAST_CAP+1];
    cap_flag_t cap_flags;
    cap_flag_value_t cap_flags_value;
    const char *cap_name[CAP_LAST_CAP+1] = {
        "cap_chown",
        "cap_dac_override",
        "cap_dac_read_search",
        "cap_fowner",
        "cap_fsetid",
        "cap_kill",
        "cap_setgid",
        "cap_setuid",
        "cap_setpcap",
        "cap_linux_immutable",
        "cap_net_bind_service",
        "cap_net_broadcast",
        "cap_net_admin",
        "cap_net_raw",
        "cap_ipc_lock",
        "cap_ipc_owner",
        "cap_sys_module",
        "cap_sys_rawio",
        "cap_sys_chroot",
        "cap_sys_ptrace",
        "cap_sys_pacct",
        "cap_sys_admin",
        "cap_sys_boot",
        "cap_sys_nice",
        "cap_sys_resource",
        "cap_sys_time",
        "cap_sys_tty_config",
        "cap_mknod",
        "cap_lease",
        "cap_audit_write",
        "cap_audit_control",
        "cap_setfcap",
        "cap_mac_override",
        "cap_mac_admin",
        "cap_syslog"
    };
    pid = getpid();
    cap = cap_get_pid(pid);
    if (cap == NULL) {
        perror("cap_get_pid");
        exit(-1);
    }
    /* effetive cap */
    cap_list[0] = CAP_CHOWN;
    if (cap_set_flag(cap, CAP_EFFECTIVE, 1, cap_list, CAP_SET) == -1) {
        perror("cap_set_flag cap_chown");
        cap_free(cap);
        exit(-1);
    }
    /* permitted cap */
    cap_list[0] = CAP_MAC_ADMIN;
    if (cap_set_flag(cap, CAP_PERMITTED, 1, cap_list, CAP_SET) == -1) {
        perror("cap_set_flag cap_mac_admin");
        cap_free(cap);
        exit(-1);
    }
    /* inherit cap */
    cap_list[0] = CAP_SETFCAP;
    if (cap_set_flag(cap, CAP_INHERITABLE, 1, cap_list, CAP_SET) == -1) {
        perror("cap_set_flag cap_setfcap");
        cap_free(cap);
        exit(-1);
    }
        /* dump them */
    int i;
    for (i=0; i < CAP_LAST_CAP + 1; i++) {
        cap_from_name(cap_name[i], &cap_list[i]);
        printf("%-20s %d\t\t", cap_name[i], cap_list[i]);
        printf("flags: \t\t");
        cap_get_flag(cap, cap_list[i], CAP_EFFECTIVE, &cap_flags_value);
        printf(" EFFECTIVE %-4s ", (cap_flags_value == CAP_SET) ? "OK" : "NOK");
        cap_get_flag(cap, cap_list[i], CAP_PERMITTED, &cap_flags_value);
        printf(" PERMITTED %-4s ", (cap_flags_value == CAP_SET) ? "OK" : "NOK");
        cap_get_flag(cap, cap_list[i], CAP_INHERITABLE, &cap_flags_value);
        printf(" INHERITABLE %-4s ", (cap_flags_value == CAP_SET) ? "OK" : "NOK");
        printf("\n");
    }
    cap_free(cap);
    return 0;
}
给进程赋予能力
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#undef _POSIX_SOURCE
#include <sys/capability.h>
extern int errno;
void whoami(void)
{
  printf("uid=%i  euid=%i  gid=%i\n", getuid(), geteuid(), getgid());
}
void listCaps()
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#undef _POSIX_SOURCE
#include <sys/capability.h>
extern int errno;
void whoami(void)
{
  printf("uid=%i  euid=%i  gid=%i\n", getuid(), geteuid(), getgid());
}
void listCaps()
{
  cap_t caps = cap_get_proc();
  ssize_t y = 0;
  printf("The process %d was give capabilities %s\n",
         (int) getpid(), cap_to_text(caps, &y));
  fflush(0);
  cap_free(caps);
}
int main(int argc, char **argv)
{
  int stat;
  whoami();
  stat = setuid(geteuid());
  pid_t parentPid = getpid();
  if(!parentPid)
    return 1;
  cap_t caps = cap_init();
  cap_value_t capList[5] = { CAP_NET_RAW, CAP_NET_BIND_SERVICE , CAP_SETUID, CAP_SETGID, CAP_SETPCAP };
  unsigned num_caps = 5;
  cap_set_flag(caps, CAP_EFFECTIVE, num_caps, capList, CAP_SET);
  cap_set_flag(caps, CAP_INHERITABLE, num_caps, capList, CAP_SET);
  cap_set_flag(caps, CAP_PERMITTED, num_caps, capList, CAP_SET);
  if (cap_set_proc(caps)) {
    perror("capset()");
    return EXIT_FAILURE;
  }
  listCaps();
  printf("dropping caps\n");
  cap_clear(caps);  // resetting caps storage
  if (cap_set_proc(caps)) {
    perror("capset()");
    return EXIT_FAILURE;
  }
  listCaps();
  cap_free(caps);
  return 0;
}
上面两个示例编译方法
gcc -lcap FILE.c -o FILE
Ambient能力是Linux 4.3后面才有
/*
 * 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 <cl@linux.com>
 * 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(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 -lcap-ng FILE.c -o FILE
参考文档
capabilities(7) - Linux manual page
cap_set_proc(3) - Linux manual page