1. 权限

1)传统的Unix系统,从权限的角度可以把用户分为特权用户root(privileged user)和普通用户(unprivileged user)。通过这两种用户执行的进程,就是对应的privileged processes和unprivileged processes。

2)root进程拥有所有的权限。而普通用户进程则要进行权限的检查,这种检查一般是基于文件的UID、GID进行检查的。例如,普通用户进程要向串口ttyS0写一个字符,那么该进程就要write一个字符到文件(设备节点)/dev/ttyS0,此时,kernel就会检查进程的UID、GID是什么,文件ttyS0的User读写权限、Group读写权限和Other读写权限,匹配上以后才允许普通进程写数据到串口。

3)以上是一种基于文件读写执行(rwx)权限的机制,但有很多权限是不需要操作文件的,例如给某个进程发送信号、创建socket并绑定某个网卡等等,因此,从Linux 2.2开始引入了capability权限机制。capability机制相比文件权限检查更完善、更灵活。

4)capability把权限进行了拆分,可以把部分权限赋予给普通用户进程,而不需要切换到root。

2. capability

1)capability具体的权限,可以命令行查看

    man capabilities

目前较新的kernel支持最多41种权限。

2)capability权限分类

Linux capabilities 分为进程 capabilities 和文件 capabilities。

  • 进程 capabilities

对于进程来说,capabilities 是细分到线程的,即每个线程可以有自己的capabilities。

  • 文件capabilities

对于文件(可执行文件)来说,capabilities 保存在文件的扩展属性security.capability中。

3)capability权限集

在进程capabilities下,又把权限分成了5个权限集合,分别是:

  • Permitted

This is a limiting superset for the effective capabilities that the thread may assume. It is also a limiting superset for the capabilities that may be added to the inheritable set by a thread that does not have the CAP_SETPCAP capability in its effective set.

解析:permitted权限集就是进程所拥有的权限的集合。

  • Inheritable

This is a set of capabilities preserved across an execve(2). Inheritable capabilities remain inheritable when executing any program, and inheritable capabilities are added to the permitted set when executing a program that has the corresponding bits set in the file inheritable set.

解析:inheritable权限集就是可继承权限集,当通过execv系列函数执行一个新的程序时,新程序会继承inheritable权限集。

  • Effective

This is the set of capabilities used by the kernel to perform permission checks for the thread.

解析:effective权限集就是有效权限集,kernel最终检查进程是否有权限执行某个操作,检查的就是这个权限集里面的值。只要这个权限集里面没有这个权限,即使其他权限集里面有,kernel也不允许执行该操作。与permitted权限集的区别,permitted权限集是所拥有的权限,即使拥有某个权限,但也可以不使能(effective)这个权限。就好像你会降龙十八掌,但你却不使出来一样。

  • Bounding (per-thread since Linux 2.6.25)

The capability bounding set is a mechanism that can be used to limit the capabilities that are gained during execve(2).

解析:Bounding是一套机制,在执行execv系列函数执行一个新的程序时,bounding可以限制新程序获得的权限。

  • Ambient (since Linux 4.3)

This is a set of capabilities that are preserved across an execve(2) of a program that is not privileged.  The ambient capability set obeys the invariant that no capability can ever be ambient if it is not both permitted and inheritable.

解析:当非特权程序A调用execv系列函数执行新的程序B时,程序A的ambient权限集就会被进程B所继承。同时,ambient权限集有以下特性:

a)如果permitted或者inheritable中的某个权限P被清空,在ambient中该权限P也自动被清空;

b)当程序调用setuid改变UID或者setgid改变GID时,ambient中的所有权限会被清空;

c)当调用execv系列函数执行一个bin程序时,如果该bin文件曾经被设置了任何的文件capabilities(非进程capabilities),ambient中的所有权限会被清空;

