【操作系统实验】Linux Capability - 能力机制
Linux Capability - 能力机制
chcore 中提到了 capability 机制来实现资源的访问控制,所以先学习一下什么是 capability。
参考链接:
- https://en.wikipedia.org/wiki/Capability-based_security
- https://man7.org/linux/man-pages/man7/capabilities.7.html
- https://segmentfault.com/a/1190000020845011
- https://blog.csdn.net/weixin_39670464/article/details/111237022
什么是 capability
根据 man-pages 的描述:
为了进行权限检查,传统 UNIX 将进程分为两类:特权进程(privileged processes,uid=0 即 root 启动的进程),非特权进程(unprivileged process,即普通用户启动的进程)。特权进程能够通过内核中所有的权限检查,而非特权进程则需要被检查一些资质来判断是否具有权限( usually: effective UID, effective GID, and supplementary group list )。
从 kernel 2.2 版本开始,Linux 将进程的特权属性与超级用户(root)解耦合,并把特权划分到更细粒度的单元中,称为 capability,允许分别授予或者取消不同的特权。不同的线程可以持有不同的 capability。
因此,在线程要求执行特权操作时,检查操作就变成了:
- 检查线程用户是否是 root,如果是,则直接通过权限检查;
- 如果不是 root,则检查线程是否具有该特权操作对应的 capability。
capability 的获得与继承
capability 可以在进程执行时赋予,也可以直接从父进程集成。每个线程具有五个 capability 集合,每个集合用 64 bit 的掩码来表示:
- Permitted:定义了该线程能够使用的 capability 的上限。它作为一个规定,如果线程想要获得某一个 capability,则该 capability 必须包含在 Permitted 集合中,否则就不可以申请此权限。
- Effective:真正表示线程具有的权限。内核检查线程权限时,其实就是检查 Effective 集合。
- Inheritable:当执行 exec 系统调用时,新执行的线程将会从 Inheritable 中继承对应的 capability,将其添加到新线程的 Permitted 集合中(而不是直接添加到 Effective!)。
- Bounding:表示 Inheritable 的上限。如果某个 capability 不在 Bounding 中,那么它就不能添加到 Inheritable 中(即使它在 Permitted 中)。
- Ambient:用于弥补 Inheritable 的不足。略。
Chcore 中的 capability 与进程
process.h 中进程的定义如下:
1 | struct process { |
slot_table
指的就是进程持有的所有资源,包括线程,进程本身等。任何需要通过 capability 机制来管理的资源都是通过 object 来抽象的。
在 thread_create()
函数中,需要创建线程,然后将线程加入到 slot_table
中管理,同时返回 cap 作为索引。
1 | // in thread_create(): |
thread = obj_alloc(TYPE_THREAD, sizeof(*thread))
相当于 thread = malloc(sizeof(struct thread))
,就是给 thread 申请了空间,并完成一些线程的初始化。
然后调用 thread_init()
进行初始化,略;
最后使用 cap_alloc(process, thread, 0);
将该线程对象加入到进程的 slot_table
中。可以看到,传入的第二个参数就是 thread
,即需要将资源 thread
添加到进程 process
中。thread
指针实际上对应一个 object 对象的 opaque 字段,所以通过 container_of(ptr, type, field)
宏定义可以找到原本的 object 对象。
在 cap_alloc()
的最后调用了 install_slot()
:
1 | static inline void install_slot(struct process *process, int slot_id, |
因此实际上是将 slot_id
作为索引,存放一些列的 slot 资源。slot 中保存的内容包括:
1 | slot->slot_id = slot_id; |
最终要的就是 slot->object
,保存了资源对象。