【操作系统实验】Linux Capability - 能力机制

Linux Capability - 能力机制

chcore 中提到了 capability 机制来实现资源的访问控制,所以先学习一下什么是 capability。

参考链接:

什么是 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
2
3
4
struct process {
struct slot_table slot_table;
struct list_head thread_list;
};

slot_table 指的就是进程持有的所有资源,包括线程,进程本身等。任何需要通过 capability 机制来管理的资源都是通过 object 来抽象的。

thread_create() 函数中,需要创建线程,然后将线程加入到 slot_table 中管理,同时返回 cap 作为索引。

1
2
3
4
5
6
// in thread_create():
thread = obj_alloc(TYPE_THREAD, sizeof(*thread));
...
thread_init(thread, process, stack, pc, prio, type, aff);
...
cap = cap_alloc(process, thread, 0);

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 对象。

image-20211009102840965

cap_alloc() 的最后调用了 install_slot()

1
2
3
4
5
6
static inline void install_slot(struct process *process, int slot_id,
struct object_slot *slot)
{
BUG_ON(!get_bit(slot_id, process->slot_table.slots_bmp));
process->slot_table.slots[slot_id] = slot;
}

因此实际上是将 slot_id 作为索引,存放一些列的 slot 资源。slot 中保存的内容包括:

1
2
3
4
5
6
7
8
9
slot->slot_id = slot_id;
slot->process = process;
slot->isvalid = true;
slot->rights = rights;
slot->object = object;
list_add(&slot->copies, &object->copies_head);

BUG_ON(object->refcount != 0);
object->refcount = 1;

最终要的就是 slot->object ,保存了资源对象。