d)当调用execv系列函数执行一个bin程序时,如果该bin文件没有设置过任何的文件capabilities(非进程capabilities),ambient中的所有权限就会被赋予给新程序的permited和effective权限集。

capabilites process capabilities permitted
effective
inheritable
bounding
ambient
file capabilities permitted
effective
inheritable

3. 新进程capability的计算

1)fork

当程序执行fork()创建子进程时,子进程全部继承父进程的capabilities,包括permittd、effective、inheritable、imabient 4个权限集。

2)execv系列函数

当程序执行execv系列函数运行新的程序时,新程序的权限会被系统自动修改,这个修改过程是遵循一套公式的。

  • 为方便描述,定义以下表示方式:

P         表示进程在执行execv系列函数前,该进程的权限,例如P(permitted),表示该进程中permitted权限集中所有的权限。

P'         表示进程在执行execv系列函数后,该进程的权限,例如P'(permitted),表示该进程中permitted权限集中所有的权限。

F         表示文件的权限,例如F(permitted),表示该文件中permitted权限集中所有的权限。

cap_bset         表示bounding权限集中所有的权限。

  • 计算公式

P'(ambient) = (file is privileged) ? 0 : P(ambient)

解析:如果是特权(root)进程,则执行execv()后新程序的ambient权限集被清空;如果是普通进程,则执行execv()后新程序的ambient权限集跟原来的P(ambient)一致,即P'(ambient) = P(ambient)。

P'(permitted) = (P(inheritable) & F(inheritable)) | (F(permitted) & cap_bset) | P'(ambient)

解析:执行execv()后新程序的permitted权限集是三部分结果“或”运算的结果。

对于普通进程而言,如果没有设置bin文件的权限集,那么想要新程序P'(permitted)具有某项权限,则需要在执行execv()前先设置好P(ambient)。

P'(effective) = F(effective) ? P'(permitted) : P'(ambient)

解析:如果bin文件的effective权限集具有某项权限A,那么新程序P'(effective)中的权限A就等于P'(permitted)中的权限A的值;如果bin文件的effective权限集不具有某项权限A,那么新程序P'(effective)中的权限A就等于P'(ambient)中的权限A的值。

对于普通进程而言,如果没有设置bin文件的权限集,那么想要新程序P'(effective)具有某项权限A,则需要在执行execv()前先设置好P(ambient)。

P'(inheritable) = P(inheritable)

解析:新程序的P'(inheritable)跟原来的P(inheritable) 一致,不会被kernel改变。

4. 普通进程获取某项权限A

首先,所谓的获取某项权限A,其实就是进程的effective权限集中,某项权限A被置位。其次,由于普通进程不能无中生有地给自己设置权限,并且所有的新进程都是通过fork和execv所运行起来的,所以普通进程要获取权限A,必须是由

  • 方法一

root进程对bin文件设置好各个文件权限集,那么bin文件被运行起来后,就能具有相应的权限集;

对于方法一,直接通过setcap命令,设置bin文件的F(effective)即可。

    sudo setcap cap_chown,cap_kill=e /path/to/bin/file
  • 方法二

root进程在执行setuid切换成普通进程之前,设置好自己的各个进程权限集,例子如下:

