Planet X

Linux的权能与权限

权限(Permissions)是我们在接触Linux的时候很早就会接触到的一个概念,脑海中直观的印象就是那一串rwxrwxrwx,以至于前几天我在初次听到“权能(Capabilities)”这个词的时候,还以为它们是一回事。后来才知道二者有很大的差别。
那么今天我就按自己的理解写一写这两者的区别吧,先从两者的定义聊起。

定义

权限 Permissions

在Linux中,对文件执行 ls -l 命令,最前方出现的10个字符就是文件的权限信息。


如图所示(图片来自鸟哥的linux私房菜),每一位都有其相应的含义。

  • 第一位是文件类型,如果是目录,就为d,否则为 -符号。
  • 第2-4位是文件或目录拥有者的权限。
  • 第5-7位是文件或目录所在组的权限。
  • 第8-10位是该组以外所有用户的访问权限。

文件的权限总体可以分为三种:r(读),w(写),x(执行)

除此之外,还有一些文件具有特殊权限 SetUID、SetGID、Sticky

  • SetUID 就是让程序在执行过程中拥有文件拥有者的权限。

    如果文件同时具有所有者的执行权限以及SetUID,那么把上述文件权限信息的第4位改为s

    如果文件开启SetUID但关闭执行权限,那么把上述文件权限信息的第4位改为

    例如 -rwsr--r--

  • SetGID 就是让程序在执行过程中拥有文件拥有者所在组的权限。

    如果文件同时具有所有者所在组的执行权限以及SetUID,那么把上述文件权限信息的第7位改为s

    如果文件开启SetUID但关闭执行权限,那么把上述文件权限信息的第7位改为

    例如 -rwxr-sr--

  • Sticky

    如果目录文件启用本权限位,那么该目录下所有文件仅允许其拥有者去操作,从而避免其他用户的干扰。

    如果其他文件启用本权限位,那么该文件的最后更新时间将不会改变。

    例如 drwxrw-rwt

设置UID的两种方法
方法一

chmod u+s 设置setuid权限

chmod g+s 设置setgid权限

chmod o+t 设置stickbit权限

相应地,

chmod u-s 删除setuid权限

chmod g-s 删除setgid权限

chmod o-t 删除stickbit权限

方法二

chmod 4775 设置setuid权限

chmod 2775 设置setgid权限

chmod 1775 设置stickbit权限

相应地,使用chmod+三位数字赋予权限,即可删除特殊特殊权限。

例如chmod 775 即可把相应文件的权限改为 -rwxrwxr-x,没有s位。

权能

(权能)Capabilities是Linux操作系统对特权操作进行访问控制的一种安全机制。

在最早的Linux操作系统中,进程的特权操作是根据进程的有效用户身份来进行访问控制的。即

以root为有效用户身份的进程可以进行任何特权操作,而其它用户的进程则不能进行任何特权操作

因此,在Linux系统中以root为有效用户身份的每个进程,不管需不需要,都拥有系统所有的特权。而在大多数情况下,进程完成其服务功能时并不需要什么特权,或仅仅需要很少的几种特权。拥有多余特权对系统安全总是不利的。

权能就是针对上面的问题而提出来的,目的在于实现一种更加细粒度的特权操作的访问控制,使用户能够根据实际的安全需要来控制一个进程拥有的特权范围,从而能够取消进程的多余特权及多余特权带来的安全隐患。

权能的主要思想是:将root用户拥有的所有特权分割成多种权能,每种权能代表进行某种特权操作的权限;在系统所有实体中,只有进程和可执行文件具有权能的信息;系统根据一定的策略赋予进程权能,并根据进程拥有的权能来进行特权操作的访问控制。

简而言之,权能,可以让普通用户完成超级用户才能做的事情,并且极大地降低了系统的风险。

权能的位图

