From d078f85cce5b7bbc2edbdd31ed7022ac6ad02d9a Mon Sep 17 00:00:00 2001 From: liujinchang Date: Tue, 1 Sep 2026 13:21:23 +0800 Subject: [PATCH] fix(data-transfer): launch via deepin-security-loader to avoid polkit prompt on app installation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add initSecurityLoaderReport() in main.cpp: when started by deepin-security-loader (--fd1/--fd2 passed), report the system bus unique name and the com.deepin.lastore service list to the loader in JSON so it registers the lastore allow-caller whitelist on our behalf, making InstallPackage free of polkit authentication - Call the report before any early-exit branch (including single-instance failure), otherwise the loader times out and kills the process, which shows up as a crash on startup - Add launcher script /usr/bin/deepin-data-transfer: start the real binary /usr/libexec/deepin/deepin-data-transfer through deepin-security-loader with the deepin-daemon group when available (dbus >= 1.12.20.17-deepin1), otherwise fall back to direct launch with the historical polkit behavior - Enable Qt DBus component/linking and install the real binary to libexec/deepin with the launcher taking over /usr/bin - Update debian packaging: install the libexec binary and add Recommends: deepin-security-loader 修复(data-transfer): 经 deepin-security-loader 启动以避免安装应用时弹出 polkit 鉴权框 - main.cpp 新增 initSecurityLoaderReport():经 deepin-security-loader 启动(带 --fd1/--fd2 参数)时,将 system bus 唯一连接名和 com.deepin.lastore 服务列表以 JSON 回报给 loader,由其代为注册 lastore 的 allow-caller 白名单,使 InstallPackage 等接口免 polkit 鉴权 - 回报调用置于任何提前退出分支(含单例失败)之前,否则 loader 会超时结束子进程,表现为启动闪退 - 新增启动脚本 /usr/bin/deepin-data-transfer:环境满足(dbus >= 1.12.20.17-deepin1)时经 deepin-security-loader 附加 deepin-daemon 组启动真实二进制 /usr/libexec/deepin/deepin-data-transfer,否则回退直接启动,鉴权行为与历史一致 - 启用 Qt DBus 组件与链接,真实二进制安装至 libexec/deepin,/usr/bin 由启动脚本接管 - 更新 debian 打包:安装 libexec 二进制并增加 Recommends: deepin-security-loader Log: 数据迁移工具经 deepin-security-loader 附加 deepin-daemon 组启动并回报 bus 连接名,实现 lastore 安装接口免鉴权,修复迁移三方应用时弹出密码认证框的问题 Bug: https://pms.uniontech.com/bug-view-375325.html --- debian/control | 1 + debian/deepin-data-transfer.install | 1 + src/apps/data-transfer/CMakeLists.txt | 12 ++- src/apps/data-transfer/main.cpp | 102 +++++++++++++++++- .../res/linux/deepin-data-transfer.sh | 27 +++++ 5 files changed, 140 insertions(+), 3 deletions(-) create mode 100755 src/apps/data-transfer/res/linux/deepin-data-transfer.sh diff --git a/debian/control b/debian/control index 56f79c01c..9e303b9d2 100644 --- a/debian/control +++ b/debian/control @@ -43,6 +43,7 @@ Description: Deepin cooperation backend Package: deepin-data-transfer Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends}, dde-cooperation-daemon +Recommends: deepin-security-loader Description: deepin data transfer frontend Deepin Desktop Environment (DDE) - data transfer frontend. diff --git a/debian/deepin-data-transfer.install b/debian/deepin-data-transfer.install index 1f7bb88de..462d99825 100644 --- a/debian/deepin-data-transfer.install +++ b/debian/deepin-data-transfer.install @@ -1,4 +1,5 @@ usr/bin/deepin-data-transfer +usr/libexec/deepin/deepin-data-transfer usr/lib/*/dde-cooperation/plugins/data-transfer/*.so* usr/share/deepin-data-transfer/translations/*.qm usr/share/icons/hicolor/scalable/apps/deepin-data-transfer.svg diff --git a/src/apps/data-transfer/CMakeLists.txt b/src/apps/data-transfer/CMakeLists.txt index 3730bb9bf..9d56bcda0 100644 --- a/src/apps/data-transfer/CMakeLists.txt +++ b/src/apps/data-transfer/CMakeLists.txt @@ -21,7 +21,7 @@ endif() if (WIN32) set(qt_required_components Core Gui Network Widgets Sql WinExtras) else() - set(qt_required_components Core Gui Network Widgets) + set(qt_required_components Core Gui Network Widgets DBus) endif() find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS ${qt_required_components}) find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS ${qt_required_components}) @@ -76,11 +76,19 @@ target_link_libraries(${PROJ_NAME} if (CMAKE_SYSTEM MATCHES "Linux") target_link_libraries(${PROJ_NAME} PRIVATE + Qt${QT_VERSION_MAJOR}::DBus ${DtkWidget_LIBRARIES} ) endif() -install(TARGETS ${PROJ_NAME} DESTINATION bin) +if (WIN32) + install(TARGETS ${PROJ_NAME} DESTINATION bin) +else() + # 真实二进制安装到 libexec,/usr/bin 由启动脚本占用(见下), + # 以便经 deepin-security-loader 附加 deepin-daemon 组启动 + install(TARGETS ${PROJ_NAME} DESTINATION libexec/deepin) + install(PROGRAMS res/linux/deepin-data-transfer.sh DESTINATION bin RENAME deepin-data-transfer) +endif() # translation files TRANSLATION_GENERATE(QM_FILES ${CMAKE_SOURCE_DIR}/translations/${PROJ_NAME}) diff --git a/src/apps/data-transfer/main.cpp b/src/apps/data-transfer/main.cpp index 4d598a7fd..e7aa4a375 100644 --- a/src/apps/data-transfer/main.cpp +++ b/src/apps/data-transfer/main.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2023-2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later @@ -10,7 +10,15 @@ #include #include +#include #include +#include +#include +#include +#include +#include + +#include static constexpr char kPluginInterface[] { "org.deepin.plugin.datatransfer" }; static constexpr char kPluginCore[] { "data-transfer-core" }; @@ -20,6 +28,91 @@ static constexpr char kPluginCore[] { "data-transfer-core" }; #define LIB_FILE_NAME(lib_name) QString("lib%1.so").arg(#lib_name) #endif +#ifdef linux +// deepin-security-loader 启动协议(与 deepin-home-appstore-client 行为一致): +// 进程经 loader 附加 deepin-daemon 组启动时,loader 会附加 --fd1/--fd2 命令行参数, +// 分别传入一对管道:子进程把自身 system bus 唯一连接名和需要免鉴权的服务列表 +// 以 JSON 写入 fd1,loader 收到后以组身份代为调用各服务的 SetAllowCaller 白名单 +// 注册,之后 InstallPackage 等接口免 polkit 鉴权。 +// 子进程若不回报,loader 会超时退出并结束子进程(表现为启动闪退), +// 因此经 loader 启动时必须尽早完成回报;直接启动(无 --fd1)则跳过。 +static void initSecurityLoaderReport() +{ + const QStringList args = QCoreApplication::arguments(); + int fd1 = -1, fd2 = -1; + for (int i = 1; i + 1 < args.count(); ++i) { + if (args.at(i) != QLatin1String("--fd1") && args.at(i) != QLatin1String("--fd2")) + continue; + // toInt() 解析失败会返回 0(即 stdin),必须校验,避免误把标准输入当作管道 fd + bool ok = false; + const int fd = args.at(i + 1).toInt(&ok); + if (!ok) + continue; + if (args.at(i) == QLatin1String("--fd1")) + fd1 = fd; + else + fd2 = fd; + } + if (fd1 < 0) + return; // 未经 security-loader 启动,无需回报 + + // 管道 fd 保持打开(loader 侧不依赖 EOF 读取,与参照实现 appstore-client 一致), + // 但设置 FD_CLOEXEC,避免回报管道泄漏给后续 QProcess 派生的子进程 + if (fcntl(fd1, F_SETFD, FD_CLOEXEC) < 0) + qWarning() << "failed to set FD_CLOEXEC on fd" << fd1; + if (fd2 >= 0 && fcntl(fd2, F_SETFD, FD_CLOEXEC) < 0) + qWarning() << "failed to set FD_CLOEXEC on fd" << fd2; + + const QString uniqueName = QDBusConnection::systemBus().baseService(); + if (uniqueName.isEmpty()) { + qWarning() << "system bus not connected, skip allow-caller report"; + return; + } + + QJsonObject dest; + dest.insert(QStringLiteral("DbusName"), QStringLiteral("com.deepin.lastore")); + dest.insert(QStringLiteral("DbusPath"), QStringLiteral("/com/deepin/lastore")); + dest.insert(QStringLiteral("DbusInterface"), QStringLiteral("com.deepin.lastore.Manager")); + QJsonObject msg; + msg.insert(QStringLiteral("UniqueName"), uniqueName); + msg.insert(QStringLiteral("DestList"), QJsonArray { dest }); + + QFile reportPipe; + if (!reportPipe.open(fd1, QIODevice::WriteOnly, QFileDevice::DontCloseHandle)) { + qWarning() << "failed to open security-loader report pipe:" << fd1; + return; + } + reportPipe.write(QJsonDocument(msg).toJson(QJsonDocument::Compact)); + reportPipe.flush(); + reportPipe.close(); + qInfo() << "allow-caller report sent:" << uniqueName; + + // fd2 可读回 {"Result":...,"ErrorMsg":...}。管道为非阻塞模式,loader 处理需要 + // 一次 dbus 往返,轮询等待一小段时间用于日志,失败不影响主流程; + // 最多等 500ms——该调用位于单例检查之前,不应拖慢启动与窗口激活 + if (fd2 >= 0) { + QFile resultPipe; + if (resultPipe.open(fd2, QIODevice::ReadOnly, QFileDevice::DontCloseHandle)) { + QByteArray resultData; + for (int i = 0; i < 10 && resultData.isEmpty(); ++i) { + resultData = resultPipe.readAll(); + if (resultData.isEmpty()) + QThread::msleep(50); + } + resultPipe.close(); + if (resultData.isEmpty()) { + qWarning() << "no allow-caller result from security-loader within 500ms"; + } else { + const QJsonObject result = QJsonDocument::fromJson(resultData).object(); + qInfo() << "allow-caller register result:" + << result.value(QLatin1String("Result")).toBool() + << result.value(QLatin1String("ErrorMsg")).toString(); + } + } + } +} +#endif + static bool loadPlugins() { QStringList pluginsDirs; @@ -92,6 +185,13 @@ int main(int argc, char *argv[]) #endif +#ifdef linux + // 必须先于任何可能退出的分支(含单例失败):经 security-loader 启动时若不回报 + // 连接名,loader 会超时并结束子进程,表现为启动闪退;该回报同时完成 lastore + // 免鉴权白名单注册,需早于插件里 SettingHelper 的第一次 InstallPackage + initSecurityLoaderReport(); +#endif + bool canSetSingle = app.setSingleInstance(app.applicationName()); if (!canSetSingle) { qInfo() << "single application is already running."; diff --git a/src/apps/data-transfer/res/linux/deepin-data-transfer.sh b/src/apps/data-transfer/res/linux/deepin-data-transfer.sh new file mode 100755 index 000000000..d10a9f0c8 --- /dev/null +++ b/src/apps/data-transfer/res/linux/deepin-data-transfer.sh @@ -0,0 +1,27 @@ +#!/bin/bash +# SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. +# +# SPDX-License-Identifier: GPL-3.0-or-later +# Deepin Data Transfer launcher +# 参照 deepin-home-appstore-client:满足条件时经 deepin-security-loader +# 附加 deepin-daemon 组启动,使 lastore 免鉴权接口调用可行; +# 否则直接启动,鉴权回退为系统 polkit 弹窗(等同历史行为)。 + +REAL_BIN=/usr/libexec/deepin/deepin-data-transfer + +# Check if /usr/bin/deepin-security-loader is executable and dbus version meets +# requirement(用 -x 而非 -f:exec 失败时 bash 会直接退出 127,无法走 || 回退, +# 须在 exec 前排除不可执行的 loader) +if [ -x "/usr/bin/deepin-security-loader" ]; then + # Check dbus version + if DBUS_VERSION=$(dpkg-query -W -f='${Version}' dbus 2>/dev/null); then + # Compare dbus version with 1.12.20.17-deepin1 + if dpkg --compare-versions "$DBUS_VERSION" ge "1.12.20.17-deepin1"; then + # Use security loader with deepin-daemon group + exec /usr/bin/deepin-security-loader --group deepin-daemon -- "$REAL_BIN" "$@" + fi + fi +fi + +# Fallback to direct launch +exec "$REAL_BIN" "$@"