#include <sys/capability.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/prctl.h>
#include <string.h>#define nitems(x) (sizeof(x) / sizeof(x[0]))static const char *_cap_name[] = {"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","cap_wake_alarm","cap_block_suspend","cap_audit_read",NULL
};
static const int _cap_size = nitems(_cap_name) - 1;static void _cap_eip_dump(cap_t cap)
{cap_value_t cap_list[CAP_LAST_CAP+1];cap_flag_value_t cap_flags_value;/* temporary use for cap_get_flag calls */struct {const char *str;cap_flag_t flag;} const flags[3] = {{"EFFECTIVE", CAP_EFFECTIVE},{"PERMITTED", CAP_PERMITTED},{"INHERITABLE", CAP_INHERITABLE}};int i, j;/* dump them */for (i=0; i < _cap_size && i < CAP_LAST_CAP; i++) {cap_from_name(_cap_name[i], &cap_list[i]);printf("%-20s %d\t\t", cap_to_name(cap_list[i]), cap_list[i]);printf("flags: \t\t");for (j=0; j < nitems(flags); j++) {cap_get_flag(cap, cap_list[i], flags[j].flag, &cap_flags_value);printf(" %s %-4s ", flags[j].str, (cap_flags_value == CAP_SET) ? "OK" : "NOK");}printf("\n");}printf("\n");
}static void cap_eip_dump(void)
{pid_t pid;cap_t cap;pid = getpid();cap = cap_get_pid(pid);if (cap == NULL) {perror("cap_get_pid");exit(-1);}/* dump them */printf("dump proc capabilities:\n");_cap_eip_dump(cap);cap_free(cap);
}static int _cap_eip_set(const cap_flag_t *cap_flag, int cap_flag_size,const cap_value_t *cap_list, int cap_list_size,int set)
{cap_t cap;int i;for (i = 0; i < cap_flag_size; ++i){if (cap_flag[i] != CAP_EFFECTIVE&& cap_flag[i] != CAP_PERMITTED&& cap_flag[i] != CAP_INHERITABLE){perror("invalid argument");return -1;}}for (i = 0; i < cap_list_size; ++i){if (cap_list[i] > CAP_LAST_CAP){perror("invalid argument");return -1;}}//1. get capcap = cap_get_proc();if (cap == NULL) {perror("cap_get_proc");return -1;}//2. set cap flagset = set ? CAP_SET : CAP_CLEAR;for (i = 0; i < cap_flag_size; ++i){/* set CAP_EFFECTIVE/CAP_PERMITTED/CAP_INHERITABLE cap */if (cap_set_flag(cap, cap_flag[i], cap_list_size, cap_list, set) == -1) {perror("cap_set_flag CAP_KILL");cap_free(cap);return -1;}}//3. set cap to kernelif (cap_set_proc(cap) < 0) {perror("cap_set_proc fail");cap_free(cap);return -1;}//4. cleancap_free(cap);return 0;
}
static int cap_eip_set(const cap_flag_t *cap_flag, int cap_flag_size,const cap_value_t *cap_list, int cap_list_size)
{return _cap_eip_set(cap_flag, cap_flag_size, cap_list, cap_list_size, 1);
}static void cap_ambient_dump(void)
{cap_value_t cap_list[CAP_LAST_CAP+1];int value;int i;if (!CAP_AMBIENT_SUPPORTED()) {printf("kernel NOT support AMBIENT!\n");return;}printf("dump ambient capabilities:\n");/* dump them */for (i=0; i < _cap_size; i++) {cap_from_name(_cap_name[i], &cap_list[i]);value = cap_get_ambient(cap_list[i]);if (value < 0) {continue;}printf("%-20s %d\t\t", cap_to_name(cap_list[i]), cap_list[i]);printf("flags: \t\t");printf(" %s %-4s ", "AMBIENT", (value == CAP_SET) ? "OK" : "NOK");printf("\n");}printf("\n");
}static int cap_ambient_set(const cap_value_t *cap_list, int cap_list_size)
{int i;for (i = 0; i < cap_list_size; ++i){if (cap_list[i] > CAP_LAST_CAP){perror("invalid argument");return -1;}}for (i = 0; i < cap_list_size; ++i){if (cap_set_ambient(cap_list[i], CAP_SET) < 0) {perror("cap_set_ambient fail");return -1;}}return 0;
}static int test_cap[] = {CAP_KILL};
static void child_runner(void *userp)
{char path[128] = {'\0'};char *arg[8] = {NULL};int ret;ret = readlink("/proc/self/exe", path, sizeof(path));if (ret) {//make compiler happy}arg[0] = path;arg[1] = "--test";arg[2] = userp;execv(path, arg);
}
static void child_handler(void *userp)
{/* 作为子进程被execv()所运行 *//* 验证execv()后进程是否还具有我们测试的CAP_KILL权限 */char *parent_pid = userp;printf("after execv(), I am child bin\r\n");cap_eip_dump();cap_ambient_dump();printf("========\n");sleep(1);kill(atoi(parent_pid), SIGINT);
}int main(int argc, char *argv[])
{cap_value_t cap_list[CAP_LAST_CAP+1];cap_flag_t cap_flag[3];pid_t parent_pid;pid_t child_pid;if (argc > 2 && !strcmp(argv[1], "--test")){child_handler(argv[2]);return 0;}parent_pid = getpid();child_pid = fork();if (child_pid < 0){exit(-1);}else if (child_pid > 0){//parentprintf("I am parent, pid=%d, child pid=%d\n", parent_pid, child_pid);while (1){sleep(1);}}else{//childint i;cap_eip_dump();cap_ambient_dump();printf("========\n");/* enable CAP_SETPCAP, or PR_SET_KEEPCAPS will fail */cap_flag[0] = CAP_EFFECTIVE;cap_flag[1] = CAP_PERMITTED;cap_flag[2] = CAP_INHERITABLE;memset(cap_list, 0, sizeof(cap_list));cap_list[0] = CAP_SETPCAP;cap_eip_set(cap_flag, 3, cap_list, 1);/* enable testing cap *//* 这里把我们想要测试的权限设置进去 */cap_flag[0] = CAP_EFFECTIVE;cap_flag[1] = CAP_PERMITTED;cap_flag[2] = CAP_INHERITABLE;memset(cap_list, 0, sizeof(cap_list));for (i = 0; i < nitems(test_cap); ++i) {cap_list[i] = test_cap[i];}cap_eip_set(cap_flag, 3, cap_list, nitems(test_cap));cap_eip_dump();cap_ambient_dump();printf("========\n");/*! keep caps after setuid *//* 必须设置PR_SET_KEEPCAPS, 否则调用setuid之后,所有权限会被清空 */prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0);setuid(1000);printf("after setuid\n");cap_eip_dump();cap_ambient_dump();printf("========\n");/* 运行execv()前,必须把我们想要的权限设置到ambient权限集中 *//* 否则运行execv()后,无法获得该权限 */memset(cap_list, 0, sizeof(cap_list));for (i = 0; i < nitems(test_cap); ++i) {cap_list[i] = test_cap[i];}cap_ambient_set(cap_list, nitems(test_cap));printf("before execv()\n");cap_eip_dump();cap_ambient_dump();printf("========\n");{char arg[16];snprintf(arg, sizeof(arg), "%d", parent_pid);child_runner(arg);}}return 0;
}

Linux官方说明可参考

capabilities(7) - Linux manual page

介绍-Linux capability机制相关推荐

  1. Linux的奖励机制是啥意思,Linux能力(capability)机制的继承

    1.Linux能力机制概述 在以往的UNIX系统上,为了做进程的权限检查,把进程分为两类:特权进程(有效用户ID是0)和非特权进程(有效用户ID是非0).特权进程可以通过内核所有的权限检查,而非特权进 ...

  2. linux通信机制总结

    目录 1. Linux通信机制分类简介 2. Inter-Process Communication (IPC) mechanisms: 进程间通信机制0x1: 信号量(Signals)0x2: 管道 ...

  3. 【嵌入式Linux学习七步曲之第五篇 Linux内核及驱动编程】Linux信号机制分析

    Linux信号机制分析 Sailor_forever  sailing_9806@163.com 转载请注明 http://blog.csdn.net/sailor_8318/archive/2008 ...

  4. 【Linux内核及驱动编程】Linux信号机制分析

    Linux信号机制分析 Sailor_forever sailing_9806@163.com转载请注明 http://blog.csdn.net/sailor_8318/archive/2008/0 ...

  5. Java内存溢出故障案例及Linux内存机制探究

    文章目录 Java内存溢出故障案例及Linux内存机制探究 OOM Killer触发机制分析 如何避免系统触发OOM Killer 这部分内容属于demo案例分享,解决线上运维问题,思路是最重要的 J ...

  6. Linux后台开发系列之「05.解析 Linux 命令机制」

    版权声明:本文为 cdeveloper 原创文章,可以随意转载,但必须在明确位置注明出处! 为何要了解 Linux 命令机制? 如果你每天都在使用 Linux 命令,那么你了解命令的基本原理吗?你学习 ...

  7. Linux进程同步机制-Futex

    引子 在编译2.6内核的时候,你会在编译选项中看到[*] Enable futex support这一项,上网查,有的资料会告诉你"不选这个内核不一定能正确的运行使用glibc的程序&quo ...

  8. 嵌入式Linux——uevent机制:uevent原理分析

    简介: 本文主要介绍uevent机制是什么,并通过代码分析使用uevent机制生成设备节点的过程.而本文将分为两部分,第一部分我们介绍一些预备知识和uevent的原理,而第二部分--通过代码介绍使用u ...

  9. [Linux 基础] 嵌入式 Linux ---- uevent 机制:uevent 原理分析(深度好文)

    简介: 本文主要介绍 uevent 机制是什么,并通过代码分析使用 uevent 机制生成设备节点的过程.而本文将分为两部分,第一部分我们介绍一些预备知识和 uevent 的原理,而第二部分--通过代 ...

  10. Linux cgroup机制分析之cpuset subsystem

    ------------------------------------------ 本文系本站原创,欢迎转载! 转载请注明出处:http://ericxiao.cublog.cn/ -------- ...

最新文章

  1. IT 学习资料 大全
  2. AndoridSQLite数据库开发基础教程(10)
  3. python飞机大战的实训心得体会-python之基础总结(飞机大战)
  4. 方法描述Mybatis使用之NEIS项目
  5. ssd测试工具crystaldiskmark_使用SSD增强Ceph性能并对比测试
  6. k8s使用volume将ConfigMap作为文件或目录直接挂载_从零开始入门 K8s | 如何实现应用配置管理?...
  7. Python学习笔记--数据类型
  8. linux内核_Linux内核编程风格简介
  9. 一个 react+redux 工程实例
  10. Docker三剑客之Compose
  11. LeetCode(160): Intersection of Two Linked Lists
  12. 收集的正则表达式列子大全,方面他人,自己备用
  13. [译] TensorFlow 教程 #09 - 视频数据
  14. 飞鹅打印机远程打印订单
  15. 红外倒车雷达原理图_超声波倒车雷达电路图及原理分析
  16. 论文研究14:Deep Attention Gated Dilated Temporal Convolutional Networks with Intra-Parallel model
  17. 纸飞机html,纸飞机.html
  18. 100+实用功能合集,手机中的神器,黑科技中的战斗鸡!
  19. Oracle 11g Release 1 (11.1) 单行函数——(返回字符值)字符函数
  20. 砌下落梅如雪乱——剑雪无名

热门文章

  1. 格林威治时间转北京时间
  2. 解决微信支付、微信一键登陆在安卓10以上无法调起问题
  3. 《逆龄大脑:保持大脑年轻敏锐的新科学》读书笔记
  4. Openwrt无线中继AP设置教程
  5. 1000瓶毒药里有1瓶有毒,问需要多少只老鼠能试出来哪瓶有毒
  6. 头条小程序Component构造器
  7. 计算机一直重启故障原因可能是,Windows电脑一直自动重启的原因和解决方法
  8. 数据分析常用名词解释
  9. 开学季征文 | 一位开发实习生的真情流露
  10. uniapp 调用原生插件包含第三方SDK时抛NoClassDefFoundError异常