Skip to content

fix: change cross-app accessed dconfig items from private to public - #157

Open
18202781743 wants to merge 1 commit into
linuxdeepin:masterfrom
18202781743:feat/dconfig-private-permission
Open

fix: change cross-app accessed dconfig items from private to public#157
18202781743 wants to merge 1 commit into
linuxdeepin:masterfrom
18202781743:feat/dconfig-private-permission

Conversation

@18202781743

@18202781743 18202781743 commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

fix: change cross-app accessed dconfig items from private to public

变更说明

  • 将 dde-app-services 中跨应用可访问的 dconfig 配置项权限由 private 改为 public
  • 这些配置项会被多个 app 使用,私有权限会导致其他应用读取失败

具体修改的配置项

技术方案简述

  • 运用〖限制_AM_Identity〗接口(QDBusMethod)
  • setErrorQDBusInterface on dde-dconfig-daemon
  • cross-bus: AMIdentity::fingerprint = "org.deepin.dde.appearance"
  • fallback: AMIdentity::fingerprint = getProcessNameByPid(pid) || getUidByPid(uid)

关联的其他 PR

Summary by Sourcery

Enforce app ID–based visibility checks for dconfig items and introduce an AppIdResolver utility for resolving caller identities from DBus services, including build wiring.

Enhancements:

  • Add app ID–based private/public visibility permission checks to dconfig access operations.
  • Introduce AppIdResolver to resolve standardized app IDs using Application Manager or /proc as a fallback, with caching.
  • Wire AppIdResolver into DSGConfigServer/DSGConfigConn and derive config ownership appId from config metadata paths.

Build:

  • Register new appidresolver sources and headers in the dde-dconfig-daemon CMake build.

- Add AppIdResolver class to resolve caller appId via AM Identify + /proc fallback
- Add hasPermissionByVisibility() to enforce private config access control
- Add getCallerAppId() for standard appId resolution (separate from getAppid() which returns process path)
- Add parseConfigAppId() to extract config owner appId from file path
- Insert visibility checks in value(), setValue(), reset(), isDefaultValue()
- Return AccessDenied DBus error when non-owner appId accesses private config
- Use mutable members to avoid const_cast in const methods
- Use RAII for libdbus-1 resource management in identifyByAM()
- Connect to user session bus via dbus_connection_open_private for cross-bus AM access

PMS: BUG-000000

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry @18202781743, you have reached your weekly rate limit of 500000 diff characters.

Please try again later or upgrade to continue using Sourcery

@deepin-ci-robot

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: 18202781743

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@sourcery-ai

sourcery-ai Bot commented Jul 28, 2026

Copy link
Copy Markdown

Reviewer's Guide

Implements appId-aware visibility enforcement for dconfig items by introducing an AppIdResolver, wiring it into the dconfig daemon connection/server, and adding a new visibility-based permission check around all DConfig operations. This allows cross-app public items while restricting private items to their owning appId.

Sequence diagram for visibility-based dconfig access control

sequenceDiagram
    participant ClientApp
    participant DBus
    participant DSGConfigConn
    participant AppIdResolver

    ClientApp->>DBus: call setValue(key, value)
    DBus->>DSGConfigConn: setValue(key, value)
    DSGConfigConn->>DSGConfigConn: hasPermissionByUid(key)
    DSGConfigConn->>DSGConfigConn: hasPermissionByVisibility(key)
    alt meta visibility is Public
        DSGConfigConn-->>DSGConfigConn: allow access
    else meta visibility is Private
        DSGConfigConn->>DSGConfigConn: getCallerAppId()
        DSGConfigConn->>AppIdResolver: resolveAppId(service, pid, uid)
        AppIdResolver-->>DSGConfigConn: callerAppId
        alt callerAppId == configAppId
            DSGConfigConn-->>DSGConfigConn: allow access
        else callerAppId != configAppId
            DSGConfigConn-->>DBus: sendErrorReply(AccessDenied,...)
        end
    end
    note over DSGConfigConn: On allow, continues to setValue
Loading

File-Level Changes

Change Details Files
Introduce AppIdResolver to derive a canonical appId for DBus callers using AM Identify with /proc fallback and cache results per DBus service.
  • Added AppIdResolver QObject with resolveAppId, identifyByAM (libdbus pidfd-based Identify call), and fallbackFromProc helpers.
  • Implemented per-service appId caching and cache invalidation hook.
  • Integrated logging and platform-conditional (Q_OS_LINUX) behavior for session bus connection and /proc inspection.
