From eeca10776a1fd0e1173e27f483f4004859c8b33a Mon Sep 17 00:00:00 2001 From: deepin-ci-robot Date: Thu, 11 Jun 2026 11:14:35 +0000 Subject: [PATCH] sync: from linuxdeepin/dde-session-shell Synchronize source files from linuxdeepin/dde-session-shell. Source-pull-request: https://github.com/linuxdeepin/dde-session-shell/pull/68 --- CMakeLists.txt | 9 +- files/dde-lock-loader-wrapper | 29 ++++ files/permission-interfaces/auth.json | 14 ++ resources.qrc | 1 + src/app/dde-lock.cpp | 12 +- src/dde-lock/securityloaderhelper.cpp | 217 ++++++++++++++++++++++++++ src/dde-lock/securityloaderhelper.h | 38 +++++ 7 files changed, 318 insertions(+), 2 deletions(-) create mode 100644 files/dde-lock-loader-wrapper create mode 100644 files/permission-interfaces/auth.json create mode 100644 src/dde-lock/securityloaderhelper.cpp create mode 100644 src/dde-lock/securityloaderhelper.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 1fdcafc39..a47611df5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -252,6 +252,7 @@ set(LOCK_SRCS src/dde-lock/lockframe.cpp src/dde-lock/lockworker.cpp src/dde-lock/updateworker.cpp + src/dde-lock/securityloaderhelper.cpp src/dde-lock/dbus/dbuslockagent.cpp src/dde-lock/dbus/dbuslockfrontservice.cpp src/dde-lock/dbus/dbusshutdownagent.cpp @@ -426,7 +427,13 @@ else () # snipe install(PROGRAMS ${SCRIPTS} DESTINATION ${CMAKE_INSTALL_SYSCONFDIR}/deepin/greeters.d) endif () -install(TARGETS dde-lock lightdm-deepin-greeter greeter-display-setting DESTINATION ${CMAKE_INSTALL_BINDIR}) +# dde-lock binary installed to libexec/deepin for security-loader +install(TARGETS dde-lock DESTINATION ${CMAKE_INSTALL_LIBEXECDIR}/deepin) +install(TARGETS lightdm-deepin-greeter greeter-display-setting DESTINATION ${CMAKE_INSTALL_BINDIR}) +# Install loader wrapper script as /usr/bin/dde-lock +install(PROGRAMS files/dde-lock-loader-wrapper DESTINATION ${CMAKE_INSTALL_BINDIR} + RENAME dde-lock + PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) # 指定greeter,优先级最低,允许被其他应用以更高的配置文件覆盖 install(FILES files/50-deepin.conf DESTINATION ${CMAKE_INSTALL_DATADIR}/lightdm/lightdm.conf.d) diff --git a/files/dde-lock-loader-wrapper b/files/dde-lock-loader-wrapper new file mode 100644 index 000000000..e067c4474 --- /dev/null +++ b/files/dde-lock-loader-wrapper @@ -0,0 +1,29 @@ +#!/bin/bash +# dde-lock wrapper for deepin-security-loader +# Launches dde-lock through security loader to get authorization +# for calling protected system services without polkit popup. + +REAL_BINARY="/usr/libexec/deepin/dde-lock" +LOADER="/usr/bin/deepin-security-loader" +LOADER_EXEC="/usr/bin/deepin-security-loader-exec" + +# Log to systemd journal with tag "dde-lock" +log_to_journal() { + logger -t dde-lock -p user.info "$1" +} + +# Log startup +log_to_journal "dde-lock launched with args: $*" + +# Check if loader is available and has proper capabilities +if [ -x "$LOADER" ] && [ -x "$LOADER_EXEC" ]; then + if getcap "$LOADER_EXEC" 2>/dev/null | grep -q cap_setgid; then + # Launch via loader with authorization groups + log_to_journal "Using deepin-security-loader with authorization groups" + exec "$LOADER" --group deepin-daemon -- "$REAL_BINARY" "$@" + fi +fi + +# Fallback: direct launch without loader (no polkit-free authorization) +log_to_journal "Fallback: launching directly without security loader" +exec "$REAL_BINARY" "$@" diff --git a/files/permission-interfaces/auth.json b/files/permission-interfaces/auth.json new file mode 100644 index 000000000..77f114f11 --- /dev/null +++ b/files/permission-interfaces/auth.json @@ -0,0 +1,14 @@ +{ + "DestList": [ + { + "DbusName": "org.deepin.dde.Lastore1", + "DbusPath": "/org/deepin/dde/Lastore1", + "DbusInterface": "org.deepin.dde.Lastore1.Manager" + }, + { + "DbusName": "org.deepin.dde.Authenticate1", + "DbusPath": "/org/deepin/dde/Authenticate1", + "DbusInterface": "org.deepin.dde.Authenticate1" + } + ] +} diff --git a/resources.qrc b/resources.qrc index f5ebdfaea..1bd61b33c 100644 --- a/resources.qrc +++ b/resources.qrc @@ -1,5 +1,6 @@ + files/permission-interfaces/auth.json misc/images/caps_lock.svg misc/images/login_check.svg misc/images/login_lock.svg diff --git a/src/app/dde-lock.cpp b/src/app/dde-lock.cpp index 1ccb677ac..62ba2b936 100644 --- a/src/app/dde-lock.cpp +++ b/src/app/dde-lock.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2015 - 2022 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2015 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later @@ -16,6 +16,7 @@ #include "sessionbasemodel.h" #include "warningcontent.h" #include "plugin_manager.h" +#include "securityloaderhelper.h" #include "dbusconstant.h" #include @@ -109,9 +110,18 @@ int main(int argc, char *argv[]) QCommandLineOption quickLoginProcess(QStringList() << "q" << "quicklogin", "show for quick login"); cmdParser.addOption(quickLoginProcess); + QCommandLineOption fd1Opt("fd1", "fd1 from security loader", "fd1"); + cmdParser.addOption(fd1Opt); + QCommandLineOption fd2Opt("fd2", "fd2 from security loader", "fd2"); + cmdParser.addOption(fd2Opt); + QStringList xddd = app->arguments(); cmdParser.process(*app); + int fd1 = cmdParser.isSet(fd1Opt) ? cmdParser.value(fd1Opt).toInt() : -1; + int fd2 = cmdParser.isSet(fd2Opt) ? cmdParser.value(fd2Opt).toInt() : -1; + SecurityLoaderHelper::instance().doSecurityLoader(fd1, fd2); + bool runDaemon = cmdParser.isSet(backend); bool showUserList = cmdParser.isSet(switchUser); bool showShutdown = cmdParser.isSet(shutdown); diff --git a/src/dde-lock/securityloaderhelper.cpp b/src/dde-lock/securityloaderhelper.cpp new file mode 100644 index 000000000..4f997f514 --- /dev/null +++ b/src/dde-lock/securityloaderhelper.cpp @@ -0,0 +1,217 @@ +// SPDX-FileCopyrightText: 2024 - 2026 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "securityloaderhelper.h" +#include "dbusconstant.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +Q_LOGGING_CATEGORY(secLoader, "org.deepin.dde.lock.securityloader") + +const QString SecurityLoaderHelper::DEFAULT_CONFIG_PATH = ":/files/permission-interfaces/auth.json"; + +SecurityLoaderHelper::SecurityLoaderHelper(QObject *parent) + : QObject(parent) +{ +} + +SecurityLoaderHelper::~SecurityLoaderHelper() {} + +SecurityLoaderHelper &SecurityLoaderHelper::instance() +{ + static SecurityLoaderHelper instance; + return instance; +} + +void SecurityLoaderHelper::doSecurityLoader(int fd1, int fd2) +{ + if (fd1 < 0 || fd2 < 0) { + qCWarning(secLoader) << "Not loaded by loader, skipping handshake"; + return; + } + + qCInfo(secLoader) << "Detected loader injection: fd1=" << fd1 << "fd2=" << fd2; + + loadConfig(); + // appendCurrentUserAccountsUserDest(); + if (!performHandshake(fd1, fd2)) { + qCWarning(secLoader) << "Security loader handshake failed"; + } +} + +void SecurityLoaderHelper::loadConfig(const QString &configPath) +{ + QString path = configPath.isEmpty() ? DEFAULT_CONFIG_PATH : configPath; + + qCInfo(secLoader) << "Loading permission config from:" << path; + + m_destList = QJsonArray(); + parseJsonFile(path); + + qCInfo(secLoader) << "Loaded" << m_destList.size() << "D-Bus interfaces to authorize"; +} + +void SecurityLoaderHelper::parseJsonFile(const QString &filePath) +{ + QFile file(filePath); + if (!file.open(QIODevice::ReadOnly)) { + qCWarning(secLoader) << "Cannot open file:" << filePath; + return; + } + + QJsonParseError error; + QJsonDocument doc = QJsonDocument::fromJson(file.readAll(), &error); + file.close(); + + if (error.error != QJsonParseError::NoError) { + qCWarning(secLoader) << "JSON parse error in" << filePath << ":" << error.errorString(); + return; + } + + QJsonObject root = doc.object(); + if (!root.contains("DestList") || !root["DestList"].isArray()) { + qCWarning(secLoader) << "Invalid config format in" << filePath << ": missing DestList"; + return; + } + + for (const auto &item : root["DestList"].toArray()) { + appendDest(item.toObject()); + } +} + +void SecurityLoaderHelper::appendDest(const QJsonObject &dest) +{ + const QString dbusName = dest["DbusName"].toString(); + const QString dbusPath = dest["DbusPath"].toString(); + const QString dbusInterface = dest["DbusInterface"].toString(); + + if (dbusName.isEmpty() || dbusPath.isEmpty() || dbusInterface.isEmpty()) { + qCWarning(secLoader) << "Skip invalid D-Bus destination:" << dest; + return; + } + + for (const auto &existing : m_destList) { + const QJsonObject current = existing.toObject(); + if (current["DbusName"] == dbusName && + current["DbusPath"] == dbusPath && + current["DbusInterface"] == dbusInterface) { + return; + } + } + + m_destList.append(dest); + qCInfo(secLoader) << " Added:" << dbusName << dbusPath << dbusInterface; +} + +void SecurityLoaderHelper::appendCurrentUserAccountsUserDest() +{ + const QString userPath = currentUserAccountsPath(); + if (userPath.isEmpty()) { + qCWarning(secLoader) << "Cannot resolve current user's Accounts.User path"; + return; + } + + QJsonObject dest; + dest["DbusName"] = DSS_DBUS::accountsService; + dest["DbusPath"] = userPath; + dest["DbusInterface"] = DSS_DBUS::accountsUserInterface; + appendDest(dest); +} + +QString SecurityLoaderHelper::currentUserAccountsPath() const +{ + QDBusConnection systemBus = QDBusConnection::systemBus(); + if (!systemBus.isConnected()) { + qCWarning(secLoader) << "Cannot connect to system bus when resolving current user path"; + return {}; + } + + QDBusInterface accountsInterface(DSS_DBUS::accountsService, DSS_DBUS::accountsPath, DSS_DBUS::accountsService, systemBus); + if (!accountsInterface.isValid()) { + qCWarning(secLoader) << "Accounts interface invalid when resolving current user path:" + << accountsInterface.lastError().message(); + return {}; + } + + QDBusReply reply = accountsInterface.call(QStringLiteral("FindUserById"), QString::number(getuid())); + if (!reply.isValid()) { + qCWarning(secLoader) << "FindUserById failed when resolving current user path:" + << reply.error().message(); + return {}; + } + + return reply.value(); +} + +bool SecurityLoaderHelper::performHandshake(int fd1, int fd2) +{ + if (m_destList.isEmpty()) { + qCInfo(secLoader) << "No D-Bus interfaces loaded, skipping handshake"; + return true; + } + + qCInfo(secLoader) << "Performing loader handshake..."; + + QDBusConnection systemBus = QDBusConnection::systemBus(); + if (!systemBus.isConnected()) { + qCWarning(secLoader) << "Cannot connect to system bus"; + return false; + } + // 偶现dbus的 unique name 发生变化,Qt的systemBus.baseService()实际上并未把dbus注册到总线上,要调用一下方法才行 + systemBus.interface()->isServiceRegistered("org.freedesktop.DBus"); + QString uniqueName = systemBus.baseService(); + qCInfo(secLoader) << "System Bus UniqueName:" << uniqueName; + + QJsonObject request; + request["UniqueName"] = uniqueName; + request["DestList"] = m_destList; + + QJsonDocument doc(request); + QByteArray jsonData = doc.toJson(QJsonDocument::Compact); + + qCInfo(secLoader) << "Sending request with" << m_destList.size() << "interfaces"; + + QFile fd1File; + if (!fd1File.open(fd1, QIODevice::WriteOnly)) { + qCWarning(secLoader) << "Cannot open fd1 for writing"; + return false; + } + fd1File.write(jsonData); + fd1File.close(); + qCInfo(secLoader) << "Sent authorization request to loader"; + + QFile fd2File; + if (!fd2File.open(fd2, QIODevice::ReadOnly)) { + qCWarning(secLoader) << "Cannot open fd2 for reading"; + return false; + } + + QByteArray response = fd2File.readAll(); + fd2File.close(); + + QJsonParseError parseError; + QJsonDocument responseDoc = QJsonDocument::fromJson(response, &parseError); + if (parseError.error != QJsonParseError::NoError) { + qCWarning(secLoader) << "Invalid JSON response from loader:" << parseError.errorString(); + return false; + } + + QJsonObject result = responseDoc.object(); + if (result["Result"].toBool()) { + qCInfo(secLoader) << "Loader handshake completed successfully"; + return true; + } else { + qCWarning(secLoader) << "Loader authorization response:" << result["Message"].toString(); + return false; + } +} diff --git a/src/dde-lock/securityloaderhelper.h b/src/dde-lock/securityloaderhelper.h new file mode 100644 index 000000000..c2bf6b41e --- /dev/null +++ b/src/dde-lock/securityloaderhelper.h @@ -0,0 +1,38 @@ +// SPDX-FileCopyrightText: 2024 - 2026 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef SECURITYLOADERHELPER_H +#define SECURITYLOADERHELPER_H + +#include +#include + +class SecurityLoaderHelper : public QObject +{ + Q_OBJECT + +public: + static SecurityLoaderHelper &instance(); + void doSecurityLoader(int fd1, int fd2); + +private: + explicit SecurityLoaderHelper(QObject *parent = nullptr); + ~SecurityLoaderHelper(); + + SecurityLoaderHelper(const SecurityLoaderHelper &) = delete; + SecurityLoaderHelper &operator=(const SecurityLoaderHelper &) = delete; + + void loadConfig(const QString &configPath = QString()); + void parseJsonFile(const QString &filePath); + void appendDest(const QJsonObject &dest); + void appendCurrentUserAccountsUserDest(); + QString currentUserAccountsPath() const; + bool performHandshake(int fd1, int fd2); + +private: + QJsonArray m_destList; + static const QString DEFAULT_CONFIG_PATH; +}; + +#endif // SECURITYLOADERHELPER_H