Overview
Credentials在Linux中用于访问控制(Access Control),基于uid、gid、sid,是Linux几种安全措施的一部分。同时,仅用于进程(task)中的Capabilities提供了更细化的权限控制机制。1
Note2:
- uid —— User Identifier,用户标识符,用于辨识用户。又分为euid、ruid、suid、fsuid。
- ruid —— Real UID,真实用户ID,一般称之为uid。
- euid —— Effective UID,有效用户ID。
- suid —— Saved UID,暂存用户ID。
- fsuid —— File System UID,文件系统用户ID。
- gid —— Group Identifier,用户组标识符,用户辨识用户组。也又分为rgid、egid、sgid、fsgid。因每个用户必须是一个组的成员,主组(the primary group)由组数据库中用户条目的数字gid标识。
在v4.12中,与进程访问控制有关的系统调用有以下18个:
getuid |
setuid |
getgid |
setgid |
geteuid |
getegid |
setreuid |
setregid |
getresuid |
setresuid |
getresgid |
setresgid |
getgroups |
setgroups |
getfsuid |
getfsgid |
capget |
catset |
Introduction
一些概念4:
- Real user ID / Real group ID:这些ID决定该进程的所有者是谁。
- Effective user ID / Effective group ID:内核利用这些ID决定进程对共享资源拥有怎样的访问权,比如:消息队列、共享内存和信号量。尽管大多数的UNIX系统使用这些ID决定文件的访问权,但Linux使用的是独有的filesystem ID。
- Saved set-user-ID / Saved set-group-ID:这两个ID在set-user-ID与set-group-ID程序执行后,保存相应的effective ID。因此,__一个set-user-ID程序的effective user ID可以在real user ID与saved set-user-ID之间来回切换,从而可以恢复/抛弃特权__。
- Filesystem user ID / Filesystem group ID:这些ID用于决定进程对文件与其他共享资源的访问权。进程无论何时更改effective user/group ID,内核也同时更改filesystem user/group ID。
- Supplementary group IDs:它是一组额外的group IDs,也用于文件、共享资源的访问控制。
Note567:
Set-user-id / Set-group-id区别于进程中的saved set-user-ID / saved set-group-ID,是文件上的概念。设置一个Saved set-user-ID的意义在于,在execv
可执行文件之后,__如果可执行文件的set-user-ID位被设置了,进程的effective user ID, saved set-user-ID会设置成可执行文件所有者的uid__;effective group ID也有类似的操作。下面是内核中与此有关的具体代码:
1 | /* |
举例说明:用户zzz执行了文件a.out,a.out的属主为hzzz且设置了set-user-ID位。现在本进程的real uid为zzz,effective uid = saved uid = hzzz。
进程执行了一会之后,突然想用zzz的权限访问一个文件,于是进程可能会调用setuid(zzz), 此时检测进程的权限,进程的effective uid是hzzz,不是root,所以不能更改real uid(只有root才能更改real uid),所以只能设置effective uid,发现effective uid可以被设置为zzz(因为real uid是zzz),所以函数调用成功,只将effective uid设置成zzz。
现在进程访问完zzz的文件了,又想回到hzzz的环境中执行,所以有可能会调用setuid(hzzz),这次saved uid的作用就表现出来了,因为刚刚只是改变了effective uid, 而saved uid还保存着之前的effective uid,所以可以调用setuid(hzzz)来要回原来的权限。
描述uid/gid转换的有限状态自动机(FSA)在Proceedings of the 11th USENIX Security Symposium中的第10-12页有展示。
关于setuid
可以简单记为:在没有特权的情况下,euid / fsuid
可以通过setuid
设置成ruid
或suid
。
Pre-internal
User namespaces
A new approach to user namespaces3梗概:
容器可以被看做一种轻量级的虚拟化技术。因为与宿主机共享内核,所以比真正的虚拟机运行效率更高。但必须提供一种机制,把全局可见的资源封装进命名空间中,对容器展现只属于自己的那部分资源(比如进程ID、文件系统、网络接口)的视图。
用户命名空间(user namespaces)可以被认为是user/group ID以及相关权限的封装,它允许容器的所有者进程(不一定为root用户)在容器内部以root的身份运行,同时将容器内用户与系统的其余部分隔离。那么,同一个进程怎样才能在不同的上下文中有不同的uid呢?
Eric Biederman提交了一组patch解决了这个问题。这组patch中定义了两种新的数据类型kuid_t
(Kernel UID)与kgid_t
(Kernel GID)。Kernel UID用于描述进程在内核中的身份,而不管它在容器中可能采用的任何uid;它是用于大多数特权检查的值;并且进程并没有办法知道它的值。
为了kernel ID与user ID、group ID在不同的命名空间中做转换,除了知道Kernel ID之外,还需要知道特定的namespace ID,因此有了下面的一组用于uid的转换函数(gid同样存在):
1 | kuid_t make_kuid(struct user_namespace *from, uid_t uid); |
在Kernel与user ID、group ID之间建立映射是一种特权操作,需要CAP_SETUID, CAP_SETGID
标志。
Internal
set*uid
setuid(uid)
1 | kuid <- make kuid using uid and namespace |
setreuid(ruid, euid)
1 | kruid <- make kruid using ruid and namespace |
setresuid(ruid, euid, suid)
1 | kruid <- make kruid using ruid and namespace |
set*gid
the same as set*uid
.
Reference
- Linux doc: Credentials in Linux#task-credentials
- Wikipedia: 用户ID
- LWN.net: A new approach to user namespaces
- Linux Programmer’s Manual: credentials - process identifiers
- setuid和seteuid
- set-user-id (suid), set-group-id (sgid), saved-suid 筆記
- 深刻理解——real user id, effective user id, saved user id in Linux
- Proceedings of the 11th USENIX Security Symposium