每个进程有三个和能力有关的位图:inheritable(I),permitted(P)和effective(E)

  • cap_effective:Linux内核真正检查的权能集。

  • cap_permitted:表示进程能够使用的能力,在cap_permitted中可以包含cap_effective中没有的能力,这些能力是被进程自己临时放弃的,也可以说cap_effective是cap_permitted的一个子集。

  • cap_inheritable:表示能够被当前进程执行的程序继承的能力。

权能继承规则

P’(permitted) = (P(inheritable) &F(inheritable)) | (F(permitted) & cap_bset)

新进程的permitted由旧进程的inheritable和可执行文件的inheritable和permitted集及cap_bset运算得到。

P‘(effective) = F’(effective) ? P’(permitted) : 0

新进程的effective集依赖对应可执行文件的effective位如果设置了该为则和旧进程的permitted一样,否则为空。

P’(inheritable) = P(inheritable)

新进程的inheritable直接继承旧进程的Inheritable

P 在执行execve函数前,进程的能力,表示旧进程。

P’ 在执行execve函数后,进程的能力,表示新进程。

F 可执行文件的能力。

cap_bset 系统能力的边界值,在此处默认全为1

相关操作代码

查看文件或进程地权能

  • 查看文件的权能 getcap

  • 查看进程的权能 cap_get_proc

    cap_get_proc函数的原型是:cap_t cap_get_proc(void);

    cap_get_proc函数返回当前进程的能力值给cap变量.

设置权能

  • 给文件设置权能 setcap

    setcap cap_net_raw+ep /bin/ping

    +ep即给权能赋予effective和permitted位图

  • 给进程设置权能 cap_set_flag cap_set_proc

    cap_set_flag函数的原型是:

    int cap_set_flag(cap_t cap_p, cap_flag_t flag, intncap,const cap_value_t *caps, cap_flag_value_t value);

    第一个参数cap_p是存放能力值的变量。

    第二个参数flag是是三种能力位图。

    第三个参数ncap是要设定能力的个数。

    第四个参数 *caps 是要设定的能力值,是权能capList数组。

    第五个参数value是决定要设定还是清除。

    cap_set_proc函数的原型是:int cap_set_proc(cap_t cap_p);

    cap_set_proc函数通过cap_p中的能力值设定给当前的进程.

删除权能

  • 删除文件的权能 setcap -r

  • 删除进程的权能 cap_free

    cap_free函数的原型是:cap_free(caps);

    cap_free函数清理/释放cap变量.

初始化

cap_init()函数初始化存放cap能力值的状态

例子:ping

我通过ping这个命令,对权能和权限有了一个初步的感受。

首先,我们可以试着查看一下ping的权限信息

1
2
jerry@ubuntu:~$ ls -l /bin/ping
-rwsr-xr-x. 1 root root 44168 5月 7 2014 /bin/ping

我们可以发现,第一组权限的第三位是s,即设置了SetUID,所以,我们可以在普通用户下直接运行ping。

试一试吧:

1
2
3
4
5
6
7
jerry@ubuntu:~$ ping -c 1 127.0.0.1
PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data.
64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.016 ms
--- 127.0.0.1 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.016/0.016/0.016/0.000 ms

那么,现在试一下把这一位SetUID删掉:

1
2
3
jerry@ubuntu:~$ sudo chmod u-s /bin/ping
jerry@ubuntu:~$ ls -l /bin/ping
-rwxr-xr-x. 1 root root 44168 5月 7 2014 /bin/ping

s不见了~

再试一下ping:

1
2
jerry@ubuntu:~$ ping -c 1 127.0.0.1
ping: icmp open socket: Operation not permitted

太好了,ping不了了。这说明SetUID真的起了作用,使得普通用户可以拥有root用户的特权。

那么现在可以试一下权能的作用。

给ping添加 CAP_NET_RAW 权能

1
2
3
4
5
6
7
8
jerry@ubuntu:~$ sudo setcap cap_net_raw+ep /bin/ping
jerry@ubuntu:~$ ping -c 1 127.0.0.1
PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data.
64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.021 ms
--- 127.0.0.1 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.021/0.021/0.021/0.000 ms

然后就可以在普通用户没有SetUID的情况下进行ping啦~

总的来说,权能的出现加强了系统地安全性,防止了恶意程序利用系统漏洞获取root权限。在嵌入式系统的设计中,如何根据物联网设备的具体情况来进行权能的细粒度划分,将在很大程度上决定我们项目的实用性和可靠性。

参考资料

鸟哥的linux私房菜

Linux下Capabilities安全机制的分析与改进

周师兄的PPT

附:权能列表

下面的列表展示了 Linux 上实现的权能,以及每种权能允许的操作或行为:

  • CAP_AUDIT_CONTROL(自 Linux 2.6.11)

    启用和禁用内核审计;修改审计过滤器规则;提取审计状态和过滤规则。

  • CAP_AUDIT_READ(自 Linux 3.16)

    允许通过一个多播 netlink socket 读取审计日志。

  • CAP_AUDIT_WRITE(自 Linux 2.6.11)

    向内核审计日志写记录。

  • CAP_BLOCK_SUSPEND(自 Linux 3.5)

    可以阻塞系统挂起的特性。

  • CAP_CHOWN

    对文件的 UIDs 和 GIDs 做任意的修改(参考 chown(2))。

  • CAP_DAC_OVERRIDE

    绕过文件的读,写,和执行权限检查。(DAC 是 “discretionary access control” 的缩写。)

  • CAP_DAC_READ_SEARCH

    • 绕过文件的读权限检查和目录的读和执行权限检查;
    • 调用 open_by_handle_at(2)。
  • CAP_FOWNER

    • 对于通常要求进程的文件系统 UID 与文件的 UID 匹配的操作,绕过权限检查 (比如,chmod(2),utime(2)),除了那些包含在 CAP_DAC_OVERRIDECAP_DAC_READ_SEARCH 中的操作;
    • 为任意文件设置扩展文件属性(参考 chattr(1));
    • 为任意文件设置访问控制表(ACLs);
    • 对文件删除操作忽略目录的 sticky 位;
    • open(2) 和 fcntl(2) 任意文件时设置 O_NOATIME
  • CAP_FSETID

    当文件修改时不清除 set-user-ID 和 set-group-ID 模式位;为文件 GID 与调用进程的文件系统或补充 GIDs 不匹配的文件设置 set-group-ID 位。

  • CAP_IPC_LOCK

    锁定内存 (mlock(2),mlockall(2),mmap(2),shmctl(2))。

  • CAP_IPC_OWNER

    绕过对 System V IPC 对象的操作的权限检查。

  • CAP_KILL

    绕过发送信号 (参考 kill(2)) 时的权限检查。这包括使用 ioctl(2) KDSIGACCEPT 操作。

  • CAP_LEASE(自 Linux 2.4)

    为任意文件建立租约 (参考 fcntl(2))。

  • CAP_LINUX_IMMUTABLE

    设置FS_APPEND_FLFS_IMMUTABLE_FL inode 标记 (参考 chattr(1))。

  • CAP_MAC_ADMIN(自 Linux 2.6.25)

    覆盖强制访问控制 (Mandatory Access Control (MAC)). 为 Smack Linux 安全模块(Linux Security Module (LSM)) 而实现。

  • CAP_MAC_OVERRIDE(自 Linux 2.6.25)

    允许 MAC 配置或状态改变。为 Smack LSM 而实现。

  • CAP_MKNOD(自 Linux 2.4)

    使用 mknod(2) 创建特殊文件。

  • CAP_NET_ADMIN

    执行多种网络有关的操作:

    • 接口配置;
    • IP 防火墙,地址伪装,和账单管理;
    • 修改路由表;
    • 为透明代理绑定任何地址;
    • 设置服务类性 (type-of-service (TOS));
    • 清理驱动统计资料;
    • 设置混杂模式;
    • 启用组播;
    • 使用 setsockopt(2) 设置下列 socket 选项:SO_DEBUGSO_MARKSO_PRIORITY (在0到6范围之外的优先级),SO_RCVBUFFORCE,和 SO_SNDBUFFORCE
  • CAP_NET_BIND_SERVICE

    将一个 socket 绑定到一个互联网域特权端口 (端口号小于 1024)。

  • CAP_NET_BROADCAST

    (未使用) 使 socket 发送组播,并监听组播。

  • CAP_NET_RAW

    • 使用 RAW 和 PACKET sockets;
    • 为透明代理绑定任何地址。
  • CAP_SETGID

    执行任意的进程 GIDs 和补充 GID 列表管理;当通过 UNIX 域 sockets 传递 socket 认证信息时伪造 GID;在一个用户命名空间 (参考 user_namespaces(7)) 中写入组 ID 映射。

  • CAP_SYS_ADMIN

    • 执行一系列系统管理操作,包括:quotactl(2),mount(2),umount(2),swapon(2),swapoff(2),sethostname(2),和 setdomainname(2);
    • 执行特权 syslog(2) 操作 (自 Linux 2.6.37 开始,应该使用 CAP_SYSLOG 来允许这一操作);
    • 执行 VM86_REQUEST_IRQ vm86(2) 命令;
    • 对任意 System V IPC 对象执行 IPC_SET 和 IPC_RMID 操作;
    • 覆盖 RLIMIT_NPROC 资源限制;
    • 执行 trusted 和 security Extended Attributes (see xattr(7)) 操作;
    • 使用 lookup_dcookie(2);
    • 使用 ioprio_set(2) 来分配 IOPRIO_CLASS_RT 和 (Linux 2.6.25 之前) IOPRIO_CLASS_IDLE I/O 调度类别;
    • 当通过 UNIX 域 sockets 传递 socket 认证信息时伪装 PID;
    • 在系统调用打开文件 (比如,accept(2),execve(2),open(2),pipe(2)) 时,超出 /proc/sys/fs/file-max,系统范围内打开文件数的限制;
    • 通过 clone(2) 和 unshare(2) 使用 CLONE_* 标记创建新的命名空间(但是,自从 Linux 3.8 开始,创建命名空间不需要任何权能);
    • 调用 perf_event_open(2);
    • 访问特权 perf 事件信息;
    • 调用 setns(2) (在目标命名空间中需要 CAP_SYS_ADMIN);
    • 调用 fanotify_init(2);
    • 调用 bpf(2);
    • 执行 KEYCTL_CHOWNKEYCTL_SETPERM keyctl(2) 操作;
    • 执行 madvise(2) MADV_HWPOISON 操作;
    • 使用 TIOCSTI ioctl(2) 向一个终端的输入队列中插入字符,而不是调用者的控制终端;
    • 使用废弃的 nfsservctl (2) 系统调用;
    • 使用废弃的 bdflush (2) 系统调用;
    • 执行各种特权的块设备 ioctl(2) 操作;
    • 执行各种特权的文件系统 ioctl(2) 操作;
    • 对许多设备驱动执行管理操作。
  • CAP_SYS_BOOT

    使用 reboot(2) 和 kexec_load(2)。

  • CAP_SYS_CHROOT

    使用 chroot(2)。

  • CAP_SYS_MODULE

    加载和卸载内核模块(参考 init_module(2) 和 delete_module(2));在 2.6.25 之前的内核中:从系统范围内的权能边界集合中丢弃权能。

  • CAP_SYS_NICE

    • 触发进程 nice 值 (nice(2),setpriority(2)) 和为任意进程改变 nice 值;
    • 为调用进程设置实时调度策略,及为任意进程设置调度策略和优先级 (sched_setscheduler(2),sched_setparam(2),shed_setattr(2));
    • 为任意进程设置 CPU affinity (sched_setaffinity(2));
    • 为任意进程设置 I/O 调度类别和优先级 (ioprio_set(2));
    • 对任意进程应用 migrate_pages(2) 并允许进程被迁移到任意节点;
    • 对任意进程应用 move_pages(2);
    • mbind(2) 和 move_pages(2) 中使用 MPOL_MF_MOVE_ALL 标记。
  • CAP_SYS_PACCT

    使用 acct(2)。

  • CAP_SYS_PTRACE

    • 使用 ptrace(2) 追踪任意进程;
    • 对任意进程应用 get_robust_list(2);
    • 使用 process_vm_readv(2) 和 process_vm_writev(2) 同任意进程的内存传输数据;
    • 使用 kcmp(2) 检查进程。
  • CAP_SYS_RAWIO

    • 执行 I/O 端口操作 (iopl(2) 和 ioperm(2));
    • 访问 /proc/kcore;
    • 使用 FIBMAP ioctl(2) 操作;
    • 打开设备访问 x86 模式特有寄存器 (MSRs,参考 msr(4));
    • 更新 /proc/sys/vm/mmap_min_addr;
    • 在地址低于 /proc/sys/vm/mmap_min_addr 的位置创建内存映射;
    • 在 /proc/bus/pci 中映射文件;
    • 打开 /dev/mem 和 /dev/kmem;
    • 执行各种 SCSI 设备命令;
    • hpsa(4) 和 cciss(4) 设备上执行某一操作;
    • 在其它设备上执行一系列设备特有操作。
  • CAP_SYS_RESOURCE

    • 使用 ext2 文件系统上的预留空间;
    • 执行 ioctl(2) 调用控制 ext3 日志;
    • 覆盖磁盘配额限制;
    • 增加资源限制 (参考 setrlimit(2));
    • 覆盖 RLIMIT_NPROC 资源限制;
    • 在终端分配上覆盖最大的终端数;
    • 覆盖最大的 keymaps 个数;
    • 允许实时时钟中断大于64 hz;
    • 触发一个 System V 消息队列的 msg_qbytes 限制超过 /proc/sys/kernel/msgmnb 中的限制 (参考 msgop(2) 和 msgctl(2));
    • 当使用 F_SETPIPE_SZ fcntl(2) 命令设置一个管道的容量时覆盖 /proc/sys/fs/pipe-size-max 的限制;
    • 使用 F_SETPIPE_SZ 增加管道的容量超出 /proc/sys/fs/pipe-max-size 指定的限制;
    • 当创建 POSIX 消息队列 (参考 mq_overview(7)) 时覆盖 /proc/sys/fs/mqueue/queues_max 的限制;
    • 使用 prctl(2) PR_SET_MM 操作;
    • 设置 /proc/PID/oom_score_adj 为一个小于由一个具有 CAP_SYS_RESOURCE 的进程最近设置的值的值。
  • CAP_SYS_TIME

    设置系统时钟 (settimeofday(2),stime(2),adjtimex(2));设置实时 (硬件) 时钟。

  • CAP_SYS_TTY_CONFIG

    使用 vhangup(2);对虚拟终端使用各种特权 ioctl(2) 操作。

  • CAP_SYSLOG (自 Linux 2.6.37)

    • 执行特权 syslog(2) 操作。参考 syslog(2) 来获取哪些操作需要特权的信息;
    • 当 /proc/sys/kernel/kptr_restrict 值为 1 时,查看通过 /proc 和其它接口暴露
    • 的内核地址。(参考 proc(5) 中 kptr_restrict 的讨论。)
  • CAP_WAKE_ALARM (自 Linux 3.0)

    触发将唤醒系统的东西 (设置 CLOCK_REALTIME_ALARM 和 CLOCK_BOOTTIME_ALARM 定时器)。