dconfig-center/dde-dconfig-daemon/appidresolver.h
dconfig-center/dde-dconfig-daemon/appidresolver.cpp
Wire AppIdResolver into DSGConfigServer and connections to derive config file appId and inject the resolver into each connection instance.
  • Extended DSGConfigServer to own an AppIdResolver instance and expose an accessor.
  • In acquireManagerV2, parse the config appId from the meta file path and set both config appId and resolver on the connection.
  • Implemented parseConfigAppId using DConfigMeta::genericMetaDirs and getMetaConfigureId to discover appId based on file layout.
  • Updated CMake lists to compile and install the new resolver sources and headers.
dconfig-center/dde-dconfig-daemon/dconfigserver.h
dconfig-center/dde-dconfig-daemon/dconfigserver.cpp
dconfig-center/dde-dconfig-daemon/src.cmake
Add appId-based visibility permission checks in DSGConfigConn so private dconfig items are only accessible by the owning appId while public/generic items remain accessible.
  • Added setters for config appId and AppIdResolver on DSGConfigConn and extended internal state to cache caller appId and last service.
  • Introduced getCallerAppId to resolve and cache the caller's standard appId via AppIdResolver, using DBus service/pid/uid.
  • Implemented hasPermissionByVisibility to enforce DConfigFile::visibility rules: allow public/generic configs, restrict private items to matching appId, and send QDBusError::AccessDenied on mismatch.
  • Hooked hasPermissionByVisibility into value, setValue, reset, and isDefaultValue to enforce visibility at all critical access points.
  • Documented behavior and adjusted getAppid caching to be non-const-mutable friendly.
dconfig-center/dde-dconfig-daemon/dconfigconn.h
dconfig-center/dde-dconfig-daemon/dconfigconn.cpp

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@deepin-ci-robot

Copy link
Copy Markdown

deepin pr auto review

★ 总体评分:40分

■ 【总体评价】

代码实现了基于visibility的配置项权限控制机制,但存在严重的编译期依赖风险和通过伪造进程路径绕过权限的安全漏洞。
逻辑设计因未知接口缺失可能导致编译失败,且fallback机制存在高危权限绕过风险扣60分。

■ 【详细分析】

  • 1.语法逻辑(存在严重错误)✕

DSGConfigServer::parseConfigAppId 中使用了 DConfigMeta::genericMetaDirsConfigureId 结构体,在 DSGConfigConn::hasPermissionByVisibility 中调用了 meta()->visibility(key)DConfigFile::Public,如果底层库 dtkcore 未提供这些接口,将直接导致编译失败。
潜在问题:编译期依赖未确认导致构建失败;getCallerAppIdm_lastAppIdService 缓存逻辑在 service 异常断开重连时可能未及时更新。
建议:确认 dtkcore 版本是否支持所需接口,如果不支持需自行实现相关解析逻辑;在 clearCache 中增加对 service 重置的监听。

  • 2.代码质量(良好)✓

代码结构清晰,新增了 AppIdResolver 类专门处理 appId 解析,职责单一。在 identifyByAM 中正确使用了 RAII 机制管理 DBusErrorDBusConnectionDBusMessage 资源,避免了内存泄漏。注释详尽,解释了跨总线连接和 fallback 机制的原因。
潜在问题:fallbackFromProc 中正则表达式编译为 static,虽然性能友好但需注意线程安全(此处在单线程或加锁环境下安全)。
建议:保持现有代码风格,可考虑将正则表达式提取为类成员常量以提升可读性。

  • 3.代码性能(存在性能问题)✕

DSGConfigServer::parseConfigAppId 在每次调用 acquireManagerV2 时都会使用 QDirIterator 递归遍历所有 meta 目录查找配置文件,这在配置文件较多时会带来明显的磁盘 I/O 开销。
潜在问题:频繁的目录遍历可能导致 D-Bus 调用延迟,影响系统配置读写性能。
建议:在 DSGConfigServer 中缓存 name + subpathconfigAppId 的映射关系,避免每次 acquireManagerV2 都进行文件系统扫描。

  • 4.代码安全(存在 1 个安全漏洞)✕

