linux能力机制
一、理论
1. Capabilities的主要思想在于分割root用户的特权,即将root的特权分割成不同的能力,每种能力代表一定的特权操作。例如:能力 CAP_SYS_MODULE 表示用户能够加载(或卸载)内核模块的特权操作,而 CAP_SETUID 表示用户能够修改进程。
2. 用户身份的特权操作。在 Capbilities 中,系统将根据进程拥有的能力来进行特权操作的访问控制。在Capilities中,只有进程和可执行文件才具有能力,每个进程拥有三组能力集,分别称为 cap_effective, cap_inheritable, cap_permitted,分别简记为 pE pI pP,其中:
(1) cap_permitted 表示进程所拥有的最大能力集;
(2) cap_effective 表示进程当前可用的能力集,可以看做是 cap_permitted 的一个子集;
(3) cap_inheitable 则表示进程可以传递给其子进程的能力集。
系统根据进程的 cap_effective 能力集进行访问控制,cap_effective 为 cap_permitted 的子集,进程可以通过取消 cap_effective 中的某些能力来放弃进程的一些特权。
3. 可执行文件也拥有三组能力集,对应于进程的三组能力集,分别称为 cap_effective, cap_allowed, cap_forced,分别简记为 fE fI fP,其中:
(1) cap_allowed 表示程序运行时可从原进程的 cap_inheritable 中继承的能力集;
(2) cap_forced 表示运行文件时必须拥有才能完成其服务的能力集;
(3) ap_effective 表示文件开始运行时可以使用的能力。
4. Linux内核从2.2版本开始,就加进的 Capabilities 的概念与机制,并随着版本升高逐步得到改进。在linux中,root权限被分割成以下29中能力:
CAP_CHOWN: 修改文件属主的权限
CAP_DAC_OVERRIDE: 忽略文件的DAC访问限制
CAP_DAC_READ_SEARCH: 忽略文件读及目录搜索的DAC访问限制
CAP_FOWNER:忽略文件属主ID必须和进程用户ID相匹配的限制
CAP_FSETID: 允许设置文件的 setuid 位
CAP_KILL: 允许对不属于自己的进程发送信号
CAP_SETGID: 允许改变进程的组ID
CAP_SETUID: 允许改变进程的用户ID
CAP_SETPCAP: 允许向其他进程转移能力以及删除其他进程的能力
CAP_LINUX_IMMUTABLE: 允许修改文件的 IMMUTABLE 和 APPEND 属性标志
CAP_NET_BIND_SERVICE: 允许绑定到小于1024的端口
CAP_NET_BROADCAST: 允许网络广播和多播访问
CAP_NET_ADMIN: 允许执行网络管理任务
CAP_NET_RAW: 允许使用原始套接字
CAP_IPC_LOCK: 允许锁定共享内存片段
CAP_IPC_OWNER: 忽略IPC所有权检查
CAP_SYS_MODULE: 允许插入和删除内核模块
CAP_SYS_RAWIO: 允许直接访问/devport,/dev/mem,/dev/kmem及原始块设备
CAP_SYS_CHROOT: 允许使用chroot()系统调用
CAP_SYS_PTRACE: 允许跟踪任何进程
CAP_SYS_PACCT: 允许执行进程的BSD式审计
CAP_SYS_ADMIN: 允许执行系统管理任务,如加载或卸载文件系统、设置磁盘配额等
CAP_SYS_BOOT: 允许重新启动系统
CAP_SYS_NICE: a.允许提升自己的优先级和设置其他进程的优先级;b.允许设置自己的RT调度策略和将其它线程设置为RT线程;c.允许设置其它线程的cpu亲和性
CAP_SYS_RESOURCE: 忽略资源限制
CAP_SYS_TIME: 允许改变系统时钟
CAP_SYS_TTY_CONFIG: 允许配置TTY设备
CAP_MKNOD: 允许使用 mknod()系统调用
CAP_LEASE: 允许修改文件锁的 FL_LEASE 标志
5. 可以使用 setcap/getcap 命令行工具进行设置这些权限,可以使用 capset 系统调用设置权限。
if (AID_ROOT == getuid()) { LOGI("[%s]setmediaservercapinrootmode,adjustitforthreadRTschedulepolicy",__func__); if(-1 == prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0)) { LOGW("mediaserverprctlforsetcapsfailed:%s",strerror(errno)); } else { __user_cap_header_struct hdr; __user_cap_data_struct data; hdr.version = _LINUX_CAPABILITY_VERSION; //setcaps hdr.pid = getpid(); data.effective = ((1 << CAP_SYS_NICE) | (1 << CAP_SETUID) | (1 << CAP_SETGID)); data.permitted = ((1 << CAP_SYS_NICE) | (1 << CAP_SETUID) | (1 << CAP_SETGID)); data.inheritable = 0xffffffff; if (-1 == capset(&hdr, &data)) { LOGW("mediaservercapsettingfailed,%s",strerror(errno)); } } }
上面代码,把 root 的权限先使用 prctl 进行 keep,然后通过 capset 进行了限制。capset 的限制过程中,将目录访问的权限消除掉了,会导致无法在/data/system下创建文件,做如下修改便可以恢复:
data.effective = ((1 << CAP_SYS_NICE) | (1 << CAP_SETUID) | (1 << CAP_SETGID) | (1<<CAP_DAC_OVERRIDE)); data.permitted = ((1 << CAP_SYS_NICE) | (1 << CAP_SETUID) | (1 << CAP_SETGID) | (1<<CAP_DAC_OVERRIDE));
6. 其它相关命令工具有:chattr lcap
7. sched_setscheduler()的 capability 需求
一般子进程会继承父进程的调度策略,在 Linux 2.6.32 之后,可以使用 SCHED_RESET_ON_FORK 按位与参数的方式调用 sched_setscheduler(), 使用之后效果是:如果调用进程使用 SCHED_FIFO 或 SCHED_RR 调度策略,使用 SCHED_RESET_ON_FORK 后fork创建的子进程创建时将会自动重置为 SCHED_OTHER 调度策略;如果调用进程使用负值nice,那么使用 SCHED_RESET_ON_FORK 后 fork 创建的子进程创建时将会自动将其 nice 重置为0。 这个标记激活时只有当进程具有 CAP_SYS_NICE 标记时才能被重置,而这个 CAP_SYS_NICE 标记在使用fork()创建子进程后,在子进程中被禁止。
8. 补充:有两个系统调用用于判断对应的cap属性,man 可以查看。 capget, capset - set/get capabilities of thread(s)。如下,Android代码中判断是否有CAP_SYS_NICE属性。
//vnd/system/core/libcutils/sched_policy_test.cpp bool hasCapSysNice() { __user_cap_header_struct header; memset(&header, 0, sizeof(header)); header.version = _LINUX_CAPABILITY_VERSION_3; __user_cap_data_struct caps[_LINUX_CAPABILITY_U32S_3]; if (capget(&header, &caps[0])) { GTEST_LOG_(WARNING) << "failed to get process capabilities"; return false; } auto nice_idx = CAP_TO_INDEX(CAP_SYS_NICE); auto nice_mask = CAP_TO_MASK(CAP_SYS_NICE); return caps[nice_idx].effective & nice_mask; }
设置 CAP_SYS_NICE 属性参考:
//摘取自:vnd/frameworks/native/services/vr/performanced/main.cpp #include <errno.h> #include <sys/capability.h> #include <sys/prctl.h> #include <sys/stat.h> namespace { // Annoying that sys/capability.h doesn't define this directly. constexpr int kMaxCapNumber = (CAP_TO_INDEX(CAP_LAST_CAP) + 1); } // anonymous namespace int main(int /*argc*/, char** /*argv*/) { int ret = -1; struct __user_cap_header_struct capheader; struct __user_cap_data_struct capdata[kMaxCapNumber]; ALOGI("Starting up..."); // We need to be able to create endpoints with full perms. umask(0000); // Keep capabilities when switching UID to AID_SYSTEM. ret = prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0); CHECK_ERROR(ret < 0, error, "Failed to set KEEPCAPS: %s", strerror(errno)); // Set UID and GID to system. ret = setresgid(AID_SYSTEM, AID_SYSTEM, AID_SYSTEM); CHECK_ERROR(ret < 0, error, "Failed to set GID: %s", strerror(errno)); ret = setresuid(AID_SYSTEM, AID_SYSTEM, AID_SYSTEM); CHECK_ERROR(ret < 0, error, "Failed to set UID: %s", strerror(errno)); // Keep CAP_SYS_NICE, allowing control of scheduler class, priority, and // cpuset for other tasks in the system. memset(&capheader, 0, sizeof(capheader)); memset(&capdata, 0, sizeof(capdata)); capheader.version = _LINUX_CAPABILITY_VERSION_3; capdata[CAP_TO_INDEX(CAP_SYS_NICE)].effective |= CAP_TO_MASK(CAP_SYS_NICE); capdata[CAP_TO_INDEX(CAP_SYS_NICE)].permitted |= CAP_TO_MASK(CAP_SYS_NICE); // Drop all caps but the ones configured above. ret = capset(&capheader, capdata); return ret; }
二、补充
1. setcap/getcap
setcap CAP_NET_RAW+ep /usr/bin/ping //赋予能力,将CAP_NET_RAW能力赋予ping命令 setcap -r /usr/bin/ping //移除能力,从文件中移除特定能力。 getcap /usr/bin/ping //查看文件能力
实测并不好用,比如 setcap CAP_SYS_NICE+ep /usr/bin/ping 实验,需要sudo执行才行,需要有sudo权限。Android功能中默认不带这两个命令。
参考:
setcap详解:https://www.cnblogs.com/nf01/articles/10418141.html
用capability 特征加强Linux系统安全:https://blog.csdn.net/cuikeng1956/article/details/100400918
优秀博文 《Linux的capability深入分析》:https://www.cnblogs.com/iamfy/archive/2012/09/20/2694977.html
posted on 2021-09-03 21:13 Hello-World3 阅读(1139) 评论(0) 编辑 收藏 举报