// 安全研究 · 2026-05-31

Polkit 深度解析:架构、执行原理与规则体系

一、概述

Polkit(原 PolicyKit)是 Linux 系统中广泛使用的权限控制框架,其主要功能是为系统级服务提供细粒度的访问控制机制。与 sudo 不同,Polkit 不授予整个会话 root 权限,而是仅针对**单个动作(Action)**进行授权判断,实现了“最小权限原则”的模块化设计。该框架通过解耦特权操作与身份验证流程,让非特权进程能够安全地与特权进程通信。

Polkit 的核心设计哲学是:将“执行机制”(Mechanism)与“用户界面”(UI)分离。特权程序(如系统守护进程)只负责执行操作,而是否允许执行该操作的决策权交给 Polkit 这个独立的授权机构。

二、系统架构

Polkit 的架构由三个核心组件构成:

2.1 核心组件

graph TB
    subgraph "用户空间"
        A[Subject<br/>非特权进程<br/>如:图形应用] 
        B[Authentication Agent<br/>认证代理<br/>如:GNOME Polkit Agent]
    end
    
    subgraph "系统总线 D-Bus"
        C[org.freedesktop.PolicyKit1<br/>D-Bus 服务接口]
    end
    
    subgraph "系统空间"
        D[polkitd<br/>授权守护进程<br/>以 polkitd 用户运行]
        E[Mechanism<br/>特权服务<br/>如:systemd-logind<br/>NetworkManager]
        F[pkexec / pkcheck<br/>命令行工具]
    end
    
    A -->|请求特权操作| E
    E -->|CheckAuthorization| C
    C -->|转发请求| D
    D -->|查询规则| G[(Action 策略<br/>*.policy XML)]
    D -->|评估规则| H[(Authorization Rules<br/>*.rules JS)]
    D -->|需要认证| B
    B -->|用户输入密码| D
    D -->|返回授权结果| E
    E -->|执行/拒绝| A
    A -->|直接调用| F
    F -->|查询授权| C

2.2 组件详解

组件 角色 说明
Subject 主体 请求执行特权操作的非特权进程,通常是用户会话中的应用程序
Mechanism 机制 执行特权操作的系统守护进程,如 systemd-logind、udisks2、NetworkManager
polkitd 授权机构 核心守护进程,以非特权用户 polkitd 身份运行,通过 D-Bus 接收授权查询并做出决策
Authentication Agent 认证代理 运行在用户会话中的图形或文本界面,负责向用户展示认证对话框并收集凭据
pkexec 执行工具 类似 sudo 的命令行工具,允许授权用户以另一个用户身份执行命令

三、执行原理与授权流程

3.1 标准授权流程

当用户尝试执行特权操作时,Polkit 的完整执行流程如下:

sequenceDiagram
    participant U as 用户/应用 (Subject)
    participant M as 特权服务 (Mechanism)
    participant D as D-Bus 总线
    participant P as polkitd
    participant A as 认证代理
    participant Auth as PAM/密码验证

    U->>M: 请求特权操作<br/>(如:重启系统)
    M->>D: 调用 CheckAuthorization
    D->>P: 转发授权检查请求
    P->>P: 解析 Action ID<br/>(如:org.freedesktop.login1.reboot)
    
    alt 无需认证
        P->>P: 匹配规则返回 YES
        P-->>D: 授权通过
        D-->>M: 允许执行
        M->>U: 执行操作
    else 需要认证
        P->>P: 确定认证类型<br/>(auth_self / auth_admin)
        P->>D: 请求启动认证代理
        D->>A: 调用 BeginAuthentication
        A->>U: 显示认证对话框
        U->>A: 输入密码
        A->>Auth: 通过 PAM 验证
        Auth-->>A: 验证结果
        A->>D: 发送 AuthenticationAgentResponse2
        D->>P: 验证 Cookie 和结果
        P-->>D: 认证成功/失败
        D-->>M: 返回授权结果
        opt 授权成功
            M->>U: 执行特权操作
        end
    else 拒绝访问
        P-->>D: 返回 NO
        D-->>M: 拒绝执行
        M->>U: 操作被拒绝
    end