漏洞对比统计:新增漏洞 1 个,减少漏洞 0 个,持平 0 个
代码通过 /proc 或 AM 获取调用方 appId 进行权限校验,但 fallback 机制极易被伪造,导致 private 配置项被未授权访问,攻击面为本地任意进程。

  • 安全漏洞1【高危】:权限绕过 在 AppIdResolver::fallbackFromProc 中,当 AM 服务不可用时,通过读取 /proc/{pid}/exe 获取进程路径并格式化为 appId。攻击者可以将恶意程序放置在特定路径(如 /usr/bin/org.deepin.dde.control-center)或通过软链接伪造进程路径,从而骗过 hasPermissionByVisibility 中的 callerAppId == configAppId 检查,越权读写 private 配置项,可能导致敏感配置泄露或被篡改。 ——非常重要

  • 建议:废弃基于 /proc 路径的 fallback 机制,或引入额外的安全校验(如检查文件的数字签名、SELinux/AppArmor 上下文)。如果必须保留 fallback,应仅用于非敏感的 public 配置,对于 private 配置在 AM 不可用时直接拒绝访问。

■ 【改进建议代码示例】

// 修复方案:移除不安全的 fallback,并在 AM 不可用时拒绝 private 访问
// appidresolver.cpp
QString AppIdResolver::resolveAppId(const ConnServiceName &service, uint pid, uint uid)
{
    if (m_cache.contains(service)) {
        return m_cache.value(service);
    }

    QString appId;
#ifdef Q_OS_LINUX
    appId = identifyByAM(pid, uid);
    // 移除 fallbackFromProc 调用,防止伪造
#else
    appId = QString();
#endif

    if (!appId.isEmpty()) {
        m_cache.insert(service, appId);
    }
    return appId;
}

// dconfigconn.cpp
bool DSGConfigConn::hasPermissionByVisibility(const QString &key) const
{
    if (!calledFromDBus()) 
        return true;

    if (meta()->visibility(key) == DConfigFile::Public) 
        return true;

    const QString &callerAppId = getCallerAppId();
    const QString &configAppId = m_configAppId;

    if (configAppId.isEmpty() || configAppId == VirtualInterAppId) 
        return true;

    // 如果 callerAppId 为空,说明 AM 不可用且无安全 fallback,直接拒绝
    if (callerAppId.isEmpty()) {
        sendErrorReply(QDBusError::AccessDenied, "Cannot resolve caller appId, access denied for private config.");
        return false;
    }

    if (callerAppId == configAppId) 
        return true;

    QString errorMsg = QString("[%1] No permission to access private config item [%2] in [%3], owner appId is [%4].")
                           .arg(callerAppId).arg(key).arg(m_key).arg(configAppId);
    sendErrorReply(QDBusError::AccessDenied, errorMsg);
    return false;
}

// dconfigserver.cpp
// 建议增加缓存以优化性能
QString DSGConfigServer::parseConfigAppId(const QString &name, const QString &subpath)
{
    static QHash<QString, QString> s_appIdCache;
    QString cacheKey = name + "/" + subpath;
    if (s_appIdCache.contains(cacheKey)) {
        return s_appIdCache.value(cacheKey);
    }

    const QStringList metaDirs = DConfigMeta::genericMetaDirs(m_localPrefix);
    for (const QString &dir : metaDirs) {
        QDirIterator it(dir, QStringList() << name + ".json", 
                       QDir::Files | QDir::Readable, QDirIterator::Subdirectories);
        while (it.hasNext()) {
            QString path = it.next();
            ConfigureId configId = getMetaConfigureId(path);
            if (configId.resource == name && configId.subpath == subpath) {
                if (!configId.appid.isEmpty()) {
                    s_appIdCache.insert(cacheKey, configId.appid);
                    return configId.appid;
                }
            }
        }
    }
    
    s_appIdCache.insert(cacheKey, QString());
    return QString();
}

@deepin-ci-robot

Copy link
Copy Markdown

@18202781743: The following test failed, say /retest to rerun all failed tests or /retest-required to rerun all mandatory failed tests:

Test name Commit Details Required Rerun command
github-pr-review-ci 83faf63 link true /test github-pr-review-ci

Full PR test history. Your PR dashboard.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes/test-infra repository. I understand the commands that are listed here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants