cred —— 进程的凭证(权限身份)
这是 task-struct 里
task->cred那一项的展开。cred(credentials)是内核判断"这个进程能干什么"的依据:它是谁(uid/gid)、属于哪些组、有哪些特权(capabilities)。谁能读某个文件、能不能 bind 低端口、能不能重启系统,全看这里。本篇讲cred的结构、四种 uid 为什么存在、setuid 怎么"变身"、capabilities 怎么细分 root 权限。
零、一句话认知:cred 回答"这个进程以谁的身份、有多大权限运行"
内核每做一次权限检查(打开文件、发信号、绑端口……),都拿当前进程的 cred 去比对。cred 主要装三样:
核心记忆:
cred= 身份(uid/gid + 附加组) + 特权(capabilities)。传统 Unix 只看 uid(0 = root 万能);现代 Linux 用 capabilities 把 root 的"万能"拆成几十个独立特权位,按需授予,最小权限。
一、四种 uid:为什么不止一个
一个进程的用户身份其实有四个 uid(gid 同理),各有用途:
| uid | 全称 | 作用 |
|---|---|---|
| real uid (ruid) | 真实用户 | "我本来是谁"——启动这个进程的用户 |
| effective uid (euid) | 有效用户 | 权限检查实际用的就是它——"我现在以谁的身份行事" |
| saved uid (suid) | 保存的 | 暂存一个 uid,方便在 euid 间来回切(提权/降权后能还原) |
| fs uid (fsuid) | 文件系统 | 专用于文件访问检查(Linux 特有,历史遗留,一般跟随 euid) |
为什么要 real / effective / saved 三个:setuid 程序
经典例子 passwd:普通用户运行它却要改 /etc/shadow(只有 root 能写)。靠setuid 位:

- setuid 位(文件权限的
s):execve一个带 setuid 位的程序,euid 被设成文件属主的 uid(如 root),于是普通用户运行时临时获得属主权限。 - saved uid 的用途:程序可以在 euid=0 和 euid=1000 之间切换(需要特权时提到 0,平时降到 1000 减少风险),
saved存着"另一个身份"供还原。最小权限原则:setuid 程序应尽快把 euid 降回去,只在必要时提权。
二、capabilities:把"root 万能"拆成几十个开关
传统模型:uid=0 就是 root,能干一切——太粗暴,一个 setuid-root 程序被攻破就等于全盘沦陷。Linux capabilities 把 root 的特权拆成几十个独立的位,按需授予:
| capability | 允许的特权操作 |
|---|---|
CAP_NET_BIND_SERVICE | bind 1024 以下的低端口(不必是 root) |
CAP_NET_ADMIN | 配置网络(网卡、路由、防火墙) |
CAP_SYS_ADMIN | 一大堆管理操作(mount 等)——最"胖"、最接近万能 |
CAP_SYS_NICE | 提升调度优先级/设实时优先级(见 scheduling) |
CAP_SYS_PTRACE | ptrace 别的进程(调试器、strace) |
CAP_DAC_OVERRIDE | 无视文件读写权限检查 |
CAP_CHOWN | 改文件属主 |
- 进程持有的 capability 分几套:
Permitted(允许拥有的上限)、Effective(当前生效的)、Inheritable(可传给 exec 后的程序)、Bounding(边界上限)、Ambient。权限检查看Effective集。 - 好处:给一个 web server
CAP_NET_BIND_SERVICE就能监听 80 端口,而不必以 root 运行——被攻破也只多这一个特权,不是整台机器。这是现代服务(和容器)最小权限的基石。
getcap /usr/bin/ping # 看文件被授予了哪些 capability
setcap cap_net_raw+ep ./myprog # 给程序授 raw socket 权限(需 root)
cat /proc/<pid>/status | grep Cap # CapPrm/CapEff/CapInh/CapBnd 位掩码
capsh --decode=<hex> # 把 Cap 位掩码解码成名字三、凭证怎么变、什么时候变
setuid/setgid/seteuid/setresuid:进程主动改自己的 uid(受权限约束——非特权进程只能在 real/effective/saved 之间切)。execve:普通 exec 保留凭证;setuid/setgid 程序的 exec 会按文件位设置 euid/egid。- fork/线程:子进程/线程继承父的凭证(见 process-creation)。
cred是写时复制的:内核用引用计数共享cred,要改时复制一份新的——避免每次检查都拷贝。
四、观测
cat /proc/<pid>/status | grep -E 'Uid|Gid|Groups|Cap'
# Uid: 1000 1000 1000 1000 ← real / effective / saved / fs
# Gid: 1000 1000 1000 1000
# Groups: 4 24 27 1000 ← 附加组
# CapEff: 0000000000000000 ← 当前生效的 capabilities(0=无特权)
id # 当前 shell 的 uid/gid/组
ps -o pid,euser,ruser,cmd # 按有效/真实用户列进程Uid:行四个数正是 real/effective/saved/fs——euid(第二个)才是权限检查用的。CapEff非 0 = 该进程持有特权;root 进程通常满位,普通服务应尽量少。
五、和其它文档的关系
- 父篇:task-struct(cred 是 task 的资源对象)。
- 继承:process-creation(fork/exec 时凭证怎么传)。
- 特权与其它子系统:scheduling(
CAP_SYS_NICE设实时优先级)、kernel-tuning(改 sysctl 多需特权)、../namespaces-cgroups... 见(user namespace 让容器内"看似 root")。 - 观测:procfs(
/proc/<pid>/status的 Uid/Cap)。
六、一句话总结
cred是进程的权限身份:用户/组 ID + 附加组 + capabilities。uid 有四个——real(本来是谁)、effective(权限检查实际用的)、saved(供提权/降权来回切)、fs(文件专用);setuid 程序靠 execve 把 euid 设成文件属主(如 root)实现临时提权,用完应降回(最小权限)。capabilities 把"root 万能"拆成几十个独立特权位(如 CAP_NET_BIND_SERVICE 绑低端口),按需授予,不必真给 root——现代服务和容器最小权限的基石。透过/proc/<pid>/status的 Uid/CapEff 观测。