3.2 pkexec 的执行流程

pkexec 是 Polkit 提供的命令行工具,功能类似 sudo,但实现机制有本质差异:

flowchart TD
    A[用户执行<br/>pkexec &lt;command&gt;] --> B{参数检查}
    B -->|argc < 1| C[拒绝执行<br/>CVE-2021-4034 修复]
    B -->|正常参数| D[解析目标程序路径]
    D --> E{路径是否绝对?}
    E -->|否| F[在 PATH 中搜索]
    E -->|是| G[构造 D-Bus 请求]
    F --> G
    G --> H[调用 polkitd<br/>检查授权]
    H --> I{授权结果?}
    I -->|YES| J[以目标用户身份<br/>执行命令]
    I -->|NO| K[退出码 127<br/>未授权]
    I -->|需要认证| L[启动认证流程]
    L --> M[用户取消?]
    M -->|是| N[退出码 126<br/>用户取消]
    M -->|否| O[验证密码]
    O -->|成功| J
    O -->|失败| K

与 sudo 的关键区别在于:pkexec 通过动态查询 polkitd 服务进行实时授权验证,而非依赖静态的 /etc/sudoers 配置文件。这使得 Polkit 能够实现更细粒度的、基于动作(Action)的访问控制策略。

3.3 临时授权缓存机制

Polkit 支持授权缓存,当使用 auth_self_keep 或 auth_admin_keep 时,成功认证后的授权会被保留一段时间(通常约 5 分钟)。在此期间,同一用户执行相同 Action 无需重复认证。

graph LR
    A[首次认证] -->|auth_admin_keep| B[授权成功]
    B --> C[缓存临时授权<br/>temporary_authorization_id]
    C --> D[5分钟内再次请求<br/>相同 Action]
    D --> E{缓存有效?}
    E -->|是| F[直接授权<br/>无需密码]
    E -->|否| G[重新认证]

可通过 pkcheck --list-temp 查看当前缓存的临时授权。

四、Action 动作定义

4.1 Action 概念

Action(动作)是 Polkit 授权模型的基本单位,代表一个受 Polkit 规则约束的单一活动。每个 Action 都有唯一的标识符,例如重启计算机的动作 ID 为 org.freedesktop.login1.reboot。

4.2 Action 配置文件

Actions 定义在 XML 格式的 .policy 文件中,位于 /usr/share/polkit-1/actions/。每个文件可定义一个或多个动作,包含人类可读的描述和默认授权设置。

重要提示:系统默认的 policy 文件不应直接编辑,应通过 .rules 文件覆盖。

4.3 Action 文件结构

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE policyconfig PUBLIC
 "-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN"
 "http://www.freedesktop.org/software/polkit/policyconfig-1.dtd">
<policyconfig>
  <vendor>GNOME</vendor>
  <vendor_url>https://www.gnome.org</vendor_url>
  
  <action id="org.gnome.gparted">
    <description>Run GParted as root</description>
    <description xml:lang="zh_CN">以 root 身份运行 GParted</description>
    <message>Authentication is required to run GParted</message>
    <message xml:lang="zh_CN">运行 GParted 需要身份验证</message>
    <icon_name>gparted</icon_name>
    
    <defaults>
      <allow_any>auth_admin</allow_any>
      <allow_inactive>auth_admin</allow_inactive>
      <allow_active>auth_admin</allow_active>
    </defaults>
    
    <annotate key="org.freedesktop.policykit.exec.path">/usr/bin/gparted</annotate>
    <annotate key="org.freedesktop.policykit.exec.allow_gui">true</annotate>
  </action>
</policyconfig>

4.4 隐式授权类型

每个 Action 的 <defaults> 标签定义了三种会话状态下的默认授权策略:

授权值 含义 适用场景
no 拒绝授权 敏感操作,禁止普通用户执行
yes 无需认证直接授权 低风险操作,如修改用户自己的密码
auth_self 需要用户认证自己的密码 中等风险,用户需证明身份
auth_self_keep 同上,但缓存授权约 5 分钟 频繁执行的同类操作
auth_admin 需要管理员(root)密码 高风险系统操作
auth_admin_keep 同上,但缓存授权约 5 分钟 管理员批量操作

三种会话状态:

  • allow_any:适用于所有上下文,包括 SSH/VNC 远程会话
  • allow_active:适用于本地活动会话(当前正在使用的图形/文本控制台)
  • allow_inactive:适用于本地非活动会话(如切换到其他虚拟终端后的原会话)

4.5 常用 Annotation 注解

Action 可通过 <annotate> 标签添加键值对元数据:

键名 用途
org.freedesktop.policykit.exec.path 指定 pkexec 执行的目标程序完整路径
org.freedesktop.policykit.exec.argv1 限制 pkexec 调用时的第一个参数
org.freedesktop.policykit.exec.allow_gui 布尔值,允许在图形环境中安全启动特权程序
org.freedesktop.policykit.imply 定义元动作,授权此动作时同时授权列出的其他动作 ID
org.freedesktop.policykit.owner 指定可查询此动作授权状态的用户列表

五、Authorization Rules 授权规则

5.1 Rules 概述

Authorization Rules 是写在 JavaScript 中的 .rules 文件,位于两个目录:

  • /usr/share/polkit-1/rules.d/:系统软件包使用的规则
  • /etc/polkit-1/rules.d/:本地管理员自定义规则(优先级更高)

规则文件包含比 Action 默认设置更复杂的逻辑,可以基于用户、组、会话状态、动作参数等条件动态决定授权结果。

5.2 规则评估机制

graph TD
    A[收到授权请求] --> B[加载所有 .rules 文件]
    B --> C[按文件名排序<br/>数字小的优先]
    C --> D[依次执行 addRule 回调]
    D --> E{返回结果?}
    E -->|YES/NO/<br/>AUTH_*| F[终止评估<br/>返回结果]
    E -->|null/undefined| G[继续下一个规则]
    G --> H{还有规则?}
    H -->|是| D
    H -->|否| I[使用 Action 的<br/>默认隐式授权]

5.3 规则语法与对象

规则文件使用 JavaScript 语法,核心 API 包括:

// 添加授权规则
polkit.addRule(function(action, subject) {
    // action 对象包含:
    // - action.id: 动作标识符
    // - action.lookup(key): 查询 annotation 值
    
    // subject 对象包含:
    // - subject.user: 用户名
    // - subject.isInGroup("groupname"): 是否属于指定组
    // - subject.local: 是否为本地会话
    // - subject.active: 是否为活动会话
    
    // 返回 polkit.Result 枚举值:
    // - polkit.Result.YES
    // - polkit.Result.NO  
    // - polkit.Result.AUTH_SELF
    // - polkit.Result.AUTH_SELF_KEEP
    // - polkit.Result.AUTH_ADMIN
    // - polkit.Result.AUTH_ADMIN_KEEP
    // - null (继续评估后续规则)
});

5.4 典型规则示例

示例 1:允许 wheel 组用户无需密码执行 NetworkManager 操作

polkit.addRule(function(action, subject) {
    if (/^org\.freedesktop\.NetworkManager\./.test(action.id) &&
        subject.local && 
        subject.active && 
        subject.isInGroup("wheel")) {
        return polkit.Result.YES;
    }
});

示例 2:允许特定用户免密设置时区

polkit.addRule(function(action, subject) {
    if (action.id == "org.freedesktop.timedate1.set-timezone" &&
        subject.user == "archie") {
        return polkit.Result.YES;
    }
});

示例 3:基于动作参数细粒度控制

polkit.addRule(function(action, subject) {
    if (action.id == "org.freedesktop.systemd1.manage-units" &&
        action.lookup("unit") == "hybrid.service" &&
        subject.user == "michael") {
        return polkit.Result.YES;
    }
});

示例 4:调用外部程序辅助决策

polkit.addRule(function(action, subject) {
    if (action.id.indexOf("org.freedesktop.login1.reboot") == 0) {
        try {
            // 外部程序返回 0 则授权
            polkit.spawn(["/opt/company/bin/user-may-reboot", subject.user]);
            return polkit.Result.YES;
        } catch (error) {
            return polkit.Result.AUTH_ADMIN;
        }
    }
});

5.5 Admin Rules 管理员规则

除了 addRule(),Polkit 还提供 addAdminRule() 用于定义哪些用户被视为管理员:

polkit.addAdminRule(function(action, subject) {
    return ["unix-group:wheel"];
});

这决定了当需要 auth_admin 认证时,系统会提示输入哪个管理员账户的密码。

六、Polkit 与 Sudo 的对比

graph BT
    subgraph "Sudo 模型"
        A1[用户] -->|sudo cmd| B1[检查 /etc/sudoers<br/>静态配置]
        B1 -->|授权| C1[以 root 执行<br/>整个命令]
    end
    
    subgraph "Polkit 模型"
        A2[用户/应用] -->|请求 Action| B2[机制服务<br/>如 systemd-logind]
        B2 -->|CheckAuthorization| C2[polkitd<br/>动态评估]
        C2 -->|查询| D2[Action 策略<br/>& Rules 规则]
        D2 -->|决策| C2
        C2 -->|授权| E2[仅执行<br/>特定操作]
    end
特性 Sudo Polkit
控制粒度 基于命令和用户 基于动作(Action)和会话状态
配置方式 静态 /etc/sudoers 动态 XML + JavaScript 规则
认证方式 密码验证 集成图形认证代理 + PAM
桌面集成 弱(需终端) 强(原生图形对话框)
权限范围 授予整个命令的 root 权限 仅授权特定操作
缓存机制 timestamp_timeout auth_*_keep(约 5 分钟)
适用场景 命令行管理员操作 桌面应用和系统服务交互

七、安全考量与历史漏洞

Polkit 的设计虽然降低了传统 setuid 程序的安全风险,但其实现中的漏洞也曾引发严重安全问题:

7.1 CVE-2021-4034(PwnKit)

2022 年披露的 pkexec 本地提权漏洞,存在于 2009 年以来的所有版本。根本原因是 pkexec 在处理 argc=0 时未正确验证参数,导致越界读写环境变量,最终可注入 GCONV_PATH 执行任意代码获得 root 权限。

修复方案:在 pkcheck.c 和 pkexec.c 中添加 argc < 1 的检查。

7.2 CVE-2021-3560

另一个著名漏洞,利用 dbus-send 请求与 polkitd 查询 UID 之间的竞态条件,通过快速取消请求使 polkitd 无法获取正确的 UID,从而绕过授权检查。

八、总结

Polkit 作为现代 Linux 系统的核心授权框架,通过其独特的架构设计实现了:

  1. 解耦授权与执行:特权服务专注于业务逻辑,授权决策交给独立的 polkitd
  2. 细粒度访问控制:基于 Action 的授权模型比传统 sudo 更精确
  3. 灵活的规则体系:JavaScript 规则文件支持复杂的动态授权逻辑
  4. 无缝的桌面集成:图形认证代理提供友好的用户体验
  5. 安全的进程隔离:polkitd 以非特权用户运行,通过 D-Bus 通信

理解 Polkit 的 Action 定义和 Rules 评估机制,是进行 Linux 系统权限管理和安全加固的重要基础。管理员应当避免直接修改系统 .policy 文件,而是通过 /etc/polkit-1/rules.d/ 中的自定义规则实现权限调整,确保系统升级时配置不会丢失。

原文 https://vortex.blog.csdn.net/article/details/161573448