介绍-Linux capability机制
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机制相关推荐
- Linux的奖励机制是啥意思,Linux能力(capability)机制的继承
1.Linux能力机制概述 在以往的UNIX系统上,为了做进程的权限检查,把进程分为两类:特权进程(有效用户ID是0)和非特权进程(有效用户ID是非0).特权进程可以通过内核所有的权限检查,而非特权进 ...
- linux通信机制总结
目录 1. Linux通信机制分类简介 2. Inter-Process Communication (IPC) mechanisms: 进程间通信机制0x1: 信号量(Signals)0x2: 管道 ...
- 【嵌入式Linux学习七步曲之第五篇 Linux内核及驱动编程】Linux信号机制分析
Linux信号机制分析 Sailor_forever sailing_9806@163.com 转载请注明 http://blog.csdn.net/sailor_8318/archive/2008 ...
- 【Linux内核及驱动编程】Linux信号机制分析
Linux信号机制分析 Sailor_forever sailing_9806@163.com转载请注明 http://blog.csdn.net/sailor_8318/archive/2008/0 ...
- Java内存溢出故障案例及Linux内存机制探究
文章目录 Java内存溢出故障案例及Linux内存机制探究 OOM Killer触发机制分析 如何避免系统触发OOM Killer 这部分内容属于demo案例分享,解决线上运维问题,思路是最重要的 J ...
- Linux后台开发系列之「05.解析 Linux 命令机制」
版权声明:本文为 cdeveloper 原创文章,可以随意转载,但必须在明确位置注明出处! 为何要了解 Linux 命令机制? 如果你每天都在使用 Linux 命令,那么你了解命令的基本原理吗?你学习 ...
- Linux进程同步机制-Futex
引子 在编译2.6内核的时候,你会在编译选项中看到[*] Enable futex support这一项,上网查,有的资料会告诉你"不选这个内核不一定能正确的运行使用glibc的程序&quo ...
- 嵌入式Linux——uevent机制:uevent原理分析
简介: 本文主要介绍uevent机制是什么,并通过代码分析使用uevent机制生成设备节点的过程.而本文将分为两部分,第一部分我们介绍一些预备知识和uevent的原理,而第二部分--通过代码介绍使用u ...
- [Linux 基础] 嵌入式 Linux ---- uevent 机制:uevent 原理分析(深度好文)
简介: 本文主要介绍 uevent 机制是什么,并通过代码分析使用 uevent 机制生成设备节点的过程.而本文将分为两部分,第一部分我们介绍一些预备知识和 uevent 的原理,而第二部分--通过代 ...
- Linux cgroup机制分析之cpuset subsystem
------------------------------------------ 本文系本站原创,欢迎转载! 转载请注明出处:http://ericxiao.cublog.cn/ -------- ...
最新文章
- IT 学习资料 大全
- AndoridSQLite数据库开发基础教程(10)
- python飞机大战的实训心得体会-python之基础总结(飞机大战)
- 方法描述Mybatis使用之NEIS项目
- ssd测试工具crystaldiskmark_使用SSD增强Ceph性能并对比测试
- k8s使用volume将ConfigMap作为文件或目录直接挂载_从零开始入门 K8s | 如何实现应用配置管理?...
- Python学习笔记--数据类型
- linux内核_Linux内核编程风格简介
- 一个 react+redux 工程实例
- Docker三剑客之Compose
- LeetCode(160): Intersection of Two Linked Lists
- 收集的正则表达式列子大全,方面他人,自己备用
- [译] TensorFlow 教程 #09 - 视频数据
- 飞鹅打印机远程打印订单
- 红外倒车雷达原理图_超声波倒车雷达电路图及原理分析
- 论文研究14:Deep Attention Gated Dilated Temporal Convolutional Networks with Intra-Parallel model
- 纸飞机html,纸飞机.html
- 100+实用功能合集,手机中的神器,黑科技中的战斗鸡!
- Oracle 11g Release 1 (11.1) 单行函数——(返回字符值)字符函数
- 砌下落梅如雪乱——剑雪无名