权能 Capability 提要 背景 权能的含义 数据结构、宏和inline函数 内部函数和系统调用 总结 背景 在早期的Unix中,你或者是root用户,或者 不是。所以,你或者可以进行任何希望进行 的操作,或者不能执行任何重要的系统管理 任务。不幸的是,很多应用程序的需要都介 于这两个安全性极端之间。 Linux对于这个问题的解决方法是使用从 POSIX草案标准中抽取出来的思想——权能。 权能的含义 权能代表了进程对某些特定资源的使用权限。 权能可以更精确地定义经授权的进程所允许 处理的事情。例如,可以给一个进程授予修 改系统时间的权利,而没有授予它可以杀掉 系统中的其他进程、毁坏文件、胡乱运行的 权利。 权能的含义 每个进程有三个与权能有关的位图: effective(E)、permitted(P)、 inheritable(I) 分别对应进程描述符task_struct里面的 cap_effective 、 cap_permitted 、 cap_inheritable 每种权能由其中的一位表示,1表示具有,0 表示没有 数据结构(include/linux/capability.h) //该结构定义了用户空间的权能头 typedef struct __user_cap_header_struct { __u32 version; //版本号 int pid; //进程号 } *cap_user_header_t; //该结构定义了用户空间的权能数据 typedef struct __user_cap_data_struct { __u32 effective; //有效权能位图 __u32 permitted; //允许权能位图 __u32 inheritable; //可继承权能位图 } *cap_user_data_t; 宏(include/linux/capability.h) //定义两个权能操作宏,to_cap_t(x) 将x变成权能, 而cap_t(x)获得x的权能。 #ifdef STRICT_CAP_T_TYPECHECKS #define to_cap_t(x) { x } #define cap_t(x) (x).cap #else #define to_cap_t(x) (x) #define cap_t(x) (x) #endif inline函数(include/linux/capability.h) static inline kernel_cap_t cap_combine(kernel_cap_t a, kernel_cap_t b) { kernel_cap_t dest; cap_t(dest) = cap_t(a) | cap_t(b); return dest; } static inline kernel_cap_t cap_intersect(kernel_cap_t a, kernel_cap_t b) { kernel_cap_t dest; cap_t(dest) = cap_t(a) & cap_t(b); return dest; } inline函数(include/linux/capability.h) static inline kernel_cap_t cap_drop(kernel_cap_t a, kernel_cap_t drop) { kernel_cap_t dest; cap_t(dest) = cap_t(a) & ~cap_t(drop); return dest; } static inline kernel_cap_t cap_invert(kernel_cap_t c) { kernel_cap_t dest; cap_t(dest) = ~cap_t(c); return dest; } POSIX定义的能力 Linux中特定的能力 Linux中特定的能力 内部函数(kernel/capability.c) static void cap_set_pg(int pgrp, kernel_cap_t *effective, kernel_cap_t *inheritable, kernel_cap_t *permitted) 设置一个进程组里所有进程的权能 内部函数(kernel/capability.c) static void cap_set_all(kernel_cap_t *effective, kernel_cap_t *inheritable, kernel_cap_t *permitted) 设置除1号进程和自身之外所有进程的权能 系统调用(capget #184) 1. 2. 3. 4. long sys_capget(cap_user_header_t header, cap_user_data_t dataptr) 获取某个进程的权能 检查权能版本号 取进程的pid号 通过pid取进程指针 将目标进程的权能位图复制到用户空间 系统调用(capset #185) 1. 2. 3. 4. 5. 6. long sys_capset(cap_user_header_t header, const cap_user_data_t data) 设置某个进程的权能 检查版本号 取pid号 将用户空间复制到权能变量中 取进程指针 检查权能边界集 设置权能 总结 A full implementation of capabilities requires: 1. that for all privileged operations, the kernel check whether the process has the required capability in its effective set. 2. that the kernel provide system calls allowing a process's capability sets to be changed and retrieved. 3. file system support for attaching capabilities to an executable file, so that a process gains those capabilities when the file is execed. 总结 As at Linux 2.4.20, only the first two of these requirements are met. Eventually, it should be possible to associate three capability sets with an executable file, which, in conjunction with the capability sets of the process, will determine the capabilities of a process after an exec: 总结 Allowed: this set is ANDed with the process's inherited set to determine which inherited capabilities are permitted to the process after the exec. Forced: the capabilities automatically permitted to the process, regardless of the process's inherited capabilities. Effective: those capabilities in the process's new permitted set are also to be set in the new effective set. (F(effective) would normally be either all zeroes or all ones.) 总结 In the meantime, since the current implementation does not support file capability sets, during an exec: 1. All three file capability sets are initially assumed to be cleared. 2. If a set-UID-root program is being execed, or the real user ID of the process is 0 (root) then the file allowed and forced sets are defined to be all ones (i.e., all capabilities set). 3. If a set-UID-root program is being executed, then the file effective set is defined to be all ones. 总结 During an exec, the kernel calculates the new capabilities of the process using the following algorithm: P'(permitted) = (P(inherited) & F(allowed)) | (F(forced) & cap_bset) P'(effective) = P'(permitted) & F(effective) P'(inherited) = P(inherited) where: P denotes the value of a process capability set before the exec P' denotes the value of a capability set after the exec F denotes a file capability set cap_bset is the value of the capability bounding set. Thanks 权能的含义 cap_effective 当一个进程要进行某个特权操作时,操作系 统会检查cap_effective的对应位是否有效。 若无效,则禁止改操作 权能的含义 cap_permitted 表示进程能够使用的权能,是cap_effective 的超集,因为某些权能被临时放弃了 权能的含义 cap_inheritable 表示能够被其子进程所继承的权能 检查权能版本号 if (get_user(version, &header->version))//取得版本号 return -EFAULT;//返回地址错误 error = -EINVAL; //错误返回号为参数错误 //错误版本号则将正确的版本号写回到header->version, 并返回相应错误 if (version != _LINUX_CAPABILITY_VERSION) { version = _LINUX_CAPABILITY_VERSION; if (put_user(version, &header->version)) error = -EFAULT; return error; } 取进程的pid号 if (get_user(pid, &header->pid)) return -EFAULT; //错误的进程号,返回 if (pid < 0) return -EINVAL; //错误返回值赋值为0,表示无错误 error = 0; 通过pid取进程指针 spin_lock(&task_capability_lock); //锁定进程权能位图 if (pid && pid != current->pid) //进程id有效且不是当前 进程时候 { read_lock(&tasklist_lock); //锁定进程队列 target = find_task_by_pid(pid); //根据id寻找进程的 指针 if (!target) //找不到该进程,返回错误 error = -ESRCH; } else { target = current; //是当前进程id,进程指针指向当前进程 } 目标进程权能位图复制到用户空间 if (!error) { data.permitted = cap_t(target->cap_permitted); data.inheritable = cap_t(target->cap_inheritable); data.effective = cap_t(target->cap_effective); } if (target != current) read_unlock(&tasklist_lock); //进程队列解锁 spin_unlock(&task_capability_lock);//进程权能位图解锁 if (!error) {//没有错误则将data结构内容复制到用户空间 if (copy_to_user(dataptr, &data, sizeof data)) return -EFAULT; } return error; 将用户空间复制到权能变量中 if (copy_from_user(&effective, &data>effective, sizeof(effective))||copy_from_user(&inherita ble,&data>inheritable,sizeof(inheritable))||copy_from_ user(&permitted,&data>permitted,sizeof(permitted))) return -EFAULT; //返回值赋值为不允许的操作 error = -EPERM; 取进程指针 //进程号有效时候则首先锁定进程队列,并按照进程id找到 相应的进程指针,找不到则返回找不到进程的错误 if (pid > 0 && pid != current->pid) { read_lock(&tasklist_lock); target = find_task_by_pid(pid); if (!target) { error = -ESRCH; goto out; } } else { target = current; } 检查权能边界集 if (!cap_issubset(inheritable,cap_combine(target>cap_inheritable,current->cap_permitted))) { goto out; } if (!cap_issubset(permitted,cap_combine(target>cap_permitted,current->cap_permitted))) { goto out; } if (!cap_issubset(effective, permitted)) { goto out; } 设置权能 if (pid < 0) { if (pid == -1) //将1号和自己以外的所有进程重设权能 cap_set_all(&effective, &inheritable, &permitted); else //进程组中所有进程重设权能 cap_set_pg(-pid, &effective, &inheritable,&permitted); goto spin_out; } else { target->cap_effective = effective; target->cap_inheritable = inheritable; target->cap_permitted = permitted; }