In continuation of the previous text第6章:字符设备驱动的高级操作5:Using the ioctl Argument let’s GO ahead.
Access to a device is controlled by the permissions on the device file(s), and the driver is not normally involved in permissions checking. There are situations, however, where any user is granted read/write permission on the device, but some control operations should still be denied. For example, not all users of a tape drive should be able to set its default block size, and a user who has been granted read/write access to a disk device should probably still be denied the ability to format it. In cases like these, the driver must perform additional checks to be sure that the user is capable of performing the requested operation.
对设备的访问是由设备文件的权限位控制的,驱动程序通常不参与权限检查。然而,在某些情况下,尽管所有用户都被授予了设备的读写权限,但某些控制操作仍然应该被禁止。例如,并非所有使用磁带驱动器的用户都应该能够设置其默认块大小;被授予磁盘设备读写权限的用户,通常也应当被禁止执行格式化操作。在类似这样的场景中,驱动程序必须执行额外的检查,以确保用户有权执行所请求的操作。
Unix systems have traditionally restricted privileged operations to the superuser account. This meant that privilege was an all-or-nothing thing—the superuser can do absolutely anything, but all other users are highly restricted. The Linux kernel provides a more flexible system called capabilities. A capability-based system leaves the all-or-nothing mode behind and breaks down privileged operations into separate subgroups. In this way, a particular user (or program) can be empowered to perform a specific privileged operation without giving away the ability to perform other, unrelated operations. The kernel uses capabilities exclusively for permissions management and exports two system calls capget and capset, to allow them to be managed from user space.
传统的 Unix 系统将特权操作限制为超级用户(root)才能执行。这意味着权限是 “全有或全无” 的 —— 超级用户可以做任何事,但其他所有用户都受到严格限制。Linux 内核提供了一种更灵活的机制,称为能力(capabilities)。基于能力的系统摆脱了 “全有或全无” 的模式,将特权操作分解为独立的子组。通过这种方式,特定的用户(或程序)可以被授权执行某一项特定的特权操作,而无需赋予其执行其他无关操作的权限。内核完全使用能力机制来进行权限管理,并导出了两个系统调用capget和capset,允许从用户空间对能力进行管理。
The full set of capabilities can be found in . These are the only capabilities known to the system; it is not possible for driver authors or system administrators to define new ones without modifying the kernel source. A subset of those capabilities that might be of interest to device driver writers includes the following<linux/capability.h>:
完整的能力集定义在<linux/capability.h>头文件中。这些是系统已知的仅有的能力;驱动开发者或系统管理员如果不修改内核源码,就无法定义新的能力。设备驱动开发者可能关心的能力子集如下:
| Capability | Description |
|---|---|
CAP_DAC_OVERRIDE | The ability to override access restrictions (data access control, or DAC) on files and directories. |
CAP_NET_ADMIN | The ability to perform network administration tasks, including those that affect network interfaces. |
CAP_SYS_MODULE | The ability to load or remove kernel modules. |
CAP_SYS_RAWIO | The ability to perform "raw" I/O operations. Examples include accessing device ports or communicating directly with USB devices. |
CAP_SYS_ADMIN | A catch-all capability that provides access to many system administration operations. |
CAP_SYS_TTY_CONFIG | The ability to perform tty configuration tasks. |
| 能力名称 | 描述 |
|---|---|
| CAP_DAC_OVERRIDE | 忽略文件和目录上的访问权限限制(数据访问控制,DAC)。 |
| CAP_NET_ADMIN | 执行网络管理任务的权限,包括配置网络接口等操作。 |
| CAP_SYS_MODULE | 加载或卸载内核模块的权限。 |
| CAP_SYS_RAWIO | 执行 “原始” I/O 操作的权限。例如访问设备端口或直接与 USB 设备通信。 |
| CAP_SYS_ADMIN | 一个通用的管理能力,涵盖了许多系统管理操作。 |
| CAP_SYS_TTY_CONFIG | 执行 TTY 终端配置任务的权限。 |
Before performing a privileged operation, a device driver should check that the calling process has the appropriate capability; failure to do so could result user processes performing unauthorized operations with bad results on system stability or security. Capability checks are performed with the capable function (defined in ):
在执行特权操作之前,设备驱动程序应该检查调用进程是否拥有相应的能力;如果不这样做,可能会导致用户进程执行未授权的操作,从而对系统稳定性或安全性造成不良影响。能力检查通过capable函数完成(定义在<linux/sched.h>中):
int capable(int capability);In the scull sample driver, any user is allowed to query the quantum and quantum set sizes. Only privileged users, however, may change those values, since inappropriate values could badly affect system performance. When needed, the scull implementation of ioctl checks a user’s privilege level as follows:
在 scull 示例驱动中,允许任何用户查询quantum和quantum set的大小。但是,只有特权用户可以修改这些值,因为不恰当的数值可能严重影响系统性能。在需要时,scull 驱动的 ioctl 实现会按如下方式检查用户权限:
if (! capable (CAP_SYS_ADMIN)) return -EPERM;In the absence of a more specific capability for this task, CAP_SYS_ADMIN was chosen for this test.
由于没有更具体的能力适用于此任务,因此选择了CAP_SYS_ADMIN进行此项检查。
补充说明:
1. 为什么需要在驱动里做权限检查?
- 设备文件权限太粗糙:设备文件通常只设置
rwx(读 / 写 / 执行)三种权限。如果一个普通用户拥有设备的写权限,他就可以通过write写入数据。但是,修改硬件配置、格式化磁盘、重启设备这类高危操作,不能仅仅因为有写权限就允许执行。 - 细粒度控制:驱动内的能力检查,可以实现 “允许读写数据,但禁止修改配置” 的精细化控制。
2. 什么是 Capabilities(能力)?
- 打破 root 垄断:传统上只有 root (UID 0) 能做所有事。Capabilities 把 root 的超级权限拆分成几十个小权限(如
CAP_SYS_BOOT只允许重启,CAP_NET_RAW只允许抓包)。 - 最小权限原则:给程序分配刚好够用的权限,而不是直接给 root 权限,大大提升系统安全性。
- 内核机制:内核在执行关键操作时,会检查当前进程是否具备对应的能力。
3. 核心函数:capable ()
- 功能:检查当前发起调用的进程是否拥有指定的特权能力。
- 返回值:
- 非 0:拥有权限,可以执行操作。
- 0:没有权限,驱动应拒绝请求。
- 标准错误码:检查失败时,必须返回
-EPERM(Operation not permitted),这是 Linux 标准约定。
4. 关于 CAP_SYS_ADMIN
- 定位:它是一个 **“万能兜底”** 能力(catch-all)。
- 含义:涵盖了各种杂项的系统管理操作。
- 使用建议:
- 如果有更具体的能力(如
CAP_NET_ADMIN管理网络),优先使用具体的。 - 只有当没有合适的专用能力时(如修改驱动内部配置参数),才使用
CAP_SYS_ADMIN。 - 拥有此能力的进程,权限非常高,接近传统 root。
- 如果有更具体的能力(如
5. 驱动权限检查的典型流程
在ioctl处理高危命令时,代码逻辑通常如下:
- 检查命令幻数、序号是否合法。
- 检查用户指针是否合法。
- 检查能力(Capabilities):
// 示例:修改设备配置 case SCULL_IOCSQUANTUM: /* 检查是否有系统管理员权限 */ if (!capable(CAP_SYS_ADMIN)) return -EPERM; /* 权限不足 */ /* 执行设置操作... */ break;
6. 总结
- 设备文件权限管的是 “能不能打开这个设备”。
- Capabilities (能力)管的是 “打开后,能不能执行高危操作”。
- 驱动实现
ioctl时,凡是涉及修改系统配置、硬件参数、敏感数据的命令,必须加 capable () 检查,这是保证系统安全的